From f3650c77c65769d5d6d2c9f873327f3a1ed52127 Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 30 Sep 2026 15:19:16 -0400 Subject: [PATCH 1/8] Add Kentucky LIHEAP regular heating assistance --- changelog.d/ky-liheap.added.md | 1 + .../additional_person_income_limit.yaml | 9 +++ .../ky/chfs/liheap/earned_income_age.yaml | 9 +++ .../states/ky/chfs/liheap/fpg_year_lag.yaml | 9 +++ .../states/ky/chfs/liheap/income_limit.yaml | 9 +++ .../ky/chfs/liheap/maximum_table_size.yaml | 9 +++ .../chfs/liheap/payment/coal_adjustment.yaml | 22 ++++++ .../liheap/payment/dollars_per_point.yaml | 9 +++ .../ky/chfs/liheap/payment/fuel_points.yaml | 31 ++++++++ .../ky/chfs/liheap/payment/income_bands.yaml | 9 +++ .../payment/income_points/subsidized.yaml | 26 +++++++ .../payment/income_points/unsubsidized.yaml | 26 +++++++ .../payment/maximum_household_points.yaml | 9 +++ .../payment/subsidized_housing_rate.yaml | 9 +++ .../chfs/liheap/unearned_income_sources.yaml | 23 ++++++ policyengine_us/programs.yaml | 9 ++- .../states/ky/chfs/liheap/integration.yaml | 38 ++++++++++ .../gov/states/ky/chfs/liheap/ky_liheap.yaml | 71 +++++++++++++++++++ .../ky/chfs/liheap/ky_liheap_income.yaml | 53 ++++++++++++++ .../liheap/ky_liheap_income_eligible.yaml | 17 +++++ .../gov/states/ky/chfs/liheap/ky_liheap.py | 51 +++++++++++++ .../ky/chfs/liheap/ky_liheap_eligible.py | 22 ++++++ .../states/ky/chfs/liheap/ky_liheap_fpg.py | 23 ++++++ .../states/ky/chfs/liheap/ky_liheap_income.py | 37 ++++++++++ .../chfs/liheap/ky_liheap_income_eligible.py | 30 ++++++++ 25 files changed, 560 insertions(+), 1 deletion(-) create mode 100644 changelog.d/ky-liheap.added.md create mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/additional_person_income_limit.yaml create mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/earned_income_age.yaml create mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/fpg_year_lag.yaml create mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/income_limit.yaml create mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/maximum_table_size.yaml create mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/coal_adjustment.yaml create mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/dollars_per_point.yaml create mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/fuel_points.yaml create mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_bands.yaml create mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/subsidized.yaml create mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/unsubsidized.yaml create mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/maximum_household_points.yaml create mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/subsidized_housing_rate.yaml create mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/unearned_income_sources.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/integration.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.yaml create mode 100644 policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap.py create mode 100644 policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_eligible.py create mode 100644 policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_fpg.py create mode 100644 policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income.py create mode 100644 policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.py diff --git a/changelog.d/ky-liheap.added.md b/changelog.d/ky-liheap.added.md new file mode 100644 index 00000000000..770c9542f64 --- /dev/null +++ b/changelog.d/ky-liheap.added.md @@ -0,0 +1 @@ +Add partial FY2026 Kentucky LIHEAP regular heating assistance using existing inputs. diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/additional_person_income_limit.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/additional_person_income_limit.yaml new file mode 100644 index 00000000000..25bb2d0e7c0 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/chfs/liheap/additional_person_income_limit.yaml @@ -0,0 +1,9 @@ +description: Kentucky adds this amount to its monthly income limit for each person beyond its published table under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 688 +metadata: + unit: currency-USD + label: Kentucky LIHEAP additional person income limit + reference: + - title: Kentucky FY2025-2026 LIHEAP fact sheet, income eligibility + href: https://www.mkcap.org/uploads/3/4/8/3/34834615/2025-2026-liheap-fact-sheet-v2.jpg diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/earned_income_age.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/earned_income_age.yaml new file mode 100644 index 00000000000..8a15009b9e0 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/chfs/liheap/earned_income_age.yaml @@ -0,0 +1,9 @@ +description: Kentucky counts earned income beginning at this age under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 18 +metadata: + unit: year + label: Kentucky LIHEAP earned income age + reference: + - title: Kentucky FY2026 state plan, Section 1.9 + href: https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/fpg_year_lag.yaml new file mode 100644 index 00000000000..6fcb96a386d --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/chfs/liheap/fpg_year_lag.yaml @@ -0,0 +1,9 @@ +description: Kentucky uses the federal poverty guidelines from this many years before the heating season ending year under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 1 +metadata: + unit: year + label: Kentucky LIHEAP fpg year lag + reference: + - title: Kentucky FY2025-2026 LIHEAP fact sheet, income eligibility + href: https://www.mkcap.org/uploads/3/4/8/3/34834615/2025-2026-liheap-fact-sheet-v2.jpg diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/income_limit.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/income_limit.yaml new file mode 100644 index 00000000000..e3d66882331 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/chfs/liheap/income_limit.yaml @@ -0,0 +1,9 @@ +description: Kentucky limits gross monthly household income to this share of the federal poverty guideline under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 1.5 +metadata: + unit: /1 + label: Kentucky LIHEAP income limit + reference: + - title: Kentucky FY2026 state plan, Section 2.1 + href: https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=8 diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/maximum_table_size.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/maximum_table_size.yaml new file mode 100644 index 00000000000..07e6c4ae4fe --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/chfs/liheap/maximum_table_size.yaml @@ -0,0 +1,9 @@ +description: Kentucky publishes monthly income limits through this household size under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 8 +metadata: + unit: person + label: Kentucky LIHEAP maximum table size + reference: + - title: Kentucky FY2025-2026 LIHEAP fact sheet, income eligibility + href: https://www.mkcap.org/uploads/3/4/8/3/34834615/2025-2026-liheap-fact-sheet-v2.jpg diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/coal_adjustment.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/coal_adjustment.yaml new file mode 100644 index 00000000000..018f7f2c56a --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/coal_adjustment.yaml @@ -0,0 +1,22 @@ +description: Kentucky adjusts coal heating-payment points by household size to reproduce its published Low Income Home Energy Assistance Program matrix. +brackets: + - threshold: + 2025-10-01: 0 + amount: + 2025-10-01: 0 + - threshold: + 2025-10-01: 2 + amount: + 2025-10-01: -1 + - threshold: + 2025-10-01: 3 + amount: + 2025-10-01: 0 +metadata: + type: single_amount + threshold_unit: person + amount_unit: /1 + label: Kentucky LIHEAP coal household-size adjustment + reference: + - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet, coal column + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/dollars_per_point.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/dollars_per_point.yaml new file mode 100644 index 00000000000..eec5df48131 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/dollars_per_point.yaml @@ -0,0 +1,9 @@ +description: Kentucky pays this amount per point before rounding up to whole dollars under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 16.66 +metadata: + unit: currency-USD + label: Kentucky LIHEAP dollars per point + reference: + - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/fuel_points.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/fuel_points.yaml new file mode 100644 index 00000000000..1121a3f23bc --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/fuel_points.yaml @@ -0,0 +1,31 @@ +description: Kentucky assigns these heating-payment points by primary heating fuel under the Low Income Home Energy Assistance Program. +UNSPECIFIED: + 2025-10-01: 0 +ELECTRICITY: + 2025-10-01: 6 +NATURAL_GAS: + 2025-10-01: 3 +FUEL_OIL: + 2025-10-01: 4 +KEROSENE: + 2025-10-01: 4 +PROPANE: + 2025-10-01: 5 +WOOD: + 2025-10-01: 1 +COAL: + 2025-10-01: 2 +SOLAR: + 2025-10-01: 0 +OTHER: + 2025-10-01: 0 +NONE: + 2025-10-01: 0 +metadata: + unit: /1 + label: Kentucky LIHEAP heating fuel points + breakdown: + - heating_type + reference: + - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_bands.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_bands.yaml new file mode 100644 index 00000000000..78acdff2f6b --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_bands.yaml @@ -0,0 +1,9 @@ +description: Kentucky uses these federal poverty guideline shares as the upper boundaries of its first three heating-payment income bands under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: [0.75, 1, 1.25] +metadata: + unit: /1 + label: Kentucky LIHEAP income bands + reference: + - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/subsidized.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/subsidized.yaml new file mode 100644 index 00000000000..0ff02bcc4a0 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/subsidized.yaml @@ -0,0 +1,26 @@ +description: Kentucky assigns these income points for heating assistance to households in subsidized housing, by monthly income band, under the Low Income Home Energy Assistance Program. +brackets: + - threshold: + 2025-10-01: 0 + amount: + 2025-10-01: 1 + - threshold: + 2025-10-01: 1 + amount: + 2025-10-01: 2 + - threshold: + 2025-10-01: 2 + amount: + 2025-10-01: 3 + - threshold: + 2025-10-01: 3 + amount: + 2025-10-01: 4 +metadata: + type: single_amount + threshold_unit: /1 + amount_unit: /1 + label: Kentucky LIHEAP subsidized housing income points + reference: + - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/unsubsidized.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/unsubsidized.yaml new file mode 100644 index 00000000000..cd94f233019 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/unsubsidized.yaml @@ -0,0 +1,26 @@ +description: Kentucky assigns these income points for heating assistance to households in unsubsidized housing, by monthly income band, under the Low Income Home Energy Assistance Program. +brackets: + - threshold: + 2025-10-01: 0 + amount: + 2025-10-01: 4 + - threshold: + 2025-10-01: 1 + amount: + 2025-10-01: 3 + - threshold: + 2025-10-01: 2 + amount: + 2025-10-01: 2 + - threshold: + 2025-10-01: 3 + amount: + 2025-10-01: 1 +metadata: + type: single_amount + threshold_unit: /1 + amount_unit: /1 + label: Kentucky LIHEAP unsubsidized housing income points + reference: + - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/maximum_household_points.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/maximum_household_points.yaml new file mode 100644 index 00000000000..098cce5c9ab --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/maximum_household_points.yaml @@ -0,0 +1,9 @@ +description: Kentucky caps the household-size contribution to heating payment points at this amount under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 5 +metadata: + unit: /1 + label: Kentucky LIHEAP maximum household points + reference: + - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/subsidized_housing_rate.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/subsidized_housing_rate.yaml new file mode 100644 index 00000000000..317f6150089 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/subsidized_housing_rate.yaml @@ -0,0 +1,9 @@ +description: Kentucky reduces the published subsidized-housing heating payment to this share of its point-based amount before rounding up under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 0.3 +metadata: + unit: /1 + label: Kentucky LIHEAP subsidized housing rate + reference: + - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/unearned_income_sources.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/unearned_income_sources.yaml new file mode 100644 index 00000000000..e47f65bce6f --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/chfs/liheap/unearned_income_sources.yaml @@ -0,0 +1,23 @@ +description: Kentucky counts these available person-level unearned income sources under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: + - unemployment_compensation + - strike_benefits + - ssi + - pension_income + - retirement_distributions + - financial_assistance + - gambling_winnings + - rental_income + - alimony_income + - child_support_received + - interest_income + - dividend_income + - veterans_benefits + - salt_refund_income +metadata: + unit: list + label: Kentucky LIHEAP unearned income sources + reference: + - title: Kentucky FY2026 state plan, Section 1.9 + href: https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=5,6,7 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 81c10bc47b6..a3e865c235b 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, TX + coverage: OR, DC, Riverside County, MA, IL, TX, KY state_implementations: - state: OR status: in_progress @@ -823,6 +823,13 @@ programs: name: Illinois LIHEAP full_name: Illinois Low Income Home Energy Assistance Program variable: il_liheap + - state: KY + status: partial + name: Kentucky LIHEAP + full_name: Kentucky Low Income Home Energy Assistance Program + variable: ky_liheap + parameter_prefix: gov.states.ky.chfs.liheap + notes: Regular heating only, FY2026 coverage. Uses the published regular benefit worksheet despite its stale title and anomalous cells. Annual income and SPM units approximate application-month energy households. Detailed income exclusions, lump sums, mixed-immigration adjustments, and administrative eligibility remain incomplete; no new inputs are added. - state: TX status: partial name: Texas CEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/integration.yaml new file mode 100644 index 00000000000..6a1a719598d --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/integration.yaml @@ -0,0 +1,38 @@ +# Sources: Kentucky FY2026 state plan, regular heating benefit workbook, and fact sheet. +- name: Regular subsidy can exceed the bill and applies to heat included in rent + period: 2_026 + input: + state_code: [KY, KY, KY, KY, KY, IN] + age: [35, 35, 35, 35, 35, 35] + employment_income: [0, 0, 0, 30_000, 0, 0] + ssi: [0, 0, 0, 0, 0, 0] + social_security: [0, 0, 0, 0, 0, 0] + tanf: [0, 0, 0, 0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [10, 0, 0, 1_000, 1_000, 1_000] + heat_expense_included_in_rent: [false, true, false, false, false, false] + is_citizen_or_legal_immigrant: [true, true, true, true, false, true] + hud_utility_allowance: [0, 0, 0, 0, 0, 0] + output: + ky_liheap_eligible: [true, true, false, false, false, false] + ky_liheap: [134, 134, 0, 0, 0, 0] +- name: Existing public housing and utility allowance inputs select reduced schedule + period: 2_026 + input: + state_code: [KY, KY, KY] + ky_liheap_income: [0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_000, 1_000, 1_000] + is_in_public_housing: [true, false, false] + receives_housing_assistance: [false, true, false] + hud_utility_allowance: [0, 0, 100] + output: + ky_liheap: [26, 26, 26] +- name: Earlier years have no encoded payment schedule + period: 2_022 + input: + state_code: KY + heating_type: ELECTRICITY + heating_expense: 1_000 + output: + ky_liheap: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap.yaml new file mode 100644 index 00000000000..a0d2565a7bd --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap.yaml @@ -0,0 +1,71 @@ +# Sources: Kentucky FY2026 state plan, regular heating benefit workbook, and fact sheet. +- name: Published fuel amounts for one-person households + period: 2_026 + input: + state_code: [KY, KY, KY, KY, KY, KY] + ky_liheap_eligible: [true, true, true, true, true, true] + spm_unit_size: [1, 1, 1, 1, 1, 1] + heating_type: [ELECTRICITY, NATURAL_GAS, PROPANE, COAL, WOOD, FUEL_OIL] + ky_liheap_income: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + receives_housing_assistance: [false, false, false, false, false, false] + hud_utility_allowance: [0, 0, 0, 0, 0, 0] + output: + ky_liheap: [184, 134, 167, 117, 100, 150] +- name: Two-person coal exception and the five-person points cap + period: 2_026 + input: + state_code: [KY, KY, KY, KY, KY, KY] + ky_liheap_eligible: [true, true, true, true, true, true] + spm_unit_size: [2, 4, 5, 6, 7, 8] + heating_type: [COAL, COAL, COAL, COAL, COAL, COAL] + ky_liheap_income: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] + receives_housing_assistance: [false, false, false, false, false, false] + hud_utility_allowance: [0, 0, 0, 0, 0, 0] + output: + ky_liheap: [117, 167, 184, 184, 184, 184] +- name: Subsidized matrix increases across income bands + period: 2_026 + input: + state_code: [KY, KY, KY, KY] + ky_liheap_eligible: [true, true, true, true] + spm_unit_size: [1, 1, 1, 1] + heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY] + ky_liheap_income: [0.0, 11_748.12, 15_660.12, 19_572.12] + receives_housing_assistance: [true, true, true, true] + hud_utility_allowance: [0, 0, 0, 0] + output: + ky_liheap: [41, 45, 51, 56] +- name: Unsubsidized matrix decreases across income bands + period: 2_026 + input: + state_code: [KY, KY, KY, KY] + ky_liheap_eligible: [true, true, true, true] + spm_unit_size: [1, 1, 1, 1] + heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY] + ky_liheap_income: [0.0, 11_748.12, 15_660.12, 19_572.12] + receives_housing_assistance: [false, false, false, false] + hud_utility_allowance: [0, 0, 0, 0] + output: + ky_liheap: [184, 167, 150, 134] +- name: Closed upper income bands use rounded monthly boundaries + period: 2_026 + input: + state_code: [KY, KY, KY, KY, KY, KY] + ky_liheap_eligible: [true, true, true, true, true, true] + spm_unit_size: [1, 1, 1, 1, 1, 1] + heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY] + hud_utility_allowance: [0, 0, 0, 0, 0, 0] + ky_liheap_income: [11_748, 11_748.12, 15_660, 15_660.12, 19_572, 19_572.12] + output: + ky_liheap: [184, 167, 167, 150, 150, 134] +- name: Kerosene matches oil; unspecified and unsupported fuels have no schedule + period: 2_026 + input: + state_code: [KY, KY, KY, KY, KY] + ky_liheap_eligible: [true, true, true, true, true] + spm_unit_size: [1, 1, 1, 1, 1] + ky_liheap_income: [0, 0, 0, 0, 0] + hud_utility_allowance: [0, 0, 0, 0, 0] + heating_type: [KEROSENE, OTHER, SOLAR, NONE, UNSPECIFIED] + output: + ky_liheap: [150, 0, 0, 0, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income.yaml new file mode 100644 index 00000000000..8dc6d3046a9 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income.yaml @@ -0,0 +1,53 @@ +# Sources: Kentucky FY2026 state plan, regular heating benefit workbook, and fact sheet. +- name: Adult business loss does not offset wages; child earnings are excluded + period: 2_026 + input: + people: + adult: + age: 35 + employment_income: 12_000 + self_employment_income: -3_000 + unemployment_compensation: 1_200 + strike_benefits: 600 + ssi: 0 + social_security: 0 + child: + age: 17 + employment_income: 4_000 + self_employment_income: 2_000 + child_support_received: 2_400 + ssi: 0 + social_security: 0 + spm_units: + unit: + members: [adult, child] + tanf: 0 + households: + household: + members: [adult, child] + state_code: KY + output: + ky_liheap_income: 16_200 +- name: Net business income is counted once without an additional expense deduction + period: 2_026 + input: + state_code: KY + age: 18 + employment_income: 12_000 + self_employment_income: 6_000 + ssi: 0 + social_security: 0 + tanf: 0 + output: + ky_liheap_income: 18_000 +- name: Reported Medicare premium deduction cannot make SSA income negative + period: 2_026 + input: + state_code: [KY, KY] + age: [70, 70] + social_security: [12_000, 1_000] + medicare_part_b_premiums_reported: [2_000, 2_000] + ssi: [0, 0] + tanf: [0, 0] + output: + ky_liheap_income: [10_000, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.yaml new file mode 100644 index 00000000000..5d65fcf99f0 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.yaml @@ -0,0 +1,17 @@ +# Sources: Kentucky FY2026 state plan, regular heating benefit workbook, and fact sheet. +- name: Published monthly limits, including the additional-person rule + period: 2_026 + input: + state_code: [KY, KY, KY, KY, KY, KY, KY, KY, KY, KY] + spm_unit_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + ky_liheap_income: [23_484, 31_728, 39_984, 48_228, 56_484, 64_728, 72_984, 81_228, 89_484, 97_740] + output: + ky_liheap_income_eligible: [true, true, true, true, true, true, true, true, true, true] +- name: One dollar per month above the published limits + period: 2_026 + input: + state_code: [KY, KY, KY, KY, KY, KY, KY, KY, KY, KY] + spm_unit_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + ky_liheap_income: [23_496, 31_740, 39_996, 48_240, 56_496, 64_740, 72_996, 81_240, 89_496, 97_752] + output: + ky_liheap_income_eligible: [false, false, false, false, false, false, false, false, false, false] diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap.py b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap.py new file mode 100644 index 00000000000..823ef6ac491 --- /dev/null +++ b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap.py @@ -0,0 +1,51 @@ +from policyengine_us.model_api import * + + +class ky_liheap(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Kentucky LIHEAP regular heating assistance" + defined_for = "ky_liheap_eligible" + reference = "https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx" + + def formula_2026(spm_unit, period, parameters): + p = parameters(period).gov.states.ky.chfs.liheap.payment + income = spm_unit("ky_liheap_income", period) / MONTHS_IN_YEAR + fpg = spm_unit("ky_liheap_fpg", period) / MONTHS_IN_YEAR + size = spm_unit("spm_unit_size", period) + fuel = spm_unit("heating_type", period) + types = fuel.possible_values + subsidized = ( + spm_unit("receives_housing_assistance", period) + | spm_unit.household("is_in_public_housing", period) + | (spm_unit.household("hud_utility_allowance", period) > 0) + ) + band = 0 + for rate in p.income_bands: + band = band + (income > np.ceil(fpg * rate)) + income_points = where( + subsidized, + p.income_points.subsidized.calc(band), + p.income_points.unsubsidized.calc(band), + ) + fuel_points = p.fuel_points[fuel] + coal_adjustment = (fuel == types.COAL) * p.coal_adjustment.calc(size) + points = ( + min_(size, p.maximum_household_points) + + income_points + + fuel_points + + coal_adjustment + ) + # Reproduces every heating cell in "rev benefit income range", including + # its reversed subsidized income points and two-person coal exception. + # That sheet uses 2025 FPG despite its stale title and matches the FY2026 + # plan's $15-$250 range. The conditional "if no funding FFY26" sheet is + # not used. These source inconsistencies need agency clarification. + amount = np.ceil(points * p.dollars_per_point) + amount = where(subsidized, np.ceil(amount * p.subsidized_housing_rate), amount) + # No verified table mapping exists for OTHER/SOLAR/UNSPECIFIED/NONE. + # The full seasonal subsidy may credit the account, so it is not capped + # at the current bill. Coverage starts in 2026, not program inception. + return where(fuel_points > 0, amount, 0) diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_eligible.py b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_eligible.py new file mode 100644 index 00000000000..2bbb3a0b5b0 --- /dev/null +++ b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_eligible.py @@ -0,0 +1,22 @@ +from policyengine_us.model_api import * + + +class ky_liheap_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Kentucky LIHEAP regular heating eligibility" + defined_for = StateCode.KY + reference = "https://apps.legislature.ky.gov/law/kar/titles/921/004/116/" + + def formula_2026(spm_unit, period, parameters): + income_eligible = spm_unit("ky_liheap_income_eligible", period) + responsible = (spm_unit("heating_expense", period) > 0) | spm_unit( + "heat_expense_included_in_rent", period + ) + has_eligible_member = spm_unit.any( + spm_unit.members("is_citizen_or_legal_immigrant", period) + ) + # Documentation, enrollment windows, and mixed-immigration household + # adjustments are not modeled. No categorical or resource test applies. + return income_eligible & responsible & has_eligible_member diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_fpg.py b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_fpg.py new file mode 100644 index 00000000000..7105ba112a0 --- /dev/null +++ b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_fpg.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class ky_liheap_fpg(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Kentucky LIHEAP annual federal poverty guideline" + defined_for = StateCode.KY + reference = "https://www.mkcap.org/uploads/3/4/8/3/34834615/2025-2026-liheap-fact-sheet-v2.jpg" + + def formula_2026(spm_unit, period, parameters): + p = parameters(period).gov.states.ky.chfs.liheap + size = spm_unit("spm_unit_size", period) + state_group = spm_unit.household("state_group_str", period) + # Annual 2026 represents the heating season ending in 2026. Only the + # guideline year is lagged; household composition remains current. + fpg_year = period.start.year - int(p.fpg_year_lag) + fpg = parameters(f"{fpg_year}-01-01").gov.hhs.fpg + return fpg.first_person[state_group] + fpg.additional_person[ + state_group + ] * max_(size - 1, 0) diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income.py b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income.py new file mode 100644 index 00000000000..b06a30ac6e2 --- /dev/null +++ b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income.py @@ -0,0 +1,37 @@ +from policyengine_us.model_api import * + + +class ky_liheap_income(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Kentucky LIHEAP countable household income" + defined_for = StateCode.KY + reference = ( + "https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=5,6,7" + ) + + def formula_2026(spm_unit, period, parameters): + p = parameters(period).gov.states.ky.chfs.liheap + person = spm_unit.members + # Annual income divided by twelve approximates the calendar month before + # application. SPM units approximate the energy-purchasing household. + adult = person("age", period) >= p.earned_income_age + # Use existing net business income, without another business deduction. + earned = ( + max_(person("employment_income", period), 0) + + max_(person("self_employment_income", period), 0) + ) * adult + unearned = add(person, period, p.unearned_income_sources) + # Reported Part B premiums approximate the Medicare deduction from SSA; + # modeled premium liability does not establish an actual withholding. + ssa = max_( + person("social_security", period) + - person("medicare_part_b_premiums_reported", period), + 0, + ) + # Detailed excluded WIA/work-study earnings, jury duty, settlements, + # insurance payments, royalties, deposits, and non-taxable refunds cannot + # be isolated with existing inputs. Do not add new inputs for this draft. + return spm_unit.sum(earned + max_(unearned, 0) + ssa) + spm_unit("tanf", period) diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.py b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.py new file mode 100644 index 00000000000..c4b375c4988 --- /dev/null +++ b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.py @@ -0,0 +1,30 @@ +from policyengine_us.model_api import * + + +class ky_liheap_income_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Kentucky LIHEAP income eligibility" + defined_for = StateCode.KY + reference = "https://www.mkcap.org/uploads/3/4/8/3/34834615/2025-2026-liheap-fact-sheet-v2.jpg" + + def formula_2026(spm_unit, period, parameters): + p = parameters(period).gov.states.ky.chfs.liheap + size = spm_unit("spm_unit_size", period) + fpg = spm_unit("ky_liheap_fpg", period) + state_group = spm_unit.household("state_group_str", period) + fpg_year = period.start.year - int(p.fpg_year_lag) + additional_fpg = parameters(f"{fpg_year}-01-01").gov.hhs.fpg.additional_person[ + state_group + ] + extra = max_(size - p.maximum_table_size, 0) + # The fact sheet specifies $688 above size eight. The workbook's $687 + # extension conflicts with that explicit eligibility instruction. + table_fpg = fpg - extra * additional_fpg + monthly_limit = ( + np.ceil(table_fpg * p.income_limit / MONTHS_IN_YEAR) + + extra * p.additional_person_income_limit + ) + income = spm_unit("ky_liheap_income", period) / MONTHS_IN_YEAR + return income <= monthly_limit From ad90747c0a5de62ae76fd520ab604d36b06ebd30 Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 30 Sep 2026 19:59:24 -0400 Subject: [PATCH 2/8] Apply LIHEAP review fixes and shared guideline convention --- policyengine_us/programs.yaml | 3 ++- .../gov/states/ky/chfs/liheap/integration.yaml | 15 +++++++++++++-- .../variables/gov/hhs/tax_unit_fpg.py | 11 +++++++++-- .../gov/states/ky/chfs/liheap/ky_liheap.py | 8 ++++++-- .../states/ky/chfs/liheap/ky_liheap_eligible.py | 11 ++++++++--- .../gov/states/ky/chfs/liheap/ky_liheap_fpg.py | 15 +++++++-------- .../states/ky/chfs/liheap/ky_liheap_income.py | 8 ++++++-- .../ky/chfs/liheap/ky_liheap_income_eligible.py | 16 +++++++++------- 8 files changed, 60 insertions(+), 27 deletions(-) diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index a3e865c235b..43d2d392824 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -829,7 +829,8 @@ programs: full_name: Kentucky Low Income Home Energy Assistance Program variable: ky_liheap parameter_prefix: gov.states.ky.chfs.liheap - notes: Regular heating only, FY2026 coverage. Uses the published regular benefit worksheet despite its stale title and anomalous cells. Annual income and SPM units approximate application-month energy households. Detailed income exclusions, lump sums, mixed-immigration adjustments, and administrative eligibility remain incomplete; no new inputs are added. + verified_years: "2026" + notes: Regular heating only, FY2026 coverage. Uses the published regular benefit worksheet despite its stale title and anomalous cells. Annual income and SPM units approximate application-month energy households. Detailed income exclusions, lump sums, mixed-immigration adjustments, and administrative eligibility remain incomplete; no new inputs are added. Earlier results use parameter backfilling and are unverified historical estimates. - state: TX status: partial name: Texas CEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/integration.yaml index 6a1a719598d..e8a8a9966f6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/integration.yaml @@ -28,11 +28,22 @@ hud_utility_allowance: [0, 0, 100] output: ky_liheap: [26, 26, 26] -- name: Earlier years have no encoded payment schedule +# Backfilling checks the model convention, not verified historical Kentucky policy. +- name: Parameter backfill uses the 2021 guideline for a 2022 estimate period: 2_022 input: state_code: KY + age: 40 + employment_income: 0 + ssi: 0 + social_security: 0 + tanf: 0 heating_type: ELECTRICITY heating_expense: 1_000 + receives_housing_assistance: false + is_in_public_housing: false + hud_utility_allowance: 0 output: - ky_liheap: 0 + ky_liheap: 184 + ky_liheap_fpg: 12_880 + ky_liheap_eligible: true diff --git a/policyengine_us/variables/gov/hhs/tax_unit_fpg.py b/policyengine_us/variables/gov/hhs/tax_unit_fpg.py index f492918fcbb..51714218342 100644 --- a/policyengine_us/variables/gov/hhs/tax_unit_fpg.py +++ b/policyengine_us/variables/gov/hhs/tax_unit_fpg.py @@ -1,8 +1,15 @@ from policyengine_us.model_api import * +from policyengine_core.periods import instant -def fpg(unit_size, state_group, period, parameters): - p_fpg = parameters(period).gov.hhs.fpg +def fpg(unit_size, state_group, period, parameters, year_lag=0): + """Return the annual guideline for the supplied size and guideline year. + + Lag only the parameter date, preserving its month and day. Callers supply + the current household size and apply any caps, policy rates, or rounding. + """ + guideline_date = instant(period).offset(-int(year_lag), "year") + p_fpg = parameters(guideline_date).gov.hhs.fpg p1 = p_fpg.first_person[state_group] pn = p_fpg.additional_person[state_group] return p1 + pn * (unit_size - 1) diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap.py b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap.py index 823ef6ac491..632a4468758 100644 --- a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap.py +++ b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap.py @@ -7,10 +7,14 @@ class ky_liheap(Variable): definition_period = YEAR unit = USD label = "Kentucky LIHEAP regular heating assistance" + documentation = ( + "Verified for FY2026. Earlier years use model parameter backfilling " + "and are unverified historical estimates." + ) defined_for = "ky_liheap_eligible" reference = "https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx" - def formula_2026(spm_unit, period, parameters): + def formula(spm_unit, period, parameters): p = parameters(period).gov.states.ky.chfs.liheap.payment income = spm_unit("ky_liheap_income", period) / MONTHS_IN_YEAR fpg = spm_unit("ky_liheap_fpg", period) / MONTHS_IN_YEAR @@ -47,5 +51,5 @@ def formula_2026(spm_unit, period, parameters): amount = where(subsidized, np.ceil(amount * p.subsidized_housing_rate), amount) # No verified table mapping exists for OTHER/SOLAR/UNSPECIFIED/NONE. # The full seasonal subsidy may credit the account, so it is not capped - # at the current bill. Coverage starts in 2026, not program inception. + # at the current bill. return where(fuel_points > 0, amount, 0) diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_eligible.py b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_eligible.py index 2bbb3a0b5b0..0705190fb06 100644 --- a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_eligible.py @@ -9,7 +9,7 @@ class ky_liheap_eligible(Variable): defined_for = StateCode.KY reference = "https://apps.legislature.ky.gov/law/kar/titles/921/004/116/" - def formula_2026(spm_unit, period, parameters): + def formula(spm_unit, period, parameters): income_eligible = spm_unit("ky_liheap_income_eligible", period) responsible = (spm_unit("heating_expense", period) > 0) | spm_unit( "heat_expense_included_in_rent", period @@ -17,6 +17,11 @@ def formula_2026(spm_unit, period, parameters): has_eligible_member = spm_unit.any( spm_unit.members("is_citizen_or_legal_immigrant", period) ) - # Documentation, enrollment windows, and mixed-immigration household - # adjustments are not modeled. No categorical or resource test applies. + # 921 KAR 4:116 Section 1(10) defines the energy-purchasing household; + # it does not establish the treatment of ineligible immigrants. Counting + # all SPM members for size while requiring one qualified member is a + # provisional assumption, not a verified Kentucky mixed-status rule. + # Confirm household-size and income adjustments with the operational + # manual before treating this pathway as supported. Documentation and + # enrollment windows are unmodeled. No categorical or resource test applies. return income_eligible & responsible & has_eligible_member diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_fpg.py b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_fpg.py index 7105ba112a0..08d2344c085 100644 --- a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_fpg.py +++ b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_fpg.py @@ -1,4 +1,5 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg class ky_liheap_fpg(Variable): @@ -10,14 +11,12 @@ class ky_liheap_fpg(Variable): defined_for = StateCode.KY reference = "https://www.mkcap.org/uploads/3/4/8/3/34834615/2025-2026-liheap-fact-sheet-v2.jpg" - def formula_2026(spm_unit, period, parameters): + def formula(spm_unit, period, parameters): p = parameters(period).gov.states.ky.chfs.liheap size = spm_unit("spm_unit_size", period) state_group = spm_unit.household("state_group_str", period) - # Annual 2026 represents the heating season ending in 2026. Only the - # guideline year is lagged; household composition remains current. - fpg_year = period.start.year - int(p.fpg_year_lag) - fpg = parameters(f"{fpg_year}-01-01").gov.hhs.fpg - return fpg.first_person[state_group] + fpg.additional_person[ - state_group - ] * max_(size - 1, 0) + # The annual period represents the heating season ending in that year. + # Only the guideline is lagged; household composition remains current. + return fpg( + max_(size, 1), state_group, period, parameters, year_lag=p.fpg_year_lag + ) diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income.py b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income.py index b06a30ac6e2..80d0ab92088 100644 --- a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income.py +++ b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income.py @@ -9,10 +9,11 @@ class ky_liheap_income(Variable): label = "Kentucky LIHEAP countable household income" defined_for = StateCode.KY reference = ( - "https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=5,6,7" + "https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=5,6,7", + "https://apps.legislature.ky.gov/law/kar/titles/921/004/116/", ) - def formula_2026(spm_unit, period, parameters): + def formula(spm_unit, period, parameters): p = parameters(period).gov.states.ky.chfs.liheap person = spm_unit.members # Annual income divided by twelve approximates the calendar month before @@ -34,4 +35,7 @@ def formula_2026(spm_unit, period, parameters): # Detailed excluded WIA/work-study earnings, jury duty, settlements, # insurance payments, royalties, deposits, and non-taxable refunds cannot # be isolated with existing inputs. Do not add new inputs for this draft. + # 921 KAR 4:116 Section 1(8) counts income received. The annual tanf + # aggregate includes ky_ktap and applies take-up; ky_ktap alone represents + # monthly entitlement and would count benefits a nonrecipient could get. return spm_unit.sum(earned + max_(unearned, 0) + ssa) + spm_unit("tanf", period) diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.py b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.py index c4b375c4988..ce5d414eed5 100644 --- a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.py +++ b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.py @@ -1,4 +1,5 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg class ky_liheap_income_eligible(Variable): @@ -9,19 +10,20 @@ class ky_liheap_income_eligible(Variable): defined_for = StateCode.KY reference = "https://www.mkcap.org/uploads/3/4/8/3/34834615/2025-2026-liheap-fact-sheet-v2.jpg" - def formula_2026(spm_unit, period, parameters): + def formula(spm_unit, period, parameters): p = parameters(period).gov.states.ky.chfs.liheap size = spm_unit("spm_unit_size", period) - fpg = spm_unit("ky_liheap_fpg", period) state_group = spm_unit.household("state_group_str", period) - fpg_year = period.start.year - int(p.fpg_year_lag) - additional_fpg = parameters(f"{fpg_year}-01-01").gov.hhs.fpg.additional_person[ - state_group - ] extra = max_(size - p.maximum_table_size, 0) # The fact sheet specifies $688 above size eight. The workbook's $687 # extension conflicts with that explicit eligibility instruction. - table_fpg = fpg - extra * additional_fpg + table_fpg = fpg( + clip(size, 1, p.maximum_table_size), + state_group, + period, + parameters, + year_lag=p.fpg_year_lag, + ) monthly_limit = ( np.ceil(table_fpg * p.income_limit / MONTHS_IN_YEAR) + extra * p.additional_person_income_limit From c32df0e8dc8d84a8126be88b43fd6afb4c0624cb Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 30 Sep 2026 23:05:50 -0400 Subject: [PATCH 3/8] Fix issues from review: Kentucky LIHEAP folder, income sources and income limit - Move the program from ky/chfs to ky/dcbs - Derive the above-eight income-limit increment from the lagged poverty guideline in place of the stored $688 - Drop the hud_utility_allowance proxy from the subsidized-housing test - Floor each unearned source at zero per source - Count ky_ssp, farm operations and farm rent income, workers' compensation and disability benefits; replace the deprecated dividend_income alias - Cite the statewide fact sheet PDF; add metadata.period to parameters - Document the workbook's reversed subsidized rows and the mixed-status gate assumption without changing behavior - Add end-to-end, income, subsidized-cell and large-household tests Co-Authored-By: Claude Fable 5.1 --- .../additional_person_income_limit.yaml | 9 - .../states/ky/chfs/liheap/fpg_year_lag.yaml | 9 - .../payment/income_points/subsidized.yaml | 26 --- .../chfs/liheap/unearned_income_sources.yaml | 23 -- .../liheap/earned_income_age.yaml | 1 + .../states/ky/dcbs/liheap/fpg_year_lag.yaml | 16 ++ .../{chfs => dcbs}/liheap/income_limit.yaml | 1 + .../liheap/maximum_table_size.yaml | 3 +- .../liheap/payment/coal_adjustment.yaml | 1 + .../liheap/payment/dollars_per_point.yaml | 1 + .../liheap/payment/fuel_points.yaml | 1 + .../liheap/payment/income_bands.yaml | 1 + .../payment/income_points/subsidized.yaml | 35 +++ .../payment/income_points/unsubsidized.yaml | 1 + .../payment/maximum_household_points.yaml | 1 + .../payment/subsidized_housing_rate.yaml | 1 + .../dcbs/liheap/unearned_income_sources.yaml | 36 +++ policyengine_us/programs.yaml | 2 +- .../states/ky/chfs/liheap/integration.yaml | 49 ----- .../ky/chfs/liheap/ky_liheap_income.yaml | 53 ----- .../states/ky/dcbs/liheap/integration.yaml | 205 ++++++++++++++++++ .../ky/{chfs => dcbs}/liheap/ky_liheap.yaml | 32 +++ .../ky/dcbs/liheap/ky_liheap_eligible.yaml | 51 +++++ .../ky/dcbs/liheap/ky_liheap_income.yaml | 132 +++++++++++ .../liheap/ky_liheap_income_eligible.yaml | 15 ++ .../chfs/liheap/ky_liheap_income_eligible.py | 32 --- .../ky/{chfs => dcbs}/liheap/ky_liheap.py | 13 +- .../liheap/ky_liheap_eligible.py | 16 +- .../ky/{chfs => dcbs}/liheap/ky_liheap_fpg.py | 4 +- .../{chfs => dcbs}/liheap/ky_liheap_income.py | 27 ++- .../dcbs/liheap/ky_liheap_income_eligible.py | 43 ++++ 31 files changed, 612 insertions(+), 228 deletions(-) delete mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/additional_person_income_limit.yaml delete mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/fpg_year_lag.yaml delete mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/subsidized.yaml delete mode 100644 policyengine_us/parameters/gov/states/ky/chfs/liheap/unearned_income_sources.yaml rename policyengine_us/parameters/gov/states/ky/{chfs => dcbs}/liheap/earned_income_age.yaml (95%) create mode 100644 policyengine_us/parameters/gov/states/ky/dcbs/liheap/fpg_year_lag.yaml rename policyengine_us/parameters/gov/states/ky/{chfs => dcbs}/liheap/income_limit.yaml (96%) rename policyengine_us/parameters/gov/states/ky/{chfs => dcbs}/liheap/maximum_table_size.yaml (73%) rename policyengine_us/parameters/gov/states/ky/{chfs => dcbs}/liheap/payment/coal_adjustment.yaml (97%) rename policyengine_us/parameters/gov/states/ky/{chfs => dcbs}/liheap/payment/dollars_per_point.yaml (96%) rename policyengine_us/parameters/gov/states/ky/{chfs => dcbs}/liheap/payment/fuel_points.yaml (97%) rename policyengine_us/parameters/gov/states/ky/{chfs => dcbs}/liheap/payment/income_bands.yaml (96%) create mode 100644 policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/subsidized.yaml rename policyengine_us/parameters/gov/states/ky/{chfs => dcbs}/liheap/payment/income_points/unsubsidized.yaml (98%) rename policyengine_us/parameters/gov/states/ky/{chfs => dcbs}/liheap/payment/maximum_household_points.yaml (96%) rename policyengine_us/parameters/gov/states/ky/{chfs => dcbs}/liheap/payment/subsidized_housing_rate.yaml (96%) create mode 100644 policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml delete mode 100644 policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/integration.yaml delete mode 100644 policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/integration.yaml rename policyengine_us/tests/policy/baseline/gov/states/ky/{chfs => dcbs}/liheap/ky_liheap.yaml (63%) create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml rename policyengine_us/tests/policy/baseline/gov/states/ky/{chfs => dcbs}/liheap/ky_liheap_income_eligible.yaml (59%) delete mode 100644 policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.py rename policyengine_us/variables/gov/states/ky/{chfs => dcbs}/liheap/ky_liheap.py (83%) rename policyengine_us/variables/gov/states/ky/{chfs => dcbs}/liheap/ky_liheap_eligible.py (52%) rename policyengine_us/variables/gov/states/ky/{chfs => dcbs}/liheap/ky_liheap_fpg.py (82%) rename policyengine_us/variables/gov/states/ky/{chfs => dcbs}/liheap/ky_liheap_income.py (55%) create mode 100644 policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.py diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/additional_person_income_limit.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/additional_person_income_limit.yaml deleted file mode 100644 index 25bb2d0e7c0..00000000000 --- a/policyengine_us/parameters/gov/states/ky/chfs/liheap/additional_person_income_limit.yaml +++ /dev/null @@ -1,9 +0,0 @@ -description: Kentucky adds this amount to its monthly income limit for each person beyond its published table under the Low Income Home Energy Assistance Program. -values: - 2025-10-01: 688 -metadata: - unit: currency-USD - label: Kentucky LIHEAP additional person income limit - reference: - - title: Kentucky FY2025-2026 LIHEAP fact sheet, income eligibility - href: https://www.mkcap.org/uploads/3/4/8/3/34834615/2025-2026-liheap-fact-sheet-v2.jpg diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/fpg_year_lag.yaml deleted file mode 100644 index 6fcb96a386d..00000000000 --- a/policyengine_us/parameters/gov/states/ky/chfs/liheap/fpg_year_lag.yaml +++ /dev/null @@ -1,9 +0,0 @@ -description: Kentucky uses the federal poverty guidelines from this many years before the heating season ending year under the Low Income Home Energy Assistance Program. -values: - 2025-10-01: 1 -metadata: - unit: year - label: Kentucky LIHEAP fpg year lag - reference: - - title: Kentucky FY2025-2026 LIHEAP fact sheet, income eligibility - href: https://www.mkcap.org/uploads/3/4/8/3/34834615/2025-2026-liheap-fact-sheet-v2.jpg diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/subsidized.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/subsidized.yaml deleted file mode 100644 index 0ff02bcc4a0..00000000000 --- a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/subsidized.yaml +++ /dev/null @@ -1,26 +0,0 @@ -description: Kentucky assigns these income points for heating assistance to households in subsidized housing, by monthly income band, under the Low Income Home Energy Assistance Program. -brackets: - - threshold: - 2025-10-01: 0 - amount: - 2025-10-01: 1 - - threshold: - 2025-10-01: 1 - amount: - 2025-10-01: 2 - - threshold: - 2025-10-01: 2 - amount: - 2025-10-01: 3 - - threshold: - 2025-10-01: 3 - amount: - 2025-10-01: 4 -metadata: - type: single_amount - threshold_unit: /1 - amount_unit: /1 - label: Kentucky LIHEAP subsidized housing income points - reference: - - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet - href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/unearned_income_sources.yaml b/policyengine_us/parameters/gov/states/ky/chfs/liheap/unearned_income_sources.yaml deleted file mode 100644 index e47f65bce6f..00000000000 --- a/policyengine_us/parameters/gov/states/ky/chfs/liheap/unearned_income_sources.yaml +++ /dev/null @@ -1,23 +0,0 @@ -description: Kentucky counts these available person-level unearned income sources under the Low Income Home Energy Assistance Program. -values: - 2025-10-01: - - unemployment_compensation - - strike_benefits - - ssi - - pension_income - - retirement_distributions - - financial_assistance - - gambling_winnings - - rental_income - - alimony_income - - child_support_received - - interest_income - - dividend_income - - veterans_benefits - - salt_refund_income -metadata: - unit: list - label: Kentucky LIHEAP unearned income sources - reference: - - title: Kentucky FY2026 state plan, Section 1.9 - href: https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=5,6,7 diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/earned_income_age.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/earned_income_age.yaml similarity index 95% rename from policyengine_us/parameters/gov/states/ky/chfs/liheap/earned_income_age.yaml rename to policyengine_us/parameters/gov/states/ky/dcbs/liheap/earned_income_age.yaml index 8a15009b9e0..253b9d04700 100644 --- a/policyengine_us/parameters/gov/states/ky/chfs/liheap/earned_income_age.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/earned_income_age.yaml @@ -3,6 +3,7 @@ values: 2025-10-01: 18 metadata: unit: year + period: year label: Kentucky LIHEAP earned income age reference: - title: Kentucky FY2026 state plan, Section 1.9 diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/fpg_year_lag.yaml new file mode 100644 index 00000000000..68438d86924 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/fpg_year_lag.yaml @@ -0,0 +1,16 @@ +description: Kentucky uses the federal poverty guidelines from this many years before the heating season ending year under the Low Income Home Energy Assistance Program. +# The fact sheet publishes the limits without naming the guideline year. The +# lag is inferred from the amounts (1,957 = 150% of the 2025 one-person +# guideline / 12, rounded up); the workbook's poverty table shows the same +# 2025 guideline. +values: + 2025-10-01: 1 +metadata: + unit: year + period: year + label: Kentucky LIHEAP fpg year lag + reference: + - title: Kentucky FY2025-2026 LIHEAP fact sheet, income eligibility + href: https://www.capky.org/wp-content/uploads/2026/01/2025-2026-LIHEAP-Fact-Sheet-V2.pdf + - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet, poverty guideline table + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/income_limit.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/income_limit.yaml similarity index 96% rename from policyengine_us/parameters/gov/states/ky/chfs/liheap/income_limit.yaml rename to policyengine_us/parameters/gov/states/ky/dcbs/liheap/income_limit.yaml index e3d66882331..5c10ee10a7e 100644 --- a/policyengine_us/parameters/gov/states/ky/chfs/liheap/income_limit.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/income_limit.yaml @@ -3,6 +3,7 @@ values: 2025-10-01: 1.5 metadata: unit: /1 + period: year label: Kentucky LIHEAP income limit reference: - title: Kentucky FY2026 state plan, Section 2.1 diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/maximum_table_size.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/maximum_table_size.yaml similarity index 73% rename from policyengine_us/parameters/gov/states/ky/chfs/liheap/maximum_table_size.yaml rename to policyengine_us/parameters/gov/states/ky/dcbs/liheap/maximum_table_size.yaml index 07e6c4ae4fe..de8308f14b3 100644 --- a/policyengine_us/parameters/gov/states/ky/chfs/liheap/maximum_table_size.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/maximum_table_size.yaml @@ -3,7 +3,8 @@ values: 2025-10-01: 8 metadata: unit: person + period: year label: Kentucky LIHEAP maximum table size reference: - title: Kentucky FY2025-2026 LIHEAP fact sheet, income eligibility - href: https://www.mkcap.org/uploads/3/4/8/3/34834615/2025-2026-liheap-fact-sheet-v2.jpg + href: https://www.capky.org/wp-content/uploads/2026/01/2025-2026-LIHEAP-Fact-Sheet-V2.pdf diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/coal_adjustment.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/coal_adjustment.yaml similarity index 97% rename from policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/coal_adjustment.yaml rename to policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/coal_adjustment.yaml index 018f7f2c56a..76920c5ee6c 100644 --- a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/coal_adjustment.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/coal_adjustment.yaml @@ -16,6 +16,7 @@ metadata: type: single_amount threshold_unit: person amount_unit: /1 + period: year label: Kentucky LIHEAP coal household-size adjustment reference: - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet, coal column diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/dollars_per_point.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/dollars_per_point.yaml similarity index 96% rename from policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/dollars_per_point.yaml rename to policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/dollars_per_point.yaml index eec5df48131..da32bda18b9 100644 --- a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/dollars_per_point.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/dollars_per_point.yaml @@ -3,6 +3,7 @@ values: 2025-10-01: 16.66 metadata: unit: currency-USD + period: year label: Kentucky LIHEAP dollars per point reference: - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/fuel_points.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/fuel_points.yaml similarity index 97% rename from policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/fuel_points.yaml rename to policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/fuel_points.yaml index 1121a3f23bc..5cd57bd2a43 100644 --- a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/fuel_points.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/fuel_points.yaml @@ -23,6 +23,7 @@ NONE: 2025-10-01: 0 metadata: unit: /1 + period: year label: Kentucky LIHEAP heating fuel points breakdown: - heating_type diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_bands.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_bands.yaml similarity index 96% rename from policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_bands.yaml rename to policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_bands.yaml index 78acdff2f6b..2987f059c59 100644 --- a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_bands.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_bands.yaml @@ -3,6 +3,7 @@ values: 2025-10-01: [0.75, 1, 1.25] metadata: unit: /1 + period: year label: Kentucky LIHEAP income bands reference: - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/subsidized.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/subsidized.yaml new file mode 100644 index 00000000000..869254fbe9e --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/subsidized.yaml @@ -0,0 +1,35 @@ +description: Kentucky uses these income points to reproduce its published subsidized-housing heating payments under the Low Income Home Energy Assistance Program. +# These values reproduce the published FY2026 workbook. Its subsidized rows +# reference the unsubsidized rows in reverse order (the lowest-income +# subsidized row takes 30% of the highest-income unsubsidized row, and so on), +# so the published subsidized payment rises with income. 921 KAR 4:116 Section +# 4(1)(c) says the household with the lowest income receives the highest +# benefit. Which ordering agencies pay is unconfirmed. +brackets: + - threshold: + 2025-10-01: 0 + amount: + 2025-10-01: 1 + - threshold: + 2025-10-01: 1 + amount: + 2025-10-01: 2 + - threshold: + 2025-10-01: 2 + amount: + 2025-10-01: 3 + - threshold: + 2025-10-01: 3 + amount: + 2025-10-01: 4 +metadata: + type: single_amount + threshold_unit: /1 + amount_unit: /1 + period: year + label: Kentucky LIHEAP subsidized housing income points + reference: + - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx + - title: 921 KAR 4:116 Section 4(1)(c) + href: https://apps.legislature.ky.gov/law/kar/titles/921/004/116/ diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/unsubsidized.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/unsubsidized.yaml similarity index 98% rename from policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/unsubsidized.yaml rename to policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/unsubsidized.yaml index cd94f233019..9db8d6232a7 100644 --- a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/income_points/unsubsidized.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/unsubsidized.yaml @@ -20,6 +20,7 @@ metadata: type: single_amount threshold_unit: /1 amount_unit: /1 + period: year label: Kentucky LIHEAP unsubsidized housing income points reference: - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/maximum_household_points.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/maximum_household_points.yaml similarity index 96% rename from policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/maximum_household_points.yaml rename to policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/maximum_household_points.yaml index 098cce5c9ab..2aa26fb76c0 100644 --- a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/maximum_household_points.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/maximum_household_points.yaml @@ -3,6 +3,7 @@ values: 2025-10-01: 5 metadata: unit: /1 + period: year label: Kentucky LIHEAP maximum household points reference: - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet diff --git a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/subsidized_housing_rate.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/subsidized_housing_rate.yaml similarity index 96% rename from policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/subsidized_housing_rate.yaml rename to policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/subsidized_housing_rate.yaml index 317f6150089..88cc4612f6b 100644 --- a/policyengine_us/parameters/gov/states/ky/chfs/liheap/payment/subsidized_housing_rate.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/subsidized_housing_rate.yaml @@ -3,6 +3,7 @@ values: 2025-10-01: 0.3 metadata: unit: /1 + period: year label: Kentucky LIHEAP subsidized housing rate reference: - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml new file mode 100644 index 00000000000..f2a3e24dcb4 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml @@ -0,0 +1,36 @@ +description: Kentucky counts these available person-level unearned income sources under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: + - unemployment_compensation + - strike_benefits + - ssi + # Plan Section 17.4 lists the DCBS State Supplementation award letter as + # income documentation. + - ky_ssp + - pension_income + - retirement_distributions + - financial_assistance + - gambling_winnings + - rental_income + # Plan Section 17.4 lists records of farm or rental income. + - farm_rent_income + - alimony_income + - child_support_received + - interest_income + - ordinary_dividend_income + # The plan has no dedicated row for workers' compensation or employer + # disability pay. Section 1.9 counts insurance payments made directly to + # the insured, and 921 KAR 4:116 Section 1(8) counts all unearned income. + - workers_compensation + - disability_benefits + - veterans_benefits + - salt_refund_income +metadata: + unit: list + period: year + label: Kentucky LIHEAP unearned income sources + reference: + - title: Kentucky FY2026 state plan, Sections 1.9 and 17.4 + href: https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=5,6,7,34 + - title: 921 KAR 4:116 Section 1(8) + href: https://apps.legislature.ky.gov/law/kar/titles/921/004/116/ diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 43d2d392824..6b55d6c4c4d 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -828,7 +828,7 @@ programs: name: Kentucky LIHEAP full_name: Kentucky Low Income Home Energy Assistance Program variable: ky_liheap - parameter_prefix: gov.states.ky.chfs.liheap + parameter_prefix: gov.states.ky.dcbs.liheap verified_years: "2026" notes: Regular heating only, FY2026 coverage. Uses the published regular benefit worksheet despite its stale title and anomalous cells. Annual income and SPM units approximate application-month energy households. Detailed income exclusions, lump sums, mixed-immigration adjustments, and administrative eligibility remain incomplete; no new inputs are added. Earlier results use parameter backfilling and are unverified historical estimates. - state: TX diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/integration.yaml deleted file mode 100644 index e8a8a9966f6..00000000000 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/integration.yaml +++ /dev/null @@ -1,49 +0,0 @@ -# Sources: Kentucky FY2026 state plan, regular heating benefit workbook, and fact sheet. -- name: Regular subsidy can exceed the bill and applies to heat included in rent - period: 2_026 - input: - state_code: [KY, KY, KY, KY, KY, IN] - age: [35, 35, 35, 35, 35, 35] - employment_income: [0, 0, 0, 30_000, 0, 0] - ssi: [0, 0, 0, 0, 0, 0] - social_security: [0, 0, 0, 0, 0, 0] - tanf: [0, 0, 0, 0, 0, 0] - heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] - gas_expense: [10, 0, 0, 1_000, 1_000, 1_000] - heat_expense_included_in_rent: [false, true, false, false, false, false] - is_citizen_or_legal_immigrant: [true, true, true, true, false, true] - hud_utility_allowance: [0, 0, 0, 0, 0, 0] - output: - ky_liheap_eligible: [true, true, false, false, false, false] - ky_liheap: [134, 134, 0, 0, 0, 0] -- name: Existing public housing and utility allowance inputs select reduced schedule - period: 2_026 - input: - state_code: [KY, KY, KY] - ky_liheap_income: [0, 0, 0] - heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] - gas_expense: [1_000, 1_000, 1_000] - is_in_public_housing: [true, false, false] - receives_housing_assistance: [false, true, false] - hud_utility_allowance: [0, 0, 100] - output: - ky_liheap: [26, 26, 26] -# Backfilling checks the model convention, not verified historical Kentucky policy. -- name: Parameter backfill uses the 2021 guideline for a 2022 estimate - period: 2_022 - input: - state_code: KY - age: 40 - employment_income: 0 - ssi: 0 - social_security: 0 - tanf: 0 - heating_type: ELECTRICITY - heating_expense: 1_000 - receives_housing_assistance: false - is_in_public_housing: false - hud_utility_allowance: 0 - output: - ky_liheap: 184 - ky_liheap_fpg: 12_880 - ky_liheap_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income.yaml deleted file mode 100644 index 8dc6d3046a9..00000000000 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income.yaml +++ /dev/null @@ -1,53 +0,0 @@ -# Sources: Kentucky FY2026 state plan, regular heating benefit workbook, and fact sheet. -- name: Adult business loss does not offset wages; child earnings are excluded - period: 2_026 - input: - people: - adult: - age: 35 - employment_income: 12_000 - self_employment_income: -3_000 - unemployment_compensation: 1_200 - strike_benefits: 600 - ssi: 0 - social_security: 0 - child: - age: 17 - employment_income: 4_000 - self_employment_income: 2_000 - child_support_received: 2_400 - ssi: 0 - social_security: 0 - spm_units: - unit: - members: [adult, child] - tanf: 0 - households: - household: - members: [adult, child] - state_code: KY - output: - ky_liheap_income: 16_200 -- name: Net business income is counted once without an additional expense deduction - period: 2_026 - input: - state_code: KY - age: 18 - employment_income: 12_000 - self_employment_income: 6_000 - ssi: 0 - social_security: 0 - tanf: 0 - output: - ky_liheap_income: 18_000 -- name: Reported Medicare premium deduction cannot make SSA income negative - period: 2_026 - input: - state_code: [KY, KY] - age: [70, 70] - social_security: [12_000, 1_000] - medicare_part_b_premiums_reported: [2_000, 2_000] - ssi: [0, 0] - tanf: [0, 0] - output: - ky_liheap_income: [10_000, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/integration.yaml new file mode 100644 index 00000000000..cf7487e8213 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/integration.yaml @@ -0,0 +1,205 @@ +# Sources: Kentucky FY2026 state plan, regular heating benefit workbook, and fact sheet. +- name: Regular subsidy can exceed the bill and applies to heat included in rent + period: 2_026 + input: + state_code: [KY, KY, KY, KY, KY, IN] + age: [35, 35, 35, 35, 35, 35] + employment_income: [0, 0, 0, 30_000, 0, 0] + ssi: [0, 0, 0, 0, 0, 0] + social_security: [0, 0, 0, 0, 0, 0] + tanf: [0, 0, 0, 0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [10, 0, 0, 1_000, 1_000, 1_000] + heat_expense_included_in_rent: [false, true, false, false, false, false] + is_citizen_or_legal_immigrant: [true, true, true, true, false, true] + hud_utility_allowance: [0, 0, 0, 0, 0, 0] + output: + ky_liheap_eligible: [true, true, false, false, false, false] + ky_liheap: [134, 134, 0, 0, 0, 0] +- name: Public housing and housing assistance select the reduced schedule; a utility allowance schedule amount alone does not + period: 2_026 + input: + state_code: [KY, KY, KY] + ky_liheap_income: [0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_000, 1_000, 1_000] + is_in_public_housing: [true, false, false] + receives_housing_assistance: [false, true, false] + hud_utility_allowance: [0, 0, 100] + output: + # One person, natural gas (3 fuel points), 0-75% band. + # Households 1-2 (subsidized): 1 + 1 + 3 = 5 points; + # ceil(5 * 16.66 = 83.3) = 84; ceil(84 * 0.3 = 25.2) = 26. + # Household 3: hud_utility_allowance is a schedule amount, not receipt of + # housing assistance, so the unsubsidized schedule applies: + # 1 + 4 + 3 = 8 points; ceil(8 * 16.66 = 133.28) = 134. + ky_liheap: [26, 26, 134] +# Backfilling checks the model convention, not verified historical Kentucky policy. +- name: Parameter backfill uses the 2021 guideline for a 2022 estimate + period: 2_022 + input: + state_code: KY + age: 40 + employment_income: 0 + ssi: 0 + social_security: 0 + tanf: 0 + heating_type: ELECTRICITY + heating_expense: 1_000 + receives_housing_assistance: false + is_in_public_housing: false + hud_utility_allowance: 0 + output: + ky_liheap: 184 + ky_liheap_fpg: 12_880 + ky_liheap_eligible: true +- name: Case 4, family of four with two earners heats with natural gas. + period: 2026 + input: + people: + person1: + age: 34 + # $15.00 an hour, 40 hours a week: 15 * 2_080 = 31_200. + employment_income: 31_200 + person2: + age: 32 + # $12.00 an hour, 20 hours a week: 12 * 1_040 = 12_480. + employment_income: 12_480 + person3: + age: 8 + person4: + age: 4 + spm_units: + spm_unit: + members: [person1, person2, person3, person4] + heating_type: NATURAL_GAS + gas_expense: 840 + receives_housing_assistance: false + # The household does not receive K-TAP. + tanf: 0 + households: + household: + members: [person1, person2, person3, person4] + state_code: KY + is_in_public_housing: false + output: + spm_unit_size: 4 + # 2025 guideline (one-year lag): 15_650 + 3 * 5_500 = 32_150. + ky_liheap_fpg: 32_150 + # 31_200 + 12_480 = 43_680, or 3_640 a month. + ky_liheap_income: 43_680 + # Fact sheet limit for four: ceil(32_150 * 1.5 / 12 = 4_018.75) = 4_019. + ky_liheap_income_eligible: true + heating_expense: 840 + ky_liheap_eligible: true + # 3_640 a month is above ceil(32_150 * 1.25 / 12 = 3_348.96) = 3_349, so + # the 126.01-150% band gives 1 income point. + # 4 (size) + 1 (income) + 3 (natural gas) = 8 points; + # ceil(8 * 16.66 = 133.28) = 134, the workbook cell for four people, + # 126.01-150%, natural gas. + ky_liheap: 134 +- name: Case 5, parent and child with a housing voucher get the published $30 cell. + period: 2026 + input: + people: + person1: + age: 29 + # $12.00 an hour, 20 hours a week: 12 * 1_040 = 12_480. + employment_income: 12_480 + person2: + age: 5 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 720 + receives_housing_assistance: true + # The household does not receive K-TAP. + tanf: 0 + households: + household: + members: [person1, person2] + state_code: KY + is_in_public_housing: false + output: + spm_unit_size: 2 + # 2025 guideline (one-year lag): 15_650 + 5_500 = 21_150. + ky_liheap_fpg: 21_150 + # 12_480 a year, or 1_040 a month. + ky_liheap_income: 12_480 + # Fact sheet limit for two: ceil(21_150 * 1.5 / 12 = 2_643.75) = 2_644. + ky_liheap_income_eligible: true + heating_expense: 720 + ky_liheap_eligible: true + # 1_040 a month is not above ceil(21_150 * 0.75 / 12 = 1_321.875) = 1_322, + # so the 0-75% band applies; the subsidized schedule gives it 1 income + # point. + # 2 (size) + 1 (income) + 3 (natural gas) = 6 points; + # ceil(6 * 16.66 = 99.96) = 100; ceil(100 * 0.3 = 30) = 30. + # Workbook subsidized cell for two people, 0-75%, natural gas = 30, which + # is 30% of the unsubsidized two-person, 126.01-150%, natural gas cell + # (100). + ky_liheap: 30 +- name: Case 6, nine-person household qualifies under the additional-person limit. + period: 2026 + input: + people: + person1: + age: 41 + # $22.00 an hour, 40 hours a week: 22 * 2_080 = 45_760. + employment_income: 45_760 + person2: + age: 39 + # $16.00 an hour, 30 hours a week: 16 * 1_560 = 24_960. + employment_income: 24_960 + person3: + age: 70 + # $1_100 a month in Social Security retirement benefits. + social_security_retirement: 13_200 + ssi: 0 + person4: + age: 16 + # Earnings of a member under 18 are not counted. + employment_income: 2_400 + person5: + age: 14 + person6: + age: 11 + person7: + age: 9 + person8: + age: 6 + person9: + age: 3 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9] + heating_type: PROPANE + bottled_gas_expense: 1_800 + receives_housing_assistance: false + # The household does not receive K-TAP. + tanf: 0 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9] + state_code: KY + is_in_public_housing: false + output: + spm_unit_size: 9 + # 2025 guideline (one-year lag): 15_650 + 8 * 5_500 = 59_650. + ky_liheap_fpg: 59_650 + # 45_760 + 24_960 + 13_200 = 83_920, or 6_993.33 a month. + ky_liheap_income: 83_920 + # Eight-person limit: ceil(54_150 * 1.5 / 12 = 6_768.75) = 6_769. + # Additional person: ceil(5_500 * 1.5 / 12 = 687.5) = 688, the amount the + # fact sheet states. Nine-person limit: 6_769 + 688 = 7_457. + # 6_993.33 is above the eight-person limit and within the nine-person one. + ky_liheap_income_eligible: true + heating_expense: 1_800 + ky_liheap_eligible: true + # 6_993.33 a month is above ceil(59_650 * 1.25 / 12 = 6_213.54) = 6_214, so + # the 126.01-150% band gives 1 income point. + # min(9, 5) (size) + 1 (income) + 5 (propane) = 11 points; + # ceil(11 * 16.66 = 183.26) = 184, the workbook cell for five or more + # people, 126.01-150%, propane. + ky_liheap: 184 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap.yaml similarity index 63% rename from policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap.yaml rename to policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap.yaml index a0d2565a7bd..fc90aaee151 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap.yaml @@ -69,3 +69,35 @@ heating_type: [KEROSENE, OTHER, SOLAR, NONE, UNSPECIFIED] output: ky_liheap: [150, 0, 0, 0, 0] +- name: Case 7, subsidized cells where 30 percent of the unsubsidized amount is a whole dollar. + period: 2026 + input: + state_code: [KY, KY, KY, KY] + ky_liheap_eligible: [true, true, true, true] + spm_unit_size: [1, 2, 5, 5] + heating_type: [WOOD, NATURAL_GAS, ELECTRICITY, ELECTRICITY] + # Monthly income: 900, 1_200, 2_000 and 4_300. + ky_liheap_income: [10_800, 14_400, 24_000, 51_600] + receives_housing_assistance: [true, true, false, false] + is_in_public_housing: [false, false, true, true] + output: + # Each published subsidized cell is 30% of an unsubsidized cell, rounded + # up. These four have a whole-dollar product, so rounding must not add a + # dollar. + # Household 1: one person, wood, 0-75% (900 <= 979). + # 1 + 1 + 1 = 3 points; ceil(3 * 16.66 = 49.98) = 50; 50 * 0.3 = 15. + # Workbook subsidized cell = 15; unsubsidized one-person, 126.01-150%, + # wood cell = 50. + # Household 2: two people, natural gas, 0-75% (1_200 <= 1_322). + # 2 + 1 + 3 = 6 points; ceil(6 * 16.66 = 99.96) = 100; 100 * 0.3 = 30. + # Workbook subsidized cell = 30; unsubsidized two-person, 126.01-150%, + # natural gas cell = 100. + # Household 3: five people, electricity, 0-75% (2_000 <= 2_354). + # 5 + 1 + 6 = 12 points; ceil(12 * 16.66 = 199.92) = 200; 200 * 0.3 = 60. + # Workbook subsidized cell = 60; unsubsidized five-person, 126.01-150%, + # electricity cell = 200. + # Household 4: five people, electricity, 126.01-150% (4_300 > 3_922). + # 5 + 4 + 6 = 15 points; ceil(15 * 16.66 = 249.9) = 250; 250 * 0.3 = 75. + # Workbook subsidized cell = 75; unsubsidized five-person, 0-75%, + # electricity cell = 250. + ky_liheap: [15, 30, 60, 75] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_eligible.yaml new file mode 100644 index 00000000000..48a55d9fbc8 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_eligible.yaml @@ -0,0 +1,51 @@ +# Sources: Kentucky FY2026 state plan, regular heating benefit workbook, and fact sheet. +- name: Case 1, mixed-status household with citizen children counts every member. + period: 2026 + input: + people: + person1: + age: 38 + immigration_status: UNDOCUMENTED + # $12.50 an hour, 40 hours a week: 12.5 * 2_080 = 26_000. + employment_income: 26_000 + person2: + age: 36 + immigration_status: UNDOCUMENTED + person3: + age: 10 + immigration_status: CITIZEN + person4: + age: 6 + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [person1, person2, person3, person4] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_560 + receives_housing_assistance: false + # The household does not receive K-TAP. + tanf: 0 + households: + household: + members: [person1, person2, person3, person4] + state_code: KY + is_in_public_housing: false + output: + is_citizen_or_legal_immigrant: [false, false, true, true] + # Modeling assumption: one citizen or qualified member is enough, and all + # four members count for household size and income. + spm_unit_size: 4 + # The undocumented parent's wages are counted. + ky_liheap_income: 26_000 + # Fact sheet limit for four: ceil(32_150 * 1.5 / 12 = 4_018.75) = 4_019; + # income is 2_166.67 a month. + ky_liheap_income_eligible: true + heating_expense: 1_560 + ky_liheap_eligible: true + # 2_166.67 a month is above ceil(32_150 * 0.75 / 12 = 2_009.375) = 2_010 + # and not above ceil(32_150 / 12 = 2_679.17) = 2_680, so the 75.01-100% + # band gives 3 income points. + # 4 (size) + 3 (income) + 6 (electricity) = 13 points; + # ceil(13 * 16.66 = 216.58) = 217, the workbook cell for four people, + # 75.01-100%, electricity. + ky_liheap: 217 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml new file mode 100644 index 00000000000..4f31563d117 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml @@ -0,0 +1,132 @@ +# Sources: Kentucky FY2026 state plan, regular heating benefit workbook, and fact sheet. +- name: Adult business loss does not offset wages; child earnings are excluded + period: 2_026 + input: + people: + adult: + age: 35 + employment_income: 12_000 + self_employment_income: -3_000 + unemployment_compensation: 1_200 + strike_benefits: 600 + ssi: 0 + social_security: 0 + child: + age: 17 + employment_income: 4_000 + self_employment_income: 2_000 + child_support_received: 2_400 + ssi: 0 + social_security: 0 + spm_units: + unit: + members: [adult, child] + tanf: 0 + households: + household: + members: [adult, child] + state_code: KY + output: + ky_liheap_income: 16_200 +- name: Net business income is counted once without an additional expense deduction + period: 2_026 + input: + state_code: KY + age: 18 + employment_income: 12_000 + self_employment_income: 6_000 + ssi: 0 + social_security: 0 + tanf: 0 + output: + ky_liheap_income: 18_000 +- name: Reported Medicare premium deduction cannot make SSA income negative + period: 2_026 + input: + state_code: [KY, KY] + age: [70, 70] + social_security: [12_000, 1_000] + medicare_part_b_premiums_reported: [2_000, 2_000] + ssi: [0, 0] + tanf: [0, 0] + output: + ky_liheap_income: [10_000, 0] +- name: Case 4, a rental loss or a farm loss does not offset other income. + period: 2026 + input: + state_code: [KY, KY] + age: [60, 45] + # Household 1: a 1_000 a month pension and a net loss on a rented house. + taxable_private_pension_income: [12_000, 0] + rental_income: [-3_000, 0] + # Household 2: $13.00 an hour, 40 hours a week (13 * 2_080 = 27_040) and + # a net loss from a small farm. + employment_income: [0, 27_040] + farm_operations_income: [0, -5_000] + ssi: [0, 0] + tanf: [0, 0] + output: + # Each source is floored at zero before the sources are added. + # Household 1: max(12_000, 0) + max(-3_000, 0) = 12_000. + # Household 2: max(27_040, 0) + max(-5_000, 0) = 27_040. + ky_liheap_income: [12_000, 27_040] +- name: Case 5, SSI, State Supplementation and K-TAP are counted. + period: 2026 + input: + people: + person1: + age: 61 + # 2026 SSI federal benefit rate: 994 a month. + ssi: 11_928 + # State Supplementation for caretaker services at home: the 1_056 + # standard less 994 of SSI is 62 a month. + ky_ssp: 744 + person2: + age: 35 + person3: + age: 9 + spm_units: + spm_unit: + members: [person1, person2, person3] + # K-TAP maximum payment for two eligible persons: 293 a month. + tanf: 3_516 + households: + household: + members: [person1, person2, person3] + state_code: KY + output: + # 11_928 (SSI) + 744 (State Supplementation) + 3_516 (K-TAP) = 16_188. + ky_liheap_income: 16_188 +- name: Case 6, farm income, dividends, workers compensation and disability pay are counted. + period: 2026 + input: + people: + person1: + age: 58 + farm_operations_income: 14_400 + farm_rent_income: 3_600 + qualified_dividend_income: 240 + non_qualified_dividend_income: 60 + ssi: 0 + person2: + age: 55 + workers_compensation: 7_800 + disability_benefits: 2_400 + person3: + age: 16 + # Farm earnings of a member under 18 are not counted. + farm_operations_income: 1_500 + spm_units: + spm_unit: + members: [person1, person2, person3] + tanf: 0 + households: + household: + members: [person1, person2, person3] + state_code: KY + output: + # Adult farm earnings: 14_400. + # Unearned: 3_600 (farm rent) + 240 + 60 (ordinary dividends) + # + 7_800 (workers compensation) + 2_400 (disability pay) = 14_100. + # 14_400 + 14_100 = 28_500. + ky_liheap_income: 28_500 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.yaml similarity index 59% rename from policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.yaml rename to policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.yaml index 5d65fcf99f0..180b7e07c38 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.yaml @@ -15,3 +15,18 @@ ky_liheap_income: [23_496, 31_740, 39_996, 48_240, 56_496, 64_740, 72_996, 81_240, 89_496, 97_752] output: ky_liheap_income_eligible: [false, false, false, false, false, false, false, false, false, false] +- name: Case 3, twelve-person limit adds four additional-person amounts. + period: 2026 + input: + state_code: [KY, KY] + spm_unit_size: [12, 12] + # 9_521 and 9_522 a month. + ky_liheap_income: [114_252, 114_264] + output: + # 2025 guideline (one-year lag): 54_150 for eight people, 5_500 for each + # additional person. + # Eight-person limit: ceil(54_150 * 1.5 / 12 = 6_768.75) = 6_769. + # Additional person: ceil(5_500 * 1.5 / 12 = 687.5) = 688, the amount the + # fact sheet states. + # Twelve-person limit: 6_769 + 4 * 688 = 9_521 a month. + ky_liheap_income_eligible: [true, false] diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.py b/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.py deleted file mode 100644 index ce5d414eed5..00000000000 --- a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income_eligible.py +++ /dev/null @@ -1,32 +0,0 @@ -from policyengine_us.model_api import * -from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg - - -class ky_liheap_income_eligible(Variable): - value_type = bool - entity = SPMUnit - definition_period = YEAR - label = "Kentucky LIHEAP income eligibility" - defined_for = StateCode.KY - reference = "https://www.mkcap.org/uploads/3/4/8/3/34834615/2025-2026-liheap-fact-sheet-v2.jpg" - - def formula(spm_unit, period, parameters): - p = parameters(period).gov.states.ky.chfs.liheap - size = spm_unit("spm_unit_size", period) - state_group = spm_unit.household("state_group_str", period) - extra = max_(size - p.maximum_table_size, 0) - # The fact sheet specifies $688 above size eight. The workbook's $687 - # extension conflicts with that explicit eligibility instruction. - table_fpg = fpg( - clip(size, 1, p.maximum_table_size), - state_group, - period, - parameters, - year_lag=p.fpg_year_lag, - ) - monthly_limit = ( - np.ceil(table_fpg * p.income_limit / MONTHS_IN_YEAR) - + extra * p.additional_person_income_limit - ) - income = spm_unit("ky_liheap_income", period) / MONTHS_IN_YEAR - return income <= monthly_limit diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap.py b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap.py similarity index 83% rename from policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap.py rename to policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap.py index 632a4468758..a76f67c5219 100644 --- a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap.py +++ b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap.py @@ -15,17 +15,18 @@ class ky_liheap(Variable): reference = "https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx" def formula(spm_unit, period, parameters): - p = parameters(period).gov.states.ky.chfs.liheap.payment + p = parameters(period).gov.states.ky.dcbs.liheap.payment income = spm_unit("ky_liheap_income", period) / MONTHS_IN_YEAR fpg = spm_unit("ky_liheap_fpg", period) / MONTHS_IN_YEAR size = spm_unit("spm_unit_size", period) fuel = spm_unit("heating_type", period) types = fuel.possible_values - subsidized = ( - spm_unit("receives_housing_assistance", period) - | spm_unit.household("is_in_public_housing", period) - | (spm_unit.household("hud_utility_allowance", period) > 0) - ) + # 921 KAR 4:116 Section 4(1)(e) lowers benefits for federally assisted + # housing or receipt of a utility allowance. A household receiving a + # utility allowance is already an assisted household. + subsidized = spm_unit( + "receives_housing_assistance", period + ) | spm_unit.household("is_in_public_housing", period) band = 0 for rate in p.income_bands: band = band + (income > np.ceil(fpg * rate)) diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_eligible.py b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_eligible.py similarity index 52% rename from policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_eligible.py rename to policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_eligible.py index 0705190fb06..f4556d409ad 100644 --- a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_eligible.py @@ -17,11 +17,13 @@ def formula(spm_unit, period, parameters): has_eligible_member = spm_unit.any( spm_unit.members("is_citizen_or_legal_immigrant", period) ) - # 921 KAR 4:116 Section 1(10) defines the energy-purchasing household; - # it does not establish the treatment of ineligible immigrants. Counting - # all SPM members for size while requiring one qualified member is a - # provisional assumption, not a verified Kentucky mixed-status rule. - # Confirm household-size and income adjustments with the operational - # manual before treating this pathway as supported. Documentation and - # enrollment windows are unmodeled. No categorical or resource test applies. + # The "at least one qualified member" gate is a modeling assumption with + # no Kentucky source. 921 KAR 4:116 Section 2(1)(d) requires a Social + # Security number or permanent residency card for each household member, + # and Section 2(2) treats the application as incomplete until that is + # received; neither says whether a mixed-status household is denied, + # served in full, or served without that member. Section 1(10) defines + # the energy-purchasing household with no immigration carve-out, so all + # SPM members count for size and income. Documentation and enrollment + # windows are unmodeled. No categorical or resource test applies. return income_eligible & responsible & has_eligible_member diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_fpg.py b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_fpg.py similarity index 82% rename from policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_fpg.py rename to policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_fpg.py index 08d2344c085..aaae247a81b 100644 --- a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_fpg.py +++ b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_fpg.py @@ -9,10 +9,10 @@ class ky_liheap_fpg(Variable): unit = USD label = "Kentucky LIHEAP annual federal poverty guideline" defined_for = StateCode.KY - reference = "https://www.mkcap.org/uploads/3/4/8/3/34834615/2025-2026-liheap-fact-sheet-v2.jpg" + reference = "https://www.capky.org/wp-content/uploads/2026/01/2025-2026-LIHEAP-Fact-Sheet-V2.pdf" def formula(spm_unit, period, parameters): - p = parameters(period).gov.states.ky.chfs.liheap + p = parameters(period).gov.states.ky.dcbs.liheap size = spm_unit("spm_unit_size", period) state_group = spm_unit.household("state_group_str", period) # The annual period represents the heating season ending in that year. diff --git a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income.py b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py similarity index 55% rename from policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income.py rename to policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py index 80d0ab92088..4377acb355a 100644 --- a/policyengine_us/variables/gov/states/ky/chfs/liheap/ky_liheap_income.py +++ b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py @@ -9,22 +9,28 @@ class ky_liheap_income(Variable): label = "Kentucky LIHEAP countable household income" defined_for = StateCode.KY reference = ( - "https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=5,6,7", + "https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=5,6,7,34", "https://apps.legislature.ky.gov/law/kar/titles/921/004/116/", ) def formula(spm_unit, period, parameters): - p = parameters(period).gov.states.ky.chfs.liheap + p = parameters(period).gov.states.ky.dcbs.liheap person = spm_unit.members # Annual income divided by twelve approximates the calendar month before # application. SPM units approximate the energy-purchasing household. adult = person("age", period) >= p.earned_income_age - # Use existing net business income, without another business deduction. + # Use existing net business and farm income, without another business + # deduction. 921 KAR 4:116 Section 1(8) counts gross income received, so + # each earned and unearned source is floored at zero and a loss in one + # source cannot offset another. earned = ( max_(person("employment_income", period), 0) + max_(person("self_employment_income", period), 0) + + max_(person("farm_operations_income", period), 0) ) * adult - unearned = add(person, period, p.unearned_income_sources) + unearned = 0 + for source in p.unearned_income_sources: + unearned = unearned + max_(person(source, period), 0) # Reported Part B premiums approximate the Medicare deduction from SSA; # modeled premium liability does not establish an actual withholding. ssa = max_( @@ -33,9 +39,10 @@ def formula(spm_unit, period, parameters): 0, ) # Detailed excluded WIA/work-study earnings, jury duty, settlements, - # insurance payments, royalties, deposits, and non-taxable refunds cannot - # be isolated with existing inputs. Do not add new inputs for this draft. - # 921 KAR 4:116 Section 1(8) counts income received. The annual tanf - # aggregate includes ky_ktap and applies take-up; ky_ktap alone represents - # monthly entitlement and would count benefits a nonrecipient could get. - return spm_unit.sum(earned + max_(unearned, 0) + ssa) + spm_unit("tanf", period) + # other insurance payments, royalties, deposits, and non-taxable refunds + # cannot be isolated with existing inputs. Do not add new inputs for this + # draft. + # The annual tanf aggregate includes ky_ktap and applies take-up; ky_ktap + # alone represents monthly entitlement and would count benefits a + # nonrecipient could get. + return spm_unit.sum(earned + unearned + ssa) + spm_unit("tanf", period) diff --git a/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.py b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.py new file mode 100644 index 00000000000..43154e7a10f --- /dev/null +++ b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.py @@ -0,0 +1,43 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg + + +class ky_liheap_income_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Kentucky LIHEAP income eligibility" + defined_for = StateCode.KY + reference = "https://www.capky.org/wp-content/uploads/2026/01/2025-2026-LIHEAP-Fact-Sheet-V2.pdf" + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.ky.dcbs.liheap + size = spm_unit("spm_unit_size", period) + state_group = spm_unit.household("state_group_str", period) + table_size = clip(size, 1, p.maximum_table_size) + additional_people = max_(size - p.maximum_table_size, 0) + guideline = fpg( + table_size, state_group, period, parameters, year_lag=p.fpg_year_lag + ) + additional_guideline = ( + fpg( + table_size + 1, + state_group, + period, + parameters, + year_lag=p.fpg_year_lag, + ) + - guideline + ) + # The fact sheet states "Add $688 for each additional family member" + # without a derivation. Rounding the additional-person guideline up at + # the income limit rate reproduces $688 for FY2026 and all eight + # published limits. The workbook's own table for sizes 9-14 rounds each + # whole limit instead and differs by up to $3 (for example $8,144 + # against $8,145 at size 10); the fact sheet is followed. + monthly_limit = np.ceil(guideline * p.income_limit / MONTHS_IN_YEAR) + monthly_increment = np.ceil( + additional_guideline * p.income_limit / MONTHS_IN_YEAR + ) + income = spm_unit("ky_liheap_income", period) / MONTHS_IN_YEAR + return income <= monthly_limit + additional_people * monthly_increment From 63625186605603ec4920d83b9342fdba482f20b4 Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 30 Sep 2026 23:50:58 -0400 Subject: [PATCH 4/8] Stop counting workers' compensation and disability benefits in Kentucky LIHEAP The FY2026 plan's Section 1.9 has no dedicated row for either source, so they are recorded as a documented gap instead of counted income. Update the income test case accordingly. Co-Authored-By: Claude Fable 5.1 --- .../ky/dcbs/liheap/unearned_income_sources.yaml | 7 ++----- .../gov/states/ky/dcbs/liheap/ky_liheap_income.yaml | 11 ++++------- 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml index f2a3e24dcb4..1355dc50300 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml @@ -18,11 +18,8 @@ values: - child_support_received - interest_income - ordinary_dividend_income - # The plan has no dedicated row for workers' compensation or employer - # disability pay. Section 1.9 counts insurance payments made directly to - # the insured, and 921 KAR 4:116 Section 1(8) counts all unearned income. - - workers_compensation - - disability_benefits + # Workers' compensation and employer disability pay are not counted: plan + # Section 1.9 has no dedicated row for them. This is a documented gap. - veterans_benefits - salt_refund_income metadata: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml index 4f31563d117..abdb1bd20aa 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml @@ -97,7 +97,7 @@ output: # 11_928 (SSI) + 744 (State Supplementation) + 3_516 (K-TAP) = 16_188. ky_liheap_income: 16_188 -- name: Case 6, farm income, dividends, workers compensation and disability pay are counted. +- name: Case 6, farm operations income, farm rent and dividends are counted. period: 2026 input: people: @@ -110,8 +110,6 @@ ssi: 0 person2: age: 55 - workers_compensation: 7_800 - disability_benefits: 2_400 person3: age: 16 # Farm earnings of a member under 18 are not counted. @@ -126,7 +124,6 @@ state_code: KY output: # Adult farm earnings: 14_400. - # Unearned: 3_600 (farm rent) + 240 + 60 (ordinary dividends) - # + 7_800 (workers compensation) + 2_400 (disability pay) = 14_100. - # 14_400 + 14_100 = 28_500. - ky_liheap_income: 28_500 + # Unearned: 3_600 (farm rent) + 240 + 60 (ordinary dividends) = 3_900. + # 14_400 + 3_900 = 18_300. + ky_liheap_income: 18_300 From 88403030b17bb62800ec9ed2d83d130bee61113e Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 1 Oct 2026 01:24:55 -0400 Subject: [PATCH 5/8] Count omitted business and wage replacement income in Kentucky LIHEAP --- .../dcbs/liheap/unearned_income_sources.yaml | 6 +++-- .../ky/dcbs/liheap/ky_liheap_income.yaml | 27 ++++++++++++++++++- .../states/ky/dcbs/liheap/ky_liheap_income.py | 1 + 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml index 1355dc50300..84c5857c4bd 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml @@ -18,8 +18,10 @@ values: - child_support_received - interest_income - ordinary_dividend_income - # Workers' compensation and employer disability pay are not counted: plan - # Section 1.9 has no dedicated row for them. This is a documented gap. + # 921 KAR 4:116 Section 1(8) includes earned and unearned income; plan + # Section 1.9 counts insurance payments made directly to the insured. + - workers_compensation + - disability_benefits - veterans_benefits - salt_refund_income metadata: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml index abdb1bd20aa..5e446e56b47 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml @@ -7,6 +7,7 @@ age: 35 employment_income: 12_000 self_employment_income: -3_000 + sstb_self_employment_income: -1_000 unemployment_compensation: 1_200 strike_benefits: 600 ssi: 0 @@ -15,6 +16,7 @@ age: 17 employment_income: 4_000 self_employment_income: 2_000 + sstb_self_employment_income: 2_000 child_support_received: 2_400 ssi: 0 social_security: 0 @@ -35,11 +37,13 @@ age: 18 employment_income: 12_000 self_employment_income: 6_000 + sstb_self_employment_income: 3_000 ssi: 0 social_security: 0 tanf: 0 output: - ky_liheap_income: 18_000 + # 12,000 wages + 6,000 ordinary business + 3,000 specified service business. + ky_liheap_income: 21_000 - name: Reported Medicare premium deduction cannot make SSA income negative period: 2_026 input: @@ -127,3 +131,24 @@ # Unearned: 3_600 (farm rent) + 240 + 60 (ordinary dividends) = 3_900. # 14_400 + 3_900 = 18_300. ky_liheap_income: 18_300 + +- name: Case 7, service business profits and wage replacement income can exceed the income limit. + # 921 KAR 4:116 Section 1(8) counts earned and unearned income. Plan + # Section 1.9 includes insurance payments made directly to the insured. + period: 2026 + input: + state_code: [KY, KY, KY] + age: [40, 40, 40] + sstb_self_employment_income: [60_000, 0, 0] + workers_compensation: [0, 30_000, 0] + disability_benefits: [0, 0, 30_000] + ssi: [0, 0, 0] + social_security: [0, 0, 0] + tanf: [0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_000, 1_000, 1_000] + output: + ky_liheap_income: [60_000, 30_000, 30_000] + # Each exceeds the one-person monthly ceiling of 1,957, annualized. + ky_liheap_eligible: [false, false, false] + ky_liheap: [0, 0, 0] diff --git a/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py index 4377acb355a..c5d4ce6d766 100644 --- a/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py +++ b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py @@ -26,6 +26,7 @@ def formula(spm_unit, period, parameters): earned = ( max_(person("employment_income", period), 0) + max_(person("self_employment_income", period), 0) + + max_(person("sstb_self_employment_income", period), 0) + max_(person("farm_operations_income", period), 0) ) * adult unearned = 0 From 568a9f79e9be2722eb1e3447f4bc0addc332ac3a Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 1 Oct 2026 17:43:24 -0400 Subject: [PATCH 6/8] Shorten the Kentucky LIHEAP registry note Co-Authored-By: Claude Fable 5.1 --- policyengine_us/programs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 6b55d6c4c4d..5205578cb74 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -830,7 +830,7 @@ programs: variable: ky_liheap parameter_prefix: gov.states.ky.dcbs.liheap verified_years: "2026" - notes: Regular heating only, FY2026 coverage. Uses the published regular benefit worksheet despite its stale title and anomalous cells. Annual income and SPM units approximate application-month energy households. Detailed income exclusions, lump sums, mixed-immigration adjustments, and administrative eligibility remain incomplete; no new inputs are added. Earlier results use parameter backfilling and are unverified historical estimates. + notes: FY2026 regular heating only, from the published benefit worksheet. Crisis and cooling assistance are out of scope. - state: TX status: partial name: Texas CEAP From e850fa8bf42e38707ce2b8ee9bc425b86f5a4d30 Mon Sep 17 00:00:00 2001 From: Ziming Date: Fri, 2 Oct 2026 12:35:30 -0400 Subject: [PATCH 7/8] Address Kentucky LIHEAP review Require a Social Security number or permanent residency card for every household member (921 KAR 4:116 Sections 2(1)(d), 2(2) and 8(3)), keeping the citizen or qualified member requirement. Count partnership and S corporation income and railroad benefits, move both source lists into parameters, and count Social Security gross with the Medicare premium deduction left as a documented gap. Date the parameter values from FY2025 with FY2025 plan and workbook references and record verified years 2025-2026. Name the worksheets and cells that hold each value, mark the regulation that conflicts with the subsidized ordering, use single-page references, fix test periods and names, and add coal, documentation and FY2025 cases. Co-Authored-By: Claude Fable 5.1 --- changelog.d/ky-liheap.added.md | 2 +- .../ky/dcbs/liheap/earned_income_age.yaml | 8 +- .../ky/dcbs/liheap/earned_income_sources.yaml | 21 +++ .../states/ky/dcbs/liheap/fpg_year_lag.yaml | 12 +- .../states/ky/dcbs/liheap/income_limit.yaml | 8 +- .../ky/dcbs/liheap/maximum_table_size.yaml | 10 +- .../dcbs/liheap/payment/coal_adjustment.yaml | 18 +- .../liheap/payment/dollars_per_point.yaml | 8 +- .../ky/dcbs/liheap/payment/fuel_points.yaml | 28 +-- .../ky/dcbs/liheap/payment/income_bands.yaml | 8 +- .../payment/income_points/subsidized.yaml | 29 ++-- .../payment/income_points/unsubsidized.yaml | 22 +-- .../payment/maximum_household_points.yaml | 10 +- .../payment/subsidized_housing_rate.yaml | 8 +- .../dcbs/liheap/unearned_income_sources.yaml | 19 +- policyengine_us/programs.yaml | 6 +- .../states/ky/dcbs/liheap/integration.yaml | 62 +++++-- .../gov/states/ky/dcbs/liheap/ky_liheap.yaml | 49 ++++-- .../ky/dcbs/liheap/ky_liheap_eligible.yaml | 162 ++++++++++++++++-- .../ky/dcbs/liheap/ky_liheap_income.yaml | 55 ++++-- .../liheap/ky_liheap_income_eligible.yaml | 8 +- .../gov/states/ky/dcbs/liheap/ky_liheap.py | 32 ++-- .../ky/dcbs/liheap/ky_liheap_eligible.py | 45 +++-- .../states/ky/dcbs/liheap/ky_liheap_income.py | 31 ++-- .../dcbs/liheap/ky_liheap_income_eligible.py | 8 +- 25 files changed, 489 insertions(+), 180 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/ky/dcbs/liheap/earned_income_sources.yaml diff --git a/changelog.d/ky-liheap.added.md b/changelog.d/ky-liheap.added.md index 770c9542f64..c5b821efd4a 100644 --- a/changelog.d/ky-liheap.added.md +++ b/changelog.d/ky-liheap.added.md @@ -1 +1 @@ -Add partial FY2026 Kentucky LIHEAP regular heating assistance using existing inputs. +Add Kentucky LIHEAP regular heating assistance for FY2025 and FY2026. diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/earned_income_age.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/earned_income_age.yaml index 253b9d04700..f7ebe5568fc 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/earned_income_age.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/earned_income_age.yaml @@ -1,10 +1,12 @@ -description: Kentucky counts earned income beginning at this age under the Low Income Home Energy Assistance Program. +description: Kentucky excludes the earned income of household members below this age under the Low Income Home Energy Assistance Program. values: - 2025-10-01: 18 + 2024-10-01: 18 metadata: unit: year period: year label: Kentucky LIHEAP earned income age reference: - - title: Kentucky FY2026 state plan, Section 1.9 + - title: Kentucky FY2026 state plan, Section 1.9, page 6 (earned income of a child under 18 not counted) href: https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=6 + - title: Kentucky FY2025 state plan, Section 1.9, page 6 + href: https://liheapch.acf.gov/docs/2025/state-plans/KY_Plan_2025.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/earned_income_sources.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/earned_income_sources.yaml new file mode 100644 index 00000000000..0d4219e68bb --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/earned_income_sources.yaml @@ -0,0 +1,21 @@ +description: Kentucky counts these earned income sources under the Low Income Home Energy Assistance Program. +values: + 2024-10-01: + - employment_income + # Net business and farm income are counted without another business + # deduction. + - self_employment_income + - sstb_self_employment_income + - partnership_s_corp_income + - farm_operations_income +metadata: + unit: list + period: year + label: Kentucky LIHEAP earned income sources + reference: + - title: Kentucky FY2026 state plan, Section 1.9, page 5 (wages, self-employment income and contract income) + href: https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=5 + - title: Kentucky FY2025 state plan, Section 1.9, page 5 + href: https://liheapch.acf.gov/docs/2025/state-plans/KY_Plan_2025.pdf#page=5 + - title: 921 KAR 4:116 Section 1(8) + href: https://apps.legislature.ky.gov/law/kar/titles/921/004/116/ diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/fpg_year_lag.yaml index 68438d86924..5c8e3e3268e 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/fpg_year_lag.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/fpg_year_lag.yaml @@ -1,16 +1,18 @@ description: Kentucky uses the federal poverty guidelines from this many years before the heating season ending year under the Low Income Home Energy Assistance Program. # The fact sheet publishes the limits without naming the guideline year. The # lag is inferred from the amounts (1,957 = 150% of the 2025 one-person -# guideline / 12, rounded up); the workbook's poverty table shows the same -# 2025 guideline. +# guideline / 12, rounded up); each workbook's poverty table shows the same +# one-year lag. values: - 2025-10-01: 1 + 2024-10-01: 1 metadata: unit: year period: year - label: Kentucky LIHEAP fpg year lag + label: Kentucky LIHEAP federal poverty guideline year lag reference: - title: Kentucky FY2025-2026 LIHEAP fact sheet, income eligibility href: https://www.capky.org/wp-content/uploads/2026/01/2025-2026-LIHEAP-Fact-Sheet-V2.pdf - - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet, poverty guideline table + - title: Kentucky FY2026 benefit workbook, rev benefit income range worksheet, poverty guideline table href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx + - title: Kentucky FY2025 benefit workbook, subsidy benefits worksheet, poverty guideline table + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/KY_BenefitMatrix_2025.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/income_limit.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/income_limit.yaml index 5c10ee10a7e..fab713ae551 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/income_limit.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/income_limit.yaml @@ -1,10 +1,14 @@ description: Kentucky limits gross monthly household income to this share of the federal poverty guideline under the Low Income Home Energy Assistance Program. values: - 2025-10-01: 1.5 + 2024-10-01: 1.5 metadata: unit: /1 period: year label: Kentucky LIHEAP income limit reference: - - title: Kentucky FY2026 state plan, Section 2.1 + - title: Kentucky FY2026 state plan, Section 2.1, page 8 href: https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=8 + - title: Kentucky FY2025 state plan, Section 2.1, page 8 + href: https://liheapch.acf.gov/docs/2025/state-plans/KY_Plan_2025.pdf#page=8 + - title: 921 KAR 4:116 Section 3(1)(b) (cabinet-approved increase above the 130 percent base) + href: https://apps.legislature.ky.gov/law/kar/titles/921/004/116/ diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/maximum_table_size.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/maximum_table_size.yaml index de8308f14b3..64343a6fd4e 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/maximum_table_size.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/maximum_table_size.yaml @@ -1,10 +1,14 @@ -description: Kentucky publishes monthly income limits through this household size under the Low Income Home Energy Assistance Program. +description: Kentucky sets income limits by household size up to this size under the Low Income Home Energy Assistance Program. values: - 2025-10-01: 8 + 2024-10-01: 8 metadata: unit: person period: year label: Kentucky LIHEAP maximum table size reference: - - title: Kentucky FY2025-2026 LIHEAP fact sheet, income eligibility + - title: Kentucky FY2025-2026 LIHEAP fact sheet, income eligibility (sizes 1 to 8) href: https://www.capky.org/wp-content/uploads/2026/01/2025-2026-LIHEAP-Fact-Sheet-V2.pdf + - title: Kentucky FY2026 benefit workbook, rev benefit income range worksheet + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx + - title: Kentucky FY2025 benefit workbook, subsidy benefits worksheet + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/KY_BenefitMatrix_2025.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/coal_adjustment.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/coal_adjustment.yaml index 76920c5ee6c..fc998431f15 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/coal_adjustment.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/coal_adjustment.yaml @@ -1,17 +1,17 @@ -description: Kentucky adjusts coal heating-payment points by household size to reproduce its published Low Income Home Energy Assistance Program matrix. +description: Kentucky sets this coal heating point adjustment by household size under the Low Income Home Energy Assistance Program. brackets: - threshold: - 2025-10-01: 0 + 2024-10-01: 0 amount: - 2025-10-01: 0 + 2024-10-01: 0 - threshold: - 2025-10-01: 2 + 2024-10-01: 2 amount: - 2025-10-01: -1 + 2024-10-01: -1 - threshold: - 2025-10-01: 3 + 2024-10-01: 3 amount: - 2025-10-01: 0 + 2024-10-01: 0 metadata: type: single_amount threshold_unit: person @@ -19,5 +19,7 @@ metadata: period: year label: Kentucky LIHEAP coal household-size adjustment reference: - - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet, coal column + - title: Kentucky FY2026 benefit workbook, Subsidy points worksheet, cells H8-H11 (two-person coal rows) href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx + - title: Kentucky FY2025 benefit workbook, Subsidy points worksheet, cells H8-H11 + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/KY_BenefitMatrix_2025.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/dollars_per_point.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/dollars_per_point.yaml index da32bda18b9..59e6fc81c09 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/dollars_per_point.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/dollars_per_point.yaml @@ -1,10 +1,12 @@ -description: Kentucky pays this amount per point before rounding up to whole dollars under the Low Income Home Energy Assistance Program. +description: Kentucky provides this amount per heating payment point, rounded up to whole dollars, under the Low Income Home Energy Assistance Program. values: - 2025-10-01: 16.66 + 2024-10-01: 16.66 metadata: unit: currency-USD period: year label: Kentucky LIHEAP dollars per point reference: - - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + - title: Kentucky FY2026 benefit workbook, rev benefit income range worksheet, column L href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx + - title: Kentucky FY2025 benefit workbook, subsidy benefits worksheet, column L + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/KY_BenefitMatrix_2025.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/fuel_points.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/fuel_points.yaml index 5cd57bd2a43..fcb790ed2e6 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/fuel_points.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/fuel_points.yaml @@ -1,26 +1,26 @@ -description: Kentucky assigns these heating-payment points by primary heating fuel under the Low Income Home Energy Assistance Program. +description: Kentucky sets these heating payment points by primary heating fuel under the Low Income Home Energy Assistance Program. UNSPECIFIED: - 2025-10-01: 0 + 2024-10-01: 0 ELECTRICITY: - 2025-10-01: 6 + 2024-10-01: 6 NATURAL_GAS: - 2025-10-01: 3 + 2024-10-01: 3 FUEL_OIL: - 2025-10-01: 4 + 2024-10-01: 4 KEROSENE: - 2025-10-01: 4 + 2024-10-01: 4 PROPANE: - 2025-10-01: 5 + 2024-10-01: 5 WOOD: - 2025-10-01: 1 + 2024-10-01: 1 COAL: - 2025-10-01: 2 + 2024-10-01: 2 SOLAR: - 2025-10-01: 0 + 2024-10-01: 0 OTHER: - 2025-10-01: 0 + 2024-10-01: 0 NONE: - 2025-10-01: 0 + 2024-10-01: 0 metadata: unit: /1 period: year @@ -28,5 +28,7 @@ metadata: breakdown: - heating_type reference: - - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + - title: Kentucky FY2026 benefit workbook, Points Description worksheet, cells J2-J7 href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx + - title: Kentucky FY2025 benefit workbook, points worksheet, cells J2-J7 + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/KY_BenefitMatrix_2025.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_bands.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_bands.yaml index 2987f059c59..bae35f2a9b0 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_bands.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_bands.yaml @@ -1,10 +1,12 @@ -description: Kentucky uses these federal poverty guideline shares as the upper boundaries of its first three heating-payment income bands under the Low Income Home Energy Assistance Program. +description: Kentucky uses these federal poverty guideline shares as the upper boundaries of its first three heating payment income bands under the Low Income Home Energy Assistance Program. values: - 2025-10-01: [0.75, 1, 1.25] + 2024-10-01: [0.75, 1, 1.25] metadata: unit: /1 period: year label: Kentucky LIHEAP income bands reference: - - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + - title: Kentucky FY2026 benefit workbook, rev benefit income range worksheet, income range boundaries (cells Q10-S10) href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx + - title: Kentucky FY2025 benefit workbook, subsidy benefits worksheet, income range boundaries (cells Q27-S27) + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/KY_BenefitMatrix_2025.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/subsidized.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/subsidized.yaml index 869254fbe9e..0a99e65fe43 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/subsidized.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/subsidized.yaml @@ -1,27 +1,28 @@ -description: Kentucky uses these income points to reproduce its published subsidized-housing heating payments under the Low Income Home Energy Assistance Program. -# These values reproduce the published FY2026 workbook. Its subsidized rows +description: Kentucky provides these income points by monthly income band to households in subsidized housing under the Low Income Home Energy Assistance Program. +# These values reproduce the published workbooks. Their subsidized rows # reference the unsubsidized rows in reverse order (the lowest-income # subsidized row takes 30% of the highest-income unsubsidized row, and so on), # so the published subsidized payment rises with income. 921 KAR 4:116 Section # 4(1)(c) says the household with the lowest income receives the highest -# benefit. Which ordering agencies pay is unconfirmed. +# benefit. The FY2025 and FY2026 workbooks both use the reversed ordering; +# which ordering agencies pay is unconfirmed. brackets: - threshold: - 2025-10-01: 0 + 2024-10-01: 0 amount: - 2025-10-01: 1 + 2024-10-01: 1 - threshold: - 2025-10-01: 1 + 2024-10-01: 1 amount: - 2025-10-01: 2 + 2024-10-01: 2 - threshold: - 2025-10-01: 2 + 2024-10-01: 2 amount: - 2025-10-01: 3 + 2024-10-01: 3 - threshold: - 2025-10-01: 3 + 2024-10-01: 3 amount: - 2025-10-01: 4 + 2024-10-01: 4 metadata: type: single_amount threshold_unit: /1 @@ -29,7 +30,9 @@ metadata: period: year label: Kentucky LIHEAP subsidized housing income points reference: - - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + - title: Kentucky FY2026 benefit workbook, rev benefit income range worksheet, cells E52-E55 (30 percent of the unsubsidized rows in reverse order) href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx - - title: 921 KAR 4:116 Section 4(1)(c) + - title: Kentucky FY2025 benefit workbook, subsidy benefits worksheet, cells E45-E48 (same reversed ordering) + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/KY_BenefitMatrix_2025.xlsx + - title: 921 KAR 4:116 Section 4(1)(c) (lowest income receives the highest benefit; conflicts with the workbook ordering) href: https://apps.legislature.ky.gov/law/kar/titles/921/004/116/ diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/unsubsidized.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/unsubsidized.yaml index 9db8d6232a7..1b9da8a6a09 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/unsubsidized.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/income_points/unsubsidized.yaml @@ -1,21 +1,21 @@ -description: Kentucky assigns these income points for heating assistance to households in unsubsidized housing, by monthly income band, under the Low Income Home Energy Assistance Program. +description: Kentucky provides these income points by monthly income band to households outside subsidized housing under the Low Income Home Energy Assistance Program. brackets: - threshold: - 2025-10-01: 0 + 2024-10-01: 0 amount: - 2025-10-01: 4 + 2024-10-01: 4 - threshold: - 2025-10-01: 1 + 2024-10-01: 1 amount: - 2025-10-01: 3 + 2024-10-01: 3 - threshold: - 2025-10-01: 2 + 2024-10-01: 2 amount: - 2025-10-01: 2 + 2024-10-01: 2 - threshold: - 2025-10-01: 3 + 2024-10-01: 3 amount: - 2025-10-01: 1 + 2024-10-01: 1 metadata: type: single_amount threshold_unit: /1 @@ -23,5 +23,7 @@ metadata: period: year label: Kentucky LIHEAP unsubsidized housing income points reference: - - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + - title: Kentucky FY2026 benefit workbook, Points Description worksheet, cells H2-H5 href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx + - title: Kentucky FY2025 benefit workbook, points worksheet, cells H2-H5 + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/KY_BenefitMatrix_2025.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/maximum_household_points.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/maximum_household_points.yaml index 2aa26fb76c0..927fd5a86cf 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/maximum_household_points.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/maximum_household_points.yaml @@ -1,10 +1,14 @@ -description: Kentucky caps the household-size contribution to heating payment points at this amount under the Low Income Home Energy Assistance Program. +description: Kentucky limits household size points to this amount under the Low Income Home Energy Assistance Program. values: - 2025-10-01: 5 + 2024-10-01: 5 metadata: unit: /1 period: year label: Kentucky LIHEAP maximum household points reference: - - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + - title: Kentucky FY2026 benefit workbook, Subsidy points worksheet, cell E28 (5 + 6 + 4) href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx + - title: Kentucky FY2025 benefit workbook, Subsidy points worksheet, cell E28 + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/KY_BenefitMatrix_2025.xlsx + - title: Kentucky FY2026 state plan, Section 2.6, page 9 ($250 maximum benefit) + href: https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=9 diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/subsidized_housing_rate.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/subsidized_housing_rate.yaml index 88cc4612f6b..5743af5f2c7 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/subsidized_housing_rate.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/payment/subsidized_housing_rate.yaml @@ -1,10 +1,12 @@ -description: Kentucky reduces the published subsidized-housing heating payment to this share of its point-based amount before rounding up under the Low Income Home Energy Assistance Program. +description: Kentucky provides this share of the point-based heating payment to households in subsidized housing under the Low Income Home Energy Assistance Program. values: - 2025-10-01: 0.3 + 2024-10-01: 0.3 metadata: unit: /1 period: year label: Kentucky LIHEAP subsidized housing rate reference: - - title: Kentucky FY2026 benefit matrix, rev benefit income range worksheet + - title: Kentucky FY2026 benefit workbook, rev benefit income range worksheet, cell E52 (ROUNDUP(E8*0.3,0)) href: https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx + - title: Kentucky FY2025 benefit workbook, subsidy benefits worksheet, cell E45 (ROUNDUP(E6*0.3,0)) + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/KY_BenefitMatrix_2025.xlsx diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml index 84c5857c4bd..759e79bfc35 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml @@ -1,13 +1,22 @@ -description: Kentucky counts these available person-level unearned income sources under the Low Income Home Energy Assistance Program. +description: Kentucky counts these unearned income sources under the Low Income Home Energy Assistance Program. values: - 2025-10-01: + 2024-10-01: - unemployment_compensation - strike_benefits + # The FY2026 plan counts Social Security excluding the Medicare deduction + # (the FY2025 plan counted it including the deduction). The premium is not + # deducted yet: neither medicare_part_b_premium nor + # medicare_part_b_premiums_reported is wired in. + - social_security - ssi # Plan Section 17.4 lists the DCBS State Supplementation award letter as # income documentation. - ky_ssp + # Military retirement pay is taxable pension income, so pension_income + # already covers it. - pension_income + # Plan Section 17.4 lists Railroad Retirement records. + - railroad_benefits - retirement_distributions - financial_assistance - gambling_winnings @@ -29,7 +38,9 @@ metadata: period: year label: Kentucky LIHEAP unearned income sources reference: - - title: Kentucky FY2026 state plan, Sections 1.9 and 17.4 - href: https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=5,6,7,34 + - title: Kentucky FY2026 state plan, Section 1.9, page 6, and Section 17.4, page 34 + href: https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=6 + - title: Kentucky FY2025 state plan, Section 1.9, page 6 + href: https://liheapch.acf.gov/docs/2025/state-plans/KY_Plan_2025.pdf#page=6 - title: 921 KAR 4:116 Section 1(8) href: https://apps.legislature.ky.gov/law/kar/titles/921/004/116/ diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 5205578cb74..eed40dad942 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, TX, KY + coverage: OR, DC, Riverside County, MA, IL, KY, TX state_implementations: - state: OR status: in_progress @@ -829,8 +829,8 @@ programs: full_name: Kentucky Low Income Home Energy Assistance Program variable: ky_liheap parameter_prefix: gov.states.ky.dcbs.liheap - verified_years: "2026" - notes: FY2026 regular heating only, from the published benefit worksheet. Crisis and cooling assistance are out of scope. + verified_years: "2025-2026" + notes: FY2025-FY2026 regular heating only, from the published benefit workbooks. Crisis and cooling assistance are out of scope. - state: TX status: partial name: Texas CEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/integration.yaml index cf7487e8213..48c3a7575c0 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/integration.yaml @@ -1,6 +1,7 @@ -# Sources: Kentucky FY2026 state plan, regular heating benefit workbook, and fact sheet. -- name: Regular subsidy can exceed the bill and applies to heat included in rent - period: 2_026 +# Sources: Kentucky FY2025 and FY2026 state plans, regular heating benefit +# workbooks, and the FY2025-2026 fact sheet. +- name: Case 1, the regular subsidy can exceed the bill and applies to heat included in rent. + period: 2026 input: state_code: [KY, KY, KY, KY, KY, IN] age: [35, 35, 35, 35, 35, 35] @@ -12,12 +13,14 @@ gas_expense: [10, 0, 0, 1_000, 1_000, 1_000] heat_expense_included_in_rent: [false, true, false, false, false, false] is_citizen_or_legal_immigrant: [true, true, true, true, false, true] - hud_utility_allowance: [0, 0, 0, 0, 0, 0] output: + # Household 3 pays no heating cost, household 4 is over the one-person + # limit, household 5 has no citizen or qualified member, and household 6 + # lives in Indiana. ky_liheap_eligible: [true, true, false, false, false, false] ky_liheap: [134, 134, 0, 0, 0, 0] -- name: Public housing and housing assistance select the reduced schedule; a utility allowance schedule amount alone does not - period: 2_026 +- name: Case 2, public housing and housing assistance select the reduced schedule; a utility allowance schedule amount alone does not. + period: 2026 input: state_code: [KY, KY, KY] ky_liheap_income: [0, 0, 0] @@ -35,8 +38,8 @@ # 1 + 4 + 3 = 8 points; ceil(8 * 16.66 = 133.28) = 134. ky_liheap: [26, 26, 134] # Backfilling checks the model convention, not verified historical Kentucky policy. -- name: Parameter backfill uses the 2021 guideline for a 2022 estimate - period: 2_022 +- name: Case 3, parameter backfill uses the 2021 guideline for a 2022 estimate. + period: 2022 input: state_code: KY age: 40 @@ -45,10 +48,9 @@ social_security: 0 tanf: 0 heating_type: ELECTRICITY - heating_expense: 1_000 + pre_subsidy_electricity_expense: 1_000 receives_housing_assistance: false is_in_public_housing: false - hud_utility_allowance: 0 output: ky_liheap: 184 ky_liheap_fpg: 12_880 @@ -203,3 +205,43 @@ # ceil(11 * 16.66 = 183.26) = 184, the workbook cell for five or more # people, 126.01-150%, propane. ky_liheap: 184 +- name: Case 7, FY2025 retiree with Social Security heats with natural gas. + period: 2025 + input: + people: + person1: + age: 68 + # $1_000 a month in Social Security retirement benefits. + social_security_retirement: 12_000 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 900 + receives_housing_assistance: false + # The household does not receive K-TAP. + tanf: 0 + households: + household: + members: [person1] + state_code: KY + is_in_public_housing: false + output: + # 2024 guideline (one-year lag) for one person. + ky_liheap_fpg: 15_060 + # The FY2025 plan counts Social Security including the Medicare + # deduction, so the full 12_000 (1_000 a month) is counted. + ky_liheap_income: 12_000 + # FY2025 one-person limit: ceil(15_060 * 1.5 / 12 = 1_882.5) = 1_883, the + # FY2025 workbook's upper bound for the 126-150% band. + ky_liheap_income_eligible: true + heating_expense: 900 + ky_liheap_eligible: true + # 1_000 a month is above ceil(15_060 * 0.75 / 12 = 941.25) = 942 and not + # above ceil(15_060 / 12 = 1_255) = 1_255, so the 76-100% band gives 3 + # income points. + # 1 (size) + 3 (income) + 3 (natural gas) = 7 points; + # ceil(7 * 16.66 = 116.62) = 117, the FY2025 workbook cell for one + # person, 76-100%, natural gas. + ky_liheap: 117 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap.yaml index fc90aaee151..e34ce2c92ad 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap.yaml @@ -1,6 +1,6 @@ # Sources: Kentucky FY2026 state plan, regular heating benefit workbook, and fact sheet. -- name: Published fuel amounts for one-person households - period: 2_026 +- name: Case 1, published fuel amounts for one-person households. + period: 2026 input: state_code: [KY, KY, KY, KY, KY, KY] ky_liheap_eligible: [true, true, true, true, true, true] @@ -8,11 +8,10 @@ heating_type: [ELECTRICITY, NATURAL_GAS, PROPANE, COAL, WOOD, FUEL_OIL] ky_liheap_income: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] receives_housing_assistance: [false, false, false, false, false, false] - hud_utility_allowance: [0, 0, 0, 0, 0, 0] output: ky_liheap: [184, 134, 167, 117, 100, 150] -- name: Two-person coal exception and the five-person points cap - period: 2_026 +- name: Case 2, two-person coal exception and the five-person points cap. + period: 2026 input: state_code: [KY, KY, KY, KY, KY, KY] ky_liheap_eligible: [true, true, true, true, true, true] @@ -20,11 +19,10 @@ heating_type: [COAL, COAL, COAL, COAL, COAL, COAL] ky_liheap_income: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0] receives_housing_assistance: [false, false, false, false, false, false] - hud_utility_allowance: [0, 0, 0, 0, 0, 0] output: ky_liheap: [117, 167, 184, 184, 184, 184] -- name: Subsidized matrix increases across income bands - period: 2_026 +- name: Case 3, the subsidized matrix increases across income bands. + period: 2026 input: state_code: [KY, KY, KY, KY] ky_liheap_eligible: [true, true, true, true] @@ -32,11 +30,10 @@ heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY] ky_liheap_income: [0.0, 11_748.12, 15_660.12, 19_572.12] receives_housing_assistance: [true, true, true, true] - hud_utility_allowance: [0, 0, 0, 0] output: ky_liheap: [41, 45, 51, 56] -- name: Unsubsidized matrix decreases across income bands - period: 2_026 +- name: Case 4, the unsubsidized matrix decreases across income bands. + period: 2026 input: state_code: [KY, KY, KY, KY] ky_liheap_eligible: [true, true, true, true] @@ -44,28 +41,25 @@ heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY] ky_liheap_income: [0.0, 11_748.12, 15_660.12, 19_572.12] receives_housing_assistance: [false, false, false, false] - hud_utility_allowance: [0, 0, 0, 0] output: ky_liheap: [184, 167, 150, 134] -- name: Closed upper income bands use rounded monthly boundaries - period: 2_026 +- name: Case 5, closed upper income bands use rounded monthly boundaries. + period: 2026 input: state_code: [KY, KY, KY, KY, KY, KY] ky_liheap_eligible: [true, true, true, true, true, true] spm_unit_size: [1, 1, 1, 1, 1, 1] heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY] - hud_utility_allowance: [0, 0, 0, 0, 0, 0] ky_liheap_income: [11_748, 11_748.12, 15_660, 15_660.12, 19_572, 19_572.12] output: ky_liheap: [184, 167, 167, 150, 150, 134] -- name: Kerosene matches oil; unspecified and unsupported fuels have no schedule - period: 2_026 +- name: Case 6, kerosene matches oil; unspecified and unsupported fuels have no schedule. + period: 2026 input: state_code: [KY, KY, KY, KY, KY] ky_liheap_eligible: [true, true, true, true, true] spm_unit_size: [1, 1, 1, 1, 1] ky_liheap_income: [0, 0, 0, 0, 0] - hud_utility_allowance: [0, 0, 0, 0, 0] heating_type: [KEROSENE, OTHER, SOLAR, NONE, UNSPECIFIED] output: ky_liheap: [150, 0, 0, 0, 0] @@ -101,3 +95,22 @@ # Workbook subsidized cell = 75; unsubsidized five-person, 0-75%, # electricity cell = 250. ky_liheap: [15, 30, 60, 75] +- name: Case 8, the coal adjustment ends at three people and carries into the subsidized schedule. + period: 2026 + input: + state_code: [KY, KY] + ky_liheap_eligible: [true, true] + spm_unit_size: [3, 2] + heating_type: [COAL, COAL] + ky_liheap_income: [0, 0] + receives_housing_assistance: [false, true] + output: + # Household 1: three people, coal, 0-75%, unsubsidized. + # 3 (size) + 4 (income) + 2 (coal) + 0 (adjustment) = 9 points; + # ceil(9 * 16.66 = 149.94) = 150, the workbook cell (rev benefit income + # range H15). + # Household 2: two people, coal, 0-75%, subsidized. + # 2 (size) + 1 (income) + 2 (coal) - 1 (two-person coal adjustment) = 4 + # points; ceil(4 * 16.66 = 66.64) = 67; ceil(67 * 0.3 = 20.1) = 21, the + # workbook cell (rev benefit income range H57). + ky_liheap: [150, 21] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_eligible.yaml index 48a55d9fbc8..992a0cae29e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_eligible.yaml @@ -1,16 +1,19 @@ -# Sources: Kentucky FY2026 state plan, regular heating benefit workbook, and fact sheet. -- name: Case 1, mixed-status household with citizen children counts every member. +# Sources: 921 KAR 4:116 Sections 2, 3 and 8; Kentucky FY2026 state plan, +# regular heating benefit workbook, and fact sheet. +- name: Case 1, undocumented parents without a Social Security number make the household ineligible. period: 2026 input: people: person1: age: 38 immigration_status: UNDOCUMENTED + ssn_card_type: NONE # $12.50 an hour, 40 hours a week: 12.5 * 2_080 = 26_000. employment_income: 26_000 person2: age: 36 immigration_status: UNDOCUMENTED + ssn_card_type: NONE person3: age: 10 immigration_status: CITIZEN @@ -32,20 +35,155 @@ is_in_public_housing: false output: is_citizen_or_legal_immigrant: [false, false, true, true] - # Modeling assumption: one citizen or qualified member is enough, and all - # four members count for household size and income. + # Every member counts for household size and income. spm_unit_size: 4 - # The undocumented parent's wages are counted. ky_liheap_income: 26_000 # Fact sheet limit for four: ceil(32_150 * 1.5 / 12 = 4_018.75) = 4_019; # income is 2_166.67 a month. ky_liheap_income_eligible: true heating_expense: 1_560 + # 921 KAR 4:116 Section 2(1)(d) requires a Social Security number or a + # permanent residency card for each household member. The parents have + # neither, so the application is incomplete (Section 2(2)) and is denied + # (Section 8(3)). + ky_liheap_eligible: false + ky_liheap: 0 +- name: Case 2, a parent with a work-authorized Social Security number and a citizen child qualify. + period: 2026 + input: + people: + person1: + age: 30 + immigration_status: DACA + ssn_card_type: NON_CITIZEN_VALID_EAD + # $14.00 an hour, 40 hours a week: 14 * 2_080 = 29_120. + employment_income: 29_120 + person2: + age: 4 + immigration_status: CITIZEN + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_440 + receives_housing_assistance: false + # The household does not receive K-TAP. + tanf: 0 + households: + household: + members: [person1, person2] + state_code: KY + is_in_public_housing: false + output: + # The citizen child is the qualified member, and both members hold a + # Social Security number. + is_citizen_or_legal_immigrant: [false, true] + ky_liheap_income: 29_120 + # Fact sheet limit for two: ceil(21_150 * 1.5 / 12 = 2_643.75) = 2_644; + # income is 2_426.67 a month. + ky_liheap_income_eligible: true + ky_liheap_eligible: true + # 2_426.67 a month is above ceil(21_150 * 1.25 / 12 = 2_203.13) = 2_204, + # so the 126.01-150% band gives 1 income point. + # 2 (size) + 1 (income) + 6 (electricity) = 9 points; + # ceil(9 * 16.66 = 149.94) = 150, the workbook cell for two people, + # 126.01-150%, electricity. + ky_liheap: 150 +- name: Case 3, Social Security numbers alone do not qualify a household with no citizen or qualified noncitizen. + period: 2026 + input: + people: + person1: + age: 28 + immigration_status: DACA + ssn_card_type: NON_CITIZEN_VALID_EAD + # $11.00 an hour, 40 hours a week: 11 * 2_080 = 22_880. + employment_income: 22_880 + person2: + age: 27 + immigration_status: TPS + ssn_card_type: NON_CITIZEN_VALID_EAD + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 800 + receives_housing_assistance: false + # The household does not receive K-TAP. + tanf: 0 + households: + household: + members: [person1, person2] + state_code: KY + is_in_public_housing: false + output: + is_citizen_or_legal_immigrant: [false, false] + # 22_880 a year is 1_906.67 a month, within the two-person limit of 2_644. + ky_liheap_income_eligible: true + # Section 2(1)(d) is met, but LIHEAP is a federal public benefit and needs + # at least one citizen or qualified noncitizen member. + ky_liheap_eligible: false + ky_liheap: 0 +- name: Case 4, lawful permanent residents without a Social Security card qualify through the residency card. + period: 2026 + input: + people: + person1: + age: 64 + immigration_status: LEGAL_PERMANENT_RESIDENT + ssn_card_type: NONE + # An $800 a month pension. + taxable_private_pension_income: 9_600 + person2: + age: 62 + immigration_status: LEGAL_PERMANENT_RESIDENT + ssn_card_type: NONE + spm_units: + spm_unit: + members: [person1, person2] + heating_type: PROPANE + bottled_gas_expense: 1_200 + receives_housing_assistance: false + # The household does not receive K-TAP. + tanf: 0 + households: + household: + members: [person1, person2] + state_code: KY + is_in_public_housing: false + output: + is_citizen_or_legal_immigrant: [true, true] + ky_liheap_income: 9_600 + ky_liheap_income_eligible: true + # Section 2(1)(d) accepts a permanent residency card in place of a Social + # Security number. + ky_liheap_eligible: true + # 800 a month is not above ceil(21_150 * 0.75 / 12 = 1_321.875) = 1_322, + # so the 0-75% band gives 4 income points. + # 2 (size) + 4 (income) + 5 (propane) = 11 points; + # ceil(11 * 16.66 = 183.26) = 184, the workbook cell for two people, + # 0-75%, propane. + ky_liheap: 184 +- name: Case 5, heat included in rent with an unspecified fuel is eligible but has no payment schedule. + period: 2026 + input: + state_code: KY + age: 30 + # $12.00 an hour, 20 hours a week: 12 * 1_040 = 12_480. + employment_income: 12_480 + ssi: 0 + social_security: 0 + tanf: 0 + heating_type: UNSPECIFIED + heat_expense_included_in_rent: true + receives_housing_assistance: false + is_in_public_housing: false + output: + heating_expense: 0 + # 1_040 a month is within the one-person limit of 1_957. + ky_liheap_income_eligible: true + # 921 KAR 4:116 Section 3(2)(c): heating paid as an undesignated portion + # of the rent. ky_liheap_eligible: true - # 2_166.67 a month is above ceil(32_150 * 0.75 / 12 = 2_009.375) = 2_010 - # and not above ceil(32_150 / 12 = 2_679.17) = 2_680, so the 75.01-100% - # band gives 3 income points. - # 4 (size) + 3 (income) + 6 (electricity) = 13 points; - # ceil(13 * 16.66 = 216.58) = 217, the workbook cell for four people, - # 75.01-100%, electricity. - ky_liheap: 217 + # The workbook publishes no column for an unspecified fuel. + ky_liheap: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml index 5e446e56b47..42ec29e2488 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml @@ -1,9 +1,10 @@ -# Sources: Kentucky FY2026 state plan, regular heating benefit workbook, and fact sheet. -- name: Adult business loss does not offset wages; child earnings are excluded - period: 2_026 +# Sources: Kentucky FY2025 and FY2026 state plans (Sections 1.8, 1.9 and +# 17.4) and 921 KAR 4:116 Section 1(8). +- name: Case 1, an adult business loss does not offset wages and child earnings are excluded. + period: 2026 input: people: - adult: + person1: age: 35 employment_income: 12_000 self_employment_income: -3_000 @@ -12,7 +13,7 @@ strike_benefits: 600 ssi: 0 social_security: 0 - child: + person2: age: 17 employment_income: 4_000 self_employment_income: 2_000 @@ -21,17 +22,17 @@ ssi: 0 social_security: 0 spm_units: - unit: - members: [adult, child] + spm_unit: + members: [person1, person2] tanf: 0 households: household: - members: [adult, child] + members: [person1, person2] state_code: KY output: ky_liheap_income: 16_200 -- name: Net business income is counted once without an additional expense deduction - period: 2_026 +- name: Case 2, net business income is counted once without an additional expense deduction. + period: 2026 input: state_code: KY age: 18 @@ -44,17 +45,18 @@ output: # 12,000 wages + 6,000 ordinary business + 3,000 specified service business. ky_liheap_income: 21_000 -- name: Reported Medicare premium deduction cannot make SSA income negative - period: 2_026 +- name: Case 3, Social Security is counted before the Medicare premium is withheld. + period: 2026 input: state_code: [KY, KY] age: [70, 70] social_security: [12_000, 1_000] - medicare_part_b_premiums_reported: [2_000, 2_000] ssi: [0, 0] tanf: [0, 0] output: - ky_liheap_income: [10_000, 0] + # The FY2026 plan checks "Excluding MediCare deduction"; the premium + # deduction is not modeled yet, so the gross benefit is counted. + ky_liheap_income: [12_000, 1_000] - name: Case 4, a rental loss or a farm loss does not offset other income. period: 2026 input: @@ -131,7 +133,6 @@ # Unearned: 3_600 (farm rent) + 240 + 60 (ordinary dividends) = 3_900. # 14_400 + 3_900 = 18_300. ky_liheap_income: 18_300 - - name: Case 7, service business profits and wage replacement income can exceed the income limit. # 921 KAR 4:116 Section 1(8) counts earned and unearned income. Plan # Section 1.9 includes insurance payments made directly to the insured. @@ -152,3 +153,27 @@ # Each exceeds the one-person monthly ceiling of 1,957, annualized. ky_liheap_eligible: [false, false, false] ky_liheap: [0, 0, 0] +- name: Case 8, public pensions, railroad benefits and pass-through business income are counted. + # Plan Section 1.9 counts retirement and pension benefits and + # self-employment income; Section 17.4 lists Railroad Retirement records. + period: 2026 + input: + state_code: [KY, KY, KY] + age: [62, 45, 45] + # Household 1: $1_500 a month in military retirement pay, reported as a + # taxable public pension, and $500 a month in railroad retirement + # benefits. + taxable_public_pension_income: [18_000, 0, 0] + railroad_benefits: [6_000, 0, 0] + # Households 2-3: $10.00 an hour, 40 hours a week (10 * 2_080 = 20_800) + # and a partnership share, a profit for one and a loss for the other. + employment_income: [0, 20_800, 20_800] + partnership_s_corp_income: [0, 15_000, -4_000] + ssi: [0, 0, 0] + social_security: [0, 0, 0] + tanf: [0, 0, 0] + output: + # Household 1: 18_000 + 6_000 = 24_000. + # Household 2: 20_800 + 15_000 = 35_800. + # Household 3: 20_800 + max(-4_000, 0) = 20_800. + ky_liheap_income: [24_000, 35_800, 20_800] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.yaml index 180b7e07c38..fc6cd3d9dd1 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.yaml @@ -1,14 +1,14 @@ # Sources: Kentucky FY2026 state plan, regular heating benefit workbook, and fact sheet. -- name: Published monthly limits, including the additional-person rule - period: 2_026 +- name: Case 1, published monthly limits, including the additional-person rule. + period: 2026 input: state_code: [KY, KY, KY, KY, KY, KY, KY, KY, KY, KY] spm_unit_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ky_liheap_income: [23_484, 31_728, 39_984, 48_228, 56_484, 64_728, 72_984, 81_228, 89_484, 97_740] output: ky_liheap_income_eligible: [true, true, true, true, true, true, true, true, true, true] -- name: One dollar per month above the published limits - period: 2_026 +- name: Case 2, one dollar per month above the published limits. + period: 2026 input: state_code: [KY, KY, KY, KY, KY, KY, KY, KY, KY, KY] spm_unit_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] diff --git a/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap.py b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap.py index a76f67c5219..8a98037829f 100644 --- a/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap.py +++ b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap.py @@ -8,11 +8,18 @@ class ky_liheap(Variable): unit = USD label = "Kentucky LIHEAP regular heating assistance" documentation = ( - "Verified for FY2026. Earlier years use model parameter backfilling " - "and are unverified historical estimates." + "Verified for FY2025 and FY2026. Earlier years use model parameter " + "backfilling and are unverified historical estimates." ) defined_for = "ky_liheap_eligible" - reference = "https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx" + reference = ( + "https://liheapch.acf.gov/docs/2026/benefits-matricies/KY_BenefitMatrix_Heat-Cool_2026.xlsx", + "https://liheapch.acf.gov/docs/2025/benefits-matricies/KY_BenefitMatrix_2025.xlsx", + # Sections 2.4-2.6 (pages 8-9). + "https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=8", + # Section 4(1). + "https://apps.legislature.ky.gov/law/kar/titles/921/004/116/", + ) def formula(spm_unit, period, parameters): p = parameters(period).gov.states.ky.dcbs.liheap.payment @@ -43,14 +50,17 @@ def formula(spm_unit, period, parameters): + fuel_points + coal_adjustment ) - # Reproduces every heating cell in "rev benefit income range", including - # its reversed subsidized income points and two-person coal exception. - # That sheet uses 2025 FPG despite its stale title and matches the FY2026 - # plan's $15-$250 range. The conditional "if no funding FFY26" sheet is - # not used. These source inconsistencies need agency clarification. + # Reproduces every heating cell of the FY2026 "rev benefit income + # range" and FY2025 "subsidy benefits" worksheets, including the + # two-person coal exception and the reversed subsidized income points + # that conflict with 921 KAR 4:116 Section 4(1)(c) (see + # income_points/subsidized). The FY2026 sheet uses 2025 guidelines + # despite its stale title and matches the plan's $15-$250 range; the + # conditional "if no funding FFY26" sheet is not used. amount = np.ceil(points * p.dollars_per_point) amount = where(subsidized, np.ceil(amount * p.subsidized_housing_rate), amount) - # No verified table mapping exists for OTHER/SOLAR/UNSPECIFIED/NONE. - # The full seasonal subsidy may credit the account, so it is not capped - # at the current bill. + # Fuels without a published column (OTHER, SOLAR, UNSPECIFIED, NONE) + # carry zero points, which also marks them as having no schedule. The + # full seasonal subsidy may credit the account, so it is not capped at + # the current bill. return where(fuel_points > 0, amount, 0) diff --git a/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_eligible.py b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_eligible.py index f4556d409ad..83a2b1cc83d 100644 --- a/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_eligible.py @@ -7,23 +7,42 @@ class ky_liheap_eligible(Variable): definition_period = YEAR label = "Kentucky LIHEAP regular heating eligibility" defined_for = StateCode.KY - reference = "https://apps.legislature.ky.gov/law/kar/titles/921/004/116/" + reference = ( + # Sections 2(1)(d), 2(2), 3(2) and 8(3). + "https://apps.legislature.ky.gov/law/kar/titles/921/004/116/", + # Sections 17.2-17.3 (pages 33-34). + "https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=33", + ) def formula(spm_unit, period, parameters): income_eligible = spm_unit("ky_liheap_income_eligible", period) + # 921 KAR 4:116 Section 3(2): the household pays for home heating + # directly or as an undesignated portion of the rent. responsible = (spm_unit("heating_expense", period) > 0) | spm_unit( "heat_expense_included_in_rent", period ) - has_eligible_member = spm_unit.any( - spm_unit.members("is_citizen_or_legal_immigrant", period) + # LIHEAP is a federal public benefit, so at least one member must be a + # citizen or qualified noncitizen; plan Section 17.3 verifies that + # status. + has_qualified_member = ( + add(spm_unit, period, ["is_citizen_or_legal_immigrant"]) > 0 ) - # The "at least one qualified member" gate is a modeling assumption with - # no Kentucky source. 921 KAR 4:116 Section 2(1)(d) requires a Social - # Security number or permanent residency card for each household member, - # and Section 2(2) treats the application as incomplete until that is - # received; neither says whether a mixed-status household is denied, - # served in full, or served without that member. Section 1(10) defines - # the energy-purchasing household with no immigration carve-out, so all - # SPM members count for size and income. Documentation and enrollment - # windows are unmodeled. No categorical or resource test applies. - return income_eligible & responsible & has_eligible_member + # 921 KAR 4:116 Section 2(1)(d) requires "a Social Security number, or + # a permanent residency card, for each household member". Section 2(2) + # treats the application as incomplete until it is received and + # Section 8(3) denies it after five working days, so a household with + # a member who has neither is ineligible. The plan's Section 17.2 + # checklist requires the card from adults and requests it from other + # members; the regulation's per-member rule applies here. Section 1(10) + # defines the household with no immigration carve-out, so every member + # counts for size and income. + person = spm_unit.members + ssn_card_type = person("ssn_card_type", period) + immigration_status = person("immigration_status", period) + has_ssn = ssn_card_type != ssn_card_type.possible_values.NONE + is_permanent_resident = ( + immigration_status + == immigration_status.possible_values.LEGAL_PERMANENT_RESIDENT + ) + all_documented = spm_unit.all(has_ssn | is_permanent_resident) + return income_eligible & responsible & has_qualified_member & all_documented diff --git a/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py index c5d4ce6d766..ab900b421dc 100644 --- a/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py +++ b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py @@ -9,7 +9,10 @@ class ky_liheap_income(Variable): label = "Kentucky LIHEAP countable household income" defined_for = StateCode.KY reference = ( - "https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=5,6,7,34", + # Sections 1.8-1.9 (pages 5-6) and Section 17.4 (page 34). + "https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=5", + # Sections 1.8-1.9 (pages 5-6). + "https://liheapch.acf.gov/docs/2025/state-plans/KY_Plan_2025.pdf#page=5", "https://apps.legislature.ky.gov/law/kar/titles/921/004/116/", ) @@ -23,27 +26,17 @@ def formula(spm_unit, period, parameters): # deduction. 921 KAR 4:116 Section 1(8) counts gross income received, so # each earned and unearned source is floored at zero and a loss in one # source cannot offset another. - earned = ( - max_(person("employment_income", period), 0) - + max_(person("self_employment_income", period), 0) - + max_(person("sstb_self_employment_income", period), 0) - + max_(person("farm_operations_income", period), 0) - ) * adult + earned = 0 + for source in p.earned_income_sources: + earned = earned + max_(person(source, period), 0) + earned = earned * adult unearned = 0 for source in p.unearned_income_sources: unearned = unearned + max_(person(source, period), 0) - # Reported Part B premiums approximate the Medicare deduction from SSA; - # modeled premium liability does not establish an actual withholding. - ssa = max_( - person("social_security", period) - - person("medicare_part_b_premiums_reported", period), - 0, - ) - # Detailed excluded WIA/work-study earnings, jury duty, settlements, - # other insurance payments, royalties, deposits, and non-taxable refunds - # cannot be isolated with existing inputs. Do not add new inputs for this - # draft. + # Excluded WIA and work-study earnings, jury duty, settlements, other + # insurance payments, royalties, deposits and non-taxable refunds cannot + # be isolated with existing inputs. # The annual tanf aggregate includes ky_ktap and applies take-up; ky_ktap # alone represents monthly entitlement and would count benefits a # nonrecipient could get. - return spm_unit.sum(earned + unearned + ssa) + spm_unit("tanf", period) + return spm_unit.sum(earned + unearned) + spm_unit("tanf", period) diff --git a/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.py b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.py index 43154e7a10f..15e74332cb8 100644 --- a/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.py +++ b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income_eligible.py @@ -8,7 +8,13 @@ class ky_liheap_income_eligible(Variable): definition_period = YEAR label = "Kentucky LIHEAP income eligibility" defined_for = StateCode.KY - reference = "https://www.capky.org/wp-content/uploads/2026/01/2025-2026-LIHEAP-Fact-Sheet-V2.pdf" + reference = ( + "https://www.capky.org/wp-content/uploads/2026/01/2025-2026-LIHEAP-Fact-Sheet-V2.pdf", + # Section 2.1 (page 8). + "https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=8", + # Section 3(1). + "https://apps.legislature.ky.gov/law/kar/titles/921/004/116/", + ) def formula(spm_unit, period, parameters): p = parameters(period).gov.states.ky.dcbs.liheap From c753fb4a8178cacb705d903d73085b99251fff92 Mon Sep 17 00:00:00 2001 From: Ziming Date: Fri, 2 Oct 2026 22:08:11 -0400 Subject: [PATCH 8/8] Deduct the Medicare Part B premium from Social Security from FY2026 and count military retirement pay The FY2026 plan checks "Excluding MediCare deduction" where the FY2025 plan checks "Including", so a dated parameter switches Social Security from gross to net of the Part B premium the person pays, taken from the existing medicare_part_b_premium variable. military_retirement_pay is a separate input from the pension inputs that pension_income adds, so it is listed beside pension_income. Co-Authored-By: Claude Fable 5.1 --- ...l_security_medicare_premium_deduction.yaml | 15 ++++ .../dcbs/liheap/unearned_income_sources.yaml | 14 ++-- .../states/ky/dcbs/liheap/integration.yaml | 77 +++++++++++++++++++ .../ky/dcbs/liheap/ky_liheap_income.yaml | 51 +++++++++--- .../states/ky/dcbs/liheap/ky_liheap_income.py | 14 +++- 5 files changed, 151 insertions(+), 20 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/ky/dcbs/liheap/social_security_medicare_premium_deduction.yaml diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/social_security_medicare_premium_deduction.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/social_security_medicare_premium_deduction.yaml new file mode 100644 index 00000000000..25d94c72911 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/social_security_medicare_premium_deduction.yaml @@ -0,0 +1,15 @@ +description: Kentucky counts Social Security benefits net of the Medicare Part B premium under the Low Income Home Energy Assistance Program when this is true, and gross when it is false. +values: + # FY2025 plan, Section 1.9: "Including MediCare deduction" is checked. + 2024-10-01: false + # FY2026 plan, Section 1.9: "Excluding MediCare deduction" is checked. + 2025-10-01: true +metadata: + unit: bool + period: year + label: Kentucky LIHEAP Social Security Medicare premium deduction + reference: + - title: Kentucky FY2025 state plan, Section 1.9, page 6 + href: https://liheapch.acf.gov/docs/2025/state-plans/KY_Plan_2025.pdf#page=6 + - title: Kentucky FY2026 state plan, Section 1.9, page 6 + href: https://liheapch.acf.gov/docs/2026/state-plans/KY_Plan_2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml index 759e79bfc35..4a6f6bb9760 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/liheap/unearned_income_sources.yaml @@ -3,18 +3,18 @@ values: 2024-10-01: - unemployment_compensation - strike_benefits - # The FY2026 plan counts Social Security excluding the Medicare deduction - # (the FY2025 plan counted it including the deduction). The premium is not - # deducted yet: neither medicare_part_b_premium nor - # medicare_part_b_premiums_reported is wired in. - - social_security + # Social Security is counted in the formula, gross for FY2025 and net of + # the Medicare Part B premium from FY2026 + # (social_security_medicare_premium_deduction). - ssi # Plan Section 17.4 lists the DCBS State Supplementation award letter as # income documentation. - ky_ssp - # Military retirement pay is taxable pension income, so pension_income - # already covers it. + # 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. - pension_income + - military_retirement_pay # Plan Section 17.4 lists Railroad Retirement records. - railroad_benefits - retirement_distributions diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/integration.yaml index 48c3a7575c0..e1d621b5294 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/integration.yaml @@ -245,3 +245,80 @@ # ceil(7 * 16.66 = 116.62) = 117, the FY2025 workbook cell for one # person, 76-100%, natural gas. ky_liheap: 117 + +- name: Case 8, FY2026 retiree qualifies once the Medicare Part B premium is deducted. + # FY2026 plan Section 1.9 (page 6) checks "Excluding MediCare deduction". + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + # $1_966.67 a month in Social Security retirement benefits. + social_security_retirement: 23_600 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 900 + receives_housing_assistance: false + tanf: 0 + households: + household: + members: [person1] + state_code: KY + is_in_public_housing: false + output: + # 2025 guideline (one-year lag) for one person. + ky_liheap_fpg: 15_650 + # Countable income of 1_966.67 - 20 a month is above 135% of the poverty + # guideline, so no Medicare Savings Program pays the 2026 premium of + # 202.90 a month (2_434.80 a year). + medicare_part_b_premium: 2_434.8 + # 23_600 - 2_434.80 = 21_165.20, or 1_763.77 a month. + ky_liheap_income: 21_165.2 + # FY2026 one-person limit: ceil(15_650 * 1.5 / 12 = 1_956.25) = 1_957. + # The gross 1_966.67 would exceed it; the net 1_763.77 does not. + ky_liheap_income_eligible: true + heating_expense: 900 + ky_liheap_eligible: true + # 1_763.77 is above ceil(15_650 * 1.25 / 12 = 1_630.21) = 1_631, so the + # 126-150% band gives 1 income point. + # 1 (size) + 1 (income) + 3 (natural gas) = 5 points; + # ceil(5 * 16.66 = 83.3) = 84, the FY2026 workbook cell for one person, + # 126.01-150%, natural gas. + ky_liheap: 84 + +- name: Case 9, FY2025 retiree with the same premium is over the limit because FY2025 counts Social Security gross. + # FY2025 plan Section 1.9 (page 6) checks "Including MediCare deduction". + period: 2025 + input: + people: + person1: + age: 70 + # $1_891.67 a month in Social Security retirement benefits. + social_security_retirement: 22_700 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 900 + receives_housing_assistance: false + tanf: 0 + households: + household: + members: [person1] + state_code: KY + is_in_public_housing: false + output: + ky_liheap_fpg: 15_060 + # The 2025 premium of 185 a month (2_220 a year) is paid but not deducted. + medicare_part_b_premium: 2_220 + ky_liheap_income: 22_700 + # FY2025 one-person limit: ceil(15_060 * 1.5 / 12 = 1_882.5) = 1_883; + # 1_891.67 exceeds it. + ky_liheap_income_eligible: false + ky_liheap_eligible: false + ky_liheap: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml index 42ec29e2488..696673bda4b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/dcbs/liheap/ky_liheap_income.yaml @@ -45,18 +45,29 @@ output: # 12,000 wages + 6,000 ordinary business + 3,000 specified service business. ky_liheap_income: 21_000 -- name: Case 3, Social Security is counted before the Medicare premium is withheld. +- name: Case 3, Social Security is counted net of the Medicare Part B premium the person pays from FY2026. + # FY2026 plan Section 1.9 (page 6) checks "Excluding MediCare deduction". + # The 2026 standard Part B premium is 202.90 a month (2_434.80 a year). + # Household 1: 1_000 a month of Social Security. A Medicare Savings Program + # covers the premium (1_000 - 20 of countable income is under 135% of the + # poverty guideline), so nothing is withheld and 12_000 is counted. + # Household 2: 23_600 a year (1_966.67 a month) is above the Medicare + # Savings Program limit, so the premium is paid: + # 23_600 - 2_434.80 = 21_165.20. + # Household 3: 2_000 of Social Security and a 24_000 private pension. The + # premium exceeds the benefit, which is floored at zero: 0 + 24_000. period: 2026 + absolute_error_margin: 0.01 input: - state_code: [KY, KY] - age: [70, 70] - social_security: [12_000, 1_000] - ssi: [0, 0] - tanf: [0, 0] + state_code: [KY, KY, KY] + age: [70, 70, 70] + social_security_retirement: [12_000, 23_600, 2_000] + taxable_private_pension_income: [0, 0, 24_000] + ssi: [0, 0, 0] + tanf: [0, 0, 0] output: - # The FY2026 plan checks "Excluding MediCare deduction"; the premium - # deduction is not modeled yet, so the gross benefit is counted. - ky_liheap_income: [12_000, 1_000] + medicare_part_b_premium: [0, 2_434.8, 2_434.8] + ky_liheap_income: [12_000, 21_165.2, 24_000] - name: Case 4, a rental loss or a farm loss does not offset other income. period: 2026 input: @@ -160,10 +171,10 @@ input: state_code: [KY, KY, KY] age: [62, 45, 45] - # Household 1: $1_500 a month in military retirement pay, reported as a - # taxable public pension, and $500 a month in railroad retirement + # Household 1: $1_500 a month in military retirement pay, reported in + # military_retirement_pay, and $500 a month in railroad retirement # benefits. - taxable_public_pension_income: [18_000, 0, 0] + military_retirement_pay: [18_000, 0, 0] railroad_benefits: [6_000, 0, 0] # Households 2-3: $10.00 an hour, 40 hours a week (10 * 2_080 = 20_800) # and a partnership share, a profit for one and a loss for the other. @@ -177,3 +188,19 @@ # Household 2: 20_800 + 15_000 = 35_800. # Household 3: 20_800 + max(-4_000, 0) = 20_800. ky_liheap_income: [24_000, 35_800, 20_800] + +- name: Case 9, FY2025 counts Social Security before the Medicare premium is withheld. + # FY2025 plan Section 1.9 (page 6) checks "Including MediCare deduction". + # 22_700 a year (1_891.67 a month) is above the Medicare Savings Program + # limit, so the 2025 premium of 185 a month (2_220 a year) is paid, but + # FY2025 does not deduct it. + period: 2025 + input: + state_code: KY + age: 70 + social_security_retirement: 22_700 + ssi: 0 + tanf: 0 + output: + medicare_part_b_premium: 2_220 + ky_liheap_income: 22_700 diff --git a/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py index ab900b421dc..d4baf590fcd 100644 --- a/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py +++ b/policyengine_us/variables/gov/states/ky/dcbs/liheap/ky_liheap_income.py @@ -33,10 +33,22 @@ def formula(spm_unit, period, parameters): unearned = 0 for source in p.unearned_income_sources: unearned = unearned + max_(person(source, period), 0) + # Plan Section 1.9 counts Social Security "Including MediCare deduction" + # for FY2025 and "Excluding MediCare deduction" from FY2026, so from + # FY2026 the benefit is counted net of the Part B premium the person + # pays. medicare_part_b_premium is zero for a person who is not + # enrolled and excludes the share a Medicare Savings Program pays, + # which is not withheld from the check. + social_security = max_(person("social_security", period), 0) + if p.social_security_medicare_premium_deduction: + premium = person("medicare_part_b_premium", period) + social_security = max_(social_security - premium, 0) # Excluded WIA and work-study earnings, jury duty, settlements, other # insurance payments, royalties, deposits and non-taxable refunds cannot # be isolated with existing inputs. # The annual tanf aggregate includes ky_ktap and applies take-up; ky_ktap # alone represents monthly entitlement and would count benefits a # nonrecipient could get. - return spm_unit.sum(earned + unearned) + spm_unit("tanf", period) + return spm_unit.sum(earned + unearned + social_security) + spm_unit( + "tanf", period + )