From 353aef396aff0a1bcf55a6dfecd9af61f640b643 Mon Sep 17 00:00:00 2001 From: Ziming Date: Sat, 3 Oct 2026 19:49:20 -0400 Subject: [PATCH 01/14] Add GA regular heating LIHEAP --- changelog.d/ga-liheap.added.md | 1 + .../ga/dfcs/liheap/eligibility/smi_rate.yaml | 10 + .../ga/dfcs/liheap/income/minimum_age.yaml | 10 + .../income/monthly_interest_disregard.yaml | 10 + .../dfcs/liheap/income/sources/household.yaml | 11 + .../ga/dfcs/liheap/income/sources/person.yaml | 35 +++ .../ga/dfcs/liheap/payment/base_amount.yaml | 19 ++ .../liheap/payment/lower_income_limit.yaml | 45 +++ .../liheap/payment/lower_income_uplift.yaml | 10 + .../liheap/payment/maximum_table_size.yaml | 10 + policyengine_us/programs.yaml | 10 +- .../eligibility/ga_liheap_eligible.yaml | 105 +++++++ .../eligibility/ga_liheap_income_limit.yaml | 29 ++ .../gov/states/ga/dfcs/liheap/ga_liheap.yaml | 280 ++++++++++++++++++ .../dfcs/liheap/ga_liheap_household_size.yaml | 69 +++++ .../dfcs/liheap/income/ga_liheap_income.yaml | 236 +++++++++++++++ .../payment/ga_liheap_matrix_amount.yaml | 75 +++++ .../liheap/eligibility/ga_liheap_eligible.py | 37 +++ .../eligibility/ga_liheap_income_limit.py | 27 ++ .../gov/states/ga/dfcs/liheap/ga_liheap.py | 17 ++ .../dfcs/liheap/ga_liheap_household_size.py | 16 + .../ga/dfcs/liheap/income/ga_liheap_income.py | 44 +++ .../liheap/payment/ga_liheap_matrix_amount.py | 43 +++ 23 files changed, 1148 insertions(+), 1 deletion(-) create mode 100644 changelog.d/ga-liheap.added.md create mode 100644 policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml create mode 100644 policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml create mode 100644 policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml create mode 100644 policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml create mode 100644 policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml create mode 100644 policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/base_amount.yaml create mode 100644 policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_limit.yaml create mode 100644 policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_uplift.yaml create mode 100644 policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/maximum_table_size.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap_household_size.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml create mode 100644 policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py create mode 100644 policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.py create mode 100644 policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap.py create mode 100644 policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py create mode 100644 policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py create mode 100644 policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py diff --git a/changelog.d/ga-liheap.added.md b/changelog.d/ga-liheap.added.md new file mode 100644 index 00000000000..f31013ef95a --- /dev/null +++ b/changelog.d/ga-liheap.added.md @@ -0,0 +1 @@ +Add Georgia LIHEAP household, income, ordinary eligibility, and regular heating benefits, with documented categorical and payment-table coverage limits. diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml new file mode 100644 index 00000000000..0474e764be4 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml @@ -0,0 +1,10 @@ +description: Georgia limits annual income to this fraction of state median income under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 0.6 +metadata: + unit: /1 + period: year + label: Georgia LIHEAP state median income rate + reference: + - title: Georgia FY2026 LIHEAP State Plan, section 2.1, page 8 + href: https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=8 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml new file mode 100644 index 00000000000..468baa09b29 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml @@ -0,0 +1,10 @@ +description: Georgia counts household members' income starting at this age under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 18 +metadata: + unit: year + period: year + label: Georgia LIHEAP countable income minimum age + reference: + - title: Georgia LIHEAP Policy Manual, January 2022, section 1200.2, page 62 + href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=62 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml new file mode 100644 index 00000000000..8ea3d187c7d --- /dev/null +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml @@ -0,0 +1,10 @@ +description: Georgia disregards this amount of monthly interest income under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 25 +metadata: + unit: currency-USD + period: month + label: Georgia LIHEAP monthly interest disregard + reference: + - title: Georgia LIHEAP Policy Manual, January 2022, section 1200.4, pages 66 and 69 (first interest amount disregarded) + href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=66 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml new file mode 100644 index 00000000000..3f13a65e5da --- /dev/null +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml @@ -0,0 +1,11 @@ +description: Georgia counts these household benefits once under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: + - ga_tanf +metadata: + unit: list + period: year + label: Georgia LIHEAP household income sources + reference: + - title: Georgia FY2026 LIHEAP State Plan, section 1.9, page 6 (TANF counted) + href: https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml new file mode 100644 index 00000000000..45a1f6f3874 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml @@ -0,0 +1,35 @@ +description: Georgia counts these income sources of adult household members under the Low Income Home Energy Assistance Program. +# Interest is calculated separately after its disregard. State TANF is counted +# once at SPM-unit level. Signed net business inputs have no extra deduction. +values: + 2025-10-01: + - employment_income + - self_employment_income + - sstb_self_employment_income + - farm_operations_income + - partnership_s_corp_income + - unemployment_compensation + - social_security + - ssi + - pension_income + - survivor_benefits + - veterans_benefits + - disability_benefits + - workers_compensation + - strike_benefits + - general_assistance + - rental_income + - farm_rent_income + - ordinary_dividend_income + - capital_gains + - alimony_income + - child_support_received +metadata: + unit: list + period: year + label: Georgia LIHEAP adult person income sources + reference: + - title: Georgia LIHEAP Policy Manual, January 2022, sections 1200.2-1200.4, pages 62-69 + href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=62 + - title: Georgia FY2026 LIHEAP State Plan, section 1.9, pages 5-6 (current included and excluded sources) + href: https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/base_amount.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/base_amount.yaml new file mode 100644 index 00000000000..815969b9e0d --- /dev/null +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/base_amount.yaml @@ -0,0 +1,19 @@ +description: Georgia provides this Level II heating payment by primary fuel under the Low Income Home Energy Assistance Program. +electricity: + 2025-10-01: 400 +natural_gas: + 2025-10-01: 560 +fuel_oil: + 2025-10-01: 710 +propane: + 2025-10-01: 580 +wood: + 2025-10-01: 400 +metadata: + unit: currency-USD + period: year + label: Georgia LIHEAP Level II heating payment by fuel + propagate_metadata_to_children: true + reference: + - title: Georgia Benefit Matrix, FY2026 income guidelines, step 2 (heating amounts) + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_limit.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_limit.yaml new file mode 100644 index 00000000000..b4cb7419af4 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_limit.yaml @@ -0,0 +1,45 @@ +description: Georgia limits annual income to this amount by household size for Level I heating payments under the Low Income Home Energy Assistance Program. +# Preserve the published size-four amount. The source does not define these +# cutoffs as a fixed percentage of current federal poverty or median income. +1: + 2025-10-01: 13_589 +2: + 2025-10-01: 17_770 +3: + 2025-10-01: 21_952 +4: + 2025-10-01: 25_469 +5: + 2025-10-01: 30_315 +6: + 2025-10-01: 34_496 +7: + 2025-10-01: 35_280 +8: + 2025-10-01: 36_064 +9: + 2025-10-01: 36_848 +10: + 2025-10-01: 37_632 +11: + 2025-10-01: 38_416 +12: + 2025-10-01: 39_200 +13: + 2025-10-01: 39_984 +14: + 2025-10-01: 40_768 +15: + 2025-10-01: 41_552 +16: + 2025-10-01: 42_336 +metadata: + unit: currency-USD + period: year + label: Georgia LIHEAP Level I annual income limit by household size + breakdown: + - range(1, 17) + propagate_metadata_to_children: true + reference: + - title: Georgia Benefit Matrix, FY2026 income guidelines, step 1 (inclusive Level I upper limits) + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_uplift.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_uplift.yaml new file mode 100644 index 00000000000..29aa5c064de --- /dev/null +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_uplift.yaml @@ -0,0 +1,10 @@ +description: Georgia increases the heating payment by this amount for households in income Level I under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 100 +metadata: + unit: currency-USD + period: year + label: Georgia LIHEAP lower income heating payment increase + reference: + - title: Georgia Benefit Matrix, FY2026 income guidelines, step 2 (difference between Level I and Level II heating amounts) + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/maximum_table_size.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/maximum_table_size.yaml new file mode 100644 index 00000000000..0765c12055c --- /dev/null +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/maximum_table_size.yaml @@ -0,0 +1,10 @@ +description: Georgia publishes Level I income thresholds through this household size under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 16 +metadata: + unit: person + period: year + label: Georgia LIHEAP maximum published benefit table size + reference: + - title: Georgia Benefit Matrix, FY2026 income guidelines, step 1 + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 756dc2c4988..6f66feb7ce2 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -802,8 +802,16 @@ programs: category: Benefits agency: HHS status: partial - coverage: OR, DC, Riverside County, MA, IL, KS, MS, TX + coverage: GA, OR, DC, Riverside County, MA, IL, KS, MS, TX state_implementations: + - state: GA + status: partial + name: Georgia LIHEAP + full_name: Georgia Low Income Home Energy Assistance Program + variable: ga_liheap + parameter_prefix: gov.states.ga.dfcs.liheap + verified_years: "2026" + notes: FY2026 ordinary regular heating eligibility and fuel payments. Actual categorical recipient membership, assistance-only resources and utility credit balances cannot be identified from existing inputs; those limitations are documented. Historical backfills and FY2027 draft changes are unverified. Regular heating only; crisis, cooling, weatherization and equipment assistance are excluded. - state: OR status: in_progress name: LIHEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml new file mode 100644 index 00000000000..21fab097998 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml @@ -0,0 +1,105 @@ +# Georgia ordinary heating route only. Categorical recipient facts, utility credits, +# unpriced fuels and size-above-16 coverage remain documented limitations. + +# FY2026 matrix p1 sets the size1 ordinary ceiling at34549; manual p50 requires income at or below the limit. Real adult wages are aggregated, not overridden countable income. +- name: Case 1, the ordinary one person income ceiling is inclusive. + period: 2026 + input: + state_code: [GA, GA] + age: [40, 40] + immigration_status: [CITIZEN, CITIZEN] + employment_income: [34_549, 34549.01] + ssi: [0, 0] + ga_tanf: [0, 0] + heating_type: [ELECTRICITY, ELECTRICITY] + pre_subsidy_electricity_expense: [1_200, 1_200] + output: + ga_liheap_eligible: [true, false] + +# Manual pp51-52 excludes undocumented members from size; without a qualified adult or child there is no eligible member, even with a positive bill. +- name: Case 2, a household with no qualified member fails ordinary eligibility. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: UNDOCUMENTED + employment_income: 0 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + ga_tanf: 0 + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + households: + household: + members: [person1] + state_code: GA + output: + ga_liheap_eligible: false + +# Manual pp4,57 permits a verified energy burden in subsidized and other rental housing. Positive primary bills establish burden; rent inclusion alone and no explicit obligation do not. +- name: Case 3, heat in rent needs a verified burden with or without housing subsidy. + period: 2026 + input: + state_code: [GA, GA, GA, GA] + age: [40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN] + employment_income: [12_000, 12_000, 12_000, 12_000] + ssi: [0, 0, 0, 0] + ga_tanf: [0, 0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_200, 0, 1_200, 0] + heat_expense_included_in_rent: [true, true, true, true] + receives_housing_assistance: [false, false, true, true] + is_in_public_housing: [false, false, true, true] + output: + ga_liheap_eligible: [true, false, true, false] + +# Manual p3 requires responsibility for home heating costs; v1.2 reuses an explicit existing obligation flag. NONE still means no heating; a gas household with an explicit obligation can qualify with no reported bill amount. +- name: Case 4, explicit no heating fails even when an obligation flag is supplied. + period: 2026 + input: + state_code: [GA, GA] + age: [40, 40] + immigration_status: [CITIZEN, CITIZEN] + employment_income: [12_000, 12_000] + ssi: [0, 0] + ga_tanf: [0, 0] + heating_type: [NONE, NATURAL_GAS] + gas_expense: [1_200, 0] + has_heating_expense: [true, true] + output: + ga_liheap_eligible: [false, true] + +# Manual pp3,50 requires heating responsibility or verified burden. With no explicit obligation, gas-primary households use their gas bill: an electric bill alone does not identify gas heating expense. +- name: Case 5, an unrelated utility bill does not establish the selected primary fuel burden. + period: 2026 + input: + state_code: [GA, GA] + age: [40, 40] + immigration_status: [CITIZEN, CITIZEN] + employment_income: [12_000, 12_000] + ssi: [0, 0] + ga_tanf: [0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS] + gas_expense: [0, 1] + pre_subsidy_electricity_expense: [1_200, 0] + output: + ga_liheap_eligible: [false, true] + +# Manual pp3,50 addresses Georgia resident households; the same real wage and positive primary bill in Alabama must not receive Georgia eligibility. +- name: Case 6, ordinary eligibility is limited to Georgia in mixed state arrays. + period: 2026 + input: + state_code: [GA, AL] + age: [40, 40] + immigration_status: [CITIZEN, CITIZEN] + employment_income: [12_000, 12_000] + ssi: [0, 0] + ga_tanf: [0, 0] + heating_type: [ELECTRICITY, ELECTRICITY] + pre_subsidy_electricity_expense: [1_200, 1_200] + output: + ga_liheap_eligible: [true, false] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.yaml new file mode 100644 index 00000000000..097e48f919c --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.yaml @@ -0,0 +1,29 @@ +# Source-derived Georgia independent components; no final eligibility or total award asserted. + +# FY2026 original matrix literal annual limits; all16 are checked independently of the model formula or parameters. +- name: Case 1, all sixteen published annual SMI limits match the reconciled flooring order. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA] + heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, + ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, + ELECTRICITY] + pre_subsidy_electricity_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, + 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + ga_liheap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + output: + ga_liheap_income_limit: [34_549, 45_179, 55_810, 66_441, 77_071, 87_702, 89_695, 91_688, 93_681, + 95_675, 97_668, 99_661, 101_654, 103_647, 105_641, 107_634] + +# Independent state-defined calculation returns the GA one-person limit only for Georgia; safe batch behavior. +- name: Case 2, mixed state calculation masks the Georgia income limit outside Georgia. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [GA, AL] + heating_type: [ELECTRICITY, ELECTRICITY] + pre_subsidy_electricity_expense: [1_200, 1_200] + ga_liheap_household_size: [1, 1] + output: + ga_liheap_income_limit: [34_549, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml new file mode 100644 index 00000000000..6fd699f8d25 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml @@ -0,0 +1,280 @@ +# Georgia ordinary heating route only. Categorical recipient facts, utility credits, +# unpriced fuels and size-above-16 coverage remain documented limitations. + +# FY2026 matrix p1: size1 income12000 is LevelI and electric pays500. Manual pp4,50 establishes heating burden, not an expense cap; the25 bill must not reduce the scheduled award. +- name: Case 1, a small positive primary bill receives the full fixed heating award. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + employment_income: 12_000 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + ga_tanf: 0 + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 25 + households: + household: + members: [person1] + state_code: GA + output: + ga_liheap_household_size: 1 + ga_liheap_income: 12_000 + ga_liheap_income_limit: 34_549 + heating_expense: 25 + ga_liheap_matrix_amount: 500 + ga_liheap_eligible: true + ga_liheap: 500 + +# Manual pp51-52,62: parent income counts fully, parent is excluded from size, and all under18 income is excluded. FY2026 matrix p1 gives the size1 LevelI gas award660. +- name: Case 2, a citizen child qualifies while the nonqualified parent income counts fully. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: UNDOCUMENTED + employment_income: 12_000 + ssi: 0 + person2: + age: 10 + immigration_status: CITIZEN + employment_income: 6_000 + survivor_benefits: 1_200 + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2] + ga_tanf: 0 + heating_type: NATURAL_GAS + gas_expense: 1_200 + households: + household: + members: [person1, person2] + state_code: GA + output: + ga_liheap_household_size: 1 + ga_liheap_income: 12_000 + ga_liheap_income_limit: 34_549 + ga_liheap_matrix_amount: 660 + ga_liheap_eligible: true + ga_liheap: 660 + +# Manual pp51-52,62,67 counts the full30000 wages plus5000 farm-property rent against qualified size1.35000 exceeds the FY2026 matrix ceiling34549, so ordinary eligibility gates the final grant to zero. +- name: Case 3, full nonqualified adult wages and farm rent can exceed the qualified child ceiling. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: UNDOCUMENTED + employment_income: 30_000 + farm_rent_income: 5_000 + ssi: 0 + person2: + age: 10 + immigration_status: CITIZEN + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2] + ga_tanf: 0 + heating_type: NATURAL_GAS + gas_expense: 1_200 + households: + household: + members: [person1, person2] + state_code: GA + output: + ga_liheap_household_size: 1 + ga_liheap_income: 35_000 + ga_liheap_income_limit: 34_549 + ga_liheap_eligible: false + ga_liheap: 0 + +# FY2026 matrix p1 explicitly gives size4 LevelI ceiling25469, despite its unusual pattern. Two actual four-person families with wages25469 and25469.01 remain below66441 but receive gas660 and560 respectively. +- name: Case 4, real four person families use the printed lower band boundary before the award. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + ssi: 0 + employment_income: 25_469 + person2: + age: 39 + immigration_status: CITIZEN + ssi: 0 + employment_income: 0 + person3: + age: 10 + immigration_status: CITIZEN + ssi: 0 + employment_income: 0 + person4: + age: 8 + immigration_status: CITIZEN + ssi: 0 + employment_income: 0 + person5: + age: 40 + immigration_status: CITIZEN + ssi: 0 + employment_income: 25469.01 + person6: + age: 39 + immigration_status: CITIZEN + ssi: 0 + employment_income: 0 + person7: + age: 10 + immigration_status: CITIZEN + ssi: 0 + employment_income: 0 + person8: + age: 8 + immigration_status: CITIZEN + ssi: 0 + employment_income: 0 + tax_units: + tax_unit1: + members: [person1, person2, person3, person4] + tax_unit2: + members: [person5, person6, person7, person8] + spm_units: + spm_unit1: + members: [person1, person2, person3, person4] + heating_type: NATURAL_GAS + gas_expense: 1_200 + ga_tanf: 0 + spm_unit2: + members: [person5, person6, person7, person8] + heating_type: NATURAL_GAS + gas_expense: 1_200 + ga_tanf: 0 + households: + household1: + members: [person1, person2, person3, person4] + state_code: GA + household2: + members: [person5, person6, person7, person8] + state_code: GA + output: + ga_liheap_household_size: [4, 4] + ga_liheap_income_limit: [66_441, 66_441] + ga_liheap_eligible: [true, true] + ga_liheap_matrix_amount: [660, 560] + ga_liheap: [660, 560] + +# FY2026 matrix p1: real single-adult wages20000 are above13589 and below34549. LevelII oil710, propane580 and wood400 pass through ordinary eligibility; the Alabama household receives no Georgia award. Existing unit cases already cover the full ten-cell matrix. +- name: Case 5, ordinary upper band awards use the primary fuel and exclude another state. + period: 2026 + input: + state_code: [GA, GA, GA, AL] + age: [40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN] + employment_income: [20_000, 20_000, 20_000, 20_000] + ssi: [0, 0, 0, 0] + ga_tanf: [0, 0, 0, 0] + heating_type: [FUEL_OIL, PROPANE, WOOD, FUEL_OIL] + fuel_oil_expense: [1_200, 0, 0, 1_200] + bottled_gas_expense: [0, 1_200, 0, 0] + wood_expense: [0, 0, 1_200, 0] + output: + ga_liheap_eligible: [true, true, true, false] + ga_liheap: [710, 580, 400, 0] + +# Manual pp4,57 permits verified energy burden in subsidized heat-in-rent housing. The shared Boolean defaults false for heat in rent, but the reported primary bill100 supplies burden; FY2026 LevelI propane pays680 uncapped. +- name: Case 6, a subsidized renter with verified primary expense can receive the ordinary grant. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + employment_income: 12_000 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + ga_tanf: 0 + heating_type: PROPANE + bottled_gas_expense: 100 + heat_expense_included_in_rent: true + receives_housing_assistance: true + households: + household: + members: [person1] + state_code: GA + is_in_public_housing: true + output: + ga_liheap_household_size: 1 + ga_liheap_income: 12_000 + heating_expense: 100 + has_heating_expense: false + ga_liheap_matrix_amount: 680 + ga_liheap_eligible: true + ga_liheap: 680 + +# Contract retains existing signed net-business inputs and floors combined household income0; no additional business-expense policy is introduced. FY2026 matrix p1 fixes LevelI wood at500, so a negative pre-floor total cannot increase the grant. +- name: Case 7, existing net business losses do not inflate the fixed heating award. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + employment_income: 6_000 + self_employment_income: -18_000 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + ga_tanf: 0 + heating_type: WOOD + wood_expense: 100 + households: + household: + members: [person1] + state_code: GA + output: + ga_liheap_income: 0 + ga_liheap_household_size: 1 + ga_liheap_matrix_amount: 500 + ga_liheap_eligible: true + ga_liheap: 500 + +# Manual p3 requires responsibility for energy costs or verified burden; v1.2 honors the existing explicit has_heating_expense fact even when no paid bill amount is reported. FY2026 matrix p1 gives LevelI oil810 with no spending cap. +- name: Case 8, an explicit heating obligation with a zero reported bill receives the scheduled award. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + employment_income: 12_000 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + ga_tanf: 0 + heating_type: FUEL_OIL + fuel_oil_expense: 0 + has_heating_expense: true + households: + household: + members: [person1] + state_code: GA + output: + ga_liheap_household_size: 1 + ga_liheap_income: 12_000 + heating_expense: 0 + has_heating_expense: true + ga_liheap_matrix_amount: 810 + ga_liheap_eligible: true + ga_liheap: 810 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap_household_size.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap_household_size.yaml new file mode 100644 index 00000000000..9cb2d08c61b --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap_household_size.yaml @@ -0,0 +1,69 @@ +# Source-derived Georgia independent components; no final eligibility or total award asserted. + +# Manual pp51–52 counts citizen/qualified people including minors; DACA and undocumented members do not count in size. +- name: Case 1, qualified adults and minors count without SNAP waiting bars. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: LEGAL_PERMANENT_RESIDENT + ssi: 0 + person2: + age: 39 + immigration_status: ASYLEE + ssi: 0 + person3: + age: 16 + immigration_status: CITIZEN + ssi: 0 + person4: + age: 19 + immigration_status: DACA + ssi: 0 + person5: + age: 8 + immigration_status: UNDOCUMENTED + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + ga_tanf: 0 + households: + household: + members: [person1, person2, person3, person4, person5] + state_code: GA + hud_utility_allowance: 0 + output: + ga_liheap_household_size: 3 + +# No final eligibility result is asserted; this isolates qualified-member size. +- name: Case 2, a wholly nonqualified household has size zero. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: UNDOCUMENTED + ssi: 0 + person2: + age: 8 + immigration_status: DACA + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + ga_tanf: 0 + households: + household: + members: [person1, person2] + state_code: GA + hud_utility_allowance: 0 + output: + ga_liheap_household_size: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml new file mode 100644 index 00000000000..c6f392349b6 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml @@ -0,0 +1,236 @@ +# Source-derived Georgia independent components; no final eligibility or total award asserted. + +# Manual pp65–68: wages and business17400 + ordinary/farmrent2400 + survivors600 + pension1200 + SSA2400 + SSI1200 + gains600 =25800; no duplicated component aggregates. +- name: Case 1, supported independent adult income sources are counted once. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + ssi: 1_200 + employment_income: 12_000 + self_employment_income: 3_000 + farm_operations_income: 1_200 + sstb_self_employment_income: 600 + partnership_s_corp_income: 600 + rental_income: 1_200 + farm_rent_income: 1_200 + survivor_benefits: 600 + pension_income: 1_200 + social_security: 2_400 + capital_gains: 600 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + ga_tanf: 0 + households: + household: + members: [person1] + state_code: GA + hud_utility_allowance: 0 + output: + ga_liheap_income: 25_800 + +# Manual p62 excludes ALL under18 income. At18,6000wages+2400SSI+1200SSA+600dividends+300netinterest adds10500 to parent12000. +- name: Case 2, all age 17 income follows the under eighteen exclusion boundary. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + ssi: 0 + employment_income: 12_000 + person2: + age: 17 + immigration_status: CITIZEN + ssi: 2_400 + employment_income: 6_000 + social_security: 1_200 + ordinary_dividend_income: 600 + interest_income: 600 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + ga_tanf: 0 + households: + household: + members: [person1, person2] + state_code: GA + hud_utility_allowance: 0 + output: + ga_liheap_income: 12_000 + +# Manual p62 excludes ALL under18 income. At18,6000wages+2400SSI+1200SSA+600dividends+300netinterest adds10500 to parent12000. +- name: Case 3, all age 18 income follows the under eighteen exclusion boundary. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + ssi: 0 + employment_income: 12_000 + person2: + age: 18 + immigration_status: CITIZEN + ssi: 2_400 + employment_income: 6_000 + social_security: 1_200 + ordinary_dividend_income: 600 + interest_income: 600 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + ga_tanf: 0 + households: + household: + members: [person1, person2] + state_code: GA + hud_utility_allowance: 0 + output: + ga_liheap_income: 22_500 + +# Manual pp66,69 and contractv1 household interpretation: combined adultinterest400 minus annual300, plus600dividends =700; no per-person multiplication. +- name: Case 4, interest disregard is shared once across adults while dividends fully count. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + ssi: 0 + interest_income: 200 + ordinary_dividend_income: 600 + person2: + age: 39 + immigration_status: CITIZEN + ssi: 0 + interest_income: 200 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + ga_tanf: 0 + households: + household: + members: [person1, person2] + state_code: GA + hud_utility_allowance: 0 + output: + ga_liheap_income: 700 + +# Monthly25 allowance annualizes to300; exact threshold is excluded and first dollar above it counts. +- name: Case 5, annual household interest allowance has an exact three hundred dollar boundary. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [GA, GA, GA] + heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY] + pre_subsidy_electricity_expense: [1_200, 1_200, 1_200] + age: [40, 40, 40] + ssi: [0, 0, 0] + ga_tanf: [0, 0, 0] + interest_income: [299, 300, 301] + output: + ga_liheap_income: [0, 0, 1] + +# Manual p67 TANF grant counts once at SPM level: caregiver wages1200 + annual ga_tanf3600 =4800; minorSSI2400 excluded. +- name: Case 6, caregiver TANF cash counts once despite excluding a minor SSI payment. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + ssi: 0 + employment_income: 1_200 + person2: + age: 8 + immigration_status: CITIZEN + ssi: 2_400 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + ga_tanf: 3_600 + households: + household: + members: [person1, person2] + state_code: GA + hud_utility_allowance: 0 + output: + ga_liheap_income: 4_800 + +# Contractv1 preserves signed existing net-business inputs and floors the combined household income0; no additional business-expense treatment. +- name: Case 7, existing signed net losses cannot make the household total negative. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + ssi: 0 + employment_income: 6_000 + self_employment_income: -18_000 + spm_units: + spm_unit: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + ga_tanf: 0 + households: + household: + members: [person1] + state_code: GA + hud_utility_allowance: 0 + output: + ga_liheap_income: 0 + +# Manual pp51–52,62: parent income counts in full, citizen child counts in size but all child income is excluded. Matrix1person electric LevelI=500; final eligibility is not asserted. +- name: Case 8, nonqualified adult income counts while the citizen child determines size. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: UNDOCUMENTED + ssi: 0 + employment_income: 12_000 + person2: + age: 8 + immigration_status: CITIZEN + ssi: 2_400 + social_security: 3_600 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + ga_tanf: 0 + households: + household: + members: [person1, person2] + state_code: GA + hud_utility_allowance: 0 + output: + ga_liheap_household_size: 1 + ga_liheap_income: 12_000 + ga_liheap_income_limit: 34_549 + ga_liheap_matrix_amount: 500 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml new file mode 100644 index 00000000000..6bbd10da1e0 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml @@ -0,0 +1,75 @@ +# Source-derived Georgia independent components; no final eligibility or total award asserted. + +# Original matrix lower-income cutoff and first cent above for each size1–16; preserve anomalous size4=25469 exactly. +- name: Case 1, all sixteen published lower level upper bounds remain inclusive. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, + GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, + NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, + NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, + NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, + NATURAL_GAS] + pre_subsidy_electricity_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, + 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, + 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + gas_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, + 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, + 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + ga_liheap_household_size: [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13, 14, 14, 15, 15, 16, 16] + ga_liheap_income: [13_589, 13589.01, 17_770, 17770.01, 21_952, 21952.01, 25_469, 25469.01, 30_315, + 30315.01, 34_496, 34496.01, 35_280, 35280.01, 36_064, 36064.01, 36_848, 36848.01, 37_632, 37632.01, + 38_416, 38416.01, 39_200, 39200.01, 39_984, 39984.01, 40_768, 40768.01, 41_552, 41552.01, 42_336, + 42336.01] + output: + ga_liheap_matrix_amount: [660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, + 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560] + +# Original matrix heating column only: LevelI500/660/810/680/500; LevelII400/560/710/580/400. Cooling column is not an oracle. +- name: Case 2, all five priced fuels match both heating benefit levels including zero income. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [GA, GA, GA, GA, GA, GA, GA, GA, GA, GA] + heating_type: [ELECTRICITY, NATURAL_GAS, FUEL_OIL, PROPANE, WOOD, ELECTRICITY, NATURAL_GAS, FUEL_OIL, + PROPANE, WOOD] + pre_subsidy_electricity_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, + 1_200] + gas_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + fuel_oil_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + bottled_gas_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + wood_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + ga_liheap_household_size: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + ga_liheap_income: [0, 0, 0, 0, 0, 13589.01, 13589.01, 13589.01, 13589.01, 13589.01] + output: + ga_liheap_matrix_amount: [500, 660, 810, 680, 500, 400, 560, 710, 580, 400] + +# Ungated regular heating matrix gives660 for natural gas; no actual-expense cap is stated. +- name: Case 3, a small heating bill does not cap the fixed matrix amount. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: GA + heating_type: NATURAL_GAS + pre_subsidy_electricity_expense: 1_200 + gas_expense: 50 + ga_liheap_household_size: 1 + ga_liheap_income: 0 + output: + ga_liheap_matrix_amount: 660 + +# NONE affirmatively means no heating. No assertions here establish legal ineligibility for unpriced fuel or out-of-table size. +- name: Case 4, explicit no heating has no supported matrix amount. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: GA + heating_type: NONE + pre_subsidy_electricity_expense: 1_200 + ga_liheap_household_size: 1 + ga_liheap_income: 0 + output: + ga_liheap_matrix_amount: 0 diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py new file mode 100644 index 00000000000..18cb1ebbcd0 --- /dev/null +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py @@ -0,0 +1,37 @@ +from policyengine_us.model_api import * + + +class ga_liheap_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Georgia LIHEAP ordinary eligibility" + defined_for = StateCode.GA + # State plan pages 5, 8, 9; manual pages 3, 50, 56, 57, 75. + reference = ( + "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5", + "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=3", + ) + + def formula(spm_unit, period, parameters): + size = spm_unit("ga_liheap_household_size", period) + income = spm_unit("ga_liheap_income", period) + limit = spm_unit("ga_liheap_income_limit", period) + heating_type = spm_unit("heating_type", period) + heating_expense = spm_unit("heating_expense", period) + # Explicit heating responsibility can qualify with no current bill. + # A positive primary bill also establishes burden with heat in rent, + # including subsidized housing. The rent flag alone is insufficient. + energy_burden = spm_unit("has_heating_expense", period) | (heating_expense > 0) + # Pure-assistance categorical eligibility requires actual receipt by + # every member and assistance-only income/resources. Aggregate TANF/SNAP + # inputs cannot establish those facts, so this uses the ordinary test. + # Account credits of $1,000+ and administrative household exclusions + # also need unavailable inputs; the selected heating account is assumed + # to satisfy those rules. Annual expense is not an account-credit proxy. + return ( + (size > 0) + & (income <= limit) + & energy_burden + & (heating_type != heating_type.possible_values.NONE) + ) diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.py new file mode 100644 index 00000000000..fd65d704f61 --- /dev/null +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.py @@ -0,0 +1,27 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.hhs_smi import smi + + +class ga_liheap_income_limit(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Georgia LIHEAP annual income limit" + defined_for = StateCode.GA + reference = ( + "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=8", + "https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.ga.dfcs.liheap.eligibility + size = spm_unit("ga_liheap_household_size", period) + state = spm_unit.household("state_code_str", period) + four_person_smi = parameters(period).gov.hhs.smi.amount[state] + size_share = smi(size, state, period, parameters) / four_person_smi + # Flooring 60% of the four-person SMI before applying the federal size + # factor reproduces all 16 FY2026 published limits. This is a numerical + # reconciliation; the sources do not expressly specify the floor order. + four_person_limit = np.floor(four_person_smi * p.smi_rate) + return where(size > 0, np.floor(four_person_limit * size_share), 0) diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap.py new file mode 100644 index 00000000000..3b9f608070a --- /dev/null +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap.py @@ -0,0 +1,17 @@ +from policyengine_us.model_api import * + + +class ga_liheap(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Georgia LIHEAP ordinary heating benefit" + defined_for = "ga_liheap_eligible" + reference = ( + "https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf", + "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=4", + ) + # The ordinary grant is a fixed schedule amount with no bill cap. Unpriced + # fuels and the matrix's size-above-16 approximation remain unverified. + adds = ["ga_liheap_matrix_amount"] diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py new file mode 100644 index 00000000000..bae87678c05 --- /dev/null +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py @@ -0,0 +1,16 @@ +from policyengine_us.model_api import * + + +class ga_liheap_household_size(Variable): + value_type = int + entity = SPMUnit + definition_period = YEAR + label = "Georgia LIHEAP household size" + defined_for = StateCode.GA + # Pages 51-52 exclude nonqualified members from size; page 62 counts income. + reference = "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=51" + + # The SPM unit approximates the economic unit purchasing energy together. + # Existing inputs do not identify all roomers, boarders, foster-child + # elections, or excluded group-quarter arrangements. + adds = ["is_citizen_or_legal_immigrant"] diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py new file mode 100644 index 00000000000..9bd6b139546 --- /dev/null +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py @@ -0,0 +1,44 @@ +from policyengine_us.model_api import * + + +class ga_liheap_income(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Georgia LIHEAP annual countable household income" + defined_for = StateCode.GA + reference = ( + # Manual pages 62-69; current plan pages 5-6 govern listed exclusions. + "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=62", + "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.ga.dfcs.liheap.income + person = spm_unit.members + adult = person("age", period) >= p.minimum_age + # Annual inputs approximate income in the application period. Georgia + # excludes all income of under-18s, not only their earnings, and counts + # otherwise countable income of nonqualified adults in full. + person_income = add(person, period, p.sources.person, options=[ADD]) + adult_interest = spm_unit.sum(person("interest_income", period) * adult) + # The manual excludes the first $25 of monthly interest without saying + # this allowance repeats per person or account. Apply it once to the + # household's adult interest; dividends remain fully countable. + interest = max_( + adult_interest - p.monthly_interest_disregard * MONTHS_IN_YEAR, + 0, + ) + # State TANF is included once at SPM level as a caregiver grant rather + # than projecting the household award onto every member. + household_income = add(spm_unit, period, p.sources.household, options=[ADD]) + # Keep existing signed net business inputs without another deduction; + # floor the combined total. Rental income is assumed nonnegative. + # Roomer/boarder expense allowances, Medicare premiums, mortgage-sale + # contracts, stipends, and recurring support lack the scoped handling. + # Current plan exclusions govern cash gifts and lump-sum receipts. + return max_( + spm_unit.sum(person_income * adult) + interest + household_income, + 0, + ) diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py new file mode 100644 index 00000000000..26dab672570 --- /dev/null +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py @@ -0,0 +1,43 @@ +from policyengine_us.model_api import * + + +class ga_liheap_matrix_amount(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Georgia LIHEAP heating matrix amount" + defined_for = StateCode.GA + reference = "https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf" + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.ga.dfcs.liheap.payment + size = spm_unit("ga_liheap_household_size", period) + income = spm_unit("ga_liheap_income", period) + # The published lower-income thresholds end at size 16. Capping this + # lookup is provisional for larger units, not verified policy coverage. + lookup_size = clip(size, 1, p.maximum_table_size) + lower_income = income <= p.lower_income_limit[lookup_size] + heating_type = spm_unit("heating_type", period) + fuel = heating_type.possible_values + base = select( + [ + heating_type == fuel.ELECTRICITY, + heating_type == fuel.NATURAL_GAS, + heating_type == fuel.FUEL_OIL, + heating_type == fuel.PROPANE, + heating_type == fuel.WOOD, + ], + [ + p.base_amount.electricity, + p.base_amount.natural_gas, + p.base_amount.fuel_oil, + p.base_amount.propane, + p.base_amount.wood, + ], + default=0, + ) + # This is an ungated schedule amount with no cap at the reported bill. + # Unpriced kerosene, coal, solar, other, and unspecified fuels return an + # incomplete zero estimate; this is not a legal ineligibility finding. + return where(base > 0, base + lower_income * p.lower_income_uplift, 0) From 35a47c84442444ba82593175691a3ead97dda77a Mon Sep 17 00:00:00 2001 From: Ziming Date: Sat, 3 Oct 2026 21:51:38 -0400 Subject: [PATCH 02/14] Fix Georgia LIHEAP bundled-utility rental payments --- .../income/monthly_interest_disregard.yaml | 2 +- .../eligibility/ga_liheap_eligible.yaml | 26 +++++++- .../gov/states/ga/dfcs/liheap/ga_liheap.yaml | 63 +++++++++++++++++++ .../payment/ga_liheap_matrix_amount.yaml | 19 ++++++ .../liheap/eligibility/_ga_liheap_housing.py | 17 +++++ .../liheap/eligibility/ga_liheap_eligible.py | 11 +++- .../liheap/payment/ga_liheap_matrix_amount.py | 36 ++++++++--- 7 files changed, 160 insertions(+), 14 deletions(-) create mode 100644 policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml index 8ea3d187c7d..e37c144d984 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml @@ -6,5 +6,5 @@ metadata: period: month label: Georgia LIHEAP monthly interest disregard reference: - - title: Georgia LIHEAP Policy Manual, January 2022, section 1200.4, pages 66 and 69 (first interest amount disregarded) + - title: Georgia LIHEAP Policy Manual, January 2022, section 1200.7, pages 66 and 69 (first interest amount disregarded) href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=66 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml index 21fab097998..ea4c7ee57fb 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml @@ -39,7 +39,7 @@ output: ga_liheap_eligible: false -# Manual pp4,57 permits a verified energy burden in subsidized and other rental housing. Positive primary bills establish burden; rent inclusion alone and no explicit obligation do not. +# Manual pp4,57 permits a verified energy burden in subsidized and other rental housing. These tenants pay their own utilities by default; positive primary bills establish burden, while the heat-in-rent flag alone does not. - name: Case 3, heat in rent needs a verified burden with or without housing subsidy. period: 2026 input: @@ -103,3 +103,27 @@ pre_subsidy_electricity_expense: [1_200, 1_200] output: ga_liheap_eligible: [true, false] + +# Manual p89 recognizes all utilities included in unsubsidized rent as an energy burden. +# Each column changes one requirement from the first qualifying rental household: +# separately paid utilities, no rent, housing assistance, public housing, no qualified +# member, excess income, no heating, or heat excluded from rent. Ordinary gates remain. +- name: Case 7, bundled utilities in unsubsidized rent qualify subject to ordinary eligibility. + period: 2026 + input: + state_code: [GA, GA, GA, GA, GA, GA, GA, GA, GA] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, UNDOCUMENTED, CITIZEN, CITIZEN, CITIZEN] + employment_income: [12_000, 12_000, 12_000, 12_000, 12_000, 12_000, 34549.01, 12_000, 12_000] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0] + ga_tanf: [0, 0, 0, 0, 0, 0, 0, 0, 0] + rent: [12_000, 12_000, 0, 12_000, 12_000, 12_000, 12_000, 12_000, 12_000] + tenant_pays_utilities: [false, true, false, false, false, false, false, false, false] + receives_housing_assistance: [false, false, false, true, false, false, false, false, false] + is_in_public_housing: [false, false, false, false, true, false, false, false, false] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NONE, NATURAL_GAS] + gas_expense: [0, 0, 0, 0, 0, 0, 0, 0, 0] + pre_subsidy_electricity_expense: [0, 0, 0, 0, 0, 0, 0, 0, 0] + heat_expense_included_in_rent: [true, true, true, true, true, true, true, true, false] + output: + ga_liheap_eligible: [true, false, false, false, false, false, false, false, false] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml index 6fd699f8d25..7e2368109c8 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml @@ -278,3 +278,66 @@ ga_liheap_matrix_amount: 810 ga_liheap_eligible: true ga_liheap: 810 + +# Manual p89 recognizes an energy burden when all utilities are included in +# unsubsidized rent. FY2026 plan p30 requires the lowest award within the income +# level for payment to the applicant:500 for12000 income and400 for15000 income. +# Both are below the ordinary size1 ceiling34549 and have no separately paid bill. +- name: Case 9, unsubsidized renters with all utilities bundled receive each income level minimum. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + employment_income: 12_000 + rent: 12_000 + ssi: 0 + person2: + age: 40 + immigration_status: CITIZEN + employment_income: 15_000 + rent: 12_000 + ssi: 0 + tax_units: + tax_unit1: + members: [person1] + tax_unit2: + members: [person2] + spm_units: + spm_unit1: + members: [person1] + ga_tanf: 0 + heating_type: NATURAL_GAS + heat_expense_included_in_rent: true + receives_housing_assistance: false + gas_expense: 0 + pre_subsidy_electricity_expense: 0 + spm_unit2: + members: [person2] + ga_tanf: 0 + heating_type: NATURAL_GAS + heat_expense_included_in_rent: true + receives_housing_assistance: false + gas_expense: 0 + pre_subsidy_electricity_expense: 0 + households: + household1: + members: [person1] + state_code: GA + is_in_public_housing: false + tenant_pays_utilities: false + household2: + members: [person2] + state_code: GA + is_in_public_housing: false + tenant_pays_utilities: false + output: + ga_liheap_household_size: [1, 1] + ga_liheap_income: [12_000, 15_000] + ga_liheap_income_limit: [34_549, 34_549] + heating_expense: [0, 0] + has_heating_expense: [false, false] + ga_liheap_matrix_amount: [500, 400] + ga_liheap_eligible: [true, true] + ga_liheap: [500, 400] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml index 6bbd10da1e0..3323c2a6683 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml @@ -73,3 +73,22 @@ ga_liheap_income: 0 output: ga_liheap_matrix_amount: 0 + +# FY2026 plan p30 assigns direct applicant payments the lowest award within the +# income level. The matrix gives500 through13589 and400 above it for size1. +# A separately paid utility retains the ordinary gas award660; NONE remains zero. +- name: Case 5, direct rental payments use the income level minimum and preserve no heating. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [GA, GA, GA, GA] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NONE] + ga_liheap_household_size: [1, 1, 1, 1] + ga_liheap_income: [13_589, 13589.01, 12_000, 12_000] + rent: [12_000, 12_000, 12_000, 12_000] + heat_expense_included_in_rent: [true, true, true, true] + tenant_pays_utilities: [false, false, true, false] + receives_housing_assistance: [false, false, false, false] + is_in_public_housing: [false, false, false, false] + output: + ga_liheap_matrix_amount: [500, 400, 660, 0] diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py new file mode 100644 index 00000000000..4c3e68b231d --- /dev/null +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py @@ -0,0 +1,17 @@ +from policyengine_us.model_api import add + + +def ga_liheap_has_direct_payment_route(spm_unit, period): + """Identify unsubsidized rent that includes all utilities. + + Georgia's manual energy-burden table (physical page 89) permits payment + directly to these renters. The rental agreement is assumed verified. + https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=89 + """ + return ( + spm_unit("heat_expense_included_in_rent", period) + & ~spm_unit.household("tenant_pays_utilities", period) + & (add(spm_unit, period, ["rent"]) > 0) + & ~spm_unit("receives_housing_assistance", period) + & ~spm_unit.household("is_in_public_housing", period) + ) diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py index 18cb1ebbcd0..008955b7070 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py @@ -1,4 +1,7 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.ga.dfcs.liheap.eligibility._ga_liheap_housing import ( + ga_liheap_has_direct_payment_route, +) class ga_liheap_eligible(Variable): @@ -7,7 +10,7 @@ class ga_liheap_eligible(Variable): definition_period = YEAR label = "Georgia LIHEAP ordinary eligibility" defined_for = StateCode.GA - # State plan pages 5, 8, 9; manual pages 3, 50, 56, 57, 75. + # State plan pages 5, 8, 9, 30; manual pages 3, 50, 56, 57, 75, 89. reference = ( "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5", "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=3", @@ -22,7 +25,11 @@ def formula(spm_unit, period, parameters): # Explicit heating responsibility can qualify with no current bill. # A positive primary bill also establishes burden with heat in rent, # including subsidized housing. The rent flag alone is insufficient. - energy_burden = spm_unit("has_heating_expense", period) | (heating_expense > 0) + energy_burden = ( + spm_unit("has_heating_expense", period) + | (heating_expense > 0) + | ga_liheap_has_direct_payment_route(spm_unit, period) + ) # Pure-assistance categorical eligibility requires actual receipt by # every member and assistance-only income/resources. Aggregate TANF/SNAP # inputs cannot establish those facts, so this uses the ordinary test. diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py index 26dab672570..cc1680657d1 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py @@ -1,4 +1,7 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.states.ga.dfcs.liheap.eligibility._ga_liheap_housing import ( + ga_liheap_has_direct_payment_route, +) class ga_liheap_matrix_amount(Variable): @@ -8,7 +11,11 @@ class ga_liheap_matrix_amount(Variable): unit = USD label = "Georgia LIHEAP heating matrix amount" defined_for = StateCode.GA - reference = "https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf" + reference = ( + "https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf", + "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=30", + "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=89", + ) def formula(spm_unit, period, parameters): p = parameters(period).gov.states.ga.dfcs.liheap.payment @@ -20,6 +27,13 @@ def formula(spm_unit, period, parameters): lower_income = income <= p.lower_income_limit[lookup_size] heating_type = spm_unit("heating_type", period) fuel = heating_type.possible_values + base_amounts = [ + p.base_amount.electricity, + p.base_amount.natural_gas, + p.base_amount.fuel_oil, + p.base_amount.propane, + p.base_amount.wood, + ] base = select( [ heating_type == fuel.ELECTRICITY, @@ -28,16 +42,18 @@ def formula(spm_unit, period, parameters): heating_type == fuel.PROPANE, heating_type == fuel.WOOD, ], - [ - p.base_amount.electricity, - p.base_amount.natural_gas, - p.base_amount.fuel_oil, - p.base_amount.propane, - p.base_amount.wood, - ], + base_amounts, default=0, ) + # Applicant payments use the lowest award in the income level. + direct_payment = ga_liheap_has_direct_payment_route(spm_unit, period) + base = where( + direct_payment & (heating_type != fuel.NONE), + np.minimum.reduce(base_amounts), + base, + ) # This is an ungated schedule amount with no cap at the reported bill. - # Unpriced kerosene, coal, solar, other, and unspecified fuels return an - # incomplete zero estimate; this is not a legal ineligibility finding. + # Ordinary vendor payments for unpriced kerosene, coal, solar, other, + # and unspecified fuels return an incomplete zero estimate; this is + # not a legal ineligibility finding. return where(base > 0, base + lower_income * p.lower_income_uplift, 0) From f6f2dadc5e58fbd3bdf86eae8e10f4bf7035a889 Mon Sep 17 00:00:00 2001 From: Ziming Date: Sun, 4 Oct 2026 20:17:39 -0400 Subject: [PATCH 03/14] Clarify GA LIHEAP coverage limitations --- 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 6f66feb7ce2..50ffca7f055 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -811,7 +811,7 @@ programs: variable: ga_liheap parameter_prefix: gov.states.ga.dfcs.liheap verified_years: "2026" - notes: FY2026 ordinary regular heating eligibility and fuel payments. Actual categorical recipient membership, assistance-only resources and utility credit balances cannot be identified from existing inputs; those limitations are documented. Historical backfills and FY2027 draft changes are unverified. Regular heating only; crisis, cooling, weatherization and equipment assistance are excluded. + notes: FY2026 ordinary regular heating eligibility and published fuel payments. Unlisted fuel payments and the Level I cutoff above household size 16 remain unverified; zero for an unlisted fuel is not a legal denial. Actual categorical recipient membership, assistance-only resources and utility credit balances use documented existing-input approximations. Historical backfills and FY2027 draft changes are unverified. Regular heating only; crisis, cooling, weatherization and equipment assistance are excluded. - state: OR status: in_progress name: LIHEAP From 2f108f7a1960cdf6367b5e284025c8df71d88a06 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 6 Oct 2026 17:26:20 -0400 Subject: [PATCH 04/14] Fix issues from review: FY2025 cutoffs, heat-in-rent electric route, income - Add the published FY2025 Level I income cutoffs for sizes 1-16. - Make renters whose heat is in rent and who are billed for electric eligible (manual p89 row 3; p4 for subsidized renters with the bill in their name), paid the matrix amount for the main heating fuel. - Count Social Security net of the Medicare Part B premium; count military and railroad retirement; floor each income source at zero; read take-up-gated tanf instead of ga_tanf. - Shorten the registry note; set verified_years to 2025-2026. - Add period-2025, heat-in-rent, income-source and branch cases; tidy test comments. Co-Authored-By: Claude Opus 5.5 --- .../dfcs/liheap/income/sources/household.yaml | 4 +- .../ga/dfcs/liheap/income/sources/person.yaml | 18 +- .../liheap/payment/lower_income_limit.yaml | 18 + policyengine_us/programs.yaml | 6 +- .../eligibility/ga_liheap_eligible.yaml | 54 ++- .../eligibility/ga_liheap_income_limit.yaml | 22 +- .../gov/states/ga/dfcs/liheap/ga_liheap.yaml | 316 +++++++++++++++++- .../dfcs/liheap/ga_liheap_household_size.yaml | 3 +- .../dfcs/liheap/income/ga_liheap_income.yaml | 171 +++++++++- .../payment/ga_liheap_matrix_amount.yaml | 51 ++- .../liheap/eligibility/_ga_liheap_housing.py | 24 ++ .../liheap/eligibility/ga_liheap_eligible.py | 9 +- .../ga/dfcs/liheap/income/ga_liheap_income.py | 37 +- .../liheap/payment/ga_liheap_matrix_amount.py | 6 +- 14 files changed, 674 insertions(+), 65 deletions(-) diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml index 3f13a65e5da..edc8807616d 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml @@ -1,7 +1,9 @@ description: Georgia counts these household benefits once under the Low Income Home Energy Assistance Program. values: 2025-10-01: - - ga_tanf + # The tanf aggregate includes ga_tanf and applies take-up; ga_tanf alone is + # the entitlement and would count benefits a nonrecipient could get. + - tanf metadata: unit: list period: year diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml index 45a1f6f3874..07f2fe9c65a 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml @@ -1,6 +1,8 @@ description: Georgia counts these income sources of adult household members under the Low Income Home Energy Assistance Program. -# Interest is calculated separately after its disregard. State TANF is counted -# once at SPM-unit level. Signed net business inputs have no extra deduction. +# Interest is calculated separately after its disregard. TANF is counted once +# at SPM-unit level. Each source is floored at zero in the formula, so a loss +# in one source cannot offset another; net business inputs have no extra +# deduction. values: 2025-10-01: - employment_income @@ -9,9 +11,17 @@ values: - farm_operations_income - partnership_s_corp_income - unemployment_compensation - - social_security + # Social Security is counted in the formula, net of the Medicare Part B + # premium. - ssi - pension_income + # Military retirement pay is a separate input from the pension inputs that + # pension_income adds, so both are listed; a household reports the amount + # in one or the other. + - military_retirement_pay + # Manual application item 12 groups Railroad Retirement with Social + # Security; only Social Security is netted of the Medicare premium. + - railroad_benefits - survivor_benefits - veterans_benefits - disability_benefits @@ -29,7 +39,7 @@ metadata: period: year label: Georgia LIHEAP adult person income sources reference: - - title: Georgia LIHEAP Policy Manual, January 2022, sections 1200.2-1200.4, pages 62-69 + - title: Georgia LIHEAP Policy Manual, January 2022, sections 1200.1-1200.7, pages 62-69, and application item 12, page 83 href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=62 - title: Georgia FY2026 LIHEAP State Plan, section 1.9, pages 5-6 (current included and excluded sources) href: https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_limit.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_limit.yaml index b4cb7419af4..391374081a6 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_limit.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_limit.yaml @@ -2,36 +2,52 @@ description: Georgia limits annual income to this amount by household size for L # Preserve the published size-four amount. The source does not define these # cutoffs as a fixed percentage of current federal poverty or median income. 1: + 2024-10-01: 12_995 2025-10-01: 13_589 2: + 2024-10-01: 16_993 2025-10-01: 17_770 3: + 2024-10-01: 20_992 2025-10-01: 21_952 4: + 2024-10-01: 24_355 2025-10-01: 25_469 5: + 2024-10-01: 28_989 2025-10-01: 30_315 6: + 2024-10-01: 32_987 2025-10-01: 34_496 7: + 2024-10-01: 33_737 2025-10-01: 35_280 8: + 2024-10-01: 34_487 2025-10-01: 36_064 9: + 2024-10-01: 35_236 2025-10-01: 36_848 10: + 2024-10-01: 35_986 2025-10-01: 37_632 11: + 2024-10-01: 36_736 2025-10-01: 38_416 12: + 2024-10-01: 37_486 2025-10-01: 39_200 13: + 2024-10-01: 38_235 2025-10-01: 39_984 14: + 2024-10-01: 38_985 2025-10-01: 40_768 15: + 2024-10-01: 39_735 2025-10-01: 41_552 16: + 2024-10-01: 40_485 2025-10-01: 42_336 metadata: unit: currency-USD @@ -41,5 +57,7 @@ metadata: - range(1, 17) propagate_metadata_to_children: true reference: + - title: Georgia Benefit Matrix, FY2025 income guidelines, step 1 (inclusive Level I upper limits) + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/GA_BenefitMatrix_2025.pdf - title: Georgia Benefit Matrix, FY2026 income guidelines, step 1 (inclusive Level I upper limits) href: https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 50ffca7f055..b7b9273d06c 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -802,7 +802,7 @@ programs: category: Benefits agency: HHS status: partial - coverage: GA, OR, DC, Riverside County, MA, IL, KS, MS, TX + coverage: OR, DC, GA, Riverside County, MA, IL, KS, MS, TX state_implementations: - state: GA status: partial @@ -810,8 +810,8 @@ programs: full_name: Georgia Low Income Home Energy Assistance Program variable: ga_liheap parameter_prefix: gov.states.ga.dfcs.liheap - verified_years: "2026" - notes: FY2026 ordinary regular heating eligibility and published fuel payments. Unlisted fuel payments and the Level I cutoff above household size 16 remain unverified; zero for an unlisted fuel is not a legal denial. Actual categorical recipient membership, assistance-only resources and utility credit balances use documented existing-input approximations. Historical backfills and FY2027 draft changes are unverified. Regular heating only; crisis, cooling, weatherization and equipment assistance are excluded. + verified_years: "2025-2026" + notes: FY2025-FY2026 regular heating only, with the published fuel matrix and the bundled-utility rental payment. Crisis, cooling, weatherization and equipment assistance are out of scope. - state: OR status: in_progress name: LIHEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml index ea4c7ee57fb..3965493457e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml @@ -1,7 +1,9 @@ # Georgia ordinary heating route only. Categorical recipient facts, utility credits, # unpriced fuels and size-above-16 coverage remain documented limitations. -# FY2026 matrix p1 sets the size1 ordinary ceiling at34549; manual p50 requires income at or below the limit. Real adult wages are aggregated, not overridden countable income. +# FY2026 matrix page 1 sets the size 1 ordinary ceiling at $34,549; manual page 50 +# requires income at or below the limit. Adult wages are aggregated rather than +# overriding countable income. - name: Case 1, the ordinary one person income ceiling is inclusive. period: 2026 input: @@ -16,7 +18,8 @@ output: ga_liheap_eligible: [true, false] -# Manual pp51-52 excludes undocumented members from size; without a qualified adult or child there is no eligible member, even with a positive bill. +# Manual pages 51-52 exclude undocumented members from size; without a qualified adult +# or child there is no eligible member, even with a positive bill. - name: Case 2, a household with no qualified member fails ordinary eligibility. period: 2026 input: @@ -39,7 +42,9 @@ output: ga_liheap_eligible: false -# Manual pp4,57 permits a verified energy burden in subsidized and other rental housing. These tenants pay their own utilities by default; positive primary bills establish burden, while the heat-in-rent flag alone does not. +# Manual pages 4 and 57 permit a verified energy burden in subsidized and other rental +# housing. These tenants pay their own utilities by default; positive primary bills +# establish burden, while the heat-in-rent flag alone does not. - name: Case 3, heat in rent needs a verified burden with or without housing subsidy. period: 2026 input: @@ -57,7 +62,9 @@ output: ga_liheap_eligible: [true, false, true, false] -# Manual p3 requires responsibility for home heating costs; v1.2 reuses an explicit existing obligation flag. NONE still means no heating; a gas household with an explicit obligation can qualify with no reported bill amount. +# Manual page 3 requires responsibility for home heating costs; the model reuses the +# existing explicit has_heating_expense input. NONE still means no heating; a natural +# gas household with an explicit obligation can qualify with no reported bill amount. - name: Case 4, explicit no heating fails even when an obligation flag is supplied. period: 2026 input: @@ -73,7 +80,9 @@ output: ga_liheap_eligible: [false, true] -# Manual pp3,50 requires heating responsibility or verified burden. With no explicit obligation, gas-primary households use their gas bill: an electric bill alone does not identify gas heating expense. +# Manual pages 3 and 50 require heating responsibility or a verified burden. With no +# explicit obligation, natural gas households use their gas bill: an electric bill alone +# does not establish a natural gas heating expense. - name: Case 5, an unrelated utility bill does not establish the selected primary fuel burden. period: 2026 input: @@ -89,7 +98,8 @@ output: ga_liheap_eligible: [false, true] -# Manual pp3,50 addresses Georgia resident households; the same real wage and positive primary bill in Alabama must not receive Georgia eligibility. +# Manual pages 3 and 50 address Georgia resident households; the same wages and +# positive primary bill in Alabama do not receive Georgia eligibility. - name: Case 6, ordinary eligibility is limited to Georgia in mixed state arrays. period: 2026 input: @@ -104,7 +114,7 @@ output: ga_liheap_eligible: [true, false] -# Manual p89 recognizes all utilities included in unsubsidized rent as an energy burden. +# Manual page 89 recognizes all utilities included in unsubsidized rent as an energy burden. # Each column changes one requirement from the first qualifying rental household: # separately paid utilities, no rent, housing assistance, public housing, no qualified # member, excess income, no heating, or heat excluded from rent. Ordinary gates remain. @@ -127,3 +137,33 @@ heat_expense_included_in_rent: [true, true, true, true, true, true, true, true, false] output: ga_liheap_eligible: [true, false, false, false, false, false, false, false, false] + +# Manual page 89, third row, and page 4: a renter with heat in rent who pays rent and +# is billed directly for electric has an energy burden, whether or not the housing is +# subsidized. Each household is one adult with $12,000 of wages (under the $34,549 +# limit) and heat in rent. Unsubsidized rent is $9,600 ($800 a month); the subsidized +# tenant share is $3,000 ($250 a month). +# Households 1-4 have a $1,200 electric bill: unsubsidized with natural gas, +# unsubsidized with UNSPECIFIED fuel, housing assistance, and public housing. All are +# eligible. +# Household 5: housing assistance with no electric bill: no energy burden. +# Household 6: an electric bill but no rent paid: not a renter, so no energy burden. +- name: Case 8, heat in rent renters billed for electric have an energy burden with or without a subsidy. + period: 2026 + input: + state_code: [GA, GA, GA, GA, GA, GA] + age: [40, 40, 40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN] + employment_income: [12_000, 12_000, 12_000, 12_000, 12_000, 12_000] + ssi: [0, 0, 0, 0, 0, 0] + ga_tanf: [0, 0, 0, 0, 0, 0] + rent: [9_600, 9_600, 3_000, 3_000, 3_000, 0] + heating_type: [NATURAL_GAS, UNSPECIFIED, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + heat_expense_included_in_rent: [true, true, true, true, true, true] + tenant_pays_utilities: [true, true, true, true, true, true] + receives_housing_assistance: [false, false, true, false, true, false] + is_in_public_housing: [false, false, false, true, false, false] + gas_expense: [0, 0, 0, 0, 0, 0] + pre_subsidy_electricity_expense: [1_200, 1_200, 1_200, 1_200, 0, 1_200] + output: + ga_liheap_eligible: [true, true, true, true, false, false] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.yaml index 097e48f919c..524ecaf70a8 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.yaml @@ -1,6 +1,7 @@ # Source-derived Georgia independent components; no final eligibility or total award asserted. -# FY2026 original matrix literal annual limits; all16 are checked independently of the model formula or parameters. +# FY2026 matrix step 1 Level II tops for all 16 sizes, taken from the published table +# rather than from the model formula or parameters. - name: Case 1, all sixteen published annual SMI limits match the reconciled flooring order. period: 2026 absolute_error_margin: 0.01 @@ -16,7 +17,8 @@ ga_liheap_income_limit: [34_549, 45_179, 55_810, 66_441, 77_071, 87_702, 89_695, 91_688, 93_681, 95_675, 97_668, 99_661, 101_654, 103_647, 105_641, 107_634] -# Independent state-defined calculation returns the GA one-person limit only for Georgia; safe batch behavior. +# The Georgia one-person limit is returned only for Georgia, so mixed-state batches +# stay safe. - name: Case 2, mixed state calculation masks the Georgia income limit outside Georgia. period: 2026 absolute_error_margin: 0.01 @@ -27,3 +29,19 @@ ga_liheap_household_size: [1, 1] output: ga_liheap_income_limit: [34_549, 0] + +# FY2025 matrix step 1 Level II tops for sizes 1, 4 and 16: $33,038, $63,535 and +# $102,926. In 2025 the FY2025 four-person SMI of $105,893 applies: +# floor($105,893 x 0.6) = $63,535; size 1: floor($63,535 x 0.52 = $33,038.20) = $33,038; +# size 4: $63,535 x 1.00 = $63,535; size 16: floor($63,535 x 1.62 = $102,926.70) +# = $102,926. +- name: Case 3, the FY2025 annual limits apply in 2025. + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: [GA, GA, GA] + heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY] + pre_subsidy_electricity_expense: [1_200, 1_200, 1_200] + ga_liheap_household_size: [1, 4, 16] + output: + ga_liheap_income_limit: [33_038, 63_535, 102_926] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml index 7e2368109c8..062aa6fb901 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml @@ -1,7 +1,9 @@ # Georgia ordinary heating route only. Categorical recipient facts, utility credits, # unpriced fuels and size-above-16 coverage remain documented limitations. -# FY2026 matrix p1: size1 income12000 is LevelI and electric pays500. Manual pp4,50 establishes heating burden, not an expense cap; the25 bill must not reduce the scheduled award. +# FY2026 matrix page 1: a size 1 household with $12,000 income is in Level I, and +# electric heat pays $500. Manual pages 4 and 50 establish a heating burden, not an +# expense cap, so the $25 bill does not reduce the scheduled award. - name: Case 1, a small positive primary bill receives the full fixed heating award. period: 2026 input: @@ -30,7 +32,9 @@ ga_liheap_eligible: true ga_liheap: 500 -# Manual pp51-52,62: parent income counts fully, parent is excluded from size, and all under18 income is excluded. FY2026 matrix p1 gives the size1 LevelI gas award660. +# Manual pages 51-52 and 62: the parent's income counts in full, the parent is +# excluded from size, and all income of members under 18 is excluded. FY2026 matrix +# page 1 gives the size 1 Level I natural gas award of $660. - name: Case 2, a citizen child qualifies while the nonqualified parent income counts fully. period: 2026 input: @@ -64,7 +68,9 @@ ga_liheap_eligible: true ga_liheap: 660 -# Manual pp51-52,62,67 counts the full30000 wages plus5000 farm-property rent against qualified size1.35000 exceeds the FY2026 matrix ceiling34549, so ordinary eligibility gates the final grant to zero. +# Manual pages 51-52, 62 and 67: the full $30,000 of wages plus $5,000 of farm rent +# count against qualified size 1. $35,000 exceeds the FY2026 matrix ceiling of +# $34,549, so ordinary eligibility sets the final grant to zero. - name: Case 3, full nonqualified adult wages and farm rent can exceed the qualified child ceiling. period: 2026 input: @@ -96,7 +102,9 @@ ga_liheap_eligible: false ga_liheap: 0 -# FY2026 matrix p1 explicitly gives size4 LevelI ceiling25469, despite its unusual pattern. Two actual four-person families with wages25469 and25469.01 remain below66441 but receive gas660 and560 respectively. +# FY2026 matrix page 1 prints the size 4 Level I ceiling as $25,469, despite its +# unusual pattern. Two four-person families with wages of $25,469 and $25,469.01 are +# both below the $66,441 limit but receive natural gas awards of $660 and $560. - name: Case 4, real four person families use the printed lower band boundary before the award. period: 2026 input: @@ -171,7 +179,10 @@ ga_liheap_matrix_amount: [660, 560] ga_liheap: [660, 560] -# FY2026 matrix p1: real single-adult wages20000 are above13589 and below34549. LevelII oil710, propane580 and wood400 pass through ordinary eligibility; the Alabama household receives no Georgia award. Existing unit cases already cover the full ten-cell matrix. +# FY2026 matrix page 1: single-adult wages of $20,000 are above $13,589 and below +# $34,549. Level II oil ($710), propane ($580) and wood ($400) pass ordinary +# eligibility; the Alabama household receives no Georgia award. Unit cases cover the +# full ten-cell matrix. - name: Case 5, ordinary upper band awards use the primary fuel and exclude another state. period: 2026 input: @@ -189,7 +200,9 @@ ga_liheap_eligible: [true, true, true, false] ga_liheap: [710, 580, 400, 0] -# Manual pp4,57 permits verified energy burden in subsidized heat-in-rent housing. The shared Boolean defaults false for heat in rent, but the reported primary bill100 supplies burden; FY2026 LevelI propane pays680 uncapped. +# Manual pages 4 and 57 permit a verified energy burden in subsidized heat-in-rent +# housing. has_heating_expense is false when heat is in rent, but the reported $100 +# primary bill supplies the burden; FY2026 Level I propane pays $680 with no cap. - name: Case 6, a subsidized renter with verified primary expense can receive the ordinary grant. period: 2026 input: @@ -221,8 +234,11 @@ ga_liheap_eligible: true ga_liheap: 680 -# Contract retains existing signed net-business inputs and floors combined household income0; no additional business-expense policy is introduced. FY2026 matrix p1 fixes LevelI wood at500, so a negative pre-floor total cannot increase the grant. -- name: Case 7, existing net business losses do not inflate the fixed heating award. +# Manual sections 1200.6-1200.7 (pages 64-66) net self-employment within the +# business, so each source is floored at zero and the $18,000 self-employment loss +# does not offset the $6,000 of wages: countable income is $6,000. That is at or +# below the $13,589 Level I cutoff, so FY2026 Level I wood pays $500. +- name: Case 7, a self-employment loss does not offset wages or inflate the fixed heating award. period: 2026 input: people: @@ -243,13 +259,15 @@ members: [person1] state_code: GA output: - ga_liheap_income: 0 + ga_liheap_income: 6_000 ga_liheap_household_size: 1 ga_liheap_matrix_amount: 500 ga_liheap_eligible: true ga_liheap: 500 -# Manual p3 requires responsibility for energy costs or verified burden; v1.2 honors the existing explicit has_heating_expense fact even when no paid bill amount is reported. FY2026 matrix p1 gives LevelI oil810 with no spending cap. +# Manual page 3 requires responsibility for energy costs or a verified burden; the +# model honors an explicit has_heating_expense input even when no paid bill amount is +# reported. FY2026 matrix page 1 gives Level I oil $810 with no spending cap. - name: Case 8, an explicit heating obligation with a zero reported bill receives the scheduled award. period: 2026 input: @@ -279,10 +297,10 @@ ga_liheap_eligible: true ga_liheap: 810 -# Manual p89 recognizes an energy burden when all utilities are included in -# unsubsidized rent. FY2026 plan p30 requires the lowest award within the income -# level for payment to the applicant:500 for12000 income and400 for15000 income. -# Both are below the ordinary size1 ceiling34549 and have no separately paid bill. +# Manual page 89 recognizes an energy burden when all utilities are included in +# unsubsidized rent. FY2026 plan page 30 pays an applicant the lowest award within the +# income level: $500 for $12,000 of income and $400 for $15,000 of income. Both are +# below the size 1 ceiling of $34,549 and have no separately paid bill. - name: Case 9, unsubsidized renters with all utilities bundled receive each income level minimum. period: 2026 input: @@ -341,3 +359,273 @@ ga_liheap_matrix_amount: [500, 400] ga_liheap_eligible: [true, true] ga_liheap: [500, 400] + +# FY2025 matrix step 1: size 1 Level I is $0 - $12,995 and Level II is +# $12,996 - $33,038. Deliberate boundary case: $12,995 is the Level I top and $13,000 +# is just above it (the FY2026 cutoff of $13,589 would put both in Level I). +# Limit: floor($105,893 x 0.6) = $63,535; floor($63,535 x 0.52 = $33,038.20) = $33,038. +# Step 2 electric heat pays $500 in Level I and $400 in Level II. +- name: Case 10, the FY2025 Level I cutoff applies in 2025. + period: 2025 + input: + state_code: [GA, GA] + age: [40, 40] + immigration_status: [CITIZEN, CITIZEN] + employment_income: [12_995, 13_000] + ssi: [0, 0] + ga_tanf: [0, 0] + heating_type: [ELECTRICITY, ELECTRICITY] + pre_subsidy_electricity_expense: [1_200, 1_200] + output: + ga_liheap_household_size: [1, 1] + ga_liheap_income: [12_995, 13_000] + ga_liheap_income_limit: [33_038, 33_038] + heating_expense: [1_200, 1_200] + ga_liheap_matrix_amount: [500, 400] + ga_liheap_eligible: [true, true] + ga_liheap: [500, 400] + +# FY2025 and FY2026 plans, section 1.9 (page 6), count Social Security "Excluding +# MediCare deduction", and manual section 1200.7 (page 69) excludes the premiums. +# The 2026 Part B premium is pinned as an input: $202.90 x 12 = $2,434.80. +# Countable income: $36,000 - $2,434.80 = $33,565.20. That is at or below the size 1 +# limit of $34,549 (gross Social Security would exceed it) and above the $13,589 +# Level I cutoff, so FY2026 Level II electric pays $400. +- name: Case 11, a retiree's Social Security counts net of the Medicare Part B premium. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + immigration_status: CITIZEN + social_security_retirement: 36_000 + medicare_part_b_premium: 2_434.8 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + ga_tanf: 0 + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_800 + households: + household: + members: [person1] + state_code: GA + output: + ga_liheap_household_size: 1 + ga_liheap_income: 33_565.2 + ga_liheap_income_limit: 34_549 + heating_expense: 1_800 + ga_liheap_matrix_amount: 400 + ga_liheap_eligible: true + ga_liheap: 400 + +# FY2026 plan page 6 counts retirement and pension benefits; manual page 66 counts a +# pension as unearned income, and page 83 groups Railroad Retirement with Social +# Security. Household 1: $36,000 of military retirement pay. Household 2: $36,000 of +# railroad retirement, counted gross; only Social Security is netted of the pinned +# $2,434.80 premium. Both incomes of $36,000 exceed the size 1 limit of $34,549, so +# both are ineligible. The ungated matrix amounts are Level II natural gas $560 and +# Level II electric $400. +- name: Case 12, military retirement pay and railroad retirement count as income. + period: 2026 + input: + state_code: [GA, GA] + age: [50, 70] + immigration_status: [CITIZEN, CITIZEN] + military_retirement_pay: [36_000, 0] + railroad_benefits: [0, 36_000] + medicare_part_b_premium: [0, 2_434.8] + ssi: [0, 0] + ga_tanf: [0, 0] + heating_type: [NATURAL_GAS, ELECTRICITY] + gas_expense: [1_200, 0] + pre_subsidy_electricity_expense: [0, 1_500] + output: + ga_liheap_household_size: [1, 1] + ga_liheap_income: [36_000, 36_000] + ga_liheap_income_limit: [34_549, 34_549] + ga_liheap_matrix_amount: [560, 400] + ga_liheap_eligible: [false, false] + ga_liheap: [0, 0] + +# Manual page 65 counts capital gains, not losses, and pages 64-66 net business +# income within each business. Each source is floored at zero before the sources are +# added. Household 1: max($40,000, 0) + max(-$10,000, 0) = $40,000. Household 2: +# max($36,000, 0) + max(-$5,000, 0) = $36,000. Both exceed the size 1 limit of +# $34,549; netting the losses ($30,000 and $31,000) would have paid $400 each. +- name: Case 13, a capital loss or a rental loss does not offset wages. + period: 2026 + input: + state_code: [GA, GA] + age: [40, 40] + immigration_status: [CITIZEN, CITIZEN] + employment_income: [40_000, 36_000] + long_term_capital_gains: [-10_000, 0] + rental_income: [0, -5_000] + ssi: [0, 0] + ga_tanf: [0, 0] + heating_type: [ELECTRICITY, ELECTRICITY] + pre_subsidy_electricity_expense: [1_500, 1_500] + output: + ga_liheap_income: [40_000, 36_000] + ga_liheap_income_limit: [34_549, 34_549] + ga_liheap_matrix_amount: [400, 400] + ga_liheap_eligible: [false, false] + ga_liheap: [0, 0] + +# Manual page 89 gives an energy burden to an unsubsidized renter whose rent includes +# all utilities, and FY2026 plan page 30 pays the applicant the lowest award in the +# income level. The heat-in-rent route does not need the fuel, so an UNSPECIFIED fuel +# still pays: lowest Level II award min($400, $560, $710, $580, $400) = $400, plus +# $100 in Level I. Household 1: $12,000 is at or below $13,589, so $500. +# Household 2: $24,000 is above $13,589, so $400. Household 3 pays its own utilities +# with heat outside rent: UNSPECIFIED has no primary fuel bill (heating_expense is $0), +# so there is no energy burden and no award. +- name: Case 14, bundled utilities in rent pay the income level minimum when the fuel is unspecified. + period: 2026 + input: + state_code: [GA, GA, GA] + age: [40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN] + employment_income: [12_000, 24_000, 12_000] + ssi: [0, 0, 0] + ga_tanf: [0, 0, 0] + rent: [12_000, 12_000, 12_000] + heating_type: [UNSPECIFIED, UNSPECIFIED, UNSPECIFIED] + heat_expense_included_in_rent: [true, true, false] + tenant_pays_utilities: [false, false, true] + receives_housing_assistance: [false, false, false] + is_in_public_housing: [false, false, false] + pre_subsidy_electricity_expense: [0, 0, 1_200] + output: + ga_liheap_income: [12_000, 24_000, 12_000] + heating_expense: [0, 0, 0] + has_heating_expense: [false, false, false] + ga_liheap_matrix_amount: [500, 400, 0] + ga_liheap_eligible: [true, true, false] + ga_liheap: [500, 400, 0] + +# The FY2026 matrix publishes Level I cutoffs through size 16, so a 17-person +# household uses the size 16 cutoff of $42,336. Adult wages: $22,000 + $18,000 = +# $40,000; the 13 children have no income. The eligibility limit uses the federal +# size factor 0.52 + 5 x 0.16 + 11 x 0.03 = 1.65: +# floor($66,441 x 1.65 = $109,627.65) = $109,627. $40,000 is at or below $42,336, so +# Level I natural gas pays $660. +- name: Case 15, a household above the published table size receives the size sixteen award. + period: 2026 + input: + people: + person1: {age: 45, immigration_status: CITIZEN, employment_income: 22_000, ssi: 0} + person2: {age: 43, immigration_status: CITIZEN, employment_income: 18_000, ssi: 0} + person3: {age: 41, immigration_status: CITIZEN, ssi: 0} + person4: {age: 39, immigration_status: CITIZEN, ssi: 0} + person5: {age: 17, immigration_status: CITIZEN, ssi: 0} + person6: {age: 16, immigration_status: CITIZEN, ssi: 0} + person7: {age: 15, immigration_status: CITIZEN, ssi: 0} + person8: {age: 14, immigration_status: CITIZEN, ssi: 0} + person9: {age: 13, immigration_status: CITIZEN, ssi: 0} + person10: {age: 12, immigration_status: CITIZEN, ssi: 0} + person11: {age: 11, immigration_status: CITIZEN, ssi: 0} + person12: {age: 10, immigration_status: CITIZEN, ssi: 0} + person13: {age: 9, immigration_status: CITIZEN, ssi: 0} + person14: {age: 7, immigration_status: CITIZEN, ssi: 0} + person15: {age: 5, immigration_status: CITIZEN, ssi: 0} + person16: {age: 3, immigration_status: CITIZEN, ssi: 0} + person17: {age: 1, immigration_status: CITIZEN, ssi: 0} + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8, + person9, person10, person11, person12, person13, person14, person15, person16, + person17] + ga_tanf: 0 + heating_type: NATURAL_GAS + gas_expense: 1_800 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8, + person9, person10, person11, person12, person13, person14, person15, person16, + person17] + state_code: GA + output: + ga_liheap_household_size: 17 + ga_liheap_income: 40_000 + ga_liheap_income_limit: 109_627 + heating_expense: 1_800 + ga_liheap_matrix_amount: 660 + ga_liheap_eligible: true + ga_liheap: 660 + +# Manual page 89, third row: an unsubsidized renter with heat in rent who is billed +# directly for electric has an energy burden, paid to the electric vendor. Page 4 also +# makes subsidized renters eligible when the utility bill is in their name. The award +# is the FY2026 matrix step 2 amount for the main heating fuel; it is not the +# lowest award. Each household is one adult with heat in rent and a $1,200 a year +# ($100 a month) electric bill. Unsubsidized rent is $9,600 ($800 a month); the +# subsidized tenant share is $3,000 ($250 a month). The size 1 limit is $34,549 and +# the Level I cutoff is $13,589. +# Household 1: natural gas, $12,000 wages, Level I: $560 + $100 = $660. +# Household 2: natural gas, $24,000 wages, Level II: $560. +# Household 3: electric, $24,000 wages, Level II: $400. +# Household 4: UNSPECIFIED fuel, $12,000 wages: eligible, but the matrix has no amount +# for an unspecified fuel, so $0. +# Household 5: subsidized, natural gas, $12,000 wages, with the electric bill: $660. +# Household 6: subsidized, natural gas, $12,000 wages, no bill: no energy burden, so +# the $660 matrix amount is not paid. +- name: Case 16, heat in rent renters billed for electric receive the main heating fuel award. + period: 2026 + input: + state_code: [GA, GA, GA, GA, GA, GA] + age: [40, 40, 40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN] + employment_income: [12_000, 24_000, 24_000, 12_000, 12_000, 12_000] + ssi: [0, 0, 0, 0, 0, 0] + ga_tanf: [0, 0, 0, 0, 0, 0] + rent: [9_600, 9_600, 9_600, 9_600, 3_000, 3_000] + heating_type: [NATURAL_GAS, NATURAL_GAS, ELECTRICITY, UNSPECIFIED, NATURAL_GAS, NATURAL_GAS] + heat_expense_included_in_rent: [true, true, true, true, true, true] + tenant_pays_utilities: [true, true, true, true, true, true] + receives_housing_assistance: [false, false, false, false, true, true] + is_in_public_housing: [false, false, false, false, false, false] + gas_expense: [0, 0, 0, 0, 0, 0] + pre_subsidy_electricity_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 0] + output: + ga_liheap_household_size: [1, 1, 1, 1, 1, 1] + ga_liheap_income: [12_000, 24_000, 24_000, 12_000, 12_000, 12_000] + ga_liheap_income_limit: [34_549, 34_549, 34_549, 34_549, 34_549, 34_549] + heating_expense: [0, 0, 1_200, 0, 0, 0] + has_heating_expense: [false, false, false, false, false, false] + ga_liheap_eligible: [true, true, true, true, true, false] + ga_liheap_matrix_amount: [660, 560, 400, 0, 660, 660] + ga_liheap: [660, 560, 400, 0, 660, 0] + +# Manual page 89 and FY2026 plan page 30: when unsubsidized rent includes all +# utilities, the tenant pays no utility vendor and gets the lowest award in the +# income level, even if an electric bill is also reported. Ordinary natural gas would +# pay $660 or $560; the lowest award is min($400, $560, $710, $580, $400) = $400, +# plus $100 in Level I. Rent is $9,600 ($800 a month). +# Household 1: $12,000 wages, Level I: $500. Household 2: $24,000 wages, Level II: $400. +- name: Case 17, rent including all utilities keeps the lowest award when an electric bill is reported. + period: 2026 + input: + state_code: [GA, GA] + age: [40, 40] + immigration_status: [CITIZEN, CITIZEN] + employment_income: [12_000, 24_000] + ssi: [0, 0] + ga_tanf: [0, 0] + rent: [9_600, 9_600] + heating_type: [NATURAL_GAS, NATURAL_GAS] + heat_expense_included_in_rent: [true, true] + tenant_pays_utilities: [false, false] + receives_housing_assistance: [false, false] + is_in_public_housing: [false, false] + gas_expense: [0, 0] + pre_subsidy_electricity_expense: [1_200, 1_200] + output: + ga_liheap_income: [12_000, 24_000] + heating_expense: [0, 0] + ga_liheap_eligible: [true, true] + ga_liheap_matrix_amount: [500, 400] + ga_liheap: [500, 400] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap_household_size.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap_household_size.yaml index 9cb2d08c61b..410c036b1d5 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap_household_size.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap_household_size.yaml @@ -1,6 +1,7 @@ # Source-derived Georgia independent components; no final eligibility or total award asserted. -# Manual pp51–52 counts citizen/qualified people including minors; DACA and undocumented members do not count in size. +# Manual pages 51-52 count citizens and qualified noncitizens, including minors; DACA +# and undocumented members do not count in size. - name: Case 1, qualified adults and minors count without SNAP waiting bars. period: 2026 absolute_error_margin: 0.01 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml index c6f392349b6..b541e74a15b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml @@ -1,6 +1,9 @@ # Source-derived Georgia independent components; no final eligibility or total award asserted. -# Manual pp65–68: wages and business17400 + ordinary/farmrent2400 + survivors600 + pension1200 + SSA2400 + SSI1200 + gains600 =25800; no duplicated component aggregates. +# Manual pages 65-68: wages and business income $17,400 + ordinary and farm rent $2,400 +# + survivors $600 + pension $1,200 + Social Security $2,400 + SSI $1,200 + gains $600 +# = $25,800; no component aggregate is counted twice. The person is under 65 and not on +# Medicare, so the Part B premium is pinned at $0 and Social Security counts in full. - name: Case 1, supported independent adult income sources are counted once. period: 2026 absolute_error_margin: 0.01 @@ -20,6 +23,7 @@ survivor_benefits: 600 pension_income: 1_200 social_security: 2_400 + medicare_part_b_premium: 0 capital_gains: 600 spm_units: spm_unit: @@ -35,7 +39,9 @@ output: ga_liheap_income: 25_800 -# Manual p62 excludes ALL under18 income. At18,6000wages+2400SSI+1200SSA+600dividends+300netinterest adds10500 to parent12000. +# Manual page 62 excludes all income of members under 18. At 18, $6,000 wages + $2,400 +# SSI + $1,200 Social Security + $600 dividends + $300 net interest would add $10,500 +# to the parent's $12,000. The Part B premium is pinned at $0 (not on Medicare). - name: Case 2, all age 17 income follows the under eighteen exclusion boundary. period: 2026 absolute_error_margin: 0.01 @@ -52,6 +58,7 @@ ssi: 2_400 employment_income: 6_000 social_security: 1_200 + medicare_part_b_premium: 0 ordinary_dividend_income: 600 interest_income: 600 spm_units: @@ -68,7 +75,9 @@ output: ga_liheap_income: 12_000 -# Manual p62 excludes ALL under18 income. At18,6000wages+2400SSI+1200SSA+600dividends+300netinterest adds10500 to parent12000. +# Manual page 62 excludes all income of members under 18. At 18, $6,000 wages + $2,400 +# SSI + $1,200 Social Security + $600 dividends + $300 net interest add $10,500 to the +# parent's $12,000 = $22,500. The Part B premium is pinned at $0 (not on Medicare). - name: Case 3, all age 18 income follows the under eighteen exclusion boundary. period: 2026 absolute_error_margin: 0.01 @@ -85,6 +94,7 @@ ssi: 2_400 employment_income: 6_000 social_security: 1_200 + medicare_part_b_premium: 0 ordinary_dividend_income: 600 interest_income: 600 spm_units: @@ -101,7 +111,9 @@ output: ga_liheap_income: 22_500 -# Manual pp66,69 and contractv1 household interpretation: combined adultinterest400 minus annual300, plus600dividends =700; no per-person multiplication. +# Manual pages 66 and 69 do not say whether the $25 monthly interest disregard repeats +# per person, so the model applies it once to the household's adult interest: +# $400 combined interest - $300 a year + $600 dividends = $700. - name: Case 4, interest disregard is shared once across adults while dividends fully count. period: 2026 absolute_error_margin: 0.01 @@ -132,7 +144,8 @@ output: ga_liheap_income: 700 -# Monthly25 allowance annualizes to300; exact threshold is excluded and first dollar above it counts. +# The $25 monthly allowance is $300 a year; interest of exactly $300 is fully excluded +# and the first dollar above it counts. - name: Case 5, annual household interest allowance has an exact three hundred dollar boundary. period: 2026 absolute_error_margin: 0.01 @@ -147,7 +160,9 @@ output: ga_liheap_income: [0, 0, 1] -# Manual p67 TANF grant counts once at SPM level: caregiver wages1200 + annual ga_tanf3600 =4800; minorSSI2400 excluded. +# Manual page 67: the TANF grant counts once at SPM-unit level. Caregiver wages $1,200 +# + annual TANF $3,600 = $4,800; the minor's $2,400 SSI is excluded. ga_tanf reaches +# the income through the take-up-gated tanf, which defaults to take-up. - name: Case 6, caregiver TANF cash counts once despite excluding a minor SSI payment. period: 2026 absolute_error_margin: 0.01 @@ -176,8 +191,10 @@ output: ga_liheap_income: 4_800 -# Contractv1 preserves signed existing net-business inputs and floors the combined household income0; no additional business-expense treatment. -- name: Case 7, existing signed net losses cannot make the household total negative. +# Manual sections 1200.6-1200.7 (pages 64-66) net self-employment within the business, +# so each source is floored at zero before the sources are added: +# max($6,000, 0) + max(-$18,000, 0) = $6,000. +- name: Case 7, a self-employment loss does not offset wages. period: 2026 absolute_error_margin: 0.01 input: @@ -200,9 +217,12 @@ state_code: GA hud_utility_allowance: 0 output: - ga_liheap_income: 0 + ga_liheap_income: 6_000 -# Manual pp51–52,62: parent income counts in full, citizen child counts in size but all child income is excluded. Matrix1person electric LevelI=500; final eligibility is not asserted. +# Manual pages 51-52 and 62: the parent's income counts in full, and the citizen child +# counts in size but all of the child's income is excluded. The size 1 Level I electric +# matrix amount is $500; final eligibility is not asserted. The child's Part B premium +# is pinned at $0 (not on Medicare). - name: Case 8, nonqualified adult income counts while the citizen child determines size. period: 2026 absolute_error_margin: 0.01 @@ -218,6 +238,7 @@ immigration_status: CITIZEN ssi: 2_400 social_security: 3_600 + medicare_part_b_premium: 0 spm_units: spm_unit: members: [person1, person2] @@ -234,3 +255,133 @@ ga_liheap_income: 12_000 ga_liheap_income_limit: 34_549 ga_liheap_matrix_amount: 500 + +# FY2025 and FY2026 plans, section 1.9 (page 6), count Social Security "Excluding +# MediCare deduction"; manual section 1200.7 (page 69) excludes the premiums. The 2026 +# Part B premium is pinned: $202.90 x 12 = $2,434.80. +# Household 1: $36,000 - $2,434.80 = $33,565.20. +# Household 2: $1,200 of Social Security is below the premium, so the net benefit is +# floored at zero and does not reduce wages: max($1,200 - $2,434.80, 0) + $20,000 +# = $20,000. +- name: Case 9, Social Security counts net of the Medicare Part B premium, floored at zero. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [GA, GA] + age: [70, 66] + social_security_retirement: [36_000, 1_200] + employment_income: [0, 20_000] + medicare_part_b_premium: [2_434.8, 2_434.8] + ssi: [0, 0] + ga_tanf: [0, 0] + heating_type: [ELECTRICITY, ELECTRICITY] + pre_subsidy_electricity_expense: [1_800, 1_800] + output: + ga_liheap_income: [33_565.2, 20_000] + +# FY2026 plan page 6 counts retirement and pension benefits; manual page 66 counts a +# pension as unearned income. Household 1: $36,000 of military retirement pay. +# Household 2: $36,000 of railroad retirement counted gross; manual page 69 nets only +# Social Security, so the pinned $2,434.80 premium does not reduce it. +- name: Case 10, military retirement pay and gross railroad retirement count. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [GA, GA] + age: [50, 70] + military_retirement_pay: [36_000, 0] + railroad_benefits: [0, 36_000] + medicare_part_b_premium: [0, 2_434.8] + ssi: [0, 0] + ga_tanf: [0, 0] + heating_type: [NATURAL_GAS, ELECTRICITY] + gas_expense: [1_200, 0] + pre_subsidy_electricity_expense: [0, 1_500] + output: + ga_liheap_income: [36_000, 36_000] + +# Manual page 65 counts capital gains, not losses; pages 64-66 net business income +# within each business. Each source is floored at zero before the sources are added. +# Household 1: max($40,000, 0) + max(-$10,000, 0) = $40,000. +# Household 2: max($36,000, 0) + max(-$5,000, 0) = $36,000. +# Household 3: a farm loss is the only income, so max(-$8,000, 0) = $0; a negative +# source cannot make countable income negative. +- name: Case 11, capital, rental and farm losses do not offset other income. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [GA, GA, GA] + age: [40, 40, 55] + employment_income: [40_000, 36_000, 0] + long_term_capital_gains: [-10_000, 0, 0] + rental_income: [0, -5_000, 0] + farm_operations_income: [0, 0, -8_000] + ssi: [0, 0, 0] + ga_tanf: [0, 0, 0] + heating_type: [ELECTRICITY, ELECTRICITY, PROPANE] + pre_subsidy_electricity_expense: [1_500, 1_500, 0] + bottled_gas_expense: [0, 0, 1_500] + output: + ga_liheap_income: [40_000, 36_000, 0] + +# FY2026 plan page 6 and manual page 67 count TANF benefits received. A parent with two +# children and no earnings has the Georgia size 3 TANF maximum of $280 a month +# ($3,360 a year). Family 1 takes up TANF: tanf = $3,360, counted once. +# Family 2 does not take up TANF, so tanf = $0 and nothing is counted. +- name: Case 12, only TANF the family takes up counts. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + immigration_status: CITIZEN + ssi: 0 + person2: + age: 7 + immigration_status: CITIZEN + ssi: 0 + person3: + age: 4 + immigration_status: CITIZEN + ssi: 0 + person4: + age: 30 + immigration_status: CITIZEN + ssi: 0 + person5: + age: 7 + immigration_status: CITIZEN + ssi: 0 + person6: + age: 4 + immigration_status: CITIZEN + ssi: 0 + tax_units: + tax_unit1: + members: [person1, person2, person3] + tax_unit2: + members: [person4, person5, person6] + spm_units: + spm_unit1: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + gas_expense: 1_200 + ga_tanf: 3_360 + takes_up_tanf_if_eligible: true + spm_unit2: + members: [person4, person5, person6] + heating_type: NATURAL_GAS + gas_expense: 1_200 + ga_tanf: 3_360 + takes_up_tanf_if_eligible: false + households: + household1: + members: [person1, person2, person3] + state_code: GA + household2: + members: [person4, person5, person6] + state_code: GA + output: + tanf: [3_360, 0] + ga_liheap_income: [3_360, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml index 3323c2a6683..698c85748c3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml @@ -1,6 +1,7 @@ # Source-derived Georgia independent components; no final eligibility or total award asserted. -# Original matrix lower-income cutoff and first cent above for each size1–16; preserve anomalous size4=25469 exactly. +# FY2026 matrix step 1: the Level I cutoff and the first cent above it for each size +# from 1 to 16. The unusual size 4 cutoff of $25,469 is preserved exactly. - name: Case 1, all sixteen published lower level upper bounds remain inclusive. period: 2026 absolute_error_margin: 0.01 @@ -28,7 +29,9 @@ ga_liheap_matrix_amount: [660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560, 660, 560] -# Original matrix heating column only: LevelI500/660/810/680/500; LevelII400/560/710/580/400. Cooling column is not an oracle. +# FY2026 matrix heating column only, for electric, natural gas, oil, propane and wood: +# Level I $500 / $660 / $810 / $680 / $500 and Level II $400 / $560 / $710 / $580 / $400. +# The cooling column is not used. - name: Case 2, all five priced fuels match both heating benefit levels including zero income. period: 2026 absolute_error_margin: 0.01 @@ -47,7 +50,8 @@ output: ga_liheap_matrix_amount: [500, 660, 810, 680, 500, 400, 560, 710, 580, 400] -# Ungated regular heating matrix gives660 for natural gas; no actual-expense cap is stated. +# The regular heating matrix gives $660 for natural gas; no cap at the actual expense +# is stated. - name: Case 3, a small heating bill does not cap the fixed matrix amount. period: 2026 absolute_error_margin: 0.01 @@ -61,7 +65,8 @@ output: ga_liheap_matrix_amount: 660 -# NONE affirmatively means no heating. No assertions here establish legal ineligibility for unpriced fuel or out-of-table size. +# NONE means the home has no heating. These assertions do not establish legal +# ineligibility for unpriced fuels or sizes above the table. - name: Case 4, explicit no heating has no supported matrix amount. period: 2026 absolute_error_margin: 0.01 @@ -74,9 +79,10 @@ output: ga_liheap_matrix_amount: 0 -# FY2026 plan p30 assigns direct applicant payments the lowest award within the -# income level. The matrix gives500 through13589 and400 above it for size1. -# A separately paid utility retains the ordinary gas award660; NONE remains zero. +# FY2026 plan page 30 gives direct applicant payments the lowest award within the +# income level. For size 1 the matrix gives $500 through $13,589 and $400 above it. +# A separately paid utility keeps the ordinary natural gas award of $660; NONE remains +# zero. - name: Case 5, direct rental payments use the income level minimum and preserve no heating. period: 2026 absolute_error_margin: 0.01 @@ -92,3 +98,34 @@ is_in_public_housing: [false, false, false, false] output: ga_liheap_matrix_amount: [500, 400, 660, 0] + +# FY2025 matrix step 1 Level I cutoffs: size 1 $12,995, size 4 $24,355 and size 16 +# $40,485 (first, middle-table and last sizes). Deliberate boundary case: each cutoff +# and the first cent above it. Step 2 natural gas pays $660 in Level I and $560 in +# Level II. +- name: Case 6, the FY2025 Level I cutoffs remain inclusive in 2025. + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: [GA, GA, GA, GA, GA, GA] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + ga_liheap_household_size: [1, 1, 4, 4, 16, 16] + ga_liheap_income: [12_995, 12_995.01, 24_355, 24_355.01, 40_485, 40_485.01] + output: + ga_liheap_matrix_amount: [660, 560, 660, 560, 660, 560] + +# The FY2026 matrix publishes Level I cutoffs through size 16, so larger households use +# the size 16 cutoff of $42,336. Deliberate boundary case: sizes 17 and 25 at $42,336 +# pay Level I natural gas $660, and size 17 one cent above pays Level II $560. +- name: Case 7, households above the published table size use the size sixteen cutoff. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [GA, GA, GA] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_200, 1_200, 1_200] + ga_liheap_household_size: [17, 17, 25] + ga_liheap_income: [42_336, 42_336.01, 42_336] + output: + ga_liheap_matrix_amount: [660, 560, 660] diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py index 4c3e68b231d..24f4b6ef1e5 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py @@ -15,3 +15,27 @@ def ga_liheap_has_direct_payment_route(spm_unit, period): & ~spm_unit("receives_housing_assistance", period) & ~spm_unit.household("is_in_public_housing", period) ) + + +def ga_liheap_has_electric_vendor_route(spm_unit, period): + """Identify heat-in-rent renters billed directly for electric. + + Georgia's manual energy-burden table (physical page 89, third row) gives + unsubsidized renters with heat in rent and their own electric bill an + energy burden, "Paid to electric vendor because gas usage is + indeterminate". Page 4 makes subsidized renters eligible when "their + utility bill is in their name", so this route ignores subsidy status; + subsidized heat-in-rent renters without a bill stay ineligible. + tenant_pays_utilities separates these renters from the rows where all + utilities are in rent, and a positive electric bill is the utility-bill + documentation. The pre-subsidy bill matches heating_expense and avoids + the program-dependent subsidies in electricity_expense. + Manual pages 4 and 89: + https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=89 + """ + return ( + spm_unit("heat_expense_included_in_rent", period) + & spm_unit.household("tenant_pays_utilities", period) + & (add(spm_unit, period, ["rent"]) > 0) + & (spm_unit("pre_subsidy_electricity_expense", period) > 0) + ) diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py index 008955b7070..5890dbf7e0b 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py @@ -1,6 +1,7 @@ from policyengine_us.model_api import * from policyengine_us.variables.gov.states.ga.dfcs.liheap.eligibility._ga_liheap_housing import ( ga_liheap_has_direct_payment_route, + ga_liheap_has_electric_vendor_route, ) @@ -10,7 +11,7 @@ class ga_liheap_eligible(Variable): definition_period = YEAR label = "Georgia LIHEAP ordinary eligibility" defined_for = StateCode.GA - # State plan pages 5, 8, 9, 30; manual pages 3, 50, 56, 57, 75, 89. + # State plan pages 5, 8, 9, 30; manual pages 3, 4, 50, 56, 57, 75, 89. reference = ( "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5", "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=3", @@ -24,11 +25,15 @@ def formula(spm_unit, period, parameters): heating_expense = spm_unit("heating_expense", period) # Explicit heating responsibility can qualify with no current bill. # A positive primary bill also establishes burden with heat in rent, - # including subsidized housing. The rent flag alone is insufficient. + # including subsidized housing. Heat-in-rent renters also qualify when + # billed directly for electric, subsidized or not (manual pages 4 and + # 89), or when unsubsidized with all utilities in rent (page 89). The + # rent flag alone is insufficient. energy_burden = ( spm_unit("has_heating_expense", period) | (heating_expense > 0) | ga_liheap_has_direct_payment_route(spm_unit, period) + | ga_liheap_has_electric_vendor_route(spm_unit, period) ) # Pure-assistance categorical eligibility requires actual receipt by # every member and assistance-only income/resources. Aggregate TANF/SNAP diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py index 9bd6b139546..ef93ec604a9 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py @@ -12,6 +12,8 @@ class ga_liheap_income(Variable): # Manual pages 62-69; current plan pages 5-6 govern listed exclusions. "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=62", "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5", + # Section 1.9, page 6: Social Security "Excluding MediCare deduction". + "https://liheapch.acf.gov/docs/2025/state-plans/GA_Plan_2025.pdf#page=6", ) def formula(spm_unit, period, parameters): @@ -21,7 +23,21 @@ def formula(spm_unit, period, parameters): # Annual inputs approximate income in the application period. Georgia # excludes all income of under-18s, not only their earnings, and counts # otherwise countable income of nonqualified adults in full. - person_income = add(person, period, p.sources.person, options=[ADD]) + # Manual sections 1200.6-1200.7 (pages 64-66) net self-employment and + # farm income within the business and count capital gains, not losses, + # so each source is floored at zero and a loss in one source cannot + # offset another. + person_income = 0 + for source in p.sources.person: + person_income = person_income + max_(person(source, period), 0) + # Plan section 1.9 (FY2025 and FY2026) counts Social Security + # "Excluding MediCare deduction", and manual section 1200.7 (page 69) + # excludes the premiums. 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. + premium = person("medicare_part_b_premium", period) + social_security = max_(person("social_security", period) - premium, 0) + adult_income = spm_unit.sum((person_income + social_security) * adult) adult_interest = spm_unit.sum(person("interest_income", period) * adult) # The manual excludes the first $25 of monthly interest without saying # this allowance repeats per person or account. Apply it once to the @@ -30,15 +46,10 @@ def formula(spm_unit, period, parameters): adult_interest - p.monthly_interest_disregard * MONTHS_IN_YEAR, 0, ) - # State TANF is included once at SPM level as a caregiver grant rather - # than projecting the household award onto every member. - household_income = add(spm_unit, period, p.sources.household, options=[ADD]) - # Keep existing signed net business inputs without another deduction; - # floor the combined total. Rental income is assumed nonnegative. - # Roomer/boarder expense allowances, Medicare premiums, mortgage-sale - # contracts, stipends, and recurring support lack the scoped handling. - # Current plan exclusions govern cash gifts and lump-sum receipts. - return max_( - spm_unit.sum(person_income * adult) + interest + household_income, - 0, - ) + # TANF is included once at SPM level as a caregiver grant rather than + # projecting the household award onto every member. + household_income = add(spm_unit, period, p.sources.household) + # Roomer/boarder expense allowances, mortgage-sale contracts, stipends, + # and recurring support lack the scoped handling. Current plan + # exclusions govern cash gifts and lump-sum receipts. + return adult_income + interest + household_income diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py index cc1680657d1..de550ee615b 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py @@ -45,7 +45,11 @@ def formula(spm_unit, period, parameters): base_amounts, default=0, ) - # Applicant payments use the lowest award in the income level. + # Applicant payments use the lowest award in the income level (plan + # page 30). Heat-in-rent renters billed directly for electric, subsidized + # or not, are paid to the electric vendor (manual pages 4 and 89), so + # they keep the matrix step 2 amount for their main heating fuel and an + # unspecified fuel pays 0. direct_payment = ga_liheap_has_direct_payment_route(spm_unit, period) base = where( direct_payment & (heating_type != fuel.NONE), From 7d15ec86df52bfe37f20ba6961a1ebc882cd89b4 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 6 Oct 2026 17:31:33 -0400 Subject: [PATCH 05/14] Count lottery winnings per the Georgia LIHEAP manual The manual's income table (section 1200.7) includes lottery winnings and cash gifts; plan section 1.9 leaves both unchecked. Follow the manual: count gambling_winnings as the closest input for lottery winnings, and note that cash gifts have no input. Also note the same plan-versus-manual conflict for disability payments and workers' compensation, which the manual counts (pages 65 and 68) and the model already follows. Co-Authored-By: Claude Opus 5.5 --- .../ga/dfcs/liheap/income/sources/person.yaml | 10 +++ .../gov/states/ga/dfcs/liheap/ga_liheap.yaml | 32 +++++++++ .../dfcs/liheap/income/ga_liheap_income.yaml | 70 +++++++++++++++++++ .../ga/dfcs/liheap/income/ga_liheap_income.py | 7 +- 4 files changed, 116 insertions(+), 3 deletions(-) diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml index 07f2fe9c65a..744585b2ef5 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml @@ -24,6 +24,10 @@ values: - railroad_benefits - survivor_benefits - veterans_benefits + # Plan vs manual: manual 1200.7 includes "Disability Payment" (page 65) and + # "Workers Compensation" (page 68), both paid by insurance companies, while + # FY2026 plan 1.9 (page 6) leaves "Insurance payments made directly to the + # insured" unchecked. This follows the manual. - disability_benefits - workers_compensation - strike_benefits @@ -32,6 +36,12 @@ values: - farm_rent_income - ordinary_dividend_income - capital_gains + # Plan vs manual: manual 1200.7 (page 66) includes "Lottery Winnings", while + # FY2026 plan 1.9 (page 6) leaves "winnings from lotteries" unchecked. This + # follows the manual; the input also covers non-lottery gambling. + - gambling_winnings + # Manual 1200.7 (page 65) includes "Contribution/Cash Gift" and the plan + # leaves "Cash gifts" unchecked; cash gifts have no input and are not modeled. - alimony_income - child_support_received metadata: diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml index 062aa6fb901..5107af281dc 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml @@ -629,3 +629,35 @@ ga_liheap_eligible: [true, true] ga_liheap_matrix_amount: [500, 400] ga_liheap: [500, 400] + +# Manual section 1200.7 (page 66) counts lottery winnings. Countable income is +# $10,000 wages + $2,000 gambling winnings = $12,000. That is at or below the size 1 +# Level I cutoff of $13,589 and the $34,549 limit, so FY2026 Level I electric pays $500. +- name: Case 18, gambling winnings count toward the Level I award. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + employment_income: 10_000 + gambling_winnings: 2_000 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + ga_tanf: 0 + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + households: + household: + members: [person1] + state_code: GA + output: + ga_liheap_household_size: 1 + ga_liheap_income: 12_000 + ga_liheap_income_limit: 34_549 + heating_expense: 1_200 + ga_liheap_matrix_amount: 500 + ga_liheap_eligible: true + ga_liheap: 500 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml index b541e74a15b..de55ae82a95 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml @@ -385,3 +385,73 @@ output: tanf: [3_360, 0] ga_liheap_income: [3_360, 0] + +# Manual section 1200.7 (page 66) lists "Lottery Winnings" in the Include column; the +# model counts them through gambling_winnings. Each source is floored at zero, and +# income of members under 18 is excluded. +# Household 1: $30,000 wages + $6,000 winnings = $36,000. +# Household 2: $30,000 wages + max(-$6,000, 0) = $30,000. +# Household 3: the parent's $30,000 wages count; the 17-year-old's $6,000 raffle prize +# counts $0. +- name: Case 13, adult gambling winnings count, floored at zero, and a child's winnings are excluded. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + employment_income: 30_000 + gambling_winnings: 6_000 + ssi: 0 + person2: + age: 40 + immigration_status: CITIZEN + employment_income: 30_000 + gambling_winnings: -6_000 + ssi: 0 + person3: + age: 40 + immigration_status: CITIZEN + employment_income: 30_000 + ssi: 0 + person4: + age: 17 + immigration_status: CITIZEN + gambling_winnings: 6_000 + ssi: 0 + tax_units: + tax_unit1: + members: [person1] + tax_unit2: + members: [person2] + tax_unit3: + members: [person3, person4] + spm_units: + spm_unit1: + members: [person1] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + ga_tanf: 0 + spm_unit2: + members: [person2] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + ga_tanf: 0 + spm_unit3: + members: [person3, person4] + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + ga_tanf: 0 + households: + household1: + members: [person1] + state_code: GA + household2: + members: [person2] + state_code: GA + household3: + members: [person3, person4] + state_code: GA + output: + ga_liheap_income: [36_000, 30_000, 30_000] diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py index ef93ec604a9..315b2fb8299 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py @@ -9,7 +9,7 @@ class ga_liheap_income(Variable): label = "Georgia LIHEAP annual countable household income" defined_for = StateCode.GA reference = ( - # Manual pages 62-69; current plan pages 5-6 govern listed exclusions. + # Manual pages 62-69, followed where plan pages 5-6 conflict. "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=62", "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5", # Section 1.9, page 6: Social Security "Excluding MediCare deduction". @@ -50,6 +50,7 @@ def formula(spm_unit, period, parameters): # projecting the household award onto every member. household_income = add(spm_unit, period, p.sources.household) # Roomer/boarder expense allowances, mortgage-sale contracts, stipends, - # and recurring support lack the scoped handling. Current plan - # exclusions govern cash gifts and lump-sum receipts. + # recurring support and the manual's counted cash gifts lack the scoped + # handling. Lottery winnings follow the manual over the plan (see the + # person income sources parameter). return adult_income + interest + household_income From ea11147d254b6de347a97c09750c696f4c3437cb Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 7 Oct 2026 22:56:53 -0400 Subject: [PATCH 06/14] Fix issues from review: document the noncitizen household-size conflict Quote manual 1100.14 (page 57) and the glossary (page 76) against 101.2, 1100.3 and 1100.5 a; the code keeps counting lawful immigrants in the size. Co-Authored-By: Claude Opus 5.5 --- .../ga/dfcs/liheap/eligibility/ga_liheap_eligible.py | 2 ++ .../gov/states/ga/dfcs/liheap/ga_liheap_household_size.py | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py index 5890dbf7e0b..2527a2da42d 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py @@ -41,6 +41,8 @@ def formula(spm_unit, period, parameters): # Account credits of $1,000+ and administrative household exclusions # also need unavailable inputs; the selected heating account is assumed # to satisfy those rules. Annual expense is not an account-credit proxy. + # A household of lawful immigrants only has size > 0 and qualifies; + # see the manual conflict noted in ga_liheap_household_size. return ( (size > 0) & (income <= limit) diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py index bae87678c05..2052d174f76 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py @@ -10,6 +10,13 @@ class ga_liheap_household_size(Variable): # Pages 51-52 exclude nonqualified members from size; page 62 counts income. reference = "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=51" + # Lawful immigrants count in the size; undocumented members do not + # (manual 101.2 page 3, 1100.3 page 50, 1100.5 a page 51: "Do not include + # the undocumented adults in the household size"). The manual conflicts + # with itself: 1100.14 (page 57) lists "Applicants where ALL household + # members are non-U.S. citizens" as ineligible, and the glossary (page 76) + # counts non-citizen members' income "but do not include in the household + # size". This follows the specific eligibility sections. # The SPM unit approximates the economic unit purchasing energy together. # Existing inputs do not identify all roomers, boarders, foster-child # elections, or excluded group-quarter arrangements. From 39c66c14d9cc53c073f2b479a2f76f8d55a59ea7 Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 7 Oct 2026 23:24:32 -0400 Subject: [PATCH 07/14] Apply the reference page-citation convention One reference entry per source; single-page citations keep the page only in the href; multi-page titles end with a quoted #page list and the href opens the first cited page; variable references use '# PDF pages ...' comments above multi-page hrefs (CLAUDE.md, PolicyEngine/policyengine-us PR 10009). Reference lines only. Also cite manual section 1100.4 (not 1100.5 a) for the undocumented-adults sentence in the household-size comment. Co-Authored-By: Claude Opus 5.5 --- .../ga/dfcs/liheap/eligibility/smi_rate.yaml | 2 +- .../ga/dfcs/liheap/income/minimum_age.yaml | 2 +- .../income/monthly_interest_disregard.yaml | 2 +- .../ga/dfcs/liheap/income/sources/household.yaml | 2 +- .../ga/dfcs/liheap/income/sources/person.yaml | 4 ++-- .../liheap/eligibility/ga_liheap_eligible.py | 3 ++- .../ga/dfcs/liheap/ga_liheap_household_size.py | 16 ++++++++-------- .../ga/dfcs/liheap/income/ga_liheap_income.py | 6 ++++-- 8 files changed, 20 insertions(+), 17 deletions(-) diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml index 0474e764be4..6726118a293 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml @@ -6,5 +6,5 @@ metadata: period: year label: Georgia LIHEAP state median income rate reference: - - title: Georgia FY2026 LIHEAP State Plan, section 2.1, page 8 + - title: Georgia FY2026 LIHEAP State Plan, section 2.1 href: https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=8 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml index 468baa09b29..09c00ef9835 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml @@ -6,5 +6,5 @@ metadata: period: year label: Georgia LIHEAP countable income minimum age reference: - - title: Georgia LIHEAP Policy Manual, January 2022, section 1200.2, page 62 + - title: Georgia LIHEAP Policy Manual, January 2022, section 1200.2 href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=62 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml index e37c144d984..b7ad5f9202d 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml @@ -6,5 +6,5 @@ metadata: period: month label: Georgia LIHEAP monthly interest disregard reference: - - title: Georgia LIHEAP Policy Manual, January 2022, section 1200.7, pages 66 and 69 (first interest amount disregarded) + - title: "Georgia LIHEAP Policy Manual, January 2022, section 1200.7 (first interest amount disregarded) #page 66,69" href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=66 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml index edc8807616d..48beb71b8c1 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml @@ -9,5 +9,5 @@ metadata: period: year label: Georgia LIHEAP household income sources reference: - - title: Georgia FY2026 LIHEAP State Plan, section 1.9, page 6 (TANF counted) + - title: Georgia FY2026 LIHEAP State Plan, section 1.9 (TANF counted) href: https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml index 744585b2ef5..60d0bdf8a78 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml @@ -49,7 +49,7 @@ metadata: period: year label: Georgia LIHEAP adult person income sources reference: - - title: Georgia LIHEAP Policy Manual, January 2022, sections 1200.1-1200.7, pages 62-69, and application item 12, page 83 + - title: "Georgia LIHEAP Policy Manual, January 2022, sections 1200.1-1200.7 and application item 12 #page 62-69,83" href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=62 - - title: Georgia FY2026 LIHEAP State Plan, section 1.9, pages 5-6 (current included and excluded sources) + - title: "Georgia FY2026 LIHEAP State Plan, section 1.9 (current included and excluded sources) #page 5-6" href: https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5 diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py index 2527a2da42d..f07b693e15f 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py @@ -11,9 +11,10 @@ class ga_liheap_eligible(Variable): definition_period = YEAR label = "Georgia LIHEAP ordinary eligibility" defined_for = StateCode.GA - # State plan pages 5, 8, 9, 30; manual pages 3, 4, 50, 56, 57, 75, 89. reference = ( + # PDF pages 5, 8-9, 30 "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5", + # PDF pages 3-4, 50, 56-57, 75, 89 "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=3", ) diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py index 2052d174f76..3dcc20ff90f 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py @@ -7,16 +7,16 @@ class ga_liheap_household_size(Variable): definition_period = YEAR label = "Georgia LIHEAP household size" defined_for = StateCode.GA - # Pages 51-52 exclude nonqualified members from size; page 62 counts income. + # PDF pages 51-52, 62 reference = "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=51" - # Lawful immigrants count in the size; undocumented members do not - # (manual 101.2 page 3, 1100.3 page 50, 1100.5 a page 51: "Do not include - # the undocumented adults in the household size"). The manual conflicts - # with itself: 1100.14 (page 57) lists "Applicants where ALL household - # members are non-U.S. citizens" as ineligible, and the glossary (page 76) - # counts non-citizen members' income "but do not include in the household - # size". This follows the specific eligibility sections. + # Lawful immigrants count in the size (manual 101.2 page 3, 1100.3 page + # 50, 1100.5 a page 51); undocumented members do not (1100.4 page 51: "Do + # not include the undocumented adults in the household size"). The manual + # conflicts with itself: 1100.14 (page 57) lists "Applicants where ALL + # household members are non-U.S. citizens" as ineligible, and the glossary + # (page 76) counts non-citizen members' income "but do not include in the + # household size". This follows the specific eligibility sections. # The SPM unit approximates the economic unit purchasing energy together. # Existing inputs do not identify all roomers, boarders, foster-child # elections, or excluded group-quarter arrangements. diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py index 315b2fb8299..97946b6697e 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py @@ -9,10 +9,12 @@ class ga_liheap_income(Variable): label = "Georgia LIHEAP annual countable household income" defined_for = StateCode.GA reference = ( - # Manual pages 62-69, followed where plan pages 5-6 conflict. + # The manual is followed where the plan conflicts. + # PDF pages 62-69 "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=62", + # PDF pages 5-6 "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5", - # Section 1.9, page 6: Social Security "Excluding MediCare deduction". + # Section 1.9: Social Security "Excluding MediCare deduction". "https://liheapch.acf.gov/docs/2025/state-plans/GA_Plan_2025.pdf#page=6", ) From 7df668b9fd0bad96dd8a266acfa252ad1b2a5843 Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 8 Oct 2026 00:32:03 -0400 Subject: [PATCH 08/14] Count cash support in Georgia LIHEAP income --- .../ga/dfcs/liheap/income/sources/person.yaml | 9 ++++- .../gov/states/ga/dfcs/liheap/ga_liheap.yaml | 40 +++++++++++++++++++ .../dfcs/liheap/income/ga_liheap_income.yaml | 15 +++++++ .../ga/dfcs/liheap/income/ga_liheap_income.py | 7 ++-- 4 files changed, 65 insertions(+), 6 deletions(-) diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml index 60d0bdf8a78..dcdd2415dd6 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml @@ -40,8 +40,11 @@ values: # FY2026 plan 1.9 (page 6) leaves "winnings from lotteries" unchecked. This # follows the manual; the input also covers non-lottery gambling. - gambling_winnings - # Manual 1200.7 (page 65) includes "Contribution/Cash Gift" and the plan - # leaves "Cash gifts" unchecked; cash gifts have no input and are not modeled. + # Manual 1200.7 (page 65) explicitly includes "Contribution/Cash Gift". + # The FY2022 plan already left gifts unchecked when this manual was issued; + # the same FY2026 checkbox does not establish a later policy change. Follow + # the detailed manual, as for lottery and insurance income above. + - financial_assistance - alimony_income - child_support_received metadata: @@ -53,3 +56,5 @@ metadata: href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=62 - title: "Georgia FY2026 LIHEAP State Plan, section 1.9 (current included and excluded sources) #page 5-6" href: https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5 + - title: Georgia FY2022 LIHEAP State Plan, section 1.9 (contemporaneous unchecked gifts, lottery and insurance categories) + href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/GA_Plan_2022.pdf#page=6 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml index 5107af281dc..464a6f15589 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml @@ -661,3 +661,43 @@ ga_liheap_matrix_amount: 500 ga_liheap_eligible: true ga_liheap: 500 + +# Manual section 1200.7, page 65 counts cash gifts from outside the household. +# FY2026 matrix: size-one Level I ends at $13,589 and eligibility at $34,549. +# Gifts of $12,000 give the $500 electric award; $24,000 gives $400; $36,000 +# exceeds the limit and gives $0. Without the counted gift, all three get $500. +- name: Case 19, cash contributions determine the benefit band and eligibility. + period: 2026 + input: + state_code: [GA, GA, GA] + age: [40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN] + financial_assistance: [12_000, 24_000, 36_000] + ssi: [0, 0, 0] + ga_tanf: [0, 0, 0] + heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY] + pre_subsidy_electricity_expense: [1_200, 1_200, 1_200] + output: + ga_liheap_income: [12_000, 24_000, 36_000] + ga_liheap_income_limit: [34_549, 34_549, 34_549] + ga_liheap_eligible: [true, true, false] + ga_liheap: [500, 400, 0] + +# FY2025 uses the same detailed income rule. Its size-one limit is $33,038; +# $36,000 in outside cash contributions exceeds it and gives no heating benefit. +- name: Case 20, cash contributions above the prior year limit prevent an award. + period: 2025 + input: + state_code: GA + age: 40 + immigration_status: CITIZEN + financial_assistance: 36_000 + ssi: 0 + ga_tanf: 0 + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + output: + ga_liheap_income: 36_000 + ga_liheap_income_limit: 33_038 + ga_liheap_eligible: false + ga_liheap: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml index de55ae82a95..dc71549f679 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml @@ -455,3 +455,18 @@ state_code: GA output: ga_liheap_income: [36_000, 30_000, 30_000] + +# Manual section 1200.7, page 65 includes cash contributions and gifts. Section +# 1200.2, page 62 excludes all income of members under 18. The same $6,000 cash +# support therefore counts at age 18 but not 17. financial_assistance records +# support from outside the household, avoiding intra-household double counting. +- name: Case 14, cash contributions count from age eighteen. + period: 2026 + input: + state_code: [GA, GA] + age: [17, 18] + financial_assistance: [6_000, 6_000] + ssi: [0, 0] + ga_tanf: [0, 0] + output: + ga_liheap_income: [0, 6_000] diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py index 97946b6697e..cd9e894a6de 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py @@ -51,8 +51,7 @@ def formula(spm_unit, period, parameters): # TANF is included once at SPM level as a caregiver grant rather than # projecting the household award onto every member. household_income = add(spm_unit, period, p.sources.household) - # Roomer/boarder expense allowances, mortgage-sale contracts, stipends, - # recurring support and the manual's counted cash gifts lack the scoped - # handling. Lottery winnings follow the manual over the plan (see the - # person income sources parameter). + # Roomer/boarder expense allowances, mortgage-sale contracts and stipends + # lack scoped handling. Cash contributions use financial_assistance; + # gifts and lottery winnings follow the detailed manual (see sources). return adult_income + interest + household_income From 50aaa0096e2aee273aaae874711f9d0786a91ac0 Mon Sep 17 00:00:00 2001 From: Ziming Date: Sun, 11 Oct 2026 23:45:52 -0400 Subject: [PATCH 09/14] Use the shared SMI helper for the Georgia LIHEAP income limit Calls liheap_smi_limit from PR 10019 instead of repeating the HHS floor-then-floor calculation. Limits are unchanged. Co-Authored-By: Claude Opus 5.5 --- .../liheap/eligibility/ga_liheap_income_limit.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.py index fd65d704f61..8f2195450fa 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.py @@ -1,5 +1,5 @@ from policyengine_us.model_api import * -from policyengine_us.variables.gov.hhs.hhs_smi import smi +from policyengine_us.variables.gov.hhs.hhs_smi import liheap_smi_limit class ga_liheap_income_limit(Variable): @@ -18,10 +18,6 @@ def formula(spm_unit, period, parameters): p = parameters(period).gov.states.ga.dfcs.liheap.eligibility size = spm_unit("ga_liheap_household_size", period) state = spm_unit.household("state_code_str", period) - four_person_smi = parameters(period).gov.hhs.smi.amount[state] - size_share = smi(size, state, period, parameters) / four_person_smi - # Flooring 60% of the four-person SMI before applying the federal size - # factor reproduces all 16 FY2026 published limits. This is a numerical - # reconciliation; the sources do not expressly specify the floor order. - four_person_limit = np.floor(four_person_smi * p.smi_rate) - return where(size > 0, np.floor(four_person_limit * size_share), 0) + # The HHS floor order reproduces all 16 published FY2026 limits. + limit = liheap_smi_limit(size, state, p.smi_rate, period, parameters) + return where(size > 0, limit, 0) From b36ba40f71ae9dd723b6ff9818b148f7c0bb6c23 Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 15 Oct 2026 16:11:36 -0400 Subject: [PATCH 10/14] Address review round 1 on Georgia LIHEAP - S1: Merge blocker: the person source list reads the raw unemployment_compensation, so main's #9558 guard fails - S2: Eight income sources and two smaller pieces of logic are not pinned by any test - S3: Six parameter descriptions fall outside the house template - REC-C: Federal 110%-of-poverty floor (coordinator recommendation C; not raised by David for GA) Also applies the in-scope suggestions. Tests not run locally; CI validates. Co-Authored-By: Claude Opus 5.5 --- .../parameters/gov/hhs/liheap/fpg_floor.yaml | 11 +++ .../dfcs/liheap/eligibility/fpg_year_lag.yaml | 15 +++ .../ga/dfcs/liheap/eligibility/smi_rate.yaml | 2 + .../ga/dfcs/liheap/income/minimum_age.yaml | 2 +- .../income/monthly_interest_disregard.yaml | 2 +- .../dfcs/liheap/income/sources/household.yaml | 2 +- .../ga/dfcs/liheap/income/sources/person.yaml | 6 +- .../ga/dfcs/liheap/payment/base_amount.yaml | 2 + .../liheap/payment/lower_income_uplift.yaml | 7 +- .../liheap/payment/maximum_table_size.yaml | 4 +- .../eligibility/ga_liheap_eligible.yaml | 35 +++++++ .../eligibility/ga_liheap_income_limit.yaml | 21 ++-- .../gov/states/ga/dfcs/liheap/ga_liheap.yaml | 98 +++++++++++++++++++ .../dfcs/liheap/ga_liheap_household_size.yaml | 6 -- .../dfcs/liheap/income/ga_liheap_income.yaml | 74 +++++++------- .../payment/ga_liheap_matrix_amount.yaml | 16 --- .../liheap/eligibility/_ga_liheap_housing.py | 23 +++-- .../liheap/eligibility/ga_liheap_eligible.py | 28 ++++-- .../dfcs/liheap/ga_liheap_household_size.py | 11 ++- .../ga/dfcs/liheap/income/ga_liheap_income.py | 2 +- .../liheap/payment/ga_liheap_matrix_amount.py | 17 ++-- 21 files changed, 281 insertions(+), 103 deletions(-) create mode 100644 policyengine_us/parameters/gov/hhs/liheap/fpg_floor.yaml create mode 100644 policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/fpg_year_lag.yaml diff --git a/policyengine_us/parameters/gov/hhs/liheap/fpg_floor.yaml b/policyengine_us/parameters/gov/hhs/liheap/fpg_floor.yaml new file mode 100644 index 00000000000..ae7cb296211 --- /dev/null +++ b/policyengine_us/parameters/gov/hhs/liheap/fpg_floor.yaml @@ -0,0 +1,11 @@ +description: The US Department of Health and Human Services limits income-based exclusion to households with income of at least this percentage of the poverty guideline under the Low Income Home Energy Assistance Program. +# Pub. L. 98-558, section 605(a)(2), added the floor for fiscal year 1986 and later. +values: + 1985-10-01: 1.1 +metadata: + period: year + unit: /1 + label: LIHEAP poverty guideline income floor + reference: + - title: 42 U.S.C. 8624(b)(2)(B) + href: https://www.law.cornell.edu/uscode/text/42/8624 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/fpg_year_lag.yaml new file mode 100644 index 00000000000..bafbd3f84f7 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/fpg_year_lag.yaml @@ -0,0 +1,15 @@ +description: Georgia uses the poverty guidelines from this many years before the model year for the federal income floor under the Low Income Home Energy Assistance Program. +# HHS makes each January's guidelines mandatory for LIHEAP from the next +# October 1, so model period 2026 (FY2026) uses the 2025 guidelines and 2025 +# (FY2025) the 2024 guidelines. Georgia's own limit uses state median income. +values: + 2024-10-01: 1 +metadata: + unit: year + period: year + label: Georgia LIHEAP poverty guideline year lag + reference: + - title: LIHEAP IM 2025-02, Attachment 2, poverty guidelines for mandatory use in FY26 + href: https://acf.gov/sites/default/files/documents/ocs/COMM_LIHEAP_IM2025-02_FPGSte-Table_Att2.pdf#page=1 + - title: LIHEAP IM 2024-02, Attachment 2, poverty guidelines for mandatory use in FY25 + href: https://acf.gov/sites/default/files/documents/ocs/COMM_LIHEAP_IM%202024-02_Att2FPGTables_States.pdf#page=1 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml index 6726118a293..532a39b82e3 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml @@ -6,5 +6,7 @@ metadata: period: year label: Georgia LIHEAP state median income rate reference: + - title: Georgia FY2025 LIHEAP State Plan, section 2.1 + href: https://liheapch.acf.gov/docs/2025/state-plans/GA_Plan_2025.pdf#page=8 - title: Georgia FY2026 LIHEAP State Plan, section 2.1 href: https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=8 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml index 09c00ef9835..2febda5c356 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml @@ -1,4 +1,4 @@ -description: Georgia counts household members' income starting at this age under the Low Income Home Energy Assistance Program. +description: Georgia excludes the income of household members below this age under the Low Income Home Energy Assistance Program. values: 2025-10-01: 18 metadata: diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml index b7ad5f9202d..0684f255f85 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml @@ -1,4 +1,4 @@ -description: Georgia disregards this amount of monthly interest income under the Low Income Home Energy Assistance Program. +description: Georgia excludes this amount of monthly interest income under the Low Income Home Energy Assistance Program. values: 2025-10-01: 25 metadata: diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml index 48beb71b8c1..c87dd3d39ff 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml @@ -1,4 +1,4 @@ -description: Georgia counts these household benefits once under the Low Income Home Energy Assistance Program. +description: Georgia uses these household-level benefits as countable income under the Low Income Home Energy Assistance Program. values: 2025-10-01: # The tanf aggregate includes ga_tanf and applies take-up; ga_tanf alone is diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml index dcdd2415dd6..a347a1d64c3 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml @@ -1,4 +1,4 @@ -description: Georgia counts these income sources of adult household members under the Low Income Home Energy Assistance Program. +description: Georgia uses these sources as countable income of adult household members under the Low Income Home Energy Assistance Program. # Interest is calculated separately after its disregard. TANF is counted once # at SPM-unit level. Each source is floored at zero in the formula, so a loss # in one source cannot offset another; net business inputs have no extra @@ -10,7 +10,7 @@ values: - sstb_self_employment_income - farm_operations_income - partnership_s_corp_income - - unemployment_compensation + - total_unemployment_compensation # Social Security is counted in the formula, net of the Medicare Part B # premium. - ssi @@ -54,7 +54,7 @@ metadata: reference: - title: "Georgia LIHEAP Policy Manual, January 2022, sections 1200.1-1200.7 and application item 12 #page 62-69,83" href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=62 - - title: "Georgia FY2026 LIHEAP State Plan, section 1.9 (current included and excluded sources) #page 5-6" + - title: "Georgia FY2026 LIHEAP State Plan, section 1.9 (current included and excluded sources) #page 5-7" href: https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5 - title: Georgia FY2022 LIHEAP State Plan, section 1.9 (contemporaneous unchecked gifts, lottery and insurance categories) href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/GA_Plan_2022.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/base_amount.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/base_amount.yaml index 815969b9e0d..843e4b57581 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/base_amount.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/base_amount.yaml @@ -15,5 +15,7 @@ metadata: label: Georgia LIHEAP Level II heating payment by fuel propagate_metadata_to_children: true reference: + - title: Georgia Benefit Matrix, FY2025 income guidelines, step 2 (heating amounts) + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/GA_BenefitMatrix_2025.pdf - title: Georgia Benefit Matrix, FY2026 income guidelines, step 2 (heating amounts) href: https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_uplift.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_uplift.yaml index 29aa5c064de..80e4d8d2244 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_uplift.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_uplift.yaml @@ -1,4 +1,7 @@ -description: Georgia increases the heating payment by this amount for households in income Level I under the Low Income Home Energy Assistance Program. +description: Georgia provides this additional heating payment to households in income Level I under the Low Income Home Energy Assistance Program. +# Inferred: every printed Level I heating amount is the Level II amount plus +# $100 on the FY2025 and FY2026 matrices; the matrices print both columns, +# not this rule. Encode the printed Level I column if a matrix breaks the pattern. values: 2025-10-01: 100 metadata: @@ -6,5 +9,7 @@ metadata: period: year label: Georgia LIHEAP lower income heating payment increase reference: + - title: Georgia Benefit Matrix, FY2025 income guidelines, step 2 (difference between Level I and Level II heating amounts) + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/GA_BenefitMatrix_2025.pdf - title: Georgia Benefit Matrix, FY2026 income guidelines, step 2 (difference between Level I and Level II heating amounts) href: https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/maximum_table_size.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/maximum_table_size.yaml index 0765c12055c..38914c5ee48 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/maximum_table_size.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/maximum_table_size.yaml @@ -1,4 +1,4 @@ -description: Georgia publishes Level I income thresholds through this household size under the Low Income Home Energy Assistance Program. +description: Georgia sets Level I income thresholds through this household size under the Low Income Home Energy Assistance Program. values: 2025-10-01: 16 metadata: @@ -6,5 +6,7 @@ metadata: period: year label: Georgia LIHEAP maximum published benefit table size reference: + - title: Georgia Benefit Matrix, FY2025 income guidelines, step 1 + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/GA_BenefitMatrix_2025.pdf - title: Georgia Benefit Matrix, FY2026 income guidelines, step 1 href: https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml index 3965493457e..e3d9a8755f5 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml @@ -167,3 +167,38 @@ pre_subsidy_electricity_expense: [1_200, 1_200, 1_200, 1_200, 0, 1_200] output: ga_liheap_eligible: [true, true, true, true, false, false] + +# Manual page 4: "Renters who live in subsidized housing are eligible for assistance +# if their utility bill is in their name". Each household is one adult with $12,000 +# of wages (under the $34,549 limit), natural gas heat in rent, a subsidized tenant +# share of $3,000 ($250 a month), and tenant_pays_utilities false. +# Household 1: the $1,200 electric bill is the bill in the household's name, even +# though tenant_pays_utilities says utilities are in rent; the bill resolves the +# contradictory flag. The household is on the electric-vendor route, and the +# subsidy bars the direct-payment route. Natural gas Level II is $560; $12,000 is +# at or below the $13,589 Level I cutoff, so $560 + $100 = $660. +# Household 2: no bill. Page 4 ("Subsidized housing residents whose fuel cost is +# included in their rental payment are not eligible for assistance unless the +# applicant can verify an energy burden") and page 82 ("resides in subsidized +# housing and the rent includes utilities": "not eligible for the program") make it +# ineligible. +- name: Case 9, a subsidized heat in rent renter with an electric bill qualifies when utilities are flagged as in rent. + period: 2026 + input: + state_code: [GA, GA] + age: [40, 40] + immigration_status: [CITIZEN, CITIZEN] + employment_income: [12_000, 12_000] + ssi: [0, 0] + ga_tanf: [0, 0] + rent: [3_000, 3_000] + heating_type: [NATURAL_GAS, NATURAL_GAS] + heat_expense_included_in_rent: [true, true] + tenant_pays_utilities: [false, false] + receives_housing_assistance: [true, true] + is_in_public_housing: [false, false] + gas_expense: [0, 0] + pre_subsidy_electricity_expense: [1_200, 0] + output: + ga_liheap_eligible: [true, false] + ga_liheap: [660, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.yaml index 524ecaf70a8..0d772966a92 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_income_limit.yaml @@ -7,11 +7,6 @@ absolute_error_margin: 0.01 input: state_code: [GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA, GA] - heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, - ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, - ELECTRICITY] - pre_subsidy_electricity_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, - 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] ga_liheap_household_size: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] output: ga_liheap_income_limit: [34_549, 45_179, 55_810, 66_441, 77_071, 87_702, 89_695, 91_688, 93_681, @@ -24,8 +19,6 @@ absolute_error_margin: 0.01 input: state_code: [GA, AL] - heating_type: [ELECTRICITY, ELECTRICITY] - pre_subsidy_electricity_expense: [1_200, 1_200] ga_liheap_household_size: [1, 1] output: ga_liheap_income_limit: [34_549, 0] @@ -40,8 +33,18 @@ absolute_error_margin: 0.01 input: state_code: [GA, GA, GA] - heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY] - pre_subsidy_electricity_expense: [1_200, 1_200, 1_200] ga_liheap_household_size: [1, 4, 16] output: ga_liheap_income_limit: [33_038, 63_535, 102_926] + +# A unit with no qualified member has no Georgia income limit. Without the size-0 +# mask, the HHS size factor at size 0 (0.52) would give floor($66,441 x 0.52 = +# $34,549.32) = $34,549. +- name: Case 4, a unit with no qualified member has a zero income limit. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: GA + ga_liheap_household_size: 0 + output: + ga_liheap_income_limit: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml index 464a6f15589..7f6295d44b8 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml @@ -701,3 +701,101 @@ ga_liheap_income_limit: 33_038 ga_liheap_eligible: false ga_liheap: 0 + +# Manual section 1200.7 (page 67) and FY2026 plan item 1.9 (page 6) count +# unemployment compensation. The adult earned $12,000 before a layoff and drew +# Georgia's maximum weekly benefit, $365, for the maximum of 14 weeks: +# 14 x $365 = $5,110 (USDOL Significant Provisions of State UI Laws, January 2026, +# Georgia row: the weekly benefit is 1/42 of wages in the two highest base-period +# quarters, maximum $365; the maximum benefit is the "Lesser of 14 x WBA or 1/4 BPW"). +# The $365 maximum needs $15,330 in those two quarters ($365 x 42), about a $30,700 +# a year job before the layoff, and 1/4 of such base-period wages ($7,665) exceeds +# $5,110. Manual 1200.6 (page 64) also converts unemployment income with 14 weeks. +# Countable income: $12,000 + $5,110 = $17,110, above the $13,589 Level I cutoff and +# at or below the $34,549 limit, so FY2026 Level II electric pays $400. Without the +# unemployment compensation the award would be $500. +- name: Case 21, unemployment compensation counts toward the benefit level. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + employment_income: 12_000 + unemployment_compensation: 5_110 + ssi: 0 + spm_units: + spm_unit: + members: [person1] + ga_tanf: 0 + heating_type: ELECTRICITY + pre_subsidy_electricity_expense: 1_200 + households: + household: + members: [person1] + state_code: GA + output: + ga_liheap_income: 17_110 + ga_liheap_income_limit: 34_549 + ga_liheap_matrix_amount: 400 + ga_liheap_eligible: true + ga_liheap: 400 + +# 42 U.S.C. 8624(b)(2)(B) bars excluding a household on income below 110% of the +# poverty guideline. Four adults earn Georgia median annual wages (BLS OEWS May 2025): +# two cashiers at $29,070 and two fast food and counter workers at $26,920, so +# countable income is 2 x $29,070 + 2 x $26,920 = $58,140 + $53,840 = $111,980. +# The 13 children have no income, so the household size is 17. +# Printed-method limit: floor($66,441 x 1.65 = $109,627.65) = $109,627, where +# $66,441 = floor($110,736 x 0.6); the income exceeds it. +# Floor: LIHEAP IM 2025-02 Attachment 2 (2025 guidelines, mandatory in FY2026) gives +# 110% for 12 people as $83,765, plus $6,050 for each additional member: +# $83,765 + 5 x $6,050 = $114,015 (= 1.1 x ($15,650 + 16 x $5,500)). +# $111,980 is below $114,015, so the household stays eligible. +# $111,980 is above the size 16 Level I cutoff of $42,336, so Level II natural gas +# pays $560. +# Size 16 is not tested: its FY2026 floor exceeds the printed limit by only $331 +# ($107,634 to $107,965). +- name: Case 22, the federal poverty floor keeps a large household above the state median income limit eligible. + period: 2026 + input: + people: + person1: {age: 45, immigration_status: CITIZEN, employment_income: 29_070, ssi: 0} + person2: {age: 43, immigration_status: CITIZEN, employment_income: 29_070, ssi: 0} + person3: {age: 41, immigration_status: CITIZEN, employment_income: 26_920, ssi: 0} + person4: {age: 39, immigration_status: CITIZEN, employment_income: 26_920, ssi: 0} + person5: {age: 17, immigration_status: CITIZEN, ssi: 0} + person6: {age: 16, immigration_status: CITIZEN, ssi: 0} + person7: {age: 15, immigration_status: CITIZEN, ssi: 0} + person8: {age: 14, immigration_status: CITIZEN, ssi: 0} + person9: {age: 13, immigration_status: CITIZEN, ssi: 0} + person10: {age: 12, immigration_status: CITIZEN, ssi: 0} + person11: {age: 11, immigration_status: CITIZEN, ssi: 0} + person12: {age: 10, immigration_status: CITIZEN, ssi: 0} + person13: {age: 9, immigration_status: CITIZEN, ssi: 0} + person14: {age: 7, immigration_status: CITIZEN, ssi: 0} + person15: {age: 5, immigration_status: CITIZEN, ssi: 0} + person16: {age: 3, immigration_status: CITIZEN, ssi: 0} + person17: {age: 1, immigration_status: CITIZEN, ssi: 0} + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8, + person9, person10, person11, person12, person13, person14, person15, person16, + person17] + ga_tanf: 0 + heating_type: NATURAL_GAS + gas_expense: 1_800 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8, + person9, person10, person11, person12, person13, person14, person15, person16, + person17] + state_code: GA + output: + ga_liheap_household_size: 17 + ga_liheap_income: 111_980 + ga_liheap_income_limit: 109_627 + heating_expense: 1_800 + ga_liheap_matrix_amount: 560 + ga_liheap_eligible: true + ga_liheap: 560 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap_household_size.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap_household_size.yaml index 410c036b1d5..bdb68c8e66b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap_household_size.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap_household_size.yaml @@ -30,14 +30,11 @@ spm_units: spm_unit: members: [person1, person2, person3, person4, person5] - heating_type: ELECTRICITY - pre_subsidy_electricity_expense: 1_200 ga_tanf: 0 households: household: members: [person1, person2, person3, person4, person5] state_code: GA - hud_utility_allowance: 0 output: ga_liheap_household_size: 3 @@ -58,13 +55,10 @@ spm_units: spm_unit: members: [person1, person2] - heating_type: ELECTRICITY - pre_subsidy_electricity_expense: 1_200 ga_tanf: 0 households: household: members: [person1, person2] state_code: GA - hud_utility_allowance: 0 output: ga_liheap_household_size: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml index dc71549f679..cdf21f10d3d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/income/ga_liheap_income.yaml @@ -28,14 +28,11 @@ spm_units: spm_unit: members: [person1] - heating_type: ELECTRICITY - pre_subsidy_electricity_expense: 1_200 ga_tanf: 0 households: household: members: [person1] state_code: GA - hud_utility_allowance: 0 output: ga_liheap_income: 25_800 @@ -64,14 +61,11 @@ spm_units: spm_unit: members: [person1, person2] - heating_type: ELECTRICITY - pre_subsidy_electricity_expense: 1_200 ga_tanf: 0 households: household: members: [person1, person2] state_code: GA - hud_utility_allowance: 0 output: ga_liheap_income: 12_000 @@ -100,14 +94,11 @@ spm_units: spm_unit: members: [person1, person2] - heating_type: ELECTRICITY - pre_subsidy_electricity_expense: 1_200 ga_tanf: 0 households: household: members: [person1, person2] state_code: GA - hud_utility_allowance: 0 output: ga_liheap_income: 22_500 @@ -133,14 +124,11 @@ spm_units: spm_unit: members: [person1, person2] - heating_type: ELECTRICITY - pre_subsidy_electricity_expense: 1_200 ga_tanf: 0 households: household: members: [person1, person2] state_code: GA - hud_utility_allowance: 0 output: ga_liheap_income: 700 @@ -151,8 +139,6 @@ absolute_error_margin: 0.01 input: state_code: [GA, GA, GA] - heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY] - pre_subsidy_electricity_expense: [1_200, 1_200, 1_200] age: [40, 40, 40] ssi: [0, 0, 0] ga_tanf: [0, 0, 0] @@ -180,14 +166,11 @@ spm_units: spm_unit: members: [person1, person2] - heating_type: ELECTRICITY - pre_subsidy_electricity_expense: 1_200 ga_tanf: 3_600 households: household: members: [person1, person2] state_code: GA - hud_utility_allowance: 0 output: ga_liheap_income: 4_800 @@ -208,14 +191,11 @@ spm_units: spm_unit: members: [person1] - heating_type: ELECTRICITY - pre_subsidy_electricity_expense: 1_200 ga_tanf: 0 households: household: members: [person1] state_code: GA - hud_utility_allowance: 0 output: ga_liheap_income: 6_000 @@ -243,13 +223,11 @@ spm_unit: members: [person1, person2] heating_type: ELECTRICITY - pre_subsidy_electricity_expense: 1_200 ga_tanf: 0 households: household: members: [person1, person2] state_code: GA - hud_utility_allowance: 0 output: ga_liheap_household_size: 1 ga_liheap_income: 12_000 @@ -274,8 +252,6 @@ medicare_part_b_premium: [2_434.8, 2_434.8] ssi: [0, 0] ga_tanf: [0, 0] - heating_type: [ELECTRICITY, ELECTRICITY] - pre_subsidy_electricity_expense: [1_800, 1_800] output: ga_liheap_income: [33_565.2, 20_000] @@ -294,9 +270,6 @@ medicare_part_b_premium: [0, 2_434.8] ssi: [0, 0] ga_tanf: [0, 0] - heating_type: [NATURAL_GAS, ELECTRICITY] - gas_expense: [1_200, 0] - pre_subsidy_electricity_expense: [0, 1_500] output: ga_liheap_income: [36_000, 36_000] @@ -318,9 +291,6 @@ farm_operations_income: [0, 0, -8_000] ssi: [0, 0, 0] ga_tanf: [0, 0, 0] - heating_type: [ELECTRICITY, ELECTRICITY, PROPANE] - pre_subsidy_electricity_expense: [1_500, 1_500, 0] - bottled_gas_expense: [0, 0, 1_500] output: ga_liheap_income: [40_000, 36_000, 0] @@ -365,14 +335,10 @@ spm_units: spm_unit1: members: [person1, person2, person3] - heating_type: NATURAL_GAS - gas_expense: 1_200 ga_tanf: 3_360 takes_up_tanf_if_eligible: true spm_unit2: members: [person4, person5, person6] - heating_type: NATURAL_GAS - gas_expense: 1_200 ga_tanf: 3_360 takes_up_tanf_if_eligible: false households: @@ -430,18 +396,12 @@ spm_units: spm_unit1: members: [person1] - heating_type: ELECTRICITY - pre_subsidy_electricity_expense: 1_200 ga_tanf: 0 spm_unit2: members: [person2] - heating_type: ELECTRICITY - pre_subsidy_electricity_expense: 1_200 ga_tanf: 0 spm_unit3: members: [person3, person4] - heating_type: ELECTRICITY - pre_subsidy_electricity_expense: 1_200 ga_tanf: 0 households: household1: @@ -470,3 +430,37 @@ ga_tanf: [0, 0] output: ga_liheap_income: [0, 6_000] + +# Deliberate source-pinning case: each remaining adult source has a distinct amount, +# so dropping any one source changes the total. Manual section 1200.7 includes +# alimony, child support and disability payments (page 65), strike benefits, +# unemployment compensation and veterans' benefits (page 67), and workers' +# compensation (page 68); FY2026 plan item 1.9 (page 6) counts General Assistance. +# $100 + $200 + $300 + $400 + $500 + $600 + $700 + $800 = $3,600. +- name: Case 15, the remaining adult income sources each count once. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + ssi: 0 + unemployment_compensation: 100 + veterans_benefits: 200 + disability_benefits: 300 + workers_compensation: 400 + strike_benefits: 500 + general_assistance: 600 + alimony_income: 700 + child_support_received: 800 + spm_units: + spm_unit: + members: [person1] + ga_tanf: 0 + households: + household: + members: [person1] + state_code: GA + output: + ga_liheap_income: 3_600 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml index 698c85748c3..13d335d7f77 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml @@ -13,12 +13,6 @@ NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] - pre_subsidy_electricity_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, - 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, - 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] - gas_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, - 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, - 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] ga_liheap_household_size: [1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16] ga_liheap_income: [13_589, 13589.01, 17_770, 17770.01, 21_952, 21952.01, 25_469, 25469.01, 30_315, @@ -39,12 +33,6 @@ state_code: [GA, GA, GA, GA, GA, GA, GA, GA, GA, GA] heating_type: [ELECTRICITY, NATURAL_GAS, FUEL_OIL, PROPANE, WOOD, ELECTRICITY, NATURAL_GAS, FUEL_OIL, PROPANE, WOOD] - pre_subsidy_electricity_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, - 1_200] - gas_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] - fuel_oil_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] - bottled_gas_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] - wood_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] ga_liheap_household_size: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] ga_liheap_income: [0, 0, 0, 0, 0, 13589.01, 13589.01, 13589.01, 13589.01, 13589.01] output: @@ -58,7 +46,6 @@ input: state_code: GA heating_type: NATURAL_GAS - pre_subsidy_electricity_expense: 1_200 gas_expense: 50 ga_liheap_household_size: 1 ga_liheap_income: 0 @@ -73,7 +60,6 @@ input: state_code: GA heating_type: NONE - pre_subsidy_electricity_expense: 1_200 ga_liheap_household_size: 1 ga_liheap_income: 0 output: @@ -109,7 +95,6 @@ input: state_code: [GA, GA, GA, GA, GA, GA] heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] - gas_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200] ga_liheap_household_size: [1, 1, 4, 4, 16, 16] ga_liheap_income: [12_995, 12_995.01, 24_355, 24_355.01, 40_485, 40_485.01] output: @@ -124,7 +109,6 @@ input: state_code: [GA, GA, GA] heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] - gas_expense: [1_200, 1_200, 1_200] ga_liheap_household_size: [17, 17, 25] ga_liheap_income: [42_336, 42_336.01, 42_336] output: diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py index 24f4b6ef1e5..963c7081bba 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py @@ -24,18 +24,23 @@ def ga_liheap_has_electric_vendor_route(spm_unit, period): unsubsidized renters with heat in rent and their own electric bill an energy burden, "Paid to electric vendor because gas usage is indeterminate". Page 4 makes subsidized renters eligible when "their - utility bill is in their name", so this route ignores subsidy status; - subsidized heat-in-rent renters without a bill stay ineligible. - tenant_pays_utilities separates these renters from the rows where all - utilities are in rent, and a positive electric bill is the utility-bill - documentation. The pre-subsidy bill matches heating_expense and avoids - the program-dependent subsidies in electricity_expense. - Manual pages 4 and 89: - https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=89 + utility bill is in their name", so this route ignores subsidy status and + tenant_pays_utilities: a positive electric bill is the utility-bill + documentation. Page 82's ineligibility when "the rent includes utilities" + is read as rent covering every utility, which a separate electric bill + rules out. Unsubsidized renters whose rent includes all utilities take + the direct-payment route's lowest award instead. The FY2024 matrix note + ("The main fuel type is not always where the benefit is applied") pays + the main-fuel amount when the benefit goes to the electric bill. The + pre-subsidy bill matches heating_expense and avoids the + program-dependent subsidies in electricity_expense. + Manual pages 4, 82 and 89: + https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=4 + FY2024 matrix: + https://liheapch.acf.gov/docs/2024/benefits-matricies/GA_BenefitMatrix_2024.pdf """ return ( spm_unit("heat_expense_included_in_rent", period) - & spm_unit.household("tenant_pays_utilities", period) & (add(spm_unit, period, ["rent"]) > 0) & (spm_unit("pre_subsidy_electricity_expense", period) > 0) ) diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py index f07b693e15f..36ed4c7a6f3 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py @@ -1,4 +1,5 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg from policyengine_us.variables.gov.states.ga.dfcs.liheap.eligibility._ga_liheap_housing import ( ga_liheap_has_direct_payment_route, ga_liheap_has_electric_vendor_route, @@ -14,22 +15,37 @@ class ga_liheap_eligible(Variable): reference = ( # PDF pages 5, 8-9, 30 "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5", - # PDF pages 3-4, 50, 56-57, 75, 89 + # PDF pages 3-4, 50, 56-57, 75, 82, 89 "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=3", + "https://pamms.dhs.ga.gov/dfcs/_exports/liheap.pdf#page=4", + "https://www.law.cornell.edu/uscode/text/42/8624", ) def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.ga.dfcs.liheap.eligibility + p_hhs = parameters(period).gov.hhs.liheap size = spm_unit("ga_liheap_household_size", period) income = spm_unit("ga_liheap_income", period) limit = spm_unit("ga_liheap_income_limit", period) + # 42 U.S.C. 8624(b)(2)(B) bars excluding a household on income below 110% + # of the poverty guideline. The 60% SMI limit falls below that floor from + # household size 16 in FY2025 and FY2026; the limit keeps the printed table. + state_group = spm_unit.household("state_group_str", period) + guideline = fpg(size, state_group, period, parameters, year_lag=p.fpg_year_lag) + income_eligible = (income <= limit) | (income < p_hhs.fpg_floor * guideline) heating_type = spm_unit("heating_type", period) heating_expense = spm_unit("heating_expense", period) # Explicit heating responsibility can qualify with no current bill. # A positive primary bill also establishes burden with heat in rent, - # including subsidized housing. Heat-in-rent renters also qualify when - # billed directly for electric, subsidized or not (manual pages 4 and - # 89), or when unsubsidized with all utilities in rent (page 89). The - # rent flag alone is insufficient. + # including subsidized housing (PAMMS chapter 100: "a vendor + # bill/statement for the main heating source"). Heat-in-rent renters + # also qualify when billed directly for electric, subsidized or not + # (manual pages 4 and 89), or when unsubsidized with all utilities in + # rent (page 89). The rent flag alone is insufficient. + # FY2026 plan item 2.3 (page 8) answers No to additional or differing + # eligibility policies for subsidized renters and renters with utilities in + # rent, while the manual applies energy-burden rules to both (pages 4, 57, + # 82 and 89); this follows the manual. energy_burden = ( spm_unit("has_heating_expense", period) | (heating_expense > 0) @@ -46,7 +62,7 @@ def formula(spm_unit, period, parameters): # see the manual conflict noted in ga_liheap_household_size. return ( (size > 0) - & (income <= limit) + & income_eligible & energy_burden & (heating_type != heating_type.possible_values.NONE) ) diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py index 3dcc20ff90f..a944a126407 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py @@ -7,8 +7,12 @@ class ga_liheap_household_size(Variable): definition_period = YEAR label = "Georgia LIHEAP household size" defined_for = StateCode.GA - # PDF pages 51-52, 62 - reference = "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=51" + reference = ( + # PDF pages 51-52, 62 + "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=51", + "https://pamms.dhs.ga.gov/dfcs/_exports/liheap.pdf#page=4", + "https://dfcs.georgia.gov/services/low-income-home-energy-assistance-program-liheap", + ) # Lawful immigrants count in the size (manual 101.2 page 3, 1100.3 page # 50, 1100.5 a page 51); undocumented members do not (1100.4 page 51: "Do @@ -17,6 +21,9 @@ class ga_liheap_household_size(Variable): # household members are non-U.S. citizens" as ineligible, and the glossary # (page 76) counts non-citizen members' income "but do not include in the # household size". This follows the specific eligibility sections. + # The current DFCS PAMMS chapter 100 requires households to "Be US citizens + # or lawfully admitted immigrant", and the DFCS LIHEAP page says "The + # applicant needs to be a U.S. citizen or legally admitted immigrant". # The SPM unit approximates the economic unit purchasing energy together. # Existing inputs do not identify all roomers, boarders, foster-child # elections, or excluded group-quarter arrangements. diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py index cd9e894a6de..76b433ef041 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/income/ga_liheap_income.py @@ -12,7 +12,7 @@ class ga_liheap_income(Variable): # The manual is followed where the plan conflicts. # PDF pages 62-69 "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=62", - # PDF pages 5-6 + # PDF pages 5-7 "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5", # Section 1.9: Social Security "Excluding MediCare deduction". "https://liheapch.acf.gov/docs/2025/state-plans/GA_Plan_2025.pdf#page=6", diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py index de550ee615b..49cc22093ca 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py @@ -15,6 +15,7 @@ class ga_liheap_matrix_amount(Variable): "https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf", "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=30", "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=89", + "https://liheapch.acf.gov/docs/2024/benefits-matricies/GA_BenefitMatrix_2024.pdf", ) def formula(spm_unit, period, parameters): @@ -45,11 +46,14 @@ def formula(spm_unit, period, parameters): base_amounts, default=0, ) - # Applicant payments use the lowest award in the income level (plan - # page 30). Heat-in-rent renters billed directly for electric, subsidized - # or not, are paid to the electric vendor (manual pages 4 and 89), so - # they keep the matrix step 2 amount for their main heating fuel and an - # unspecified fuel pays 0. + # Applicant payments use the lowest award in the income level (plan page 30). + # The uplift is the same for every fuel, so the lowest Level II amount plus + # the uplift is the lowest Level I amount. + # Heat-in-rent renters billed directly for electric, subsidized or not, are + # paid to the electric vendor (manual pages 4 and 89). The FY2024 matrix note + # says "The main fuel type is not always where the benefit is applied", so + # they keep the step 2 amount for their main heating fuel and an unspecified + # fuel pays 0. direct_payment = ga_liheap_has_direct_payment_route(spm_unit, period) base = where( direct_payment & (heating_type != fuel.NONE), @@ -59,5 +63,6 @@ def formula(spm_unit, period, parameters): # This is an ungated schedule amount with no cap at the reported bill. # Ordinary vendor payments for unpriced kerosene, coal, solar, other, # and unspecified fuels return an incomplete zero estimate; this is - # not a legal ineligibility finding. + # not a legal ineligibility finding. The unspecified default is + # tracked in #9754. return where(base > 0, base + lower_income * p.lower_income_uplift, 0) From ad147608f785c95dda897a61547772d2c218d9e4 Mon Sep 17 00:00:00 2001 From: Ziming Date: Mon, 19 Oct 2026 17:01:49 -0400 Subject: [PATCH 11/14] Address Pavel's review on Georgia LIHEAP Co-Authored-By: Claude Opus 5.5 --- changelog.d/ga-liheap.added.md | 2 +- .../parameters/gov/hhs/liheap/fpg_floor.yaml | 2 +- .../dfcs/liheap/eligibility/fpg_year_lag.yaml | 11 ++- .../ga/dfcs/liheap/eligibility/smi_rate.yaml | 2 +- .../ga/dfcs/liheap/payment/base_amount.yaml | 10 +- .../liheap/payment/lower_income_uplift.yaml | 2 +- .../liheap/payment/maximum_table_size.yaml | 2 +- .../eligibility/ga_liheap_eligible.yaml | 96 +++++++++++++++++++ .../gov/states/ga/dfcs/liheap/ga_liheap.yaml | 75 ++++++++------- .../liheap/eligibility/_ga_liheap_housing.py | 26 +++-- .../liheap/eligibility/ga_liheap_eligible.py | 18 ++-- .../dfcs/liheap/ga_liheap_household_size.py | 1 + .../liheap/payment/ga_liheap_matrix_amount.py | 18 ++-- 13 files changed, 198 insertions(+), 67 deletions(-) diff --git a/changelog.d/ga-liheap.added.md b/changelog.d/ga-liheap.added.md index f31013ef95a..0a0aa56a3d1 100644 --- a/changelog.d/ga-liheap.added.md +++ b/changelog.d/ga-liheap.added.md @@ -1 +1 @@ -Add Georgia LIHEAP household, income, ordinary eligibility, and regular heating benefits, with documented categorical and payment-table coverage limits. +Add Georgia LIHEAP household, income, ordinary eligibility, and regular heating benefits, with the federal 110% poverty guideline floor (gov.hhs.liheap.fpg_floor) and documented categorical and payment-table coverage limits. diff --git a/policyengine_us/parameters/gov/hhs/liheap/fpg_floor.yaml b/policyengine_us/parameters/gov/hhs/liheap/fpg_floor.yaml index ae7cb296211..aeb7f8c5b07 100644 --- a/policyengine_us/parameters/gov/hhs/liheap/fpg_floor.yaml +++ b/policyengine_us/parameters/gov/hhs/liheap/fpg_floor.yaml @@ -8,4 +8,4 @@ metadata: label: LIHEAP poverty guideline income floor reference: - title: 42 U.S.C. 8624(b)(2)(B) - href: https://www.law.cornell.edu/uscode/text/42/8624 + href: https://www.law.cornell.edu/uscode/text/42/8624#b_2_B diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/fpg_year_lag.yaml index bafbd3f84f7..dde06d5e4d1 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/fpg_year_lag.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/fpg_year_lag.yaml @@ -1,7 +1,8 @@ description: Georgia uses the poverty guidelines from this many years before the model year for the federal income floor under the Low Income Home Energy Assistance Program. # HHS makes each January's guidelines mandatory for LIHEAP from the next -# October 1, so model period 2026 (FY2026) uses the 2025 guidelines and 2025 -# (FY2025) the 2024 guidelines. Georgia's own limit uses state median income. +# October 1, so model period 2025 (FY2025) uses the 2024 guidelines, 2026 +# (FY2026) the 2025 guidelines and the carried-forward 2027 estimate the 2026 +# guidelines. Georgia's own limit uses state median income. values: 2024-10-01: 1 metadata: @@ -9,7 +10,9 @@ metadata: period: year label: Georgia LIHEAP poverty guideline year lag reference: - - title: LIHEAP IM 2025-02, Attachment 2, poverty guidelines for mandatory use in FY26 - href: https://acf.gov/sites/default/files/documents/ocs/COMM_LIHEAP_IM2025-02_FPGSte-Table_Att2.pdf#page=1 - title: LIHEAP IM 2024-02, Attachment 2, poverty guidelines for mandatory use in FY25 href: https://acf.gov/sites/default/files/documents/ocs/COMM_LIHEAP_IM%202024-02_Att2FPGTables_States.pdf#page=1 + - title: LIHEAP IM 2025-02, Attachment 2, poverty guidelines for mandatory use in FY26 + href: https://acf.gov/sites/default/files/documents/ocs/COMM_LIHEAP_IM2025-02_FPGSte-Table_Att2.pdf#page=1 + - title: LIHEAP IM 2026-01, poverty guidelines for optional use in FY26 and mandatory use in FY27 + href: https://acf.gov/ocs/policy-guidance/liheap-im2026-01-federal-poverty-guidelines-fpg-and-state-median-income-smi diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml index 532a39b82e3..5eceb588eab 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/eligibility/smi_rate.yaml @@ -1,6 +1,6 @@ description: Georgia limits annual income to this fraction of state median income under the Low Income Home Energy Assistance Program. values: - 2025-10-01: 0.6 + 2024-10-01: 0.6 metadata: unit: /1 period: year diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/base_amount.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/base_amount.yaml index 843e4b57581..ba23ba15fe9 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/base_amount.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/base_amount.yaml @@ -1,14 +1,14 @@ description: Georgia provides this Level II heating payment by primary fuel under the Low Income Home Energy Assistance Program. electricity: - 2025-10-01: 400 + 2024-10-01: 400 natural_gas: - 2025-10-01: 560 + 2024-10-01: 560 fuel_oil: - 2025-10-01: 710 + 2024-10-01: 710 propane: - 2025-10-01: 580 + 2024-10-01: 580 wood: - 2025-10-01: 400 + 2024-10-01: 400 metadata: unit: currency-USD period: year diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_uplift.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_uplift.yaml index 80e4d8d2244..e4654fb95fd 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_uplift.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/lower_income_uplift.yaml @@ -3,7 +3,7 @@ description: Georgia provides this additional heating payment to households in i # $100 on the FY2025 and FY2026 matrices; the matrices print both columns, # not this rule. Encode the printed Level I column if a matrix breaks the pattern. values: - 2025-10-01: 100 + 2024-10-01: 100 metadata: unit: currency-USD period: year diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/maximum_table_size.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/maximum_table_size.yaml index 38914c5ee48..a842f6ceffa 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/maximum_table_size.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/payment/maximum_table_size.yaml @@ -1,6 +1,6 @@ description: Georgia sets Level I income thresholds through this household size under the Low Income Home Energy Assistance Program. values: - 2025-10-01: 16 + 2024-10-01: 16 metadata: unit: person period: year diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml index e3d9a8755f5..c1c575d2cb6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.yaml @@ -202,3 +202,99 @@ output: ga_liheap_eligible: [true, false] ga_liheap: [660, 0] + +# Threshold edge at the federal poverty floor, FY2026. Income is entered directly to pin +# the boundary; it is not a household estimate. 42 U.S.C. 8624(b)(2)(B) protects income +# "less than 110 percent" of the poverty level, and FY2026 uses the 2025 guidelines +# (LIHEAP IM 2025-02, Attachment 2: "mandatory use in FY26"). +# Size 16: the FY2026 matrix prints a $107,634 limit. Floor: 1.1 x ($15,650 + 15 x +# $5,500) = 1.1 x $98,150 = $107,965 (the IM table: $83,765 + 4 x $6,050). In float64 +# 1.1 x 98,150 is 107,965.00000000001, so the floor is rounded to the cent. +# $107,634 is at the inclusive limit: eligible. $107,964.99 is below the floor: eligible. +# $107,965 equals the floor and is not less than it: ineligible. $107,966: ineligible. +# Size 17: limit floor($66,441 x 1.65 = $109,627.65) = $109,627; floor 1.1 x ($15,650 + +# 16 x $5,500) = $114,015. $115,000 exceeds both: ineligible. With the 2026 guidelines +# (no lag) the floor would be 1.1 x ($15,960 + 16 x $5,680) = $117,524 and $115,000 +# would qualify. +- name: Case 10, threshold edge at the FY2026 federal poverty floor with the lagged guideline. + period: 2026 + input: + state_code: [GA, GA, GA, GA, GA] + ga_liheap_household_size: [16, 16, 16, 16, 17] + ga_liheap_income: [107_634, 107_964.99, 107_965, 107_966, 115_000] + heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY] + pre_subsidy_electricity_expense: [1_200, 1_200, 1_200, 1_200, 1_200] + output: + ga_liheap_income_limit: [107_634, 107_634, 107_634, 107_634, 109_627] + ga_liheap_eligible: [true, true, false, false, false] + +# Threshold edge at the federal poverty floor, FY2025. Income is entered directly to pin +# the boundary. FY2025 uses the 2024 guidelines (LIHEAP IM 2024-02, Attachment 2: +# "mandatory use in FY25 LIHEAP"). +# Size 16: the FY2025 matrix prints a $102,926 limit. Floor: 1.1 x ($15,060 + 15 x +# $5,380) = 1.1 x $95,760 = $105,336 (the IM table: $81,664 + 4 x $5,918). +# $104,000 is above the limit and below the floor: eligible. $105,335.99: eligible. +# $105,336 equals the floor: ineligible. $106,000: ineligible; with the 2025 guidelines +# (no lag) the floor would be 1.1 x $98,150 = $107,965 and $106,000 would qualify. +- name: Case 11, threshold edge at the FY2025 federal poverty floor with the 2024 guidelines. + period: 2025 + input: + state_code: [GA, GA, GA, GA] + ga_liheap_household_size: [16, 16, 16, 16] + ga_liheap_income: [104_000, 105_335.99, 105_336, 106_000] + heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY] + pre_subsidy_electricity_expense: [1_200, 1_200, 1_200, 1_200] + output: + ga_liheap_income_limit: [102_926, 102_926, 102_926, 102_926] + ga_liheap_eligible: [true, true, false, false] + +# The federal poverty floor uses Georgia's household size. Manual 1100.4 (page 51): +# "Count income of all undocumented adults. Do not include the undocumented adults in +# the household size." This is the ga_liheap.yaml Case 22 household with person4 +# undocumented: 17 SPM members, Georgia size 16. Wages are Georgia OEWS May 2025 +# medians (two cashiers at $29,070, two fast food and counter workers at $26,920), all +# counted: 2 x $29,070 + 2 x $26,920 = $111,980. +# FY2026 size 16: limit $107,634 (matrix); floor 1.1 x $98,150 = $107,965. +# $111,980 exceeds both, so the household is ineligible. At the SPM size of 17 the floor +# would be 1.1 x $103,650 = $114,015 and the household would qualify. +- name: Case 12, the federal poverty floor uses the Georgia household size rather than the SPM unit size. + period: 2026 + input: + people: + person1: {age: 45, immigration_status: CITIZEN, employment_income: 29_070, ssi: 0} + person2: {age: 43, immigration_status: CITIZEN, employment_income: 29_070, ssi: 0} + person3: {age: 41, immigration_status: CITIZEN, employment_income: 26_920, ssi: 0} + person4: {age: 39, immigration_status: UNDOCUMENTED, employment_income: 26_920, ssi: 0} + person5: {age: 17, immigration_status: CITIZEN, ssi: 0} + person6: {age: 16, immigration_status: CITIZEN, ssi: 0} + person7: {age: 15, immigration_status: CITIZEN, ssi: 0} + person8: {age: 14, immigration_status: CITIZEN, ssi: 0} + person9: {age: 13, immigration_status: CITIZEN, ssi: 0} + person10: {age: 12, immigration_status: CITIZEN, ssi: 0} + person11: {age: 11, immigration_status: CITIZEN, ssi: 0} + person12: {age: 10, immigration_status: CITIZEN, ssi: 0} + person13: {age: 9, immigration_status: CITIZEN, ssi: 0} + person14: {age: 7, immigration_status: CITIZEN, ssi: 0} + person15: {age: 5, immigration_status: CITIZEN, ssi: 0} + person16: {age: 3, immigration_status: CITIZEN, ssi: 0} + person17: {age: 1, immigration_status: CITIZEN, ssi: 0} + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8, + person9, person10, person11, person12, person13, person14, person15, person16, + person17] + ga_tanf: 0 + heating_type: NATURAL_GAS + gas_expense: 1_800 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8, + person9, person10, person11, person12, person13, person14, person15, person16, + person17] + state_code: GA + output: + spm_unit_size: 17 + ga_liheap_household_size: 16 + ga_liheap_income: 111_980 + ga_liheap_income_limit: 107_634 + ga_liheap_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml index 7f6295d44b8..635c2b7264b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml @@ -600,35 +600,44 @@ ga_liheap_matrix_amount: [660, 560, 400, 0, 660, 660] ga_liheap: [660, 560, 400, 0, 660, 0] -# Manual page 89 and FY2026 plan page 30: when unsubsidized rent includes all -# utilities, the tenant pays no utility vendor and gets the lowest award in the -# income level, even if an electric bill is also reported. Ordinary natural gas would -# pay $660 or $560; the lowest award is min($400, $560, $710, $580, $400) = $400, -# plus $100 in Level I. Rent is $9,600 ($800 a month). -# Household 1: $12,000 wages, Level I: $500. Household 2: $24,000 wages, Level II: $400. -- name: Case 17, rent including all utilities keeps the lowest award when an electric bill is reported. +# Manual page 89: row 2, "Utility costs (heat and electric) included in rent +# (non-subsidized)", is "Direct pay to Applicant (999)"; row 3, "Heat costs included in +# rent and applicant is billed directly for electric (non-subsidized)", is "Paid to +# electric vendor because gas usage is indeterminate". The $1,200 electric bill shows +# electric is not in the rent, so the household takes row 3 although +# tenant_pays_utilities is false, the same rule eligibility Case 9 applies to a +# subsidized renter (page 57: ineligible only if it "cannot verify an energy burden"). +# The award is the FY2026 matrix step 2 amount for the main heating fuel, not the plan +# page 30 lowest award. Rent is $9,600 ($800 a month); the size 1 Level I cutoff is +# $13,589. +# Household 1: $12,000 wages, Level I natural gas: $560 + $100 = $660. +# Household 2: $24,000 wages, Level II natural gas: $560. +# Household 3: UNSPECIFIED fuel, $12,000 wages: eligible, but step 2 has no amount for an +# unspecified main fuel, so $0, as in Case 16, household 4. Without the electric bill the +# all-utilities route would pay $500 (Case 14). +- name: Case 17, a reported electric bill takes the electric vendor route when rent is flagged as including all utilities. period: 2026 input: - state_code: [GA, GA] - age: [40, 40] - immigration_status: [CITIZEN, CITIZEN] - employment_income: [12_000, 24_000] - ssi: [0, 0] - ga_tanf: [0, 0] - rent: [9_600, 9_600] - heating_type: [NATURAL_GAS, NATURAL_GAS] - heat_expense_included_in_rent: [true, true] - tenant_pays_utilities: [false, false] - receives_housing_assistance: [false, false] - is_in_public_housing: [false, false] - gas_expense: [0, 0] - pre_subsidy_electricity_expense: [1_200, 1_200] + state_code: [GA, GA, GA] + age: [40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN] + employment_income: [12_000, 24_000, 12_000] + ssi: [0, 0, 0] + ga_tanf: [0, 0, 0] + rent: [9_600, 9_600, 9_600] + heating_type: [NATURAL_GAS, NATURAL_GAS, UNSPECIFIED] + heat_expense_included_in_rent: [true, true, true] + tenant_pays_utilities: [false, false, false] + receives_housing_assistance: [false, false, false] + is_in_public_housing: [false, false, false] + gas_expense: [0, 0, 0] + pre_subsidy_electricity_expense: [1_200, 1_200, 1_200] output: - ga_liheap_income: [12_000, 24_000] - heating_expense: [0, 0] - ga_liheap_eligible: [true, true] - ga_liheap_matrix_amount: [500, 400] - ga_liheap: [500, 400] + ga_liheap_income: [12_000, 24_000, 12_000] + heating_expense: [0, 0, 0] + ga_liheap_eligible: [true, true, true] + ga_liheap_matrix_amount: [660, 560, 0] + ga_liheap: [660, 560, 0] # Manual section 1200.7 (page 66) counts lottery winnings. Countable income is # $10,000 wages + $2,000 gambling winnings = $12,000. That is at or below the size 1 @@ -704,8 +713,9 @@ # Manual section 1200.7 (page 67) and FY2026 plan item 1.9 (page 6) count # unemployment compensation. The adult earned $12,000 before a layoff and drew -# Georgia's maximum weekly benefit, $365, for the maximum of 14 weeks: +# Georgia's maximum weekly benefit, $365, up to the 14 x WBA maximum benefit amount: # 14 x $365 = $5,110 (USDOL Significant Provisions of State UI Laws, January 2026, +# https://oui.doleta.gov/unemploy/content/sigpros/2020-2029/January2026.pdf#page=2, # Georgia row: the weekly benefit is 1/42 of wages in the two highest base-period # quarters, maximum $365; the maximum benefit is the "Lesser of 14 x WBA or 1/4 BPW"). # The $365 maximum needs $15,330 in those two quarters ($365 x 42), about a $30,700 @@ -742,9 +752,10 @@ ga_liheap: 400 # 42 U.S.C. 8624(b)(2)(B) bars excluding a household on income below 110% of the -# poverty guideline. Four adults earn Georgia median annual wages (BLS OEWS May 2025): -# two cashiers at $29,070 and two fast food and counter workers at $26,920, so -# countable income is 2 x $29,070 + 2 x $26,920 = $58,140 + $53,840 = $111,980. +# poverty guideline. Four adults earn Georgia median annual wages (BLS OEWS May 2025, +# https://data.bls.gov/oes/#/area/1300000/2025): two cashiers at $29,070 and two fast +# food and counter workers at $26,920, so countable income is 2 x $29,070 + 2 x +# $26,920 = $58,140 + $53,840 = $111,980. # The 13 children have no income, so the household size is 17. # Printed-method limit: floor($66,441 x 1.65 = $109,627.65) = $109,627, where # $66,441 = floor($110,736 x 0.6); the income exceeds it. @@ -754,8 +765,8 @@ # $111,980 is below $114,015, so the household stays eligible. # $111,980 is above the size 16 Level I cutoff of $42,336, so Level II natural gas # pays $560. -# Size 16 is not tested: its FY2026 floor exceeds the printed limit by only $331 -# ($107,634 to $107,965). +# Size 16 sits in a $331 window ($107,634 to $107,965); eligibility Cases 10 and 11 +# pin it at the exact floor with income entered directly. - name: Case 22, the federal poverty floor keeps a large household above the state median income limit eligible. period: 2026 input: diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py index 963c7081bba..7ae4788d98f 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py @@ -5,7 +5,10 @@ def ga_liheap_has_direct_payment_route(spm_unit, period): """Identify unsubsidized rent that includes all utilities. Georgia's manual energy-burden table (physical page 89) permits payment - directly to these renters. The rental agreement is assumed verified. + directly to these renters. The rental agreement is assumed verified. A + renter who also reports an electric bill is paid on the electric-vendor + route instead (page 89, third row); ga_liheap_matrix_amount applies that + precedence. https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=89 """ return ( @@ -24,17 +27,22 @@ def ga_liheap_has_electric_vendor_route(spm_unit, period): unsubsidized renters with heat in rent and their own electric bill an energy burden, "Paid to electric vendor because gas usage is indeterminate". Page 4 makes subsidized renters eligible when "their - utility bill is in their name", so this route ignores subsidy status and + utility bill is in their name", and page 57 (1100.14) bars subsidized + renters whose rent includes utilities only when they "cannot verify an + energy burden". This route therefore ignores subsidy status and tenant_pays_utilities: a positive electric bill is the utility-bill documentation. Page 82's ineligibility when "the rent includes utilities" is read as rent covering every utility, which a separate electric bill - rules out. Unsubsidized renters whose rent includes all utilities take - the direct-payment route's lowest award instead. The FY2024 matrix note - ("The main fuel type is not always where the benefit is applied") pays - the main-fuel amount when the benefit goes to the electric bill. The - pre-subsidy bill matches heating_expense and avoids the - program-dependent subsidies in electricity_expense. - Manual pages 4, 82 and 89: + rules out. The same reading applies to unsubsidized renters, so this + route takes precedence over the all-utilities direct payment, which keeps + renters with no separate bill. The FY2024 matrix note ("The main fuel + type is not always where the benefit is applied") pays the main-fuel + amount when the benefit goes to the electric bill. The FY2025 and FY2026 + matrices keep the asterisk on "main heating fuel type*" but print no + note, so the FY2024 note is carried forward. The pre-subsidy bill matches + heating_expense and avoids the program-dependent subsidies in + electricity_expense. + Manual pages 4, 57, 82 and 89: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=4 FY2024 matrix: https://liheapch.acf.gov/docs/2024/benefits-matricies/GA_BenefitMatrix_2024.pdf diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py index 36ed4c7a6f3..9e99936c04c 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py @@ -13,12 +13,14 @@ class ga_liheap_eligible(Variable): label = "Georgia LIHEAP ordinary eligibility" defined_for = StateCode.GA reference = ( - # PDF pages 5, 8-9, 30 + # PDF pages 5, 8-9, 30, 43 "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5", + "https://liheapch.acf.gov/docs/2025/state-plans/GA_Plan_2025.pdf#page=43", # PDF pages 3-4, 50, 56-57, 75, 82, 89 "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=3", + # PAMMS chapter 100, export dated 2026-10-16. "https://pamms.dhs.ga.gov/dfcs/_exports/liheap.pdf#page=4", - "https://www.law.cornell.edu/uscode/text/42/8624", + "https://www.law.cornell.edu/uscode/text/42/8624#b_2_B", ) def formula(spm_unit, period, parameters): @@ -29,10 +31,14 @@ def formula(spm_unit, period, parameters): limit = spm_unit("ga_liheap_income_limit", period) # 42 U.S.C. 8624(b)(2)(B) bars excluding a household on income below 110% # of the poverty guideline. The 60% SMI limit falls below that floor from - # household size 16 in FY2025 and FY2026; the limit keeps the printed table. + # household size 16 in FY2025 and FY2026. The printed matrices do too: size + # 16 prints $102,926 in FY2025 (floor $105,336) and $107,634 in FY2026 + # (floor $107,965). Georgia assures the floor (FY2025 and FY2026 plans, + # page 43), so the floor governs; the limit keeps the printed table. state_group = spm_unit.household("state_group_str", period) guideline = fpg(size, state_group, period, parameters, year_lag=p.fpg_year_lag) - income_eligible = (income <= limit) | (income < p_hhs.fpg_floor * guideline) + floor = np.round(guideline * p_hhs.fpg_floor, 2) + income_eligible = (income <= limit) | (income < floor) heating_type = spm_unit("heating_type", period) heating_expense = spm_unit("heating_expense", period) # Explicit heating responsibility can qualify with no current bill. @@ -40,8 +46,8 @@ def formula(spm_unit, period, parameters): # including subsidized housing (PAMMS chapter 100: "a vendor # bill/statement for the main heating source"). Heat-in-rent renters # also qualify when billed directly for electric, subsidized or not - # (manual pages 4 and 89), or when unsubsidized with all utilities in - # rent (page 89). The rent flag alone is insufficient. + # (manual pages 4, 57 and 89), or when unsubsidized with all utilities + # in rent (page 89). The rent flag alone is insufficient. # FY2026 plan item 2.3 (page 8) answers No to additional or differing # eligibility policies for subsidized renters and renters with utilities in # rent, while the manual applies energy-burden rules to both (pages 4, 57, diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py index a944a126407..3d2ec0cf69f 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py @@ -10,6 +10,7 @@ class ga_liheap_household_size(Variable): reference = ( # PDF pages 51-52, 62 "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=51", + # PAMMS chapter 100, export dated 2026-10-16. "https://pamms.dhs.ga.gov/dfcs/_exports/liheap.pdf#page=4", "https://dfcs.georgia.gov/services/low-income-home-energy-assistance-program-liheap", ) diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py index 49cc22093ca..5970dc27594 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py @@ -1,6 +1,7 @@ from policyengine_us.model_api import * from policyengine_us.variables.gov.states.ga.dfcs.liheap.eligibility._ga_liheap_housing import ( ga_liheap_has_direct_payment_route, + ga_liheap_has_electric_vendor_route, ) @@ -13,6 +14,7 @@ class ga_liheap_matrix_amount(Variable): defined_for = StateCode.GA reference = ( "https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf", + "https://liheapch.acf.gov/docs/2025/benefits-matricies/GA_BenefitMatrix_2025.pdf", "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=30", "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=89", "https://liheapch.acf.gov/docs/2024/benefits-matricies/GA_BenefitMatrix_2024.pdf", @@ -49,12 +51,16 @@ def formula(spm_unit, period, parameters): # Applicant payments use the lowest award in the income level (plan page 30). # The uplift is the same for every fuel, so the lowest Level II amount plus # the uplift is the lowest Level I amount. - # Heat-in-rent renters billed directly for electric, subsidized or not, are - # paid to the electric vendor (manual pages 4 and 89). The FY2024 matrix note - # says "The main fuel type is not always where the benefit is applied", so - # they keep the step 2 amount for their main heating fuel and an unspecified - # fuel pays 0. - direct_payment = ga_liheap_has_direct_payment_route(spm_unit, period) + # Heat-in-rent renters billed directly for electric are paid to the electric + # vendor (manual pages 4, 57 and 89), subsidized or not and whatever + # tenant_pays_utilities says, so that route takes precedence over the + # all-utilities direct payment. The FY2024 matrix note says "The main fuel + # type is not always where the benefit is applied", so they keep the step 2 + # amount for their main heating fuel and an unspecified fuel pays 0. + electric_vendor = ga_liheap_has_electric_vendor_route(spm_unit, period) + direct_payment = ( + ga_liheap_has_direct_payment_route(spm_unit, period) & ~electric_vendor + ) base = where( direct_payment & (heating_type != fuel.NONE), np.minimum.reduce(base_amounts), From 94a4c9c1a384103a7f16d0db981d59926cfc5445 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 20 Oct 2026 18:04:26 -0400 Subject: [PATCH 12/14] Address Pavel's round 2 review on Georgia LIHEAP Co-Authored-By: Claude Opus 5.5 --- .../ga/dfcs/liheap/income/minimum_age.yaml | 2 +- .../income/monthly_interest_disregard.yaml | 2 +- .../dfcs/liheap/income/sources/household.yaml | 4 +- .../ga/dfcs/liheap/income/sources/person.yaml | 4 +- .../gov/states/ga/dfcs/liheap/ga_liheap.yaml | 103 ++++++++++++++++-- .../payment/ga_liheap_matrix_amount.yaml | 29 +++++ .../liheap/eligibility/_ga_liheap_housing.py | 31 +++--- .../liheap/payment/ga_liheap_matrix_amount.py | 33 ++++-- 8 files changed, 170 insertions(+), 38 deletions(-) diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml index 2febda5c356..83b93bef417 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml @@ -1,6 +1,6 @@ description: Georgia excludes the 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 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml index 0684f255f85..3e7ac857573 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/monthly_interest_disregard.yaml @@ -1,6 +1,6 @@ description: Georgia excludes this amount of monthly interest income under the Low Income Home Energy Assistance Program. values: - 2025-10-01: 25 + 2024-10-01: 25 metadata: unit: currency-USD period: month diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml index c87dd3d39ff..d67354722eb 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/household.yaml @@ -1,6 +1,6 @@ description: Georgia uses these household-level benefits as countable income under the Low Income Home Energy Assistance Program. values: - 2025-10-01: + 2024-10-01: # The tanf aggregate includes ga_tanf and applies take-up; ga_tanf alone is # the entitlement and would count benefits a nonrecipient could get. - tanf @@ -9,5 +9,7 @@ metadata: period: year label: Georgia LIHEAP household income sources reference: + - title: Georgia FY2025 LIHEAP State Plan, section 1.9 (TANF counted) + href: https://liheapch.acf.gov/docs/2025/state-plans/GA_Plan_2025.pdf#page=6 - title: Georgia FY2026 LIHEAP State Plan, section 1.9 (TANF counted) href: https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml index a347a1d64c3..dbc857dff76 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml @@ -4,7 +4,7 @@ description: Georgia uses these sources as countable income of adult household m # in one source cannot offset another; net business inputs have no extra # deduction. values: - 2025-10-01: + 2024-10-01: - employment_income - self_employment_income - sstb_self_employment_income @@ -54,6 +54,8 @@ metadata: reference: - title: "Georgia LIHEAP Policy Manual, January 2022, sections 1200.1-1200.7 and application item 12 #page 62-69,83" href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=62 + - title: "Georgia FY2025 LIHEAP State Plan, section 1.9 (included and excluded sources) #page 5-7" + href: https://liheapch.acf.gov/docs/2025/state-plans/GA_Plan_2025.pdf#page=5 - title: "Georgia FY2026 LIHEAP State Plan, section 1.9 (current included and excluded sources) #page 5-7" href: https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=5 - title: Georgia FY2022 LIHEAP State Plan, section 1.9 (contemporaneous unchecked gifts, lottery and insurance categories) diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml index 635c2b7264b..67c8fa45204 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/ga_liheap.yaml @@ -603,10 +603,12 @@ # Manual page 89: row 2, "Utility costs (heat and electric) included in rent # (non-subsidized)", is "Direct pay to Applicant (999)"; row 3, "Heat costs included in # rent and applicant is billed directly for electric (non-subsidized)", is "Paid to -# electric vendor because gas usage is indeterminate". The $1,200 electric bill shows -# electric is not in the rent, so the household takes row 3 although -# tenant_pays_utilities is false, the same rule eligibility Case 9 applies to a -# subsidized renter (page 57: ineligible only if it "cannot verify an energy burden"). +# electric vendor because gas usage is indeterminate". Page 57 (1100.14) limits the +# unsubsidized all-in-rent class to rent that "includes utilities with no separate bill +# for heating/cooling costs". The $1,200 electric bill is such a separate bill, so the +# household takes row 3 although tenant_pays_utilities is false, the same rule +# eligibility Case 9 applies to a subsidized renter (page 57: ineligible only if it +# "cannot verify an energy burden"). # The award is the FY2026 matrix step 2 amount for the main heating fuel, not the plan # page 30 lowest award. Rent is $9,600 ($800 a month); the size 1 Level I cutoff is # $13,589. @@ -753,9 +755,10 @@ # 42 U.S.C. 8624(b)(2)(B) bars excluding a household on income below 110% of the # poverty guideline. Four adults earn Georgia median annual wages (BLS OEWS May 2025, -# https://data.bls.gov/oes/#/area/1300000/2025): two cashiers at $29,070 and two fast -# food and counter workers at $26,920, so countable income is 2 x $29,070 + 2 x -# $26,920 = $58,140 + $53,840 = $111,980. +# https://data.bls.gov/oes/#/area/1300000/2025): two cashiers at $29,070 (series +# OEUS130000000000041201113) and two fast food and counter workers at $26,920 (series +# OEUS130000000000035302313), so countable income is 2 x $29,070 + 2 x $26,920 = +# $58,140 + $53,840 = $111,980. # The 13 children have no income, so the household size is 17. # Printed-method limit: floor($66,441 x 1.65 = $109,627.65) = $109,627, where # $66,441 = floor($110,736 x 0.6); the income exceeds it. @@ -765,8 +768,10 @@ # $111,980 is below $114,015, so the household stays eligible. # $111,980 is above the size 16 Level I cutoff of $42,336, so Level II natural gas # pays $560. -# Size 16 sits in a $331 window ($107,634 to $107,965); eligibility Cases 10 and 11 -# pin it at the exact floor with income entered directly. +# At size 16 the floor admits income above the printed limit in a $331 window in +# FY2026 ($107,634 to $107,965) and a $2,410 window in FY2025 ($102,926 to $105,336); +# eligibility Cases 10 and 11 pin each window at the exact floor with income entered +# directly. - name: Case 22, the federal poverty floor keeps a large household above the state median income limit eligible. period: 2026 input: @@ -810,3 +815,83 @@ ga_liheap_matrix_amount: 560 ga_liheap_eligible: true ga_liheap: 560 + +# Manual page 57 (1100.14) limits the unsubsidized all-in-rent class to rent that +# "includes utilities with no separate bill for heating/cooling costs", and page 89, +# row 1, pays an applicant who "pays a vendor directly for utilities" to the vendor. A +# $1,200 a year ($100 a month) bill for the main heating fuel is such a separate bill: +# it establishes the energy burden and takes the vendor payment, the FY2026 matrix +# step 2 amount for the main heating fuel, although tenant_pays_utilities is false. +# The award is the same with or without a housing subsidy. Unsubsidized rent is $9,600 +# ($800 a month); the subsidized tenant share is $3,000 ($250 a month). The size 1 +# limit is $34,549 and the Level I cutoff $13,589. +# Household 1: unsubsidized, natural gas, $12,000 wages, Level I: $560 + $100 = $660. +# Household 2: subsidized, natural gas, $12,000 wages, Level I: $660. +# Household 3: unsubsidized, natural gas, $24,000 wages, Level II: $560. +# Household 4: unsubsidized, fuel oil, $12,000 wages, Level I: $710 + $100 = $810. +# Household 5: unsubsidized, propane, $12,000 wages, Level I: $580 + $100 = $680. +# Without the fuel bill, households 1, 4 and 5 would take the direct payment at $500 +# and household 3 at $400 (Cases 9 and 14). +- name: Case 23, a primary fuel bill takes the vendor route when rent is flagged as including all utilities. + period: 2026 + input: + state_code: [GA, GA, GA, GA, GA] + age: [40, 40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN] + employment_income: [12_000, 12_000, 24_000, 12_000, 12_000] + ssi: [0, 0, 0, 0, 0] + ga_tanf: [0, 0, 0, 0, 0] + rent: [9_600, 3_000, 9_600, 9_600, 9_600] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, FUEL_OIL, PROPANE] + heat_expense_included_in_rent: [true, true, true, true, true] + tenant_pays_utilities: [false, false, false, false, false] + receives_housing_assistance: [false, true, false, false, false] + is_in_public_housing: [false, false, false, false, false] + gas_expense: [1_200, 1_200, 1_200, 0, 0] + fuel_oil_expense: [0, 0, 0, 1_200, 0] + bottled_gas_expense: [0, 0, 0, 0, 1_200] + pre_subsidy_electricity_expense: [0, 0, 0, 0, 0] + output: + ga_liheap_income: [12_000, 12_000, 24_000, 12_000, 12_000] + heating_expense: [1_200, 1_200, 1_200, 1_200, 1_200] + ga_liheap_eligible: [true, true, true, true, true] + ga_liheap_matrix_amount: [660, 660, 560, 810, 680] + ga_liheap: [660, 660, 560, 810, 680] + +# FY2026 matrix step 2 prices electricity, natural gas, fuel oil, propane and wood +# only. The direct payment to an unsubsidized renter whose rent "includes utilities +# with no separate bill for heating/cooling costs" (manual page 57; page 89, row 2) is +# the plan page 30 lowest award and does not depend on the fuel. A separate bill takes +# a vendor payment at the main heating fuel's step 2 amount, and the matrix has none +# for kerosene, so the model returns $0, an incomplete estimate rather than an +# ineligibility finding. Each household is one adult with $12,000 of wages, kerosene +# heat in rent and unsubsidized rent of $9,600 ($800 a month). The size 1 limit is +# $34,549 and the Level I cutoff $13,589, so all four are eligible. +# Household 1: no separate bill: lowest Level II award min($400, $560, $710, $580, +# $400) = $400, plus $100 in Level I: $500. +# Household 2: a $1,200 kerosene bill (fuel_oil_expense, which heating_expense reads +# for kerosene) with tenant_pays_utilities false: $0. +# Household 3: the same bill with tenant_pays_utilities true: $0. +# Household 4: a $1,200 electric bill (page 89, row 3, the electric-vendor route): $0. +- name: Case 24, an unpriced main fuel receives the lowest award only with no separate bill. + period: 2026 + input: + state_code: [GA, GA, GA, GA] + age: [40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN] + employment_income: [12_000, 12_000, 12_000, 12_000] + ssi: [0, 0, 0, 0] + ga_tanf: [0, 0, 0, 0] + rent: [9_600, 9_600, 9_600, 9_600] + heating_type: [KEROSENE, KEROSENE, KEROSENE, KEROSENE] + heat_expense_included_in_rent: [true, true, true, true] + tenant_pays_utilities: [false, false, true, false] + receives_housing_assistance: [false, false, false, false] + is_in_public_housing: [false, false, false, false] + fuel_oil_expense: [0, 1_200, 1_200, 0] + pre_subsidy_electricity_expense: [0, 0, 0, 1_200] + output: + heating_expense: [0, 1_200, 1_200, 0] + ga_liheap_eligible: [true, true, true, true] + ga_liheap_matrix_amount: [500, 0, 0, 0] + ga_liheap: [500, 0, 0, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml index 13d335d7f77..36420c5bcaf 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml @@ -113,3 +113,32 @@ ga_liheap_income: [42_336, 42_336.01, 42_336] output: ga_liheap_matrix_amount: [660, 560, 660] + +# Manual page 57 (1100.14) limits the direct applicant payment to rent that "includes +# utilities with no separate bill for heating/cooling costs". Each household is size 1 +# with $13,589 of income, the inclusive FY2026 Level I top, and has heat in +# unsubsidized rent of $12,000 with tenant_pays_utilities false: the Case 5 element 1 +# household, which receives the $500 lowest award with no separate bill. +# Element 1: natural gas with a $1,200 electric bill (page 89, row 3): Level I natural +# gas, $560 + $100 = $660. +# Element 2: natural gas with a $1,200 gas bill (page 89, row 1): $660. +# Element 3: kerosene with a $1,200 kerosene bill (fuel_oil_expense): step 2 has no +# kerosene amount, so $0. +- name: Case 8, a separate electric or main fuel bill overrides the direct rental payment. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [GA, GA, GA] + heating_type: [NATURAL_GAS, NATURAL_GAS, KEROSENE] + ga_liheap_household_size: [1, 1, 1] + ga_liheap_income: [13_589, 13_589, 13_589] + rent: [12_000, 12_000, 12_000] + heat_expense_included_in_rent: [true, true, true] + tenant_pays_utilities: [false, false, false] + receives_housing_assistance: [false, false, false] + is_in_public_housing: [false, false, false] + pre_subsidy_electricity_expense: [1_200, 0, 0] + gas_expense: [0, 1_200, 0] + fuel_oil_expense: [0, 0, 1_200] + output: + ga_liheap_matrix_amount: [660, 660, 0] diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py index 7ae4788d98f..facc7ae6adc 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/_ga_liheap_housing.py @@ -5,11 +5,14 @@ def ga_liheap_has_direct_payment_route(spm_unit, period): """Identify unsubsidized rent that includes all utilities. Georgia's manual energy-burden table (physical page 89) permits payment - directly to these renters. The rental agreement is assumed verified. A - renter who also reports an electric bill is paid on the electric-vendor - route instead (page 89, third row); ga_liheap_matrix_amount applies that - precedence. - https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=89 + directly to these renters. The rental agreement is assumed verified. Page + 57 (1100.14) limits this class to rent that "includes utilities with no + separate bill for heating/cooling costs", so a renter who also reports an + electric bill (page 89, third row) or a bill for the main heating fuel + (first row) is paid to the vendor instead; ga_liheap_matrix_amount applies + that precedence. + Manual pages 57 and 89: + https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=57 """ return ( spm_unit("heat_expense_included_in_rent", period) @@ -33,14 +36,16 @@ def ga_liheap_has_electric_vendor_route(spm_unit, period): tenant_pays_utilities: a positive electric bill is the utility-bill documentation. Page 82's ineligibility when "the rent includes utilities" is read as rent covering every utility, which a separate electric bill - rules out. The same reading applies to unsubsidized renters, so this - route takes precedence over the all-utilities direct payment, which keeps - renters with no separate bill. The FY2024 matrix note ("The main fuel - type is not always where the benefit is applied") pays the main-fuel - amount when the benefit goes to the electric bill. The FY2025 and FY2026 - matrices keep the asterisk on "main heating fuel type*" but print no - note, so the FY2024 note is carried forward. The pre-subsidy bill matches - heating_expense and avoids the program-dependent subsidies in + rules out. For unsubsidized renters page 57 says so directly: the + all-in-rent class is rent that "includes utilities with no separate bill + for heating/cooling costs". This route, like a bill for the main heating + fuel, therefore takes precedence over the all-utilities direct payment, + which keeps renters with no separate bill. The FY2024 matrix note ("The + main fuel type is not always where the benefit is applied") pays the + main-fuel amount when the benefit goes to the electric bill. The FY2025 + and FY2026 matrices keep the asterisk on "main heating fuel type*" but + print no note, so the FY2024 note is carried forward. The pre-subsidy bill + matches heating_expense and avoids the program-dependent subsidies in electricity_expense. Manual pages 4, 57, 82 and 89: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=4 diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py index 5970dc27594..aff1215aec0 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.py @@ -16,7 +16,8 @@ class ga_liheap_matrix_amount(Variable): "https://liheapch.acf.gov/docs/2026/benefits-matricies/GA_BenefitMatrix_Heat-Cool_2026.pdf", "https://liheapch.acf.gov/docs/2025/benefits-matricies/GA_BenefitMatrix_2025.pdf", "https://liheapch.acf.gov/docs/2026/state-plans/GA_Plan_2026.pdf#page=30", - "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=89", + # PDF pages 4, 57, 89 + "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=4", "https://liheapch.acf.gov/docs/2024/benefits-matricies/GA_BenefitMatrix_2024.pdf", ) @@ -51,15 +52,20 @@ def formula(spm_unit, period, parameters): # Applicant payments use the lowest award in the income level (plan page 30). # The uplift is the same for every fuel, so the lowest Level II amount plus # the uplift is the lowest Level I amount. - # Heat-in-rent renters billed directly for electric are paid to the electric - # vendor (manual pages 4, 57 and 89), subsidized or not and whatever - # tenant_pays_utilities says, so that route takes precedence over the - # all-utilities direct payment. The FY2024 matrix note says "The main fuel - # type is not always where the benefit is applied", so they keep the step 2 - # amount for their main heating fuel and an unspecified fuel pays 0. + # Only rent that "includes utilities with no separate bill for heating/cooling + # costs" (manual page 57) takes that direct payment (page 89, row 2). A + # separate electric bill (row 3: "Paid to electric vendor because gas usage is + # indeterminate") or main heating fuel bill (row 1: "Paid to vendor") takes a + # vendor payment instead, subsidized or not (page 4) and whatever + # tenant_pays_utilities says. The FY2024 matrix note says "The main fuel type + # is not always where the benefit is applied", so vendor payments use the step + # 2 amount for the main heating fuel. electric_vendor = ga_liheap_has_electric_vendor_route(spm_unit, period) + main_fuel_bill = spm_unit("heating_expense", period) > 0 direct_payment = ( - ga_liheap_has_direct_payment_route(spm_unit, period) & ~electric_vendor + ga_liheap_has_direct_payment_route(spm_unit, period) + & ~electric_vendor + & ~main_fuel_bill ) base = where( direct_payment & (heating_type != fuel.NONE), @@ -67,8 +73,11 @@ def formula(spm_unit, period, parameters): base, ) # This is an ungated schedule amount with no cap at the reported bill. - # Ordinary vendor payments for unpriced kerosene, coal, solar, other, - # and unspecified fuels return an incomplete zero estimate; this is - # not a legal ineligibility finding. The unspecified default is - # tracked in #9754. + # Step 2 prices no kerosene, coal, solar, other or unspecified main fuel, so + # vendor payments for them return an incomplete zero estimate, not a legal + # ineligibility finding. The direct payment does not depend on the fuel, so an + # unsubsidized renter whose rent includes all utilities and who has one of these + # fuels gets the lowest award ($500 in FY2026 Level I) with no separate bill and + # $0 once it reports an electric or main heating fuel bill. The unspecified + # default is tracked in #9754. return where(base > 0, base + lower_income * p.lower_income_uplift, 0) From 10ff85eddb1e23c4ca2a679d3911e0ec86edd02a Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 21 Oct 2026 00:43:49 -0400 Subject: [PATCH 13/14] Reword the Georgia household size note for the source-conflict check Co-Authored-By: Claude Opus 5.5 --- .../gov/states/ga/dfcs/liheap/ga_liheap_household_size.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py index 3d2ec0cf69f..f9cb54c7df3 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/ga_liheap_household_size.py @@ -18,7 +18,7 @@ class ga_liheap_household_size(Variable): # Lawful immigrants count in the size (manual 101.2 page 3, 1100.3 page # 50, 1100.5 a page 51); undocumented members do not (1100.4 page 51: "Do # not include the undocumented adults in the household size"). The manual - # conflicts with itself: 1100.14 (page 57) lists "Applicants where ALL + # gives two other answers: 1100.14 (page 57) lists "Applicants where ALL # household members are non-U.S. citizens" as ineligible, and the glossary # (page 76) counts non-citizen members' income "but do not include in the # household size". This follows the specific eligibility sections. From a9dae484665339d9e2df852bfad6b71bcba85a90 Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 21 Oct 2026 13:20:51 -0400 Subject: [PATCH 14/14] Apply round-3 wording suggestions to Georgia LIHEAP Cite FY2025 and FY2026 plan item 1.9 for the unchecked person sources and the child earnings exclusion, tighten the matrix test comment, and point the eligibility comment at the manual note. Co-Authored-By: Claude Opus 5.5 --- .../states/ga/dfcs/liheap/income/minimum_age.yaml | 2 ++ .../ga/dfcs/liheap/income/sources/person.yaml | 14 ++++++++------ .../liheap/payment/ga_liheap_matrix_amount.yaml | 11 ++++++----- .../dfcs/liheap/eligibility/ga_liheap_eligible.py | 2 +- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml index 83b93bef417..16cb1e58fda 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/minimum_age.yaml @@ -8,3 +8,5 @@ metadata: reference: - title: Georgia LIHEAP Policy Manual, January 2022, section 1200.2 href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/GA_PolicyManual_2023.pdf#page=62 + - title: Georgia FY2025 LIHEAP State Plan, section 1.9 (earned income of a child under the age of 18 unchecked) + href: https://liheapch.acf.gov/docs/2025/state-plans/GA_Plan_2025.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml index dbc857dff76..6fd055784a1 100644 --- a/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml +++ b/policyengine_us/parameters/gov/states/ga/dfcs/liheap/income/sources/person.yaml @@ -26,8 +26,8 @@ values: - veterans_benefits # Plan vs manual: manual 1200.7 includes "Disability Payment" (page 65) and # "Workers Compensation" (page 68), both paid by insurance companies, while - # FY2026 plan 1.9 (page 6) leaves "Insurance payments made directly to the - # insured" unchecked. This follows the manual. + # FY2025 and FY2026 plans 1.9 (page 6) leave "Insurance payments made directly + # to the insured" unchecked. This follows the manual. - disability_benefits - workers_compensation - strike_benefits @@ -37,13 +37,15 @@ values: - ordinary_dividend_income - capital_gains # Plan vs manual: manual 1200.7 (page 66) includes "Lottery Winnings", while - # FY2026 plan 1.9 (page 6) leaves "winnings from lotteries" unchecked. This - # follows the manual; the input also covers non-lottery gambling. + # FY2025 and FY2026 plans 1.9 (page 6) leave "winnings from lotteries" + # unchecked. This follows the manual; the input also covers non-lottery + # gambling. - gambling_winnings # Manual 1200.7 (page 65) explicitly includes "Contribution/Cash Gift". # The FY2022 plan already left gifts unchecked when this manual was issued; - # the same FY2026 checkbox does not establish a later policy change. Follow - # the detailed manual, as for lottery and insurance income above. + # the same checkbox in FY2025 and FY2026 plans 1.9 (page 6) does not + # establish a later policy change. Follow the detailed manual, as for lottery + # and insurance income above. - financial_assistance - alimony_income - child_support_received diff --git a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml index 36420c5bcaf..230fc78b0f7 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ga/dfcs/liheap/payment/ga_liheap_matrix_amount.yaml @@ -114,11 +114,12 @@ output: ga_liheap_matrix_amount: [660, 560, 660] -# Manual page 57 (1100.14) limits the direct applicant payment to rent that "includes -# utilities with no separate bill for heating/cooling costs". Each household is size 1 -# with $13,589 of income, the inclusive FY2026 Level I top, and has heat in -# unsubsidized rent of $12,000 with tenant_pays_utilities false: the Case 5 element 1 -# household, which receives the $500 lowest award with no separate bill. +# Manual page 57 (1100.14) limits the unsubsidized all-in-rent class to rent that +# "includes utilities with no separate bill for heating/cooling costs". Each household +# is size 1 with $13,589 of income, the inclusive FY2026 Level I top, and has heat in +# unsubsidized rent of $12,000 with tenant_pays_utilities false. Without a separate +# bill, elements 1 and 2 are the Case 5 element 1 household, and all three would +# receive the $500 lowest award, which does not depend on the fuel. # Element 1: natural gas with a $1,200 electric bill (page 89, row 3): Level I natural # gas, $560 + $100 = $660. # Element 2: natural gas with a $1,200 gas bill (page 89, row 1): $660. diff --git a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py index 9e99936c04c..9921403a949 100644 --- a/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/ga/dfcs/liheap/eligibility/ga_liheap_eligible.py @@ -65,7 +65,7 @@ def formula(spm_unit, period, parameters): # also need unavailable inputs; the selected heating account is assumed # to satisfy those rules. Annual expense is not an account-credit proxy. # A household of lawful immigrants only has size > 0 and qualifies; - # see the manual conflict noted in ga_liheap_household_size. + # see the manual note in ga_liheap_household_size. return ( (size > 0) & income_eligible