From 2da6d77e442362b0266432b1f68e9bd091b9db14 Mon Sep 17 00:00:00 2001 From: Ziming Date: Sat, 3 Oct 2026 19:49:20 -0400 Subject: [PATCH 1/9] Add FL regular heating LIHEAP --- changelog.d/fl-liheap.added.md | 1 + .../eligibility/categorical_programs.yaml | 15 + .../commerce/liheap/eligibility/smi_rate.yaml | 12 + .../fl/commerce/liheap/income/adult_age.yaml | 12 + .../liheap/income/nonstudent_min_age.yaml | 12 + .../liheap/income/sources/earned.yaml | 18 + .../liheap/income/sources/household.yaml | 11 + .../liheap/income/sources/unearned.yaml | 31 ++ .../commerce/liheap/payment/base_amount.yaml | 19 + .../liheap/payment/child_max_age.yaml | 10 + .../liheap/payment/elderly_min_age.yaml | 10 + .../liheap/payment/income_band_fraction.yaml | 15 + .../commerce/liheap/payment/supplement.yaml | 15 + policyengine_us/programs.yaml | 10 +- .../fl_liheap_categorically_eligible.yaml | 136 +++++ .../eligibility/fl_liheap_eligible.yaml | 303 +++++++++++ .../eligibility/fl_liheap_income_limit.yaml | 191 +++++++ .../states/fl/commerce/liheap/fl_liheap.yaml | 484 +++++++++++++++++ .../liheap/fl_liheap_household_size.yaml | 69 +++ .../income/fl_liheap_countable_income.yaml | 504 ++++++++++++++++++ .../fl_liheap_categorically_eligible.py | 22 + .../liheap/eligibility/fl_liheap_eligible.py | 42 ++ .../eligibility/fl_liheap_income_limit.py | 31 ++ .../states/fl/commerce/liheap/fl_liheap.py | 50 ++ .../liheap/fl_liheap_household_size.py | 16 + .../income/fl_liheap_countable_income.py | 46 ++ 26 files changed, 2084 insertions(+), 1 deletion(-) create mode 100644 changelog.d/fl-liheap.added.md create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/base_amount.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/child_max_age.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/elderly_min_age.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/income_band_fraction.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/supplement.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap_household_size.yaml create mode 100644 policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml create mode 100644 policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.py create mode 100644 policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py create mode 100644 policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py create mode 100644 policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py create mode 100644 policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py create mode 100644 policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py diff --git a/changelog.d/fl-liheap.added.md b/changelog.d/fl-liheap.added.md new file mode 100644 index 00000000000..3d4d9579c80 --- /dev/null +++ b/changelog.d/fl-liheap.added.md @@ -0,0 +1 @@ +Add Florida LIHEAP household, income, and categorical income eligibility components. diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml new file mode 100644 index 00000000000..41f25781228 --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml @@ -0,0 +1,15 @@ +description: Florida waives the LIHEAP income test for households receiving a positive amount from any of these programs. +values: + 2025-10-01: + - snap + - fl_tca + - ssi +metadata: + unit: list + period: year + label: Florida LIHEAP categorical income programs + reference: + - title: Florida FY2026 LIHEAP State Plan, sections 1.4-1.6, page 5 (categorical income eligibility) + href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5 + - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01G(9), page 50 + href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=50 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml new file mode 100644 index 00000000000..862ba7555c9 --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml @@ -0,0 +1,12 @@ +description: Florida limits annual heating-assistance 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: Florida LIHEAP heating state median income rate + reference: + - title: Florida FY2026 LIHEAP State Plan, Section 2.1, page 8 + href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=8 + - title: FY2026 LIHEAP income eligibility by component, Florida heating column + href: https://liheapch.acf.gov/delivery/income_eligibility.htm diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml new file mode 100644 index 00000000000..f9b38038172 --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml @@ -0,0 +1,12 @@ +description: Florida counts earned income starting at this age, and unearned income regardless of student status starting at this age, under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 18 +metadata: + unit: year + period: year + label: Florida LIHEAP adult income age + reference: + - title: Florida FY2026 LIHEAP State Plan, section 1.9, page 6 (earnings under 18 excluded) + href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=6 + - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01D, page 49 + href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml new file mode 100644 index 00000000000..f51b16fe562 --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml @@ -0,0 +1,12 @@ +description: Florida counts unearned income of household members who are not full-time students starting at this age under the Low Income Home Energy Assistance Program. +# The current plan excludes under-18 earnings but does not state the unearned +# age rule. This retains the older manual's specific rule for unearned income. +values: + 2025-10-01: 16 +metadata: + unit: year + period: year + label: Florida LIHEAP nonstudent unearned income minimum age + reference: + - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01D, page 49 + href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml new file mode 100644 index 00000000000..2767ee1a28e --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml @@ -0,0 +1,18 @@ +description: Florida counts these earned income sources of adult household members under the Low Income Home Energy Assistance Program. +# Existing net business inputs have no additional LIHEAP expense deduction. +values: + 2025-10-01: + - employment_income + - self_employment_income + - sstb_self_employment_income + - farm_operations_income + - partnership_s_corp_income +metadata: + unit: list + period: year + label: Florida LIHEAP earned income sources + reference: + - title: Florida FY2026 LIHEAP State Plan, section 1.9, pages 5-6 + href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5 + - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01E and G(12), pages 49-50 + href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml new file mode 100644 index 00000000000..870ebc0d5d9 --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml @@ -0,0 +1,11 @@ +description: Florida counts these household cash grants once under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: + - fl_tca +metadata: + unit: list + period: year + label: Florida LIHEAP household cash income sources + reference: + - title: Florida FY2026 LIHEAP State Plan, section 1.9, page 6 (TANF counted) + href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml new file mode 100644 index 00000000000..6e09932d686 --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml @@ -0,0 +1,31 @@ +description: Florida counts these person-level unearned income sources subject to its age and student-status rule under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: + - social_security + - ssi + - pension_income + - survivor_benefits + - veterans_benefits + - workers_compensation + - disability_benefits + - strike_benefits + - general_assistance + - rental_income + - farm_rent_income + - interest_income + - ordinary_dividend_income + - child_support_received + - alimony_income + # Current plan's counted lump-sum line explicitly includes lottery winnings. + - gambling_winnings +# Current plan expressly excludes unemployment compensation and cash gifts. +# Pension/SSA subcomponents are not counted twice; state TCA is separate below. +metadata: + unit: list + period: year + label: Florida LIHEAP person unearned income sources + reference: + - title: Florida FY2026 LIHEAP State Plan, section 1.9, pages 5-6 (included and excluded income sources) + href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5 + - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200, pages 49-51 + href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/base_amount.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/base_amount.yaml new file mode 100644 index 00000000000..b1dd4d7e424 --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/base_amount.yaml @@ -0,0 +1,19 @@ +description: Florida provides this base regular heating payment by income band under the Low Income Home Energy Assistance Program. +1: + 2025-10-01: 350 +2: + 2025-10-01: 300 +3: + 2025-10-01: 250 +4: + 2025-10-01: 200 +metadata: + unit: currency-USD + period: year + label: Florida LIHEAP base heating payment + breakdown: + - range(1, 5) + propagate_metadata_to_children: true + reference: + - title: Florida FY2026 LIHEAP Payment Matrix, home energy benefit + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/child_max_age.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/child_max_age.yaml new file mode 100644 index 00000000000..08d32b0c890 --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/child_max_age.yaml @@ -0,0 +1,10 @@ +description: Florida provides the young-child household heating supplement when a member is no older than this age under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 5 +metadata: + unit: year + period: year + label: Florida LIHEAP child supplement maximum age + reference: + - title: Florida FY2026 LIHEAP Payment Matrix, child additional assistance + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/elderly_min_age.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/elderly_min_age.yaml new file mode 100644 index 00000000000..3f4efa1b1ea --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/elderly_min_age.yaml @@ -0,0 +1,10 @@ +description: Florida provides the elderly household heating supplement when a member is at least this age under the Low Income Home Energy Assistance Program. +values: + 2025-10-01: 60 +metadata: + unit: year + period: year + label: Florida LIHEAP elderly supplement minimum age + reference: + - title: Florida FY2026 LIHEAP Payment Matrix, elderly additional assistance + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/income_band_fraction.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/income_band_fraction.yaml new file mode 100644 index 00000000000..4d6cd9f988b --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/income_band_fraction.yaml @@ -0,0 +1,15 @@ +description: Florida sets these regular heating income-band upper bounds as fractions of the maximum household income under the Low Income Home Energy Assistance Program. +first: + 2025-10-01: 0.5 +second: + 2025-10-01: 0.7 +third: + 2025-10-01: 0.85 +metadata: + unit: /1 + period: year + label: Florida LIHEAP heating income-band fractions + propagate_metadata_to_children: true + reference: + - title: Florida FY2026 LIHEAP Payment Matrix, income bands + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/supplement.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/supplement.yaml new file mode 100644 index 00000000000..b5c57bd4e5b --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/supplement.yaml @@ -0,0 +1,15 @@ +description: Florida adds this amount once for each applicable vulnerable household category to the regular heating payment under the Low Income Home Energy Assistance Program. +elderly: + 2025-10-01: 100 +disabled: + 2025-10-01: 100 +child: + 2025-10-01: 150 +metadata: + unit: currency-USD + period: year + label: Florida LIHEAP vulnerable household heating supplements + propagate_metadata_to_children: true + reference: + - title: Florida FY2026 LIHEAP Payment Matrix, additional assistance + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 756dc2c4988..ced204b8556 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: FL, OR, DC, Riverside County, MA, IL, KS, MS, TX state_implementations: + - state: FL + status: partial + name: Florida LIHEAP + full_name: Florida Low Income Home Energy Assistance Program + variable: fl_liheap + parameter_prefix: gov.states.fl.commerce.liheap + verified_years: "2026" + notes: FY2026 regular heating eligibility and operational payment matrix. Follows the explicit heating SMI rule; published table anomalies and categorical above-range treatment are documented. Administrative receipt and housing facts use existing-input approximations; historical backfills 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/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.yaml new file mode 100644 index 00000000000..3c296bbb8e1 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.yaml @@ -0,0 +1,136 @@ +# Florida independent household income and categorical-income components; no final program eligibility or award is asserted. + +# Current planp5/manualp50 any positive SNAP receipt waives income eligibility. High reported wages do not negate this component; no final award or band is asserted. +- name: Case 1, positive SNAP receipt establishes only the categorical income exemption. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 100_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 1_200 + households: + household: + members: [person1] + state_code: FL + output: + fl_liheap_categorically_eligible: true + +# Current planp5/manualp50 TANF receipt qualifies; use family Florida TCA annual3600, not a national TANF aggregate. +- name: Case 2, positive Florida cash assistance establishes the categorical income exemption. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + person2: + age: 8 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 3_600 + snap: 0 + households: + household: + members: [person1, person2] + state_code: FL + output: + fl_liheap_categorically_eligible: true + +# Current planp5 any household member SSI receipt qualifies. ChildSSI1200 is excluded from income by manualp49 age detail; adult wages12000 count. +- name: Case 3, a child SSI receipt qualifies categorically even when excluded from income. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 12_000 + person2: + age: 8 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 1_200 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: FL + output: + fl_liheap_countable_income: 12_000 + fl_liheap_categorically_eligible: true + +# Current planp5 requires receipt, not the is_ssi_eligible demographic/resource flag; SSI,SNAP,TCA are explicitly0. +- name: Case 4, SSI demographic eligibility without any qualifying receipt does not establish categorical + status. + period: 2026 + input: + people: + person1: + age: 67 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + is_ssi_eligible: true + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1] + state_code: FL + output: + fl_liheap_categorically_eligible: false + +# State-defined Florida categorical component returnsfalse outside Florida; no final heating award is evaluated. +- name: Case 5, out of state categorical receipts do not establish Florida status. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 1_200 + households: + household: + members: [person1] + state_code: GA + output: + fl_liheap_categorically_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.yaml new file mode 100644 index 00000000000..959b310be53 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.yaml @@ -0,0 +1,303 @@ +# Accepted plan pp5,8 and manual pp23,41-43: ordinary/categorical income conditions, qualified household, heating responsibility. Housing verification uses the documented existing-input approximation. + +- name: Case 1, the ordinary income ceiling is inclusive without an asset test. + period: 2026 + input: + state_code: + - FL + - FL + age: + - 40 + - 40 + immigration_status: + - CITIZEN + - CITIZEN + is_full_time_student: + - false + - false + ssi: + - 0 + - 0 + fl_tca: + - 0 + - 0 + snap: + - 0 + - 0 + employment_income: + - 32155 + - 32155.01 + heating_type: + - NATURAL_GAS + - NATURAL_GAS + heating_expense: + - 1200 + - 1200 + spm_unit_assets: + - 1000000 + - 1000000 + output: + fl_liheap_eligible: + - true + - false +- name: Case 2, each reported receipt route waives the ordinary income ceiling. + period: 2026 + input: + state_code: + - FL + - FL + - FL + - FL + age: + - 40 + - 40 + - 40 + - 40 + immigration_status: + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + is_full_time_student: + - false + - false + - false + - false + ssi: + - 0 + - 0 + - 1200 + - 0 + fl_tca: + - 0 + - 1200 + - 0 + - 0 + snap: + - 1200 + - 0 + - 0 + - 0 + employment_income: + - 100000 + - 100000 + - 100000 + - 100000 + heating_type: + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + heating_expense: + - 1200 + - 1200 + - 1200 + - 1200 + output: + fl_liheap_eligible: + - true + - true + - true + - false +- name: Case 3, categorical receipts do not waive heating qualified membership or Florida residence. + period: 2026 + input: + state_code: + - FL + - FL + - FL + - GA + age: + - 40 + - 40 + - 40 + - 40 + immigration_status: + - CITIZEN + - UNDOCUMENTED + - CITIZEN + - CITIZEN + is_full_time_student: + - false + - false + - false + - false + ssi: + - 0 + - 0 + - 0 + - 0 + fl_tca: + - 0 + - 0 + - 0 + - 0 + snap: + - 1200 + - 1200 + - 1200 + - 1200 + employment_income: + - 0 + - 0 + - 0 + - 0 + heating_type: + - NONE + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + heating_expense: + - 100 + - 100 + - 0 + - 100 + has_heating_expense: + - true + - true + - false + - true + output: + fl_liheap_eligible: + - false + - false + - false + - false +- name: Case 4, subsidized heat in rent needs a positive residual bill even with a general obligation + flag. + period: 2026 + input: + state_code: + - FL + - FL + - FL + - FL + - FL + age: + - 40 + - 40 + - 40 + - 40 + - 40 + immigration_status: + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + is_full_time_student: + - false + - false + - false + - false + - false + ssi: + - 0 + - 0 + - 0 + - 0 + - 0 + fl_tca: + - 0 + - 0 + - 0 + - 0 + - 0 + snap: + - 0 + - 0 + - 0 + - 0 + - 0 + employment_income: + - 0 + - 0 + - 0 + - 0 + - 0 + heating_type: + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + heating_expense: + - 0 + - 0 + - 120 + - 0 + - 120 + has_heating_expense: + - false + - true + - true + - true + - true + heat_expense_included_in_rent: + - true + - true + - true + - true + - true + receives_housing_assistance: + - false + - true + - true + - false + - false + is_in_public_housing: + - false + - false + - false + - true + - true + output: + fl_liheap_eligible: + - true + - false + - true + - false + - true +- name: Case 5, a directly reported heating obligation qualifies without a current positive bill. + period: 2026 + input: + state_code: + - FL + - FL + age: + - 40 + - 40 + immigration_status: + - CITIZEN + - CITIZEN + is_full_time_student: + - false + - false + ssi: + - 0 + - 0 + fl_tca: + - 0 + - 0 + snap: + - 0 + - 0 + employment_income: + - 0 + - 0 + heating_type: + - NATURAL_GAS + - NATURAL_GAS + heating_expense: + - 0 + - 0 + has_heating_expense: + - true + - false + heat_expense_included_in_rent: + - false + - false + output: + fl_liheap_eligible: + - true + - false diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.yaml new file mode 100644 index 00000000000..6a1dfb7e03e --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.yaml @@ -0,0 +1,191 @@ +# Current FY2026 matrix p1 literal sizes1-12. Its stated60%SMI rule gives94610 at size13; printed157686 is documented as an anomaly, not a legal100%SMI category. + +- name: Case 1, all twelve consistent published heating limits match the current matrix. + period: 2026 + input: + state_code: + - FL + - FL + - FL + - FL + - FL + - FL + - FL + - FL + - FL + - FL + - FL + - FL + age: + - 40 + - 40 + - 40 + - 40 + - 40 + - 40 + - 40 + - 40 + - 40 + - 40 + - 40 + - 40 + immigration_status: + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + is_full_time_student: + - false + - false + - false + - false + - false + - false + - false + - false + - false + - false + - false + - false + ssi: + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + fl_tca: + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + snap: + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + employment_income: + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + 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 + heating_expense: + - 1200 + - 1200 + - 1200 + - 1200 + - 1200 + - 1200 + - 1200 + - 1200 + - 1200 + - 1200 + - 1200 + - 1200 + fl_liheap_household_size: + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + - 8 + - 9 + - 10 + - 11 + - 12 + output: + fl_liheap_income_limit: + - 32155 + - 42049 + - 51943 + - 61837 + - 71730 + - 81624 + - 83479 + - 85335 + - 87190 + - 89045 + - 90900 + - 92755 +- name: Case 2, size thirteen follows the stated sixty percent SMI rule despite the anomalous printed + row. + period: 2026 + input: + state_code: + - FL + age: + - 40 + immigration_status: + - CITIZEN + is_full_time_student: + - false + ssi: + - 0 + fl_tca: + - 0 + snap: + - 0 + employment_income: + - 0 + heating_type: + - NATURAL_GAS + heating_expense: + - 1200 + fl_liheap_household_size: + - 13 + output: + fl_liheap_income_limit: + - 94610 diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml new file mode 100644 index 00000000000..9527bcf89e8 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml @@ -0,0 +1,484 @@ +# FY2026 matrix p1 and accepted plan p9: base350/300/250/200; elderly100, disability100, child<=5 adds150, once per category. Manualp23 bases payment on the matrix, not actual bill. Categorical income above table uses a documented200-base estimate, not an express source instruction. + +- name: Case 1, published income band tops include exact amounts and the next cent starts the next band. + period: 2026 + input: + state_code: + - FL + - FL + - FL + - FL + - FL + - FL + - FL + - FL + age: + - 40 + - 40 + - 40 + - 40 + - 40 + - 40 + - 40 + - 40 + immigration_status: + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + is_full_time_student: + - false + - false + - false + - false + - false + - false + - false + - false + ssi: + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + fl_tca: + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + snap: + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + - 0 + employment_income: + - 0 + - 16078 + - 16078.01 + - 22509 + - 22509.01 + - 27332 + - 27332.01 + - 32155 + heating_type: + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + heating_expense: + - 1200 + - 1200 + - 1200 + - 1200 + - 1200 + - 1200 + - 1200 + - 1200 + output: + fl_liheap: + - 350 + - 350 + - 300 + - 300 + - 250 + - 250 + - 200 + - 200 +- name: Case 2, age sixty and disability each add one hundred and both categories can stack. + period: 2026 + input: + state_code: + - FL + - FL + - FL + - FL + age: + - 59 + - 60 + - 59 + - 60 + immigration_status: + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + is_full_time_student: + - false + - false + - false + - false + ssi: + - 0 + - 0 + - 0 + - 0 + fl_tca: + - 0 + - 0 + - 0 + - 0 + snap: + - 0 + - 0 + - 0 + - 0 + employment_income: + - 0 + - 0 + - 0 + - 0 + heating_type: + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + heating_expense: + - 1200 + - 1200 + - 1200 + - 1200 + is_disabled: + - false + - false + - true + - true + output: + fl_liheap: + - 350 + - 450 + - 450 + - 550 +- name: Case 3, a child age five qualifies for the young child addition. + period: 2026 + input: + people: + parent: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + child: + age: 5 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + spm_units: + family: + members: &id001 + - parent + - child + heating_type: NATURAL_GAS + heating_expense: 1200 + fl_tca: 0 + snap: 0 + households: + home: + members: *id001 + state_code: FL + output: + fl_liheap: 500 +- name: Case 4, a child age six no longer qualifies for the young child addition. + period: 2026 + input: + people: + parent: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + child: + age: 6 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + spm_units: + family: + members: &id002 + - parent + - child + heating_type: NATURAL_GAS + heating_expense: 1200 + fl_tca: 0 + snap: 0 + households: + home: + members: *id002 + state_code: FL + output: + fl_liheap: 350 +- name: Case 5, multiple elderly disabled and young members add each category only once. + period: 2026 + input: + people: + elder1: + age: 65 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + is_disabled: true + elder2: + age: 64 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + is_disabled: true + parent: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + child1: + age: 5 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + child2: + age: 3 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + spm_units: + family: + members: &id003 + - elder1 + - elder2 + - parent + - child1 + - child2 + heating_type: NATURAL_GAS + heating_expense: 1200 + fl_tca: 0 + snap: 0 + households: + home: + members: *id003 + state_code: FL + output: + fl_liheap: 700 +- name: Case 6, a nonqualified parents full income determines the band for the qualified child. + period: 2026 + input: + people: + parent: + age: 40 + immigration_status: UNDOCUMENTED + is_full_time_student: false + ssi: 0 + employment_income: 30000 + child: + age: 5 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + spm_units: + family: + members: &id004 + - parent + - child + heating_type: NATURAL_GAS + heating_expense: 1200 + fl_tca: 0 + snap: 0 + households: + home: + members: *id004 + state_code: FL + output: + fl_liheap_household_size: 1 + fl_liheap_countable_income: 30000 + fl_liheap: 350 +- name: Case 7, categorical income above the table uses the documented minimum base estimate. + period: 2026 + input: + state_code: + - FL + age: + - 40 + immigration_status: + - CITIZEN + is_full_time_student: + - false + ssi: + - 0 + fl_tca: + - 0 + snap: + - 1200 + employment_income: + - 100000 + heating_type: + - NATURAL_GAS + heating_expense: + - 1200 + output: + fl_liheap_eligible: + - true + fl_liheap: + - 200 +- name: Case 8, a subsidized household receives the full regular award without a HUD deduction or bill + cap. + period: 2026 + input: + state_code: + - FL + age: + - 40 + immigration_status: + - CITIZEN + is_full_time_student: + - false + ssi: + - 0 + fl_tca: + - 0 + snap: + - 0 + employment_income: + - 0 + heating_type: + - NATURAL_GAS + heating_expense: + - 10 + hud_utility_allowance: + - 2400 + is_in_public_housing: + - true + receives_housing_assistance: + - true + output: + fl_liheap: + - 350 +- name: Case 9, the final award follows the heat in rent and subsidized residual cost routes. + period: 2026 + input: + state_code: + - FL + - FL + - FL + age: + - 40 + - 40 + - 40 + immigration_status: + - CITIZEN + - CITIZEN + - CITIZEN + is_full_time_student: + - false + - false + - false + ssi: + - 0 + - 0 + - 0 + fl_tca: + - 0 + - 0 + - 0 + snap: + - 0 + - 0 + - 0 + employment_income: + - 0 + - 0 + - 0 + heating_type: + - NATURAL_GAS + - NATURAL_GAS + - NATURAL_GAS + heating_expense: + - 0 + - 0 + - 10 + heat_expense_included_in_rent: + - true + - true + - true + receives_housing_assistance: + - false + - true + - true + output: + fl_liheap: + - 350 + - 0 + - 350 +- name: Case 10, the regular matrix is fuel neutral and applies only within Florida. + period: 2026 + input: + state_code: + - FL + - FL + - FL + - GA + age: + - 40 + - 40 + - 40 + - 40 + immigration_status: + - CITIZEN + - CITIZEN + - CITIZEN + - CITIZEN + is_full_time_student: + - false + - false + - false + - false + ssi: + - 0 + - 0 + - 0 + - 0 + fl_tca: + - 0 + - 0 + - 0 + - 0 + snap: + - 0 + - 0 + - 0 + - 0 + employment_income: + - 0 + - 0 + - 0 + - 0 + heating_type: + - ELECTRICITY + - NATURAL_GAS + - WOOD + - NATURAL_GAS + heating_expense: + - 10 + - 10 + - 10 + - 10 + output: + fl_liheap: + - 350 + - 350 + - 350 + - 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap_household_size.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap_household_size.yaml new file mode 100644 index 00000000000..8874e8e3efe --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap_household_size.yaml @@ -0,0 +1,69 @@ +# Florida independent household income and categorical-income components; no final program eligibility or award is asserted. + +# Manual pp41-42 counts citizens and qualified noncitizens, excludes nonqualified people from size; does not import SNAP-specific waiting bars. +- name: Case 1, qualified adults and children count without a SNAP waiting bar. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: LEGAL_PERMANENT_RESIDENT + is_full_time_student: false + ssi: 0 + person2: + age: 8 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + person3: + age: 35 + immigration_status: ASYLEE + is_full_time_student: false + ssi: 0 + person4: + age: 22 + immigration_status: DACA + is_full_time_student: false + ssi: 0 + person5: + age: 39 + immigration_status: UNDOCUMENTED + is_full_time_student: false + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1, person2, person3, person4, person5] + state_code: FL + output: + fl_liheap_household_size: 3 + +# Manual pp41-42 requires at least one qualified member for final program eligibility; this independent size component returns0. +- name: Case 2, only nonqualified members produce a zero household size. + period: 2026 + input: + people: + person1: + age: 40 + immigration_status: UNDOCUMENTED + is_full_time_student: false + ssi: 0 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1] + state_code: FL + output: + fl_liheap_household_size: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml new file mode 100644 index 00000000000..2a27d162023 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml @@ -0,0 +1,504 @@ +# Florida independent household income and categorical-income components; no final program eligibility or award is asserted. + +# Manual pp49-50/current planpp5-6:12000+2400+600+1200+1200=17400 under existing net-business input approximation. +- name: Case 1, adult wages and existing net business income count without a second expense deduction. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 12_000 + self_employment_income: 2_400 + sstb_self_employment_income: 600 + farm_operations_income: 1_200 + partnership_s_corp_income: 1_200 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1] + state_code: FL + output: + fl_liheap_countable_income: 17_400 + +# Contract current-input approximation preserves signed netbusiness:12000-6000+600=6600. Source lacks an express individual loss floor; this is not a new business deduction. +- name: Case 2, existing signed business inputs offset other income before the household floor. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 12_000 + self_employment_income: -6_000 + partnership_s_corp_income: 600 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1] + state_code: FL + output: + fl_liheap_countable_income: 6_600 + +# Approved household floor: max(1200-6000,0)=0; no negative countable income. +- name: Case 3, negative combined income is floored at zero. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 1_200 + self_employment_income: -6_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1] + state_code: FL + output: + fl_liheap_countable_income: 0 + +# Current FY2026 plan p6 leaves UI/cash gifts unselected.30000 wages remain30000; UI6000, gifts2400 and SNAP6000 do not count. +- name: Case 4, current plan excludes unemployment and cash gifts. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 30_000 + unemployment_compensation: 6_000 + financial_assistance: 2_400 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 6_000 + households: + household: + members: [person1] + state_code: FL + output: + fl_liheap_countable_income: 30_000 + +# Current plan p6 excludes all earnings under18. Older detailed manualp49 supplies unearned18+ or16/17nonstudent rule. Parent12000; young adult6000 wages+1200 business+1200 SSA when all included. This reconciles authorities rather than claiming the plan states the unearned age rule. +- name: Case 5, age 15 nonstudent follows separate earned and unearned age rules. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 12_000 + person2: + age: 15 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 6_000 + self_employment_income: 1_200 + social_security: 1_200 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: FL + output: + fl_liheap_countable_income: 12_000 + +# Current plan p6 excludes all earnings under18. Older detailed manualp49 supplies unearned18+ or16/17nonstudent rule. Parent12000; young adult6000 wages+1200 business+1200 SSA when all included. This reconciles authorities rather than claiming the plan states the unearned age rule. +- name: Case 6, age 16 nonstudent follows separate earned and unearned age rules. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 12_000 + person2: + age: 16 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 6_000 + self_employment_income: 1_200 + social_security: 1_200 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: FL + output: + fl_liheap_countable_income: 13_200 + +# Current plan p6 excludes all earnings under18. Older detailed manualp49 supplies unearned18+ or16/17nonstudent rule. Parent12000; young adult6000 wages+1200 business+1200 SSA when all included. This reconciles authorities rather than claiming the plan states the unearned age rule. +- name: Case 7, age 16 full time student follows separate earned and unearned age rules. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 12_000 + person2: + age: 16 + immigration_status: CITIZEN + is_full_time_student: true + ssi: 0 + employment_income: 6_000 + self_employment_income: 1_200 + social_security: 1_200 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: FL + output: + fl_liheap_countable_income: 12_000 + +# Current plan p6 excludes all earnings under18. Older detailed manualp49 supplies unearned18+ or16/17nonstudent rule. Parent12000; young adult6000 wages+1200 business+1200 SSA when all included. This reconciles authorities rather than claiming the plan states the unearned age rule. +- name: Case 8, age 17 nonstudent follows separate earned and unearned age rules. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 12_000 + person2: + age: 17 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 6_000 + self_employment_income: 1_200 + social_security: 1_200 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: FL + output: + fl_liheap_countable_income: 13_200 + +# Current plan p6 excludes all earnings under18. Older detailed manualp49 supplies unearned18+ or16/17nonstudent rule. Parent12000; young adult6000 wages+1200 business+1200 SSA when all included. This reconciles authorities rather than claiming the plan states the unearned age rule. +- name: Case 9, age 17 full time student follows separate earned and unearned age rules. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 12_000 + person2: + age: 17 + immigration_status: CITIZEN + is_full_time_student: true + ssi: 0 + employment_income: 6_000 + self_employment_income: 1_200 + social_security: 1_200 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: FL + output: + fl_liheap_countable_income: 12_000 + +# Current plan p6 excludes all earnings under18. Older detailed manualp49 supplies unearned18+ or16/17nonstudent rule. Parent12000; young adult6000 wages+1200 business+1200 SSA when all included. This reconciles authorities rather than claiming the plan states the unearned age rule. +- name: Case 10, age 18 full time student follows separate earned and unearned age rules. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 12_000 + person2: + age: 18 + immigration_status: CITIZEN + is_full_time_student: true + ssi: 0 + employment_income: 6_000 + self_employment_income: 1_200 + social_security: 1_200 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: FL + output: + fl_liheap_countable_income: 20_400 + +# Current planp6 rent and retirement categories:30000+1200ordinaryrent+5000farmrent+1200survivor=37400; no assumed rental loss or extra allowance. +- name: Case 11, rental and survivor inputs count independently in full. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 30_000 + rental_income: 1_200 + farm_rent_income: 5_000 + survivor_benefits: 1_200 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1] + state_code: FL + output: + fl_liheap_countable_income: 37_400 + +# Current planp6 listed receipts:18000+1200+2400+1200+600+600+600+300+300+1200+600+120+240=27360. AnnualSSI counted once; no pension/SSA subcomponent duplication. +- name: Case 12, disjoint covered unearned receipts and canonical dividends count once. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 67 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 1_200 + social_security: 18_000 + pension_income: 2_400 + survivor_benefits: 1_200 + workers_compensation: 600 + disability_benefits: 600 + veterans_benefits: 600 + general_assistance: 300 + strike_benefits: 300 + child_support_received: 1_200 + alimony_income: 600 + interest_income: 120 + ordinary_dividend_income: 240 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1] + state_code: FL + output: + fl_liheap_countable_income: 27_360 + +# Current planp6 expressly marks SSA including Medicare deduction; manualp50 adds premiums back to net checks. Gross24000 remains24000, not21600. +- name: Case 13, gross Social Security remains counted before Medicare premiums. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 67 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + social_security: 24_000 + medicare_part_b_premium: 2_400 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1] + state_code: FL + output: + fl_liheap_countable_income: 24_000 + +# Current planp6 counts TANF:6000 adult wages+3600 annual Florida TCA=9600. Child SSA1200 excluded by the older detailed age rule; monthly state grant is added once perfamily. +- name: Case 14, state cash assistance is annualized once for the family. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 6_000 + person2: + age: 8 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + social_security: 1_200 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 3_600 + snap: 0 + households: + household: + members: [person1, person2] + state_code: FL + output: + fl_liheap_countable_income: 9_600 + +# Manualp42 retains all nonqualified adult income despite size1; minor SSA excluded under manualp49 age detail. No proration. +- name: Case 15, nonqualified adult income is retained in full for a qualified child household. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: UNDOCUMENTED + is_full_time_student: false + ssi: 0 + employment_income: 20_000 + person2: + age: 8 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + social_security: 1_200 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: FL + output: + fl_liheap_household_size: 1 + fl_liheap_countable_income: 20_000 + +# Current planp6 explicitly counts lottery winnings in the lump-sum row. Here gambling_winnings represents6000 lottery receipts;12000+6000=18000. Casino-versus-lottery classification is not separately represented, and no5000 loss deduction is sourced. +- name: Case 16, reported lottery winnings count without an invented gambling loss deduction. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 12_000 + gambling_winnings: 6_000 + gambling_losses: 5_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1] + state_code: FL + output: + fl_liheap_countable_income: 18_000 diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.py new file mode 100644 index 00000000000..6c288fe7d5c --- /dev/null +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.py @@ -0,0 +1,22 @@ +from policyengine_us.model_api import * + + +class fl_liheap_categorically_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Florida LIHEAP categorical income eligibility" + defined_for = StateCode.FL + reference = ( + "https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5", + "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=50", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.fl.commerce.liheap.eligibility + receipts = add(spm_unit, period, p.categorical_programs, options=[ADD]) + # Positive SNAP, state cash assistance, or SSI is the receipt proxy. + # A minor's SSI may establish this status even when excluded from income. + # This waives only the income test, not heating responsibility, residency, + # or qualified-member requirements, and does not select a benefit band. + return receipts > 0 diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py new file mode 100644 index 00000000000..cdde1ba7d1f --- /dev/null +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py @@ -0,0 +1,42 @@ +from policyengine_us.model_api import * + + +class fl_liheap_eligible(Variable): + value_type = bool + entity = SPMUnit + definition_period = YEAR + label = "Florida LIHEAP regular heating eligibility" + defined_for = StateCode.FL + # Plan pages 5 and 8; manual pages 4, 23, 41, 43, 47 and 50. + reference = ( + "https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5", + "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=4", + ) + + def formula(spm_unit, period, parameters): + size = spm_unit("fl_liheap_household_size", period) + income = spm_unit("fl_liheap_countable_income", period) + limit = spm_unit("fl_liheap_income_limit", period) + categorical = spm_unit("fl_liheap_categorically_eligible", period) + heating_expense = spm_unit("heating_expense", period) + heat_in_rent = spm_unit("heat_expense_included_in_rent", period) + subsidized = spm_unit("receives_housing_assistance", period) | ( + spm_unit.household("is_in_public_housing", period) + ) + # The nonsubsidized rent flag represents a verified landlord heating + # arrangement. Actual verification and account ownership are absent + # from existing inputs. In subsidized heat-in-rent housing, a positive + # heating expense represents the household's verified excess charge. + # A direct bill or reported obligation is otherwise residual liability. + responsible = where( + heat_in_rent, + ~subsidized | (heating_expense > 0), + spm_unit("has_heating_expense", period) | (heating_expense > 0), + ) + heating_type = spm_unit("heating_type", period) + return ( + (size > 0) + & ((income <= limit) | categorical) + & responsible + & (heating_type != heating_type.possible_values.NONE) + ) diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py new file mode 100644 index 00000000000..a6d683daafb --- /dev/null +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py @@ -0,0 +1,31 @@ +from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.hhs_smi import smi + + +class fl_liheap_income_limit(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Florida LIHEAP annual heating income limit" + defined_for = StateCode.FL + reference = ( + "https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=8", + "https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf", + "https://liheapch.acf.gov/delivery/income_eligibility.htm", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.fl.commerce.liheap.eligibility + size = spm_unit("fl_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 + # This floor order reconciles all FY2026 matrix rows for sizes 1-12; + # the sources do not expressly prescribe the rounding order. + four_person_limit = np.floor(four_person_smi * p.smi_rate) + # The printed size-13 maximum, $157,686, equals 100% of SMI. Follow + # the matrix's explicit 60% rule instead ($94,610). The federal table + # restricts its 150%-FPG large-household exception to cooling/crisis. + # Larger sizes follow the federal factors without table verification. + return where(size > 0, np.floor(four_person_limit * size_share), 0) diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py new file mode 100644 index 00000000000..6d8d4718b66 --- /dev/null +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py @@ -0,0 +1,50 @@ +from policyengine_us.model_api import * + + +class fl_liheap(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Florida LIHEAP regular heating assistance" + defined_for = StateCode.FL + # Manual pages 8-9, 23-24 and 43; plan page 9. + reference = ( + "https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf", + "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=8", + "https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=9", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.fl.commerce.liheap.payment + income = spm_unit("fl_liheap_countable_income", period) + limit = spm_unit("fl_liheap_income_limit", period) + # Half-up dollar cutoffs reproduce the printed bands without leaving + # gaps for income with cents between successive printed dollar bounds. + first = np.floor(limit * p.income_band_fraction.first + 0.5) + second = np.floor(limit * p.income_band_fraction.second + 0.5) + third = np.floor(limit * p.income_band_fraction.third + 0.5) + band = select( + [income <= first, income <= second, income <= third], + [1, 2, 3], + default=4, + ) + # Categorical households above the printed maximum use the smallest + # base as an estimate; sources waive the income test but do not supply + # an above-range benefit band. Receipt alone never selects a top band. + base = p.base_amount[band] + age = spm_unit.members("age", period) + elderly = spm_unit.any(age >= p.elderly_min_age) + disabled = spm_unit.any(spm_unit.members("is_disabled", period)) + child = spm_unit.any(age <= p.child_max_age) + supplements = ( + elderly * p.supplement.elderly + + disabled * p.supplement.disabled + + child * p.supplement.child + ) + # Follow the operative October 2025 matrix ($200-$700), as required by + # the manual; the plan's estimated $400-$1,350 extrema conflict with it. + # One award per year is modeled. No actual-bill cap or HUD deduction + # applies to regular benefits; previous awards require unavailable data. + eligible = spm_unit("fl_liheap_eligible", period) + return eligible * (base + supplements) diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py new file mode 100644 index 00000000000..ce2f3c0dab6 --- /dev/null +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py @@ -0,0 +1,16 @@ +from policyengine_us.model_api import * + + +class fl_liheap_household_size(Variable): + value_type = int + entity = SPMUnit + definition_period = YEAR + label = "Florida LIHEAP household size" + defined_for = StateCode.FL + reference = "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=41" + + # Ineligible members are excluded from size, but their otherwise countable + # income remains included in full. SPM membership approximates the energy + # economic unit; roomers, separate energy units, special status documents, + # and tribal-provider assignments are not completely identified by inputs. + adds = ["is_citizen_or_legal_immigrant"] diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py new file mode 100644 index 00000000000..22469f6f87a --- /dev/null +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py @@ -0,0 +1,46 @@ +from policyengine_us.model_api import * + + +class fl_liheap_countable_income(Variable): + value_type = float + entity = SPMUnit + definition_period = YEAR + unit = USD + label = "Florida LIHEAP annual countable household income" + defined_for = StateCode.FL + reference = ( + "https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5", + "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49", + ) + + def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.fl.commerce.liheap.income + person = spm_unit.members + age = person("age", period) + adult = age >= p.adult_age + # The current plan excludes all earnings under 18. The older detailed + # manual supplies the unearned-income age rule where the current plan + # is silent: include 16/17-year-olds who are not full-time students. + unearned_age_eligible = adult | ( + (age >= p.nonstudent_min_age) & ~person("is_full_time_student", period) + ) + # Preserve signed existing net business inputs without another expense + # deduction. The sources do not specify a loss floor per business; + # floor the combined household total. Rental income is nonnegative. + earned = add(person, period, p.sources.earned) + unearned = add(person, period, p.sources.unearned, options=[ADD]) + # Count the state cash grant once at SPM level as a caregiver grant. + # Its calculated default is a receipt proxy; observed amounts can be + # supplied through the existing variable. No national TANF aggregate. + cash_grant = add(spm_unit, period, p.sources.household, options=[ADD]) + # Annual inputs approximate the prior 30 days/current economic situation. + # Nonqualified members' countable income is retained in full. UI and + # cash gifts are excluded by the current plan. Gross SSA is included; + # the manual expressly adds Medicare premiums back to net checks. + # Existing inputs do not isolate work-study, royalties, mortgage/sale + # installments, and all countable insurance/lump-sum receipts. + return max_( + spm_unit.sum(earned * adult + unearned * unearned_age_eligible) + + cash_grant, + 0, + ) From 29465f1c19d609a7be8012a698569aa74f6b30c6 Mon Sep 17 00:00:00 2001 From: Ziming Date: Sat, 3 Oct 2026 20:56:56 -0400 Subject: [PATCH 2/9] Fix Florida LIHEAP income and child supplement --- .../liheap/income/sources/unearned.yaml | 6 +- .../liheap/payment/child_max_age.yaml | 2 +- .../states/fl/commerce/liheap/fl_liheap.yaml | 54 ++++++++++++++ .../income/fl_liheap_countable_income.yaml | 73 ++++++++++++++----- .../states/fl/commerce/liheap/fl_liheap.py | 2 +- .../income/fl_liheap_countable_income.py | 3 + 6 files changed, 119 insertions(+), 21 deletions(-) diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml index 6e09932d686..8d2444b19d4 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml @@ -14,9 +14,11 @@ values: - farm_rent_income - interest_income - ordinary_dividend_income + # Periodic estate/trust income, excluding separately recorded interest/dividends. + - estate_income - child_support_received - alimony_income - # Current plan's counted lump-sum line explicitly includes lottery winnings. + # The formula subtracts gambling losses to count net gambling/lottery winnings. - gambling_winnings # Current plan expressly excludes unemployment compensation and cash gifts. # Pension/SSA subcomponents are not counted twice; state TCA is separate below. @@ -25,7 +27,7 @@ metadata: period: year label: Florida LIHEAP person unearned income sources reference: - - title: Florida FY2026 LIHEAP State Plan, section 1.9, pages 5-6 (included and excluded income sources) + - title: Florida FY2026 LIHEAP State Plan, section 1.9, pages 5-7 (included and excluded income sources) href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5 - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200, pages 49-51 href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/child_max_age.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/child_max_age.yaml index 08d32b0c890..21f7194e711 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/child_max_age.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/child_max_age.yaml @@ -1,4 +1,4 @@ -description: Florida provides the young-child household heating supplement when a member is no older than this age under the Low Income Home Energy Assistance Program. +description: Florida provides the young-child household heating supplement when a member is no older than this age in completed years under the Low Income Home Energy Assistance Program. values: 2025-10-01: 5 metadata: diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml index 9527bcf89e8..4e48a187d28 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml @@ -482,3 +482,57 @@ - 350 - 350 - 0 + +# Accepted plan section 1.9 p7 and FY2026 matrix p1, confirmed review C1/C2: +# 40_000 - 30_000 = 10_000 net gambling is in the 350 band; 40_000 periodic +# estate receipts exceed the ordinary 32_155 limit. No categorical receipts. +- name: Case 11, net gambling and periodic estate income determine ordinary awards. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [FL, FL] + age: [40, 40] + immigration_status: [CITIZEN, CITIZEN] + is_full_time_student: [false, false] + ssi: [0, 0] + fl_tca: [0, 0] + snap: [0, 0] + gambling_winnings: [40_000, 0] + gambling_losses: [30_000, 0] + estate_income: [0, 40_000] + heating_type: [NATURAL_GAS, NATURAL_GAS] + heating_expense: [1_200, 1_200] + output: + fl_liheap_countable_income: [10_000, 40_000] + fl_liheap: [350, 0] + +# FY2026 matrix p1: five completed years includes age 5.5, before the sixth +# birthday. Zero income gives the 350 base plus the 150 child supplement. +- name: Case 12, a child between the fifth and sixth birthdays retains the addition. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + person2: + age: 5.5 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + heating_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: FL + output: + fl_liheap: 500 diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml index 2a27d162023..9f15299f670 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml @@ -113,7 +113,7 @@ output: fl_liheap_countable_income: 30_000 -# Current plan p6 excludes all earnings under18. Older detailed manualp49 supplies unearned18+ or16/17nonstudent rule. Parent12000; young adult6000 wages+1200 business+1200 SSA when all included. This reconciles authorities rather than claiming the plan states the unearned age rule. +# Current plan p6 excludes earnings under 18; manual p49 counts unearned income at 18 or 16/17 if not a full-time student. Plan p7 includes periodic estate receipts and net gambling. Parent 12_000; young member 6_000 wages + 1_200 business + 1_200 SSA + 900 estate + (600 - 100) gambling when all included. - name: Case 5, age 15 nonstudent follows separate earned and unearned age rules. period: 2026 absolute_error_margin: 0.01 @@ -133,6 +133,9 @@ employment_income: 6_000 self_employment_income: 1_200 social_security: 1_200 + estate_income: 900 + gambling_winnings: 600 + gambling_losses: 100 spm_units: spm_unit: members: [person1, person2] @@ -147,7 +150,7 @@ output: fl_liheap_countable_income: 12_000 -# Current plan p6 excludes all earnings under18. Older detailed manualp49 supplies unearned18+ or16/17nonstudent rule. Parent12000; young adult6000 wages+1200 business+1200 SSA when all included. This reconciles authorities rather than claiming the plan states the unearned age rule. +# Current plan p6 excludes earnings under 18; manual p49 counts unearned income at 18 or 16/17 if not a full-time student. Plan p7 includes periodic estate receipts and net gambling. Parent 12_000; young member 6_000 wages + 1_200 business + 1_200 SSA + 900 estate + (600 - 100) gambling when all included. - name: Case 6, age 16 nonstudent follows separate earned and unearned age rules. period: 2026 absolute_error_margin: 0.01 @@ -167,6 +170,9 @@ employment_income: 6_000 self_employment_income: 1_200 social_security: 1_200 + estate_income: 900 + gambling_winnings: 600 + gambling_losses: 100 spm_units: spm_unit: members: [person1, person2] @@ -179,9 +185,9 @@ members: [person1, person2] state_code: FL output: - fl_liheap_countable_income: 13_200 + fl_liheap_countable_income: 14_600 -# Current plan p6 excludes all earnings under18. Older detailed manualp49 supplies unearned18+ or16/17nonstudent rule. Parent12000; young adult6000 wages+1200 business+1200 SSA when all included. This reconciles authorities rather than claiming the plan states the unearned age rule. +# Current plan p6 excludes earnings under 18; manual p49 counts unearned income at 18 or 16/17 if not a full-time student. Plan p7 includes periodic estate receipts and net gambling. Parent 12_000; young member 6_000 wages + 1_200 business + 1_200 SSA + 900 estate + (600 - 100) gambling when all included. - name: Case 7, age 16 full time student follows separate earned and unearned age rules. period: 2026 absolute_error_margin: 0.01 @@ -201,6 +207,9 @@ employment_income: 6_000 self_employment_income: 1_200 social_security: 1_200 + estate_income: 900 + gambling_winnings: 600 + gambling_losses: 100 spm_units: spm_unit: members: [person1, person2] @@ -215,7 +224,7 @@ output: fl_liheap_countable_income: 12_000 -# Current plan p6 excludes all earnings under18. Older detailed manualp49 supplies unearned18+ or16/17nonstudent rule. Parent12000; young adult6000 wages+1200 business+1200 SSA when all included. This reconciles authorities rather than claiming the plan states the unearned age rule. +# Current plan p6 excludes earnings under 18; manual p49 counts unearned income at 18 or 16/17 if not a full-time student. Plan p7 includes periodic estate receipts and net gambling. Parent 12_000; young member 6_000 wages + 1_200 business + 1_200 SSA + 900 estate + (600 - 100) gambling when all included. - name: Case 8, age 17 nonstudent follows separate earned and unearned age rules. period: 2026 absolute_error_margin: 0.01 @@ -235,6 +244,9 @@ employment_income: 6_000 self_employment_income: 1_200 social_security: 1_200 + estate_income: 900 + gambling_winnings: 600 + gambling_losses: 100 spm_units: spm_unit: members: [person1, person2] @@ -247,9 +259,9 @@ members: [person1, person2] state_code: FL output: - fl_liheap_countable_income: 13_200 + fl_liheap_countable_income: 14_600 -# Current plan p6 excludes all earnings under18. Older detailed manualp49 supplies unearned18+ or16/17nonstudent rule. Parent12000; young adult6000 wages+1200 business+1200 SSA when all included. This reconciles authorities rather than claiming the plan states the unearned age rule. +# Current plan p6 excludes earnings under 18; manual p49 counts unearned income at 18 or 16/17 if not a full-time student. Plan p7 includes periodic estate receipts and net gambling. Parent 12_000; young member 6_000 wages + 1_200 business + 1_200 SSA + 900 estate + (600 - 100) gambling when all included. - name: Case 9, age 17 full time student follows separate earned and unearned age rules. period: 2026 absolute_error_margin: 0.01 @@ -269,6 +281,9 @@ employment_income: 6_000 self_employment_income: 1_200 social_security: 1_200 + estate_income: 900 + gambling_winnings: 600 + gambling_losses: 100 spm_units: spm_unit: members: [person1, person2] @@ -283,7 +298,7 @@ output: fl_liheap_countable_income: 12_000 -# Current plan p6 excludes all earnings under18. Older detailed manualp49 supplies unearned18+ or16/17nonstudent rule. Parent12000; young adult6000 wages+1200 business+1200 SSA when all included. This reconciles authorities rather than claiming the plan states the unearned age rule. +# Current plan p6 excludes earnings under 18; manual p49 counts unearned income at 18 or 16/17 if not a full-time student. Plan p7 includes periodic estate receipts and net gambling. Parent 12_000; young member 6_000 wages + 1_200 business + 1_200 SSA + 900 estate + (600 - 100) gambling when all included. - name: Case 10, age 18 full time student follows separate earned and unearned age rules. period: 2026 absolute_error_margin: 0.01 @@ -303,6 +318,9 @@ employment_income: 6_000 self_employment_income: 1_200 social_security: 1_200 + estate_income: 900 + gambling_winnings: 600 + gambling_losses: 100 spm_units: spm_unit: members: [person1, person2] @@ -315,7 +333,7 @@ members: [person1, person2] state_code: FL output: - fl_liheap_countable_income: 20_400 + fl_liheap_countable_income: 21_800 # Current planp6 rent and retirement categories:30000+1200ordinaryrent+5000farmrent+1200survivor=37400; no assumed rental loss or extra allowance. - name: Case 11, rental and survivor inputs count independently in full. @@ -346,7 +364,7 @@ output: fl_liheap_countable_income: 37_400 -# Current planp6 listed receipts:18000+1200+2400+1200+600+600+600+300+300+1200+600+120+240=27360. AnnualSSI counted once; no pension/SSA subcomponent duplication. +# Current plan pp6-7: prior disjoint receipts 27_360 + 2_400 periodic estate income = 29_760. Estate receipts exclude inherited principal and separately reported interest/dividends; annual SSI, pension and SSA count once. - name: Case 12, disjoint covered unearned receipts and canonical dividends count once. period: 2026 absolute_error_margin: 0.01 @@ -369,6 +387,7 @@ alimony_income: 600 interest_income: 120 ordinary_dividend_income: 240 + estate_income: 2_400 spm_units: spm_unit: members: [person1] @@ -381,7 +400,7 @@ members: [person1] state_code: FL output: - fl_liheap_countable_income: 27_360 + fl_liheap_countable_income: 29_760 # Current planp6 expressly marks SSA including Medicare deduction; manualp50 adds premiums back to net checks. Gross24000 remains24000, not21600. - name: Case 13, gross Social Security remains counted before Medicare premiums. @@ -475,8 +494,8 @@ fl_liheap_household_size: 1 fl_liheap_countable_income: 20_000 -# Current planp6 explicitly counts lottery winnings in the lump-sum row. Here gambling_winnings represents6000 lottery receipts;12000+6000=18000. Casino-versus-lottery classification is not separately represented, and no5000 loss deduction is sourced. -- name: Case 16, reported lottery winnings count without an invented gambling loss deduction. +# Accepted FY2026 plan section 1.9, physical p7: net gambling or lottery winnings. Confirmed review C1: 40_000 winnings - 30_000 associated losses = 10_000. +- name: Case 16, gambling winnings count net of associated losses. period: 2026 absolute_error_margin: 0.01 input: @@ -486,9 +505,8 @@ immigration_status: CITIZEN is_full_time_student: false ssi: 0 - employment_income: 12_000 - gambling_winnings: 6_000 - gambling_losses: 5_000 + gambling_winnings: 40_000 + gambling_losses: 30_000 spm_units: spm_unit: members: [person1] @@ -501,4 +519,25 @@ members: [person1] state_code: FL output: - fl_liheap_countable_income: 18_000 + fl_liheap_countable_income: 10_000 + +# Plan p7 requires net gambling; contract v1.2 preserves only the household floor. +# max(1_200 + 4_000 - losses, 0) = 1_200, 200, 0; no per-source loss floor. +- name: Case 17, net gambling losses offset income before the household floor. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [FL, FL, FL] + age: [40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN] + is_full_time_student: [false, false, false] + ssi: [0, 0, 0] + fl_tca: [0, 0, 0] + snap: [0, 0, 0] + employment_income: [1_200, 1_200, 1_200] + gambling_winnings: [4_000, 4_000, 4_000] + gambling_losses: [4_000, 5_000, 10_000] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_200, 1_200, 1_200] + output: + fl_liheap_countable_income: [1_200, 200, 0] diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py index 6d8d4718b66..3f5603162d8 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py @@ -36,7 +36,7 @@ def formula(spm_unit, period, parameters): age = spm_unit.members("age", period) elderly = spm_unit.any(age >= p.elderly_min_age) disabled = spm_unit.any(spm_unit.members("is_disabled", period)) - child = spm_unit.any(age <= p.child_max_age) + child = spm_unit.any(age < p.child_max_age + 1) supplements = ( elderly * p.supplement.elderly + disabled * p.supplement.disabled diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py index 22469f6f87a..1baedacf30a 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py @@ -8,6 +8,7 @@ class fl_liheap_countable_income(Variable): unit = USD label = "Florida LIHEAP annual countable household income" defined_for = StateCode.FL + # Plan section 1.9, pages 5-7; manual pages 49-51. reference = ( "https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5", "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49", @@ -29,6 +30,8 @@ def formula(spm_unit, period, parameters): # floor the combined household total. Rental income is nonnegative. earned = add(person, period, p.sources.earned) unearned = add(person, period, p.sources.unearned, options=[ADD]) + # The plan counts net gambling or lottery winnings. + unearned = unearned - person("gambling_losses", period) # Count the state cash grant once at SPM level as a caregiver grant. # Its calculated default is a receipt proxy; observed amounts can be # supplied through the existing variable. No national TANF aggregate. From c959520f535247c19ad353cff16a29f6449906c4 Mon Sep 17 00:00:00 2001 From: Ziming Date: Sun, 4 Oct 2026 20:17:38 -0400 Subject: [PATCH 3/9] Clarify FL 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 ced204b8556..b75d0fbd6cb 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -811,7 +811,7 @@ programs: variable: fl_liheap parameter_prefix: gov.states.fl.commerce.liheap verified_years: "2026" - notes: FY2026 regular heating eligibility and operational payment matrix. Follows the explicit heating SMI rule; published table anomalies and categorical above-range treatment are documented. Administrative receipt and housing facts use existing-input approximations; historical backfills are unverified. Regular heating only; crisis, cooling, weatherization and equipment assistance are excluded. + notes: FY2026 regular heating eligibility and operational payment matrix. Follows the explicit 60% SMI rule where the size-13 table conflicts. The $200 base for categorically eligible households above the printed income range is an unverified estimate. Administrative receipt and housing facts use existing-input approximations; historical backfills are unverified. Regular heating only; crisis, cooling, weatherization and equipment assistance are excluded. - state: OR status: in_progress name: LIHEAP From d12a606fdb5a8773833ec1a268109c8b959cda65 Mon Sep 17 00:00:00 2001 From: Ziming Date: Tue, 6 Oct 2026 17:26:02 -0400 Subject: [PATCH 4/9] Fix issues from review: FY2025 rules, income sources, TANF receipt - Encode FY2025: base amounts $1,000/$750/$550/$400 and the large-household rule (greater of 60% SMI and 150% FPG, bands keyed to FPG percentages); FY2025 rounds the SMI limit half up, FY2026 truncates like the HHS table. - Count military and railroad retirement; floor each income source at zero; count gambling net of losses, capped at winnings. - Count all income, earnings included, of members 18 and older and of 16- and 17-year-olds who are not full-time students (manual 1200.01D, followed over plan item 1.9). - Read take-up-gated tanf instead of fl_tca for income and categorical eligibility. - Shorten the registry note, fix the coverage-line order, the household-size page and the changelog line; use add() for the disabled check. - Add period-2025, income-source, band-cutoff and take-up cases; tidy tests. Co-Authored-By: Claude Opus 5.5 --- changelog.d/fl-liheap.added.md | 2 +- .../eligibility/categorical_programs.yaml | 10 +- .../commerce/liheap/eligibility/fpg_rate.yaml | 22 ++ .../liheap/eligibility/fpg_year_lag.yaml | 11 + .../commerce/liheap/eligibility/smi_rate.yaml | 4 +- .../eligibility/truncates_smi_limit.yaml | 23 ++ .../fl/commerce/liheap/income/adult_age.yaml | 10 +- .../liheap/income/nonstudent_min_age.yaml | 10 +- .../liheap/income/sources/earned.yaml | 6 +- .../liheap/income/sources/household.yaml | 8 +- .../liheap/income/sources/unearned.yaml | 19 +- .../commerce/liheap/payment/base_amount.yaml | 6 + .../liheap/payment/child_max_age.yaml | 4 +- .../liheap/payment/elderly_min_age.yaml | 4 +- .../liheap/payment/income_band_fpg_rate.yaml | 18 + .../liheap/payment/income_band_fraction.yaml | 8 +- .../commerce/liheap/payment/supplement.yaml | 8 +- policyengine_us/programs.yaml | 18 +- .../fl_liheap_categorically_eligible.yaml | 60 ++- .../eligibility/fl_liheap_eligible.yaml | 68 ++-- .../eligibility/fl_liheap_income_limit.yaml | 82 ++-- .../states/fl/commerce/liheap/fl_liheap.yaml | 366 ++++++++++++++---- .../liheap/fl_liheap_household_size.yaml | 6 +- .../income/fl_liheap_countable_income.yaml | 224 +++++++++-- .../fl_liheap_categorically_eligible.py | 2 +- .../liheap/eligibility/fl_liheap_eligible.py | 3 +- .../eligibility/fl_liheap_income_limit.py | 36 +- .../states/fl/commerce/liheap/fl_liheap.py | 61 ++- .../liheap/fl_liheap_household_size.py | 2 +- .../income/fl_liheap_countable_income.py | 52 ++- 30 files changed, 906 insertions(+), 247 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/fpg_rate.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/fpg_year_lag.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/truncates_smi_limit.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/income_band_fpg_rate.yaml diff --git a/changelog.d/fl-liheap.added.md b/changelog.d/fl-liheap.added.md index 3d4d9579c80..158a21f74f8 100644 --- a/changelog.d/fl-liheap.added.md +++ b/changelog.d/fl-liheap.added.md @@ -1 +1 @@ -Add Florida LIHEAP household, income, and categorical income eligibility components. +Add Florida LIHEAP regular heating assistance. diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml index 41f25781228..815110d2f87 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml @@ -1,8 +1,12 @@ description: Florida waives the LIHEAP income test for households receiving a positive amount from any of these programs. values: - 2025-10-01: + # The FY2025 plan also lists means-tested veterans programs, which no + # existing input identifies. + 2024-10-01: + # Each entry is take-up gated, so it reflects receipt rather than + # entitlement; tanf includes Florida Temporary Cash Assistance (fl_tca). - snap - - fl_tca + - tanf - ssi metadata: unit: list @@ -13,3 +17,5 @@ metadata: href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5 - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01G(9), page 50 href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=50 + - title: Florida FY2025 LIHEAP State Plan, Sections 1.4-1.6 + href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/fpg_rate.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/fpg_rate.yaml new file mode 100644 index 00000000000..7ab0b446b54 --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/fpg_rate.yaml @@ -0,0 +1,22 @@ +description: Florida sets the alternative heating income limit to this percentage of the federal poverty guidelines under the Low Income Home Energy Assistance Program. +# The heating maximum income value is the greater of the state median income +# limit and this share of the poverty guidelines. In FY2025 this gives 60% of +# state median income for sizes 1-8 and 150% of the guidelines for sizes 9 and +# above. FY2026 uses 60% of state median income for every size, so the +# poverty-guideline alternative is zero. +values: + 2024-10-01: 1.5 + 2025-10-01: 0 +metadata: + unit: /1 + period: year + label: Florida LIHEAP heating federal poverty guideline rate + reference: + - title: Florida FY2025 LIHEAP State Plan, Section 2.1 + href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=8 + - title: Florida FY2025 LIHEAP Payment Matrix, maximum income value footnote + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf + - title: Florida FY2026 LIHEAP State Plan, Section 2.1 + href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=8 + - title: Florida FY2026 LIHEAP Payment Matrix, maximum income value footnote + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/fpg_year_lag.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/fpg_year_lag.yaml new file mode 100644 index 00000000000..9b00c19ff04 --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/fpg_year_lag.yaml @@ -0,0 +1,11 @@ +description: Florida uses the federal poverty guidelines from this number of years before the heating year under the Low Income Home Energy Assistance Program. +# The FY2025 matrix uses the 2024 guidelines published on January 17, 2024. +values: + 2024-10-01: 1 +metadata: + unit: year + period: year + label: Florida LIHEAP federal poverty guideline year lag + reference: + - title: Florida FY2025 LIHEAP Payment Matrix, guideline footnote + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml index 862ba7555c9..7858def6718 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml @@ -1,6 +1,6 @@ description: Florida limits annual heating-assistance 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 @@ -10,3 +10,5 @@ metadata: href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=8 - title: FY2026 LIHEAP income eligibility by component, Florida heating column href: https://liheapch.acf.gov/delivery/income_eligibility.htm + - title: Florida FY2025 LIHEAP State Plan, Section 2.1 + href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=8 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/truncates_smi_limit.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/truncates_smi_limit.yaml new file mode 100644 index 00000000000..0a97b3e8d80 --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/truncates_smi_limit.yaml @@ -0,0 +1,23 @@ +description: Florida uses state median income limits truncated to whole dollars, as in the federal table, rather than rounded half up when this is true under the Low Income Home Energy Assistance Program. +# Neither matrix states a rounding rule; the pattern is inferred from the +# printed amounts. FY2025 rounds 60% of state median income for each size to +# the nearest dollar (one person: $98,038 x 60% x 52% = $30,587.856, printed +# $30,588, where the federal FY2025 table prints $30,587). FY2026 reprints the +# federal FY2026 table, which truncates 60% of the four-person amount and then +# each size ($32,155 for one person). +values: + 2024-10-01: false + 2025-10-01: true +metadata: + unit: bool + period: year + label: Florida LIHEAP state median income limit truncation + reference: + - title: Florida FY2025 LIHEAP Payment Matrix, maximum income values + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf + - title: Florida State Median Income for FFY 2025 + href: https://liheapch.acf.gov/profiles/povertytables/FY2025/flsmi.htm + - title: Florida FY2026 LIHEAP Payment Matrix, maximum income values + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf + - title: Florida State Median Income for FFY 2026 + href: https://liheapch.acf.gov/profiles/povertytables/FY2026/flsmi.htm diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml index f9b38038172..61d525353a6 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml @@ -1,12 +1,16 @@ -description: Florida counts earned income starting at this age, and unearned income regardless of student status starting at this age, under the Low Income Home Energy Assistance Program. +description: Florida uses this threshold as the age at which all members' income counts under the Low Income Home Energy Assistance Program. +# Manual 1200.01D counts earned and unearned income alike from this age, +# whether or not the member is a full-time student. values: - 2025-10-01: 18 + 2024-10-01: 18 metadata: unit: year period: year label: Florida LIHEAP adult income age reference: - - title: Florida FY2026 LIHEAP State Plan, section 1.9, page 6 (earnings under 18 excluded) + - title: Florida FY2026 LIHEAP State Plan, section 1.9, page 6 (earnings under 18; superseded by manual 1200.01D) href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=6 - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01D, page 49 href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 + - title: Florida FY2025 LIHEAP State Plan, Section 1.9 + href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml index f51b16fe562..c4885da288a 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml @@ -1,12 +1,12 @@ -description: Florida counts unearned income of household members who are not full-time students starting at this age under the Low Income Home Energy Assistance Program. -# The current plan excludes under-18 earnings but does not state the unearned -# age rule. This retains the older manual's specific rule for unearned income. +description: Florida uses this threshold as the age at which nonstudents' income counts under the Low Income Home Energy Assistance Program. +# Follows manual 1200.01D for earned and unearned income; plan item 1.9 leaves +# under-18 earnings unchecked (see fl_liheap_countable_income). values: - 2025-10-01: 16 + 2024-10-01: 16 metadata: unit: year period: year - label: Florida LIHEAP nonstudent unearned income minimum age + label: Florida LIHEAP nonstudent income minimum age reference: - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01D, page 49 href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml index 2767ee1a28e..8469473b942 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml @@ -1,7 +1,7 @@ -description: Florida counts these earned income sources of adult household members under the Low Income Home Energy Assistance Program. +description: Florida counts these earned income sources subject to its age and student-status rule under the Low Income Home Energy Assistance Program. # Existing net business inputs have no additional LIHEAP expense deduction. values: - 2025-10-01: + 2024-10-01: - employment_income - self_employment_income - sstb_self_employment_income @@ -16,3 +16,5 @@ metadata: href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5 - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01E and G(12), pages 49-50 href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 + - title: Florida FY2025 LIHEAP State Plan, Section 1.9, pages 5-6 + href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml index 870ebc0d5d9..4501eca05d2 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml @@ -1,7 +1,9 @@ description: Florida counts these household cash grants once under the Low Income Home Energy Assistance Program. values: - 2025-10-01: - - fl_tca + 2024-10-01: + # The take-up-gated TANF total includes Florida Temporary Cash Assistance + # (fl_tca) and counts it only when the household receives it. + - tanf metadata: unit: list period: year @@ -9,3 +11,5 @@ metadata: reference: - title: Florida FY2026 LIHEAP State Plan, section 1.9, page 6 (TANF counted) href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=6 + - title: Florida FY2025 LIHEAP State Plan, Section 1.9 + href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml index 8d2444b19d4..3ce70973eaa 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml @@ -1,9 +1,15 @@ description: Florida counts these person-level unearned income sources subject to its age and student-status rule under the Low Income Home Energy Assistance Program. values: - 2025-10-01: + 2024-10-01: - social_security - ssi + # 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. Plan Section 1.9 counts retirement and pension + # benefits. - pension_income + - military_retirement_pay + - railroad_benefits - survivor_benefits - veterans_benefits - workers_compensation @@ -18,10 +24,11 @@ values: - estate_income - child_support_received - alimony_income - # The formula subtracts gambling losses to count net gambling/lottery winnings. - - gambling_winnings -# Current plan expressly excludes unemployment compensation and cash gifts. -# Pension/SSA subcomponents are not counted twice; state TCA is separate below. +# Net gambling and lottery winnings are counted in the formula, with losses +# offsetting only winnings. +# The FY2025 and FY2026 plans exclude unemployment compensation and cash gifts. +# Pension/SSA subcomponents are not counted twice; TANF is counted once in +# household.yaml. metadata: unit: list period: year @@ -31,3 +38,5 @@ metadata: href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5 - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200, pages 49-51 href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 + - title: Florida FY2025 LIHEAP State Plan, Section 1.9, pages 5-7 + href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/base_amount.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/base_amount.yaml index b1dd4d7e424..e47bd84436c 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/base_amount.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/base_amount.yaml @@ -1,11 +1,15 @@ description: Florida provides this base regular heating payment by income band under the Low Income Home Energy Assistance Program. 1: + 2024-10-01: 1_000 2025-10-01: 350 2: + 2024-10-01: 750 2025-10-01: 300 3: + 2024-10-01: 550 2025-10-01: 250 4: + 2024-10-01: 400 2025-10-01: 200 metadata: unit: currency-USD @@ -15,5 +19,7 @@ metadata: - range(1, 5) propagate_metadata_to_children: true reference: + - title: Florida FY2025 LIHEAP Payment Matrix, home energy benefit + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf - title: Florida FY2026 LIHEAP Payment Matrix, home energy benefit href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/child_max_age.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/child_max_age.yaml index 21f7194e711..7eaef85fe9f 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/child_max_age.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/child_max_age.yaml @@ -1,6 +1,6 @@ description: Florida provides the young-child household heating supplement when a member is no older than this age in completed years under the Low Income Home Energy Assistance Program. values: - 2025-10-01: 5 + 2024-10-01: 5 metadata: unit: year period: year @@ -8,3 +8,5 @@ metadata: reference: - title: Florida FY2026 LIHEAP Payment Matrix, child additional assistance href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf + - title: Florida FY2025 LIHEAP Payment Matrix, child additional assistance + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/elderly_min_age.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/elderly_min_age.yaml index 3f4efa1b1ea..52f38825392 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/elderly_min_age.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/elderly_min_age.yaml @@ -1,6 +1,6 @@ description: Florida provides the elderly household heating supplement when a member is at least this age under the Low Income Home Energy Assistance Program. values: - 2025-10-01: 60 + 2024-10-01: 60 metadata: unit: year period: year @@ -8,3 +8,5 @@ metadata: reference: - title: Florida FY2026 LIHEAP Payment Matrix, elderly additional assistance href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf + - title: Florida FY2025 LIHEAP Payment Matrix, elderly additional assistance + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/income_band_fpg_rate.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/income_band_fpg_rate.yaml new file mode 100644 index 00000000000..9afcea0c2c4 --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/income_band_fpg_rate.yaml @@ -0,0 +1,18 @@ +description: Florida sets the poverty-guideline heating income-band upper bounds at this percentage of the federal poverty guidelines under the Low Income Home Energy Assistance Program. +# The FY2025 matrix prints the first band one dollar below 75% (for example, +# $43,574 at size 9), and the second band reads "At least 75%", so the first +# band excludes its upper bound. +first: + 2024-10-01: 0.75 +second: + 2024-10-01: 1 +third: + 2024-10-01: 1.25 +metadata: + unit: /1 + period: year + label: Florida LIHEAP heating income-band poverty guideline rates + propagate_metadata_to_children: true + reference: + - title: Florida FY2025 LIHEAP Payment Matrix, income bands for 9 to 20 people + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/income_band_fraction.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/income_band_fraction.yaml index 4d6cd9f988b..16661d542a8 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/income_band_fraction.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/income_band_fraction.yaml @@ -1,10 +1,10 @@ description: Florida sets these regular heating income-band upper bounds as fractions of the maximum household income under the Low Income Home Energy Assistance Program. first: - 2025-10-01: 0.5 + 2024-10-01: 0.5 second: - 2025-10-01: 0.7 + 2024-10-01: 0.7 third: - 2025-10-01: 0.85 + 2024-10-01: 0.85 metadata: unit: /1 period: year @@ -13,3 +13,5 @@ metadata: reference: - title: Florida FY2026 LIHEAP Payment Matrix, income bands href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf + - title: Florida FY2025 LIHEAP Payment Matrix, income bands for 1 to 8 people + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/supplement.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/supplement.yaml index b5c57bd4e5b..02d4e8481e0 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/supplement.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/supplement.yaml @@ -1,10 +1,10 @@ description: Florida adds this amount once for each applicable vulnerable household category to the regular heating payment under the Low Income Home Energy Assistance Program. elderly: - 2025-10-01: 100 + 2024-10-01: 100 disabled: - 2025-10-01: 100 + 2024-10-01: 100 child: - 2025-10-01: 150 + 2024-10-01: 150 metadata: unit: currency-USD period: year @@ -13,3 +13,5 @@ metadata: reference: - title: Florida FY2026 LIHEAP Payment Matrix, additional assistance href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf + - title: Florida FY2025 LIHEAP Payment Matrix, additional assistance + href: https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index b75d0fbd6cb..d7e13e4c595 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -802,16 +802,8 @@ programs: category: Benefits agency: HHS status: partial - coverage: FL, OR, DC, Riverside County, MA, IL, KS, MS, TX + coverage: OR, DC, Riverside County, MA, FL, IL, KS, MS, TX state_implementations: - - state: FL - status: partial - name: Florida LIHEAP - full_name: Florida Low Income Home Energy Assistance Program - variable: fl_liheap - parameter_prefix: gov.states.fl.commerce.liheap - verified_years: "2026" - notes: FY2026 regular heating eligibility and operational payment matrix. Follows the explicit 60% SMI rule where the size-13 table conflicts. The $200 base for categorically eligible households above the printed income range is an unverified estimate. Administrative receipt and housing facts use existing-input approximations; historical backfills are unverified. Regular heating only; crisis, cooling, weatherization and equipment assistance are excluded. - state: OR status: in_progress name: LIHEAP @@ -826,6 +818,14 @@ programs: name: Massachusetts LIHEAP full_name: Massachusetts Low Income Home Energy Assistance Program variable: ma_liheap + - state: FL + status: partial + name: Florida LIHEAP + full_name: Florida Low Income Home Energy Assistance Program + variable: fl_liheap + parameter_prefix: gov.states.fl.commerce.liheap + verified_years: "2025-2026" + notes: FY2025-FY2026 regular heating only, from the published payment matrices. Crisis, cooling, weatherization and equipment assistance are out of scope. - state: IL status: complete name: Illinois LIHEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.yaml index 3c296bbb8e1..5fa6cfd2af2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.yaml @@ -1,6 +1,8 @@ # Florida independent household income and categorical-income components; no final program eligibility or award is asserted. -# Current planp5/manualp50 any positive SNAP receipt waives income eligibility. High reported wages do not negate this component; no final award or band is asserted. +# FY2026 plan page 5 and manual page 50: any positive SNAP receipt waives the +# income test. High reported wages do not negate this component; no final +# award or band is asserted. - name: Case 1, positive SNAP receipt establishes only the categorical income exemption. period: 2026 input: @@ -25,7 +27,8 @@ output: fl_liheap_categorically_eligible: true -# Current planp5/manualp50 TANF receipt qualifies; use family Florida TCA annual3600, not a national TANF aggregate. +# FY2026 plan page 5 and manual page 50: TANF receipt qualifies. The family's +# Florida TCA of 3_600 a year is used, not a national TANF aggregate. - name: Case 2, positive Florida cash assistance establishes the categorical income exemption. period: 2026 input: @@ -54,7 +57,9 @@ output: fl_liheap_categorically_eligible: true -# Current planp5 any household member SSI receipt qualifies. ChildSSI1200 is excluded from income by manualp49 age detail; adult wages12000 count. +# FY2026 plan page 5: SSI receipt by any household member qualifies. The +# child's 1_200 of SSI is excluded from income by the manual page 49 age rule; +# the adult's 12_000 of wages count. - name: Case 3, a child SSI receipt qualifies categorically even when excluded from income. period: 2026 input: @@ -85,7 +90,8 @@ fl_liheap_countable_income: 12_000 fl_liheap_categorically_eligible: true -# Current planp5 requires receipt, not the is_ssi_eligible demographic/resource flag; SSI,SNAP,TCA are explicitly0. +# FY2026 plan page 5 requires receipt, not the is_ssi_eligible demographic and +# resource flag; SSI, SNAP and TCA are explicitly 0. - name: Case 4, SSI demographic eligibility without any qualifying receipt does not establish categorical status. period: 2026 @@ -111,7 +117,8 @@ output: fl_liheap_categorically_eligible: false -# State-defined Florida categorical component returnsfalse outside Florida; no final heating award is evaluated. +# The Florida categorical component returns false outside Florida; no final +# heating award is evaluated. - name: Case 5, out of state categorical receipts do not establish Florida status. period: 2026 input: @@ -134,3 +141,46 @@ state_code: GA output: fl_liheap_categorically_eligible: false + +# FY2026 plan page 5: TANF receipt waives the income test. A grandparent with +# 48_000 of wages cares for a grandchild who gets a child-only Florida TCA +# grant of 180 a month (180 x 12 = 2_160). Countable income is 48_000 + +# 2_160 = 50_160 with take-up and 48_000 without, both above the 42_049 +# two-person limit, so only the categorical route can qualify. TCA is the only +# qualifying receipt: it counts when taken up (first household) and not when +# it is not (second). +- name: Case 6, Florida cash assistance establishes categorical status only when taken up. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: {age: 55, ssi: 0, employment_income: 48_000} + person2: {age: 8, ssi: 0} + person3: {age: 55, ssi: 0, employment_income: 48_000} + person4: {age: 8, ssi: 0} + spm_units: + spm_unit1: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 2_160 + takes_up_tanf_if_eligible: true + snap: 0 + spm_unit2: + members: [person3, person4] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 2_160 + takes_up_tanf_if_eligible: false + snap: 0 + households: + household1: + members: [person1, person2] + state_code: FL + household2: + members: [person3, person4] + state_code: FL + output: + tanf: [2_160, 0] + fl_liheap_countable_income: [50_160, 48_000] + fl_liheap_categorically_eligible: [true, false] diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.yaml index 959b310be53..119faf62242 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.yaml @@ -1,4 +1,7 @@ -# Accepted plan pp5,8 and manual pp23,41-43: ordinary/categorical income conditions, qualified household, heating responsibility. Housing verification uses the documented existing-input approximation. +# Accepted FY2026 plan pages 5 and 8 and manual pages 23 and 41-43: ordinary +# and categorical income conditions, a qualified household and heating +# responsibility. Housing verification uses existing inputs as an +# approximation. - name: Case 1, the ordinary income ceiling is inclusive without an asset test. period: 2026 @@ -25,17 +28,17 @@ - 0 - 0 employment_income: - - 32155 - - 32155.01 + - 32_155 + - 32_155.01 heating_type: - NATURAL_GAS - NATURAL_GAS heating_expense: - - 1200 - - 1200 + - 1_200 + - 1_200 spm_unit_assets: - - 1000000 - - 1000000 + - 1_000_000 + - 1_000_000 output: fl_liheap_eligible: - true @@ -66,33 +69,33 @@ ssi: - 0 - 0 - - 1200 + - 1_200 - 0 fl_tca: - 0 - - 1200 + - 1_200 - 0 - 0 snap: - - 1200 + - 1_200 - 0 - 0 - 0 employment_income: - - 100000 - - 100000 - - 100000 - - 100000 + - 100_000 + - 100_000 + - 100_000 + - 100_000 heating_type: - NATURAL_GAS - NATURAL_GAS - NATURAL_GAS - NATURAL_GAS heating_expense: - - 1200 - - 1200 - - 1200 - - 1200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 output: fl_liheap_eligible: - true @@ -133,10 +136,10 @@ - 0 - 0 snap: - - 1200 - - 1200 - - 1200 - - 1200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 employment_income: - 0 - 0 @@ -301,3 +304,24 @@ fl_liheap_eligible: - true - false + +# Boundary amounts. FY2025 matrix: the size-1 maximum income value, +# 98_038 x 52% x 60% = 30_587.856, is printed rounded to 30_588. The size-9 +# value is 150% of the 2024 guideline, 58_100 x 1.5 = 87_150. Income at each +# printed value qualifies; one more cent does not. +- name: Case 6, fiscal year 2025 income at the printed maximum income value qualifies. + period: 2025 + input: + state_code: [FL, FL, FL, FL] + age: [40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN] + is_full_time_student: [false, false, false, false] + ssi: [0, 0, 0, 0] + fl_tca: [0, 0, 0, 0] + snap: [0, 0, 0, 0] + employment_income: [30_588, 30_588.01, 87_150, 87_150.01] + fl_liheap_household_size: [1, 1, 9, 9] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + heating_expense: [1_200, 1_200, 1_200, 1_200] + output: + fl_liheap_eligible: [true, false, true, false] diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.yaml index 6a1dfb7e03e..d7dce72fe6c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.yaml @@ -1,4 +1,6 @@ -# Current FY2026 matrix p1 literal sizes1-12. Its stated60%SMI rule gives94610 at size13; printed157686 is documented as an anomaly, not a legal100%SMI category. +# FY2026 matrix, sizes 1-12 as printed. Its stated 60% of state median income +# rule gives 94_610 at size 13; the printed 157_686 is treated as an anomaly, +# not a separate 100% of state median income category. - name: Case 1, all twelve consistent published heating limits match the current matrix. period: 2026 @@ -121,18 +123,18 @@ - NATURAL_GAS - NATURAL_GAS heating_expense: - - 1200 - - 1200 - - 1200 - - 1200 - - 1200 - - 1200 - - 1200 - - 1200 - - 1200 - - 1200 - - 1200 - - 1200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 fl_liheap_household_size: - 1 - 2 @@ -148,18 +150,18 @@ - 12 output: fl_liheap_income_limit: - - 32155 - - 42049 - - 51943 - - 61837 - - 71730 - - 81624 - - 83479 - - 85335 - - 87190 - - 89045 - - 90900 - - 92755 + - 32_155 + - 42_049 + - 51_943 + - 61_837 + - 71_730 + - 81_624 + - 83_479 + - 85_335 + - 87_190 + - 89_045 + - 90_900 + - 92_755 - name: Case 2, size thirteen follows the stated sixty percent SMI rule despite the anomalous printed row. period: 2026 @@ -183,9 +185,35 @@ heating_type: - NATURAL_GAS heating_expense: - - 1200 + - 1_200 fl_liheap_household_size: - 13 output: fl_liheap_income_limit: - - 94610 + - 94_610 + +# FY2025 matrix footnote and accepted FY2025 plan section 2.1, page 8: the +# limit is the greater of 60% of state median income and 150% of the 2024 +# poverty guidelines. State median income: 98_038 x size share x 60%, with +# shares 52%, 68%, 100%, 132% and 138% for sizes 1, 2, 4, 6 and 8, giving +# 30_587.856, 39_999.504, 58_822.8, 77_646.096 and 81_175.464 (printed +# 30_588, 40_000, 58_823, 77_646 and 81_175). Guidelines: 15_060 + 5_380 per +# additional person, times 1.5, give 87_150, 111_360 and 175_920 for sizes 9, +# 12 and 20, above the state median income amounts (82_940.148 at size 9). +- name: Case 3, fiscal year 2025 limits use the greater of state median income and poverty guideline amounts. + period: 2025 + absolute_error_margin: 0.01 + input: + state_code: [FL, FL, FL, FL, FL, FL, FL, FL] + age: [40, 40, 40, 40, 40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN] + is_full_time_student: [false, false, false, false, false, false, false, false] + ssi: [0, 0, 0, 0, 0, 0, 0, 0] + fl_tca: [0, 0, 0, 0, 0, 0, 0, 0] + snap: [0, 0, 0, 0, 0, 0, 0, 0] + employment_income: [0, 0, 0, 0, 0, 0, 0, 0] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + heating_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + fl_liheap_household_size: [1, 2, 4, 6, 8, 9, 12, 20] + output: + fl_liheap_income_limit: [30_587.856, 39_999.504, 58_822.8, 77_646.096, 81_175.464, 87_150, 111_360, 175_920] diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml index 4e48a187d28..f8b7feac01a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml @@ -1,4 +1,10 @@ -# FY2026 matrix p1 and accepted plan p9: base350/300/250/200; elderly100, disability100, child<=5 adds150, once per category. Manualp23 bases payment on the matrix, not actual bill. Categorical income above table uses a documented200-base estimate, not an express source instruction. +# FY2026 matrix and accepted FY2026 plan page 9: bases of 350/300/250/200. +# FY2025 matrix: bases of 1_000/750/550/400. Both years add 100 for a member +# age 60 or older, 100 for a disabled member and 150 for a child age 5 or +# younger, each once per household. Manual page 23 bases the payment on the +# matrix, not the actual bill. Categorical households with income above the +# table receive the smallest base as a documented estimate; the sources give +# no express instruction for that case. - name: Case 1, published income band tops include exact amounts and the next cent starts the next band. period: 2026 @@ -68,13 +74,13 @@ - 0 employment_income: - 0 - - 16078 - - 16078.01 - - 22509 - - 22509.01 - - 27332 - - 27332.01 - - 32155 + - 16_078 + - 16_078.01 + - 22_509 + - 22_509.01 + - 27_332 + - 27_332.01 + - 32_155 heating_type: - NATURAL_GAS - NATURAL_GAS @@ -85,14 +91,14 @@ - NATURAL_GAS - NATURAL_GAS heating_expense: - - 1200 - - 1200 - - 1200 - - 1200 - - 1200 - - 1200 - - 1200 - - 1200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 output: fl_liheap: - 350 @@ -152,10 +158,10 @@ - NATURAL_GAS - NATURAL_GAS heating_expense: - - 1200 - - 1200 - - 1200 - - 1200 + - 1_200 + - 1_200 + - 1_200 + - 1_200 is_disabled: - false - false @@ -171,28 +177,26 @@ period: 2026 input: people: - parent: + person1: age: 40 immigration_status: CITIZEN is_full_time_student: false ssi: 0 - child: + person2: age: 5 immigration_status: CITIZEN is_full_time_student: false ssi: 0 spm_units: - family: - members: &id001 - - parent - - child + spm_unit: + members: [person1, person2] heating_type: NATURAL_GAS - heating_expense: 1200 + heating_expense: 1_200 fl_tca: 0 snap: 0 households: - home: - members: *id001 + household: + members: [person1, person2] state_code: FL output: fl_liheap: 500 @@ -200,28 +204,26 @@ period: 2026 input: people: - parent: + person1: age: 40 immigration_status: CITIZEN is_full_time_student: false ssi: 0 - child: + person2: age: 6 immigration_status: CITIZEN is_full_time_student: false ssi: 0 spm_units: - family: - members: &id002 - - parent - - child + spm_unit: + members: [person1, person2] heating_type: NATURAL_GAS - heating_expense: 1200 + heating_expense: 1_200 fl_tca: 0 snap: 0 households: - home: - members: *id002 + household: + members: [person1, person2] state_code: FL output: fl_liheap: 350 @@ -229,82 +231,75 @@ period: 2026 input: people: - elder1: + person1: age: 65 immigration_status: CITIZEN is_full_time_student: false ssi: 0 is_disabled: true - elder2: + person2: age: 64 immigration_status: CITIZEN is_full_time_student: false ssi: 0 is_disabled: true - parent: + person3: age: 40 immigration_status: CITIZEN is_full_time_student: false ssi: 0 - child1: + person4: age: 5 immigration_status: CITIZEN is_full_time_student: false ssi: 0 - child2: + person5: age: 3 immigration_status: CITIZEN is_full_time_student: false ssi: 0 spm_units: - family: - members: &id003 - - elder1 - - elder2 - - parent - - child1 - - child2 + spm_unit: + members: [person1, person2, person3, person4, person5] heating_type: NATURAL_GAS - heating_expense: 1200 + heating_expense: 1_200 fl_tca: 0 snap: 0 households: - home: - members: *id003 + household: + members: [person1, person2, person3, person4, person5] state_code: FL output: fl_liheap: 700 -- name: Case 6, a nonqualified parents full income determines the band for the qualified child. +- name: Case 6, a nonqualified parent's full income determines the band for the qualified child. period: 2026 input: people: - parent: + person1: age: 40 immigration_status: UNDOCUMENTED is_full_time_student: false ssi: 0 - employment_income: 30000 - child: + employment_income: 30_000 + person2: age: 5 immigration_status: CITIZEN is_full_time_student: false ssi: 0 spm_units: - family: - members: &id004 - - parent - - child + spm_unit: + members: [person1, person2] heating_type: NATURAL_GAS - heating_expense: 1200 + heating_expense: 1_200 fl_tca: 0 snap: 0 households: - home: - members: *id004 + household: + members: [person1, person2] state_code: FL output: fl_liheap_household_size: 1 - fl_liheap_countable_income: 30000 + fl_liheap_countable_income: 30_000 fl_liheap: 350 - name: Case 7, categorical income above the table uses the documented minimum base estimate. period: 2026 @@ -322,13 +317,13 @@ fl_tca: - 0 snap: - - 1200 + - 1_200 employment_income: - - 100000 + - 100_000 heating_type: - NATURAL_GAS heating_expense: - - 1200 + - 1_200 output: fl_liheap_eligible: - true @@ -359,7 +354,7 @@ heating_expense: - 10 hud_utility_allowance: - - 2400 + - 2_400 is_in_public_housing: - true receives_housing_assistance: @@ -483,9 +478,9 @@ - 350 - 0 -# Accepted plan section 1.9 p7 and FY2026 matrix p1, confirmed review C1/C2: -# 40_000 - 30_000 = 10_000 net gambling is in the 350 band; 40_000 periodic -# estate receipts exceed the ordinary 32_155 limit. No categorical receipts. +# Accepted FY2026 plan section 1.9, page 7, and the FY2026 matrix: net +# gambling of 40_000 - 30_000 = 10_000 falls in the 350 band; 40_000 of +# periodic estate receipts exceeds the 32_155 limit. No categorical receipts. - name: Case 11, net gambling and periodic estate income determine ordinary awards. period: 2026 absolute_error_margin: 0.01 @@ -536,3 +531,230 @@ state_code: FL output: fl_liheap: 500 + +# FY2025 matrix ("Updated FY 2024/2025"), size 1: the maximum income value is +# 98_038 x 52% x 60% = 30_587.856 (printed 30_588). The first band ends at +# 30_587.856 x 50% = 15_293.928, printed 15_294. Wages of 10_000 fall in the +# first band, which pays 1_000; nobody qualifies for an addition. +- name: Case 13, a one-person household in fiscal year 2025 receives the first band award. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 10_000 + spm_units: + spm_unit: + members: [person1] + heating_type: NATURAL_GAS + heating_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1] + state_code: FL + output: + fl_liheap_household_size: 1 + fl_liheap_countable_income: 10_000 + fl_liheap_income_limit: 30_587.856 + fl_liheap_eligible: true + fl_liheap: 1_000 + +# Boundary amounts. FY2025 matrix, size 1, prints the bands 15_294 or less, +# 15_295-21_411, 21_412-26_000 and 26_001-30_588. These round +# 30_587.856 x 50%, 70% and 85% (15_293.928, 21_411.499 and 25_999.678) and +# the maximum income value to the nearest dollar. Each printed top pays its +# band's base; one dollar more moves to the next band, and 30_589 exceeds the +# maximum income value. +- name: Case 14, fiscal year 2025 one-person band tops follow the printed matrix. + period: 2025 + input: + state_code: [FL, FL, FL, FL, FL, FL, FL, FL] + age: [40, 40, 40, 40, 40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN] + is_full_time_student: [false, false, false, false, false, false, false, false] + ssi: [0, 0, 0, 0, 0, 0, 0, 0] + fl_tca: [0, 0, 0, 0, 0, 0, 0, 0] + snap: [0, 0, 0, 0, 0, 0, 0, 0] + employment_income: [15_294, 15_295, 21_411, 21_412, 26_000, 26_001, 30_588, 30_589] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + heating_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + output: + fl_liheap: [1_000, 750, 750, 550, 550, 400, 400, 0] + +# FY2025 matrix footnote and accepted FY2025 plan section 2.1, page 8: sizes 9 +# and above use 150% of the 2024 poverty guidelines when that exceeds 60% of +# state median income. Guideline 15_060 + 8 x 5_380 = 58_100, so the limit is +# 58_100 x 1.5 = 87_150; 60% of state median income is only +# 98_038 x 141% x 60% = 82_940.148. Wages of 50_000 + 35_000 = 85_000 fall in +# the over-125% band (72_626-87_150), which pays 400. No member is 60 or +# older, disabled, or 5 or younger. +- name: Case 15, a nine-person household in fiscal year 2025 qualifies under the poverty guideline limit. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + person1: {age: 44, ssi: 0, employment_income: 50_000} + person2: {age: 42, ssi: 0, employment_income: 35_000} + person3: {age: 17, ssi: 0} + person4: {age: 15, ssi: 0} + person5: {age: 13, ssi: 0} + person6: {age: 11, ssi: 0} + person7: {age: 9, ssi: 0} + person8: {age: 8, ssi: 0} + person9: {age: 6, ssi: 0} + spm_units: + spm_unit: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9] + heating_type: NATURAL_GAS + heating_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1, person2, person3, person4, person5, person6, person7, person8, person9] + state_code: FL + output: + fl_liheap_household_size: 9 + fl_liheap_countable_income: 85_000 + fl_liheap_income_limit: 87_150 + fl_liheap_eligible: true + fl_liheap: 400 + +# Boundary amounts. FY2025 matrix, size 9: the bands are percentages of the +# 58_100 guideline. "75% of FPG or Less" prints 43_574, and "At least 75%" +# starts at 58_100 x 75% = 43_575. The second band ends at 58_100 (100%), the +# third at 72_625 (125%), and the maximum income value is 87_150 (150%). +- name: Case 16, fiscal year 2025 nine-person bands follow poverty guideline percentages. + period: 2025 + input: + state_code: [FL, FL, FL, FL, FL, FL, FL, FL] + age: [40, 40, 40, 40, 40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN] + is_full_time_student: [false, false, false, false, false, false, false, false] + ssi: [0, 0, 0, 0, 0, 0, 0, 0] + fl_tca: [0, 0, 0, 0, 0, 0, 0, 0] + snap: [0, 0, 0, 0, 0, 0, 0, 0] + employment_income: [43_574, 43_575, 58_100, 58_101, 72_625, 72_626, 87_150, 87_151] + fl_liheap_household_size: [9, 9, 9, 9, 9, 9, 9, 9] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + heating_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + output: + fl_liheap: [1_000, 750, 750, 550, 550, 400, 400, 0] + +# Boundary amounts. FY2026 matrix: 70% of the maximum income value is exactly +# half a dollar at sizes 8, 10 and 12 (85_335 x 70% = 59_734.5, +# 89_045 x 70% = 62_331.5 and 92_755 x 70% = 64_928.5). The matrix rounds each +# up, so the second band ends at 59_735, 62_332 and 64_929. The printed top +# pays 300, and the next cent starts the third band, which pays 250. +- name: Case 17, fiscal year 2026 second band tops for sizes eight, ten and twelve round half up. + period: 2026 + input: + state_code: [FL, FL, FL, FL, FL, FL] + age: [40, 40, 40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN] + is_full_time_student: [false, false, false, false, false, false] + ssi: [0, 0, 0, 0, 0, 0] + fl_tca: [0, 0, 0, 0, 0, 0] + snap: [0, 0, 0, 0, 0, 0] + employment_income: [59_735, 59_735.01, 62_332, 62_332.01, 64_929, 64_929.01] + fl_liheap_household_size: [8, 8, 10, 10, 12, 12] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + heating_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + output: + fl_liheap: [300, 250, 300, 250, 300, 250] + +# Accepted FY2026 plan section 1.9, page 6, counts retirement and pension +# benefits; military retirement pay and railroad retirement benefits are +# separate inputs from pension_income. 35_000 of military retirement pay +# exceeds the 32_155 one-person limit. 20_000 of railroad retirement benefits +# falls in the second band (16_079-22_509): 300 base + 100 for age 60 or older. +- name: Case 18, military retirement pay and railroad retirement benefits count toward income. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [FL, FL] + age: [50, 62] + immigration_status: [CITIZEN, CITIZEN] + is_full_time_student: [false, false] + ssi: [0, 0] + fl_tca: [0, 0] + snap: [0, 0] + military_retirement_pay: [35_000, 0] + railroad_benefits: [0, 20_000] + heating_type: [NATURAL_GAS, NATURAL_GAS] + heating_expense: [1_200, 1_200] + output: + fl_liheap_countable_income: [35_000, 20_000] + fl_liheap_eligible: [false, true] + fl_liheap: [0, 400] + +# Accepted FY2026 plan section 1.8, page 5, counts gross income, and section +# 1.9, page 7, counts "net gambling or lottery winnings". A 20_000 +# self-employment loss does not offset 40_000 of wages. Gambling losses of +# 3_000 offset only the 1_000 of winnings, leaving 34_000 of wages. Both +# amounts exceed the 32_155 one-person limit. +- name: Case 19, a business loss or excess gambling losses do not bring income under the limit. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [FL, FL] + age: [40, 40] + immigration_status: [CITIZEN, CITIZEN] + is_full_time_student: [false, false] + ssi: [0, 0] + fl_tca: [0, 0] + snap: [0, 0] + employment_income: [40_000, 34_000] + self_employment_income: [-20_000, 0] + gambling_winnings: [0, 1_000] + gambling_losses: [0, 3_000] + heating_type: [NATURAL_GAS, NATURAL_GAS] + heating_expense: [1_200, 1_200] + output: + fl_liheap_countable_income: [40_000, 34_000] + fl_liheap_eligible: [false, false] + fl_liheap: [0, 0] + +# Accepted FY2025 plan section 2.6, page 10: "Maximum Benefit $1,350". FY2025 +# matrix, size 2: 98_038 x 68% x 60% = 39_999.504 (printed 40_000); the first +# band ends at 39_999.504 x 50% = 19_999.752, printed 20_000. Gross Social +# Security of 14_400 falls in the first band: 1_000 base + 100 elderly + 100 +# disabled + 150 for a child age 5 or younger = 1_350. +- name: Case 20, a fiscal year 2025 household with every addition receives the published maximum. + period: 2025 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 66 + immigration_status: CITIZEN + is_full_time_student: false + is_disabled: true + ssi: 0 + social_security: 14_400 + person2: + age: 4 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + heating_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: FL + output: + fl_liheap_countable_income: 14_400 + fl_liheap_income_limit: 39_999.504 + fl_liheap: 1_350 diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap_household_size.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap_household_size.yaml index 8874e8e3efe..017e9c8a447 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap_household_size.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap_household_size.yaml @@ -1,6 +1,7 @@ # Florida independent household income and categorical-income components; no final program eligibility or award is asserted. -# Manual pp41-42 counts citizens and qualified noncitizens, excludes nonqualified people from size; does not import SNAP-specific waiting bars. +# Manual pages 41-42 count citizens and qualified noncitizens and exclude +# nonqualified people from the size; SNAP-specific waiting periods do not apply. - name: Case 1, qualified adults and children count without a SNAP waiting bar. period: 2026 input: @@ -44,7 +45,8 @@ output: fl_liheap_household_size: 3 -# Manual pp41-42 requires at least one qualified member for final program eligibility; this independent size component returns0. +# Manual pages 41-42 require at least one qualified member for final program +# eligibility; this size component returns 0. - name: Case 2, only nonqualified members produce a zero household size. period: 2026 input: diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml index 9f15299f670..b54a6afbf74 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml @@ -1,6 +1,7 @@ # Florida independent household income and categorical-income components; no final program eligibility or award is asserted. -# Manual pp49-50/current planpp5-6:12000+2400+600+1200+1200=17400 under existing net-business input approximation. +# Manual pages 49-50 and FY2026 plan pages 5-6: 12_000 + 2_400 + 600 + 1_200 + +# 1_200 = 17_400 from the existing net business inputs. - name: Case 1, adult wages and existing net business income count without a second expense deduction. period: 2026 absolute_error_margin: 0.01 @@ -30,8 +31,10 @@ output: fl_liheap_countable_income: 17_400 -# Contract current-input approximation preserves signed netbusiness:12000-6000+600=6600. Source lacks an express individual loss floor; this is not a new business deduction. -- name: Case 2, existing signed business inputs offset other income before the household floor. +# FY2026 plan section 1.8, page 5, counts gross income, and manual 1200.01B, +# page 49, counts "the gross amount of income", so each source is floored at +# zero: 12_000 wages + max(-6_000, 0) + 600 partnership income = 12_600. +- name: Case 2, a self-employment loss does not offset wages or partnership income. period: 2026 absolute_error_margin: 0.01 input: @@ -56,10 +59,10 @@ members: [person1] state_code: FL output: - fl_liheap_countable_income: 6_600 + fl_liheap_countable_income: 12_600 -# Approved household floor: max(1200-6000,0)=0; no negative countable income. -- name: Case 3, negative combined income is floored at zero. +# Each source is floored at zero: 1_200 wages + max(-6_000, 0) = 1_200. +- name: Case 3, a self-employment loss larger than wages leaves the wages counted. period: 2026 absolute_error_margin: 0.01 input: @@ -83,9 +86,11 @@ members: [person1] state_code: FL output: - fl_liheap_countable_income: 0 + fl_liheap_countable_income: 1_200 -# Current FY2026 plan p6 leaves UI/cash gifts unselected.30000 wages remain30000; UI6000, gifts2400 and SNAP6000 do not count. +# FY2026 plan page 6 leaves unemployment compensation and cash gifts +# unselected. Wages of 30_000 count; 6_000 of unemployment compensation, 2_400 +# of gifts and 6_000 of SNAP do not. - name: Case 4, current plan excludes unemployment and cash gifts. period: 2026 absolute_error_margin: 0.01 @@ -113,8 +118,14 @@ output: fl_liheap_countable_income: 30_000 -# Current plan p6 excludes earnings under 18; manual p49 counts unearned income at 18 or 16/17 if not a full-time student. Plan p7 includes periodic estate receipts and net gambling. Parent 12_000; young member 6_000 wages + 1_200 business + 1_200 SSA + 900 estate + (600 - 100) gambling when all included. -- name: Case 5, age 15 nonstudent follows separate earned and unearned age rules. +# Manual 1200.01D, page 49, applies one age and student test to all income: +# members 18 and older count, and so do 16- and 17-year-olds who do not attend +# school full time. The model follows it over plan item 1.9, page 6, which +# leaves earned income of a child under 18 unchecked. Plan page 7 counts net +# gambling and periodic estate receipts. At 15 the young member's 6_000 wages +# + 1_200 self-employment + 1_200 Social Security + 900 estate + (600 - 100) +# gambling = 9_800 is excluded, leaving the parent's 12_000. +- name: Case 5, a 15-year-old nonstudent's income is excluded under the manual's age and student test. period: 2026 absolute_error_margin: 0.01 input: @@ -150,8 +161,11 @@ output: fl_liheap_countable_income: 12_000 -# Current plan p6 excludes earnings under 18; manual p49 counts unearned income at 18 or 16/17 if not a full-time student. Plan p7 includes periodic estate receipts and net gambling. Parent 12_000; young member 6_000 wages + 1_200 business + 1_200 SSA + 900 estate + (600 - 100) gambling when all included. -- name: Case 6, age 16 nonstudent follows separate earned and unearned age rules. +# Manual 1200.01D, page 49, counts all income of a 16- or 17-year-old who does +# not attend school full time; the model follows it over plan item 1.9, page 6. +# Parent 12_000 + young member 6_000 wages + 1_200 self-employment + 1_200 +# Social Security + 900 estate + (600 - 100) gambling = 21_800. +- name: Case 6, a 16-year-old nonstudent's earned and unearned income both count. period: 2026 absolute_error_margin: 0.01 input: @@ -185,10 +199,13 @@ members: [person1, person2] state_code: FL output: - fl_liheap_countable_income: 14_600 + fl_liheap_countable_income: 21_800 -# Current plan p6 excludes earnings under 18; manual p49 counts unearned income at 18 or 16/17 if not a full-time student. Plan p7 includes periodic estate receipts and net gambling. Parent 12_000; young member 6_000 wages + 1_200 business + 1_200 SSA + 900 estate + (600 - 100) gambling when all included. -- name: Case 7, age 16 full time student follows separate earned and unearned age rules. +# Manual 1200.01D, page 49, counts a 16- or 17-year-old's income only when the +# member does not attend school full time. The young member's 6_000 wages + +# 1_200 self-employment + 1_200 Social Security + 900 estate + (600 - 100) +# gambling = 9_800 is excluded, leaving the parent's 12_000. +- name: Case 7, a 16-year-old full-time student's earned and unearned income are both excluded. period: 2026 absolute_error_margin: 0.01 input: @@ -224,8 +241,11 @@ output: fl_liheap_countable_income: 12_000 -# Current plan p6 excludes earnings under 18; manual p49 counts unearned income at 18 or 16/17 if not a full-time student. Plan p7 includes periodic estate receipts and net gambling. Parent 12_000; young member 6_000 wages + 1_200 business + 1_200 SSA + 900 estate + (600 - 100) gambling when all included. -- name: Case 8, age 17 nonstudent follows separate earned and unearned age rules. +# Manual 1200.01D, page 49, counts all income of a 16- or 17-year-old who does +# not attend school full time; the model follows it over plan item 1.9, page 6. +# Parent 12_000 + young member 6_000 wages + 1_200 self-employment + 1_200 +# Social Security + 900 estate + (600 - 100) gambling = 21_800. +- name: Case 8, a 17-year-old nonstudent's earned and unearned income both count. period: 2026 absolute_error_margin: 0.01 input: @@ -259,10 +279,13 @@ members: [person1, person2] state_code: FL output: - fl_liheap_countable_income: 14_600 + fl_liheap_countable_income: 21_800 -# Current plan p6 excludes earnings under 18; manual p49 counts unearned income at 18 or 16/17 if not a full-time student. Plan p7 includes periodic estate receipts and net gambling. Parent 12_000; young member 6_000 wages + 1_200 business + 1_200 SSA + 900 estate + (600 - 100) gambling when all included. -- name: Case 9, age 17 full time student follows separate earned and unearned age rules. +# Manual 1200.01D, page 49, counts a 16- or 17-year-old's income only when the +# member does not attend school full time. The young member's 6_000 wages + +# 1_200 self-employment + 1_200 Social Security + 900 estate + (600 - 100) +# gambling = 9_800 is excluded, leaving the parent's 12_000. +- name: Case 9, a 17-year-old full-time student's earned and unearned income are both excluded. period: 2026 absolute_error_margin: 0.01 input: @@ -298,8 +321,11 @@ output: fl_liheap_countable_income: 12_000 -# Current plan p6 excludes earnings under 18; manual p49 counts unearned income at 18 or 16/17 if not a full-time student. Plan p7 includes periodic estate receipts and net gambling. Parent 12_000; young member 6_000 wages + 1_200 business + 1_200 SSA + 900 estate + (600 - 100) gambling when all included. -- name: Case 10, age 18 full time student follows separate earned and unearned age rules. +# Manual 1200.01D, page 49, counts any income of a member 18 and older, +# whether or not a full-time student: parent 12_000 + young member 6_000 +# wages + 1_200 self-employment + 1_200 Social Security + 900 estate + +# (600 - 100) gambling = 21_800. +- name: Case 10, an 18-year-old full-time student's earned and unearned income both count. period: 2026 absolute_error_margin: 0.01 input: @@ -335,7 +361,9 @@ output: fl_liheap_countable_income: 21_800 -# Current planp6 rent and retirement categories:30000+1200ordinaryrent+5000farmrent+1200survivor=37400; no assumed rental loss or extra allowance. +# FY2026 plan page 6 rent and retirement categories: 30_000 wages + 1_200 +# rent + 5_000 farm rent + 1_200 survivor benefits = 37_400, with no added +# allowance. - name: Case 11, rental and survivor inputs count independently in full. period: 2026 absolute_error_margin: 0.01 @@ -364,7 +392,12 @@ output: fl_liheap_countable_income: 37_400 -# Current plan pp6-7: prior disjoint receipts 27_360 + 2_400 periodic estate income = 29_760. Estate receipts exclude inherited principal and separately reported interest/dividends; annual SSI, pension and SSA count once. +# FY2026 plan pages 6-7: 1_200 SSI + 18_000 Social Security + 2_400 pension + +# 1_200 survivor + 600 workers' compensation + 600 disability + 600 veterans + +# 300 general assistance + 300 strike + 1_200 child support + 600 alimony + +# 120 interest + 240 dividends = 27_360, plus 2_400 of periodic estate income +# = 29_760. Estate receipts exclude inherited principal and separately reported +# interest and dividends; each source counts once. - name: Case 12, disjoint covered unearned receipts and canonical dividends count once. period: 2026 absolute_error_margin: 0.01 @@ -402,7 +435,9 @@ output: fl_liheap_countable_income: 29_760 -# Current planp6 expressly marks SSA including Medicare deduction; manualp50 adds premiums back to net checks. Gross24000 remains24000, not21600. +# FY2026 plan page 6 counts Social Security "Including MediCare deduction"; +# manual page 50 adds premiums back to net checks. Gross 24_000 counts, not +# 21_600. - name: Case 13, gross Social Security remains counted before Medicare premiums. period: 2026 absolute_error_margin: 0.01 @@ -429,7 +464,9 @@ output: fl_liheap_countable_income: 24_000 -# Current planp6 counts TANF:6000 adult wages+3600 annual Florida TCA=9600. Child SSA1200 excluded by the older detailed age rule; monthly state grant is added once perfamily. +# FY2026 plan page 6 counts TANF: 6_000 adult wages + 3_600 annual Florida +# TCA = 9_600. The child's 1_200 of Social Security is excluded by the manual's +# age rule; the monthly state grant is added once per family. - name: Case 14, state cash assistance is annualized once for the family. period: 2026 absolute_error_margin: 0.01 @@ -461,7 +498,9 @@ output: fl_liheap_countable_income: 9_600 -# Manualp42 retains all nonqualified adult income despite size1; minor SSA excluded under manualp49 age detail. No proration. +# Manual page 42 retains all of a nonqualified adult's income although the +# household size is 1; the minor's Social Security is excluded under the +# manual page 49 age rule. No proration. - name: Case 15, nonqualified adult income is retained in full for a qualified child household. period: 2026 absolute_error_margin: 0.01 @@ -494,7 +533,8 @@ fl_liheap_household_size: 1 fl_liheap_countable_income: 20_000 -# Accepted FY2026 plan section 1.9, physical p7: net gambling or lottery winnings. Confirmed review C1: 40_000 winnings - 30_000 associated losses = 10_000. +# Accepted FY2026 plan section 1.9, page 7: net gambling or lottery winnings. +# 40_000 winnings - 30_000 losses = 10_000. - name: Case 16, gambling winnings count net of associated losses. period: 2026 absolute_error_margin: 0.01 @@ -521,9 +561,10 @@ output: fl_liheap_countable_income: 10_000 -# Plan p7 requires net gambling; contract v1.2 preserves only the household floor. -# max(1_200 + 4_000 - losses, 0) = 1_200, 200, 0; no per-source loss floor. -- name: Case 17, net gambling losses offset income before the household floor. +# Plan section 1.9, page 7, counts net gambling winnings, so losses offset only +# winnings: 1_200 wages + max(4_000 - losses, 0) = 1_200 for losses of 4_000, +# 5_000 and 10_000. +- name: Case 17, gambling losses offset only gambling winnings. period: 2026 absolute_error_margin: 0.01 input: @@ -540,4 +581,123 @@ heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] gas_expense: [1_200, 1_200, 1_200] output: - fl_liheap_countable_income: [1_200, 200, 0] + fl_liheap_countable_income: [1_200, 1_200, 1_200] + +# FY2026 plan section 1.9, page 6, counts retirement and pension benefits. +# Military retirement pay and railroad retirement benefits are separate inputs +# from pension_income: 24_000 military retirement pay + 6_000 private pension +# + 18_000 railroad retirement benefits = 48_000. +- name: Case 18, military retirement pay and railroad retirement benefits count beside pensions. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 58 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + military_retirement_pay: 24_000 + taxable_private_pension_income: 6_000 + person2: + age: 63 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + railroad_benefits: 18_000 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: FL + output: + fl_liheap_countable_income: 48_000 + +# FY2026 plan section 1.8, page 5, counts gross income, so each earned and +# unearned source is floored at zero and a loss in one source or member cannot +# offset another. Counted: 30_000 wages + 500 interest = 30_500. Floored to +# zero: farm operations -2_500 and farm rent -4_000 (person1); self-employment +# -8_000, partnership -1_000, rental -3_000 and estate -1_500 (person2). +- name: Case 19, losses in business, rental, farm rent and estate sources are each floored at zero. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 30_000 + farm_operations_income: -2_500 + farm_rent_income: -4_000 + person2: + age: 44 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + self_employment_income: -8_000 + partnership_s_corp_income: -1_000 + rental_income: -3_000 + estate_income: -1_500 + interest_income: 500 + spm_units: + spm_unit: + members: [person1, person2] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 0 + snap: 0 + households: + household: + members: [person1, person2] + state_code: FL + output: + fl_liheap_countable_income: 30_500 + +# FY2026 plan section 1.9, page 6, counts TANF received. Florida TCA for a +# family of three with a shelter obligation over $50 is 303 a month, or +# 303 x 12 = 3_636 a year. The take-up-gated tanf total counts it when the +# family takes up TCA (first household) and adds 0 when it does not (second). +- name: Case 20, Florida cash assistance counts only when the family takes it up. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: {age: 30, ssi: 0} + person2: {age: 7, ssi: 0} + person3: {age: 4, ssi: 0} + person4: {age: 30, ssi: 0} + person5: {age: 7, ssi: 0} + person6: {age: 4, ssi: 0} + spm_units: + spm_unit1: + members: [person1, person2, person3] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 3_636 + takes_up_tanf_if_eligible: true + snap: 0 + spm_unit2: + members: [person4, person5, person6] + heating_type: NATURAL_GAS + gas_expense: 1_200 + fl_tca: 3_636 + takes_up_tanf_if_eligible: false + snap: 0 + households: + household1: + members: [person1, person2, person3] + state_code: FL + household2: + members: [person4, person5, person6] + state_code: FL + output: + tanf: [3_636, 0] + fl_liheap_countable_income: [3_636, 0] diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.py index 6c288fe7d5c..ff4810b8eec 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.py @@ -15,7 +15,7 @@ class fl_liheap_categorically_eligible(Variable): def formula(spm_unit, period, parameters): p = parameters(period).gov.states.fl.commerce.liheap.eligibility receipts = add(spm_unit, period, p.categorical_programs, options=[ADD]) - # Positive SNAP, state cash assistance, or SSI is the receipt proxy. + # Positive take-up-gated SNAP, TANF, or SSI is the receipt proxy. # A minor's SSI may establish this status even when excluded from income. # This waives only the income test, not heating responsibility, residency, # or qualified-member requirements, and does not select a benefit band. diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py index cdde1ba7d1f..15fbf232686 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py @@ -16,7 +16,8 @@ class fl_liheap_eligible(Variable): def formula(spm_unit, period, parameters): size = spm_unit("fl_liheap_household_size", period) income = spm_unit("fl_liheap_countable_income", period) - limit = spm_unit("fl_liheap_income_limit", period) + # The matrices print the maximum income value in whole dollars. + limit = np.floor(spm_unit("fl_liheap_income_limit", period) + 0.5) categorical = spm_unit("fl_liheap_categorically_eligible", period) heating_expense = spm_unit("heating_expense", period) heat_in_rent = spm_unit("heat_expense_included_in_rent", period) diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py index a6d683daafb..63754ffb0fc 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py @@ -1,5 +1,6 @@ from policyengine_us.model_api import * from policyengine_us.variables.gov.hhs.hhs_smi import smi +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg class fl_liheap_income_limit(Variable): @@ -7,25 +8,38 @@ class fl_liheap_income_limit(Variable): entity = SPMUnit definition_period = YEAR unit = USD - label = "Florida LIHEAP annual heating income limit" + label = "Florida LIHEAP annual heating maximum income value before rounding" defined_for = StateCode.FL reference = ( "https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=8", "https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf", "https://liheapch.acf.gov/delivery/income_eligibility.htm", + "https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=8", + "https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf", ) def formula(spm_unit, period, parameters): p = parameters(period).gov.states.fl.commerce.liheap.eligibility size = spm_unit("fl_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 - # This floor order reconciles all FY2026 matrix rows for sizes 1-12; - # the sources do not expressly prescribe the rounding order. - four_person_limit = np.floor(four_person_smi * p.smi_rate) - # The printed size-13 maximum, $157,686, equals 100% of SMI. Follow - # the matrix's explicit 60% rule instead ($94,610). The federal table - # restricts its 150%-FPG large-household exception to cooling/crisis. - # Larger sizes follow the federal factors without table verification. - return where(size > 0, np.floor(four_person_limit * size_share), 0) + size_smi = smi(size, state, period, parameters) + if p.truncates_smi_limit: + four_person_smi = parameters(period).gov.hhs.smi.amount[state] + size_share = size_smi / four_person_smi + # This floor order reconciles all FY2026 matrix rows for sizes 1-12; + # the sources do not expressly prescribe the rounding order. + four_person_limit = np.floor(four_person_smi * p.smi_rate) + smi_limit = np.floor(four_person_limit * size_share) + else: + # The FY2025 matrix rounds this unrounded amount, and each band + # cutoff derived from it, to the nearest dollar. + smi_limit = size_smi * p.smi_rate + # The printed FY2026 size-13 maximum, $157,686, equals 100% of SMI. + # Follow the matrix's explicit 60% rule instead ($94,610). The federal + # table restricts its 150%-FPG large-household exception to + # cooling/crisis. Larger sizes follow the federal factors without table + # verification. + state_group = spm_unit.household("state_group_str", period) + guideline = fpg(size, state_group, period, parameters, year_lag=p.fpg_year_lag) + limit = max_(smi_limit, guideline * p.fpg_rate) + return where(size > 0, limit, 0) diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py index 3f5603162d8..fb49a903046 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py @@ -1,4 +1,5 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg class fl_liheap(Variable): @@ -13,37 +14,69 @@ class fl_liheap(Variable): "https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf", "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=8", "https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=9", + "https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf", ) def formula(spm_unit, period, parameters): - p = parameters(period).gov.states.fl.commerce.liheap.payment + p = parameters(period).gov.states.fl.commerce.liheap income = spm_unit("fl_liheap_countable_income", period) limit = spm_unit("fl_liheap_income_limit", period) + size = spm_unit("fl_liheap_household_size", period) + state_group = spm_unit.household("state_group_str", period) + guideline = fpg( + size, + state_group, + period, + parameters, + year_lag=p.eligibility.fpg_year_lag, + ) + # Where the poverty-guideline limit sets the maximum income value + # (FY2025 sizes 9 and above), the bands are guideline percentages. + fpg_based = guideline * p.eligibility.fpg_rate >= limit + fraction = p.payment.income_band_fraction + band_fpg_rate = p.payment.income_band_fpg_rate # Half-up dollar cutoffs reproduce the printed bands without leaving # gaps for income with cents between successive printed dollar bounds. - first = np.floor(limit * p.income_band_fraction.first + 0.5) - second = np.floor(limit * p.income_band_fraction.second + 0.5) - third = np.floor(limit * p.income_band_fraction.third + 0.5) + first = where( + fpg_based, + guideline * band_fpg_rate.first, + np.floor(limit * fraction.first + 0.5), + ) + second = where( + fpg_based, + guideline * band_fpg_rate.second, + np.floor(limit * fraction.second + 0.5), + ) + third = where( + fpg_based, + guideline * band_fpg_rate.third, + np.floor(limit * fraction.third + 0.5), + ) + # The FY2025 second guideline band starts "At least 75%" of the + # guideline, so the first guideline band excludes its upper bound. + in_first = where(fpg_based, income < first, income <= first) band = select( - [income <= first, income <= second, income <= third], + [in_first, income <= second, income <= third], [1, 2, 3], default=4, ) # Categorical households above the printed maximum use the smallest # base as an estimate; sources waive the income test but do not supply # an above-range benefit band. Receipt alone never selects a top band. - base = p.base_amount[band] + base = p.payment.base_amount[band] age = spm_unit.members("age", period) - elderly = spm_unit.any(age >= p.elderly_min_age) - disabled = spm_unit.any(spm_unit.members("is_disabled", period)) - child = spm_unit.any(age < p.child_max_age + 1) + elderly = spm_unit.any(age >= p.payment.elderly_min_age) + disabled = add(spm_unit, period, ["is_disabled"]) > 0 + child = spm_unit.any(age < p.payment.child_max_age + 1) + supplement = p.payment.supplement supplements = ( - elderly * p.supplement.elderly - + disabled * p.supplement.disabled - + child * p.supplement.child + elderly * supplement.elderly + + disabled * supplement.disabled + + child * supplement.child ) - # Follow the operative October 2025 matrix ($200-$700), as required by - # the manual; the plan's estimated $400-$1,350 extrema conflict with it. + # FY2026 follows the operative October 2025 matrix ($200-$700), as + # required by the manual; the FY2026 plan's estimated $400-$1,350 + # extrema conflict with it. The FY2025 plan and matrix agree. # One award per year is modeled. No actual-bill cap or HUD deduction # applies to regular benefits; previous awards require unavailable data. eligible = spm_unit("fl_liheap_eligible", period) diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py index ce2f3c0dab6..021cf6e56d1 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py @@ -7,7 +7,7 @@ class fl_liheap_household_size(Variable): definition_period = YEAR label = "Florida LIHEAP household size" defined_for = StateCode.FL - reference = "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=41" + reference = "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=42" # Ineligible members are excluded from size, but their otherwise countable # income remains included in full. SPM membership approximates the energy diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py index 1baedacf30a..ce7ae95b69a 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py @@ -12,38 +12,48 @@ class fl_liheap_countable_income(Variable): reference = ( "https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5", "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49", + "https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=5", ) def formula(spm_unit, period, parameters): p = parameters(period).gov.states.fl.commerce.liheap.income person = spm_unit.members age = person("age", period) - adult = age >= p.adult_age - # The current plan excludes all earnings under 18. The older detailed - # manual supplies the unearned-income age rule where the current plan - # is silent: include 16/17-year-olds who are not full-time students. - unearned_age_eligible = adult | ( + # Plan vs manual conflict; this follows the manual for earned and + # unearned income alike. Manual 1200.01D (PDF p. 49): "Any income of a + # household member 18 and older will be counted ... Income for any + # persons ages 16 and 17 who do not attend school full time will be + # counted." The FY2025 and FY2026 plans, item 1.9 (p. 6), leave + # "Earned income of a child under the age of 18" unchecked, which + # would exclude a 16- or 17-year-old nonstudent's earnings. + income_counted = (age >= p.adult_age) | ( (age >= p.nonstudent_min_age) & ~person("is_full_time_student", period) ) - # Preserve signed existing net business inputs without another expense - # deduction. The sources do not specify a loss floor per business; - # floor the combined household total. Rental income is nonnegative. - earned = add(person, period, p.sources.earned) - unearned = add(person, period, p.sources.unearned, options=[ADD]) - # The plan counts net gambling or lottery winnings. - unearned = unearned - person("gambling_losses", period) - # Count the state cash grant once at SPM level as a caregiver grant. - # Its calculated default is a receipt proxy; observed amounts can be - # supplied through the existing variable. No national TANF aggregate. + # Use existing net business inputs without another expense deduction. + # Plan section 1.8 counts gross income and manual 1200.01B counts "the + # gross amount of income", so each source is floored at zero and a + # loss in one source cannot offset another. + earned = 0 + for source in p.sources.earned: + earned = earned + max_(person(source, period), 0) + unearned = 0 + for source in p.sources.unearned: + unearned = unearned + max_(person(source, period, options=[ADD]), 0) + # Plan section 1.9 counts "net gambling or lottery winnings", so + # losses offset only winnings. + gambling = max_( + person("gambling_winnings", period) - person("gambling_losses", period), + 0, + ) + # Count TANF once at SPM level. The take-up-gated tanf total includes + # fl_tca; fl_tca alone is the entitlement a nonrecipient could get. cash_grant = add(spm_unit, period, p.sources.household, options=[ADD]) # Annual inputs approximate the prior 30 days/current economic situation. # Nonqualified members' countable income is retained in full. UI and - # cash gifts are excluded by the current plan. Gross SSA is included; - # the manual expressly adds Medicare premiums back to net checks. + # cash gifts are excluded by the plans. Gross SSA is included; the + # manual expressly adds Medicare premiums back to net checks. # Existing inputs do not isolate work-study, royalties, mortgage/sale # installments, and all countable insurance/lump-sum receipts. - return max_( - spm_unit.sum(earned * adult + unearned * unearned_age_eligible) - + cash_grant, - 0, + return ( + spm_unit.sum((earned + unearned + gambling) * income_counted) + cash_grant ) From 1f2fe1b6ea5cf667db76779eef8d22647d4bd832 Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 7 Oct 2026 22:56:53 -0400 Subject: [PATCH 5/9] Fix issues from review: pay $0 for an unspecified fuel outside heat in rent An UNSPECIFIED heating type now qualifies only when heat is included in rent. Document the manual conflicts on large-household income limits, heat in rent and the undocumented-applicant rule. Co-Authored-By: Claude Opus 5.5 --- .../states/fl/commerce/liheap/fl_liheap.yaml | 73 +++++++++++++++++++ .../liheap/eligibility/fl_liheap_eligible.py | 11 ++- .../eligibility/fl_liheap_income_limit.py | 7 ++ .../liheap/fl_liheap_household_size.py | 4 + 4 files changed, 94 insertions(+), 1 deletion(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml index f8b7feac01a..cb634e208e6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml @@ -271,6 +271,8 @@ state_code: FL output: fl_liheap: 700 +# The qualified child is assumed to be the applicant; manual 900.04D.1 (page 41) would deny +# an application made by the undocumented parent. - name: Case 6, a nonqualified parent's full income determines the band for the qualified child. period: 2026 input: @@ -758,3 +760,74 @@ fl_liheap_countable_income: 14_400 fl_liheap_income_limit: 39_999.504 fl_liheap: 1_350 + +# Part-time wages at the Florida minimum wage (Fla. Const. art. X, sec. 24): +# 20 hours a week for 39 weeks at 14.00, then 13 weeks at 15.00 after the +# September 30, 2026 increase, so 20 x (39 x 14 + 13 x 15) = 14_820. That is +# under the FY2026 one-person maximum of 32_155. Heat is not included in rent +# and no heating fuel is reported, so the reported heating cost does not +# qualify: an unspecified fuel counts only when heat is included in rent. +- name: Case 21, an unspecified heating fuel outside rent does not qualify despite a reported heating cost. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 14_820 + spm_units: + spm_unit: + members: [person1] + heating_type: UNSPECIFIED + has_heating_expense: true + heat_expense_included_in_rent: false + fl_tca: 0 + snap: 0 + households: + household: + members: [person1] + state_code: FL + output: + fl_liheap_countable_income: 14_820 + fl_liheap_eligible: false + fl_liheap: 0 + +# Same 14_820 of part-time wages as Case 21. Heat is included in unsubsidized +# rent and the tenant does not know the building's fuel, so heating_type stays +# UNSPECIFIED. FY2026 matrix, size 1: 60% of the four-person state median +# income of 103_063 is 61_837 (truncated), and 61_837 x 52% = 32_155 +# (truncated). The first band ends at 32_155 x 50% = 16_077.5, printed 16_078. +# Wages of 14_820 fall in the first band, which pays 350. Nobody is 60 or +# older, disabled, or 5 or younger, so no addition applies. +- name: Case 22, an unspecified heating fuel included in unsubsidized rent receives the first band award. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + immigration_status: CITIZEN + is_full_time_student: false + ssi: 0 + employment_income: 14_820 + spm_units: + spm_unit: + members: [person1] + heating_type: UNSPECIFIED + heat_expense_included_in_rent: true + receives_housing_assistance: false + fl_tca: 0 + snap: 0 + households: + household: + members: [person1] + state_code: FL + is_in_public_housing: false + output: + fl_liheap_countable_income: 14_820 + fl_liheap_income_limit: 32_155 + fl_liheap_eligible: true + fl_liheap: 350 diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py index 15fbf232686..68df65f16aa 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py @@ -29,15 +29,24 @@ def formula(spm_unit, period, parameters): # from existing inputs. In subsidized heat-in-rent housing, a positive # heating expense represents the household's verified excess charge. # A direct bill or reported obligation is otherwise residual liability. + # Source conflict: manual 900.01A (page 39) allows "non-subsidized rent + # that includes utilities", but 1100.04B(1) (page 47) asks for a + # landlord statement that "Home energy costs are not included in their + # rent". This follows 900.01A, page 4 and both plans. responsible = where( heat_in_rent, ~subsidized | (heating_expense > 0), spm_unit("has_heating_expense", period) | (heating_expense > 0), ) heating_type = spm_unit("heating_type", period) + types = heating_type.possible_values + # An unspecified fuel qualifies only when heat is included in rent. + fuel_qualifies = (heating_type != types.NONE) & ( + heat_in_rent | (heating_type != types.UNSPECIFIED) + ) return ( (size > 0) & ((income <= limit) | categorical) & responsible - & (heating_type != heating_type.possible_values.NONE) + & fuel_qualifies ) diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py index 63754ffb0fc..c72b93e24b3 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py @@ -34,6 +34,13 @@ def formula(spm_unit, period, parameters): # The FY2025 matrix rounds this unrounded amount, and each band # cutoff derived from it, to the nearest dollar. smi_limit = size_smi * p.smi_rate + # Source conflict: manual 900.01D (page 39) sets 60% of SMI for sizes + # 1-8 and 150% of the poverty guideline for 9 or more, while 1100.03D + # (page 47) and 1200.01A (page 49) set 150% of the guideline for every + # size. The FY2025 plan and matrix match 900.01D. The FY2026 plan + # (page 8) and matrix use 60% of SMI for every size, and the manual + # says to follow DEO's current benefit matrix (page 9), so FY2026 + # follows the matrix. # The printed FY2026 size-13 maximum, $157,686, equals 100% of SMI. # Follow the matrix's explicit 60% rule instead ($94,610). The federal # table restricts its 150%-FPG large-household exception to diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py index 021cf6e56d1..79fa1149218 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py @@ -9,6 +9,10 @@ class fl_liheap_household_size(Variable): defined_for = StateCode.FL reference = "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=42" + # Manual 900.04A (page 41) makes a household eligible when "at least one + # member, preferably an adult," qualifies, but 900.04D.1 denies the + # application when "The applicant is an illegal alien". No input names + # the applicant, so a qualified member is assumed to apply. # Ineligible members are excluded from size, but their otherwise countable # income remains included in full. SPM membership approximates the energy # economic unit; roomers, separate energy units, special status documents, From a1018e5241dafa59b970bf4ef20e868f33693417 Mon Sep 17 00:00:00 2001 From: Ziming Date: Wed, 7 Oct 2026 23:24:34 -0400 Subject: [PATCH 6/9] 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. Co-Authored-By: Claude Opus 5.5 --- .../commerce/liheap/eligibility/categorical_programs.yaml | 4 ++-- .../gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml | 2 +- .../gov/states/fl/commerce/liheap/income/adult_age.yaml | 4 ++-- .../fl/commerce/liheap/income/nonstudent_min_age.yaml | 2 +- .../states/fl/commerce/liheap/income/sources/earned.yaml | 6 +++--- .../states/fl/commerce/liheap/income/sources/household.yaml | 2 +- .../states/fl/commerce/liheap/income/sources/unearned.yaml | 6 +++--- .../fl/commerce/liheap/eligibility/fl_liheap_eligible.py | 3 ++- .../variables/gov/states/fl/commerce/liheap/fl_liheap.py | 2 +- .../fl/commerce/liheap/income/fl_liheap_countable_income.py | 5 ++++- 10 files changed, 20 insertions(+), 16 deletions(-) diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml index 815110d2f87..9c55ff02076 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml @@ -13,9 +13,9 @@ metadata: period: year label: Florida LIHEAP categorical income programs reference: - - title: Florida FY2026 LIHEAP State Plan, sections 1.4-1.6, page 5 (categorical income eligibility) + - title: Florida FY2026 LIHEAP State Plan, sections 1.4-1.6 (categorical income eligibility) href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5 - - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01G(9), page 50 + - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01G(9) href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=50 - title: Florida FY2025 LIHEAP State Plan, Sections 1.4-1.6 href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml index 7858def6718..a2a621ea4f0 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml @@ -6,7 +6,7 @@ metadata: period: year label: Florida LIHEAP heating state median income rate reference: - - title: Florida FY2026 LIHEAP State Plan, Section 2.1, page 8 + - title: Florida FY2026 LIHEAP State Plan, Section 2.1 href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=8 - title: FY2026 LIHEAP income eligibility by component, Florida heating column href: https://liheapch.acf.gov/delivery/income_eligibility.htm diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml index 61d525353a6..0094f3774e4 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml @@ -8,9 +8,9 @@ metadata: period: year label: Florida LIHEAP adult income age reference: - - title: Florida FY2026 LIHEAP State Plan, section 1.9, page 6 (earnings under 18; superseded by manual 1200.01D) + - title: Florida FY2026 LIHEAP State Plan, section 1.9 (earnings under 18; superseded by manual 1200.01D) href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=6 - - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01D, page 49 + - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01D href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 - title: Florida FY2025 LIHEAP State Plan, Section 1.9 href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml index c4885da288a..96c1e3e0e6f 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml @@ -8,5 +8,5 @@ metadata: period: year label: Florida LIHEAP nonstudent income minimum age reference: - - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01D, page 49 + - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01D href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml index 8469473b942..93ae9a29cf3 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml @@ -12,9 +12,9 @@ metadata: period: year label: Florida LIHEAP earned income sources reference: - - title: Florida FY2026 LIHEAP State Plan, section 1.9, pages 5-6 + - title: "Florida FY2026 LIHEAP State Plan, section 1.9 #page 5-6" href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5 - - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01E and G(12), pages 49-50 + - title: "Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01E and G(12) #page 49-50" href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 - - title: Florida FY2025 LIHEAP State Plan, Section 1.9, pages 5-6 + - title: "Florida FY2025 LIHEAP State Plan, Section 1.9 #page 5-6" href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml index 4501eca05d2..597d1c05825 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml @@ -9,7 +9,7 @@ metadata: period: year label: Florida LIHEAP household cash income sources reference: - - title: Florida FY2026 LIHEAP State Plan, section 1.9, page 6 (TANF counted) + - title: Florida FY2026 LIHEAP State Plan, section 1.9 (TANF counted) href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=6 - title: Florida FY2025 LIHEAP State Plan, Section 1.9 href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml index 3ce70973eaa..582c1afc7f0 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml @@ -34,9 +34,9 @@ metadata: period: year label: Florida LIHEAP person unearned income sources reference: - - title: Florida FY2026 LIHEAP State Plan, section 1.9, pages 5-7 (included and excluded income sources) + - title: "Florida FY2026 LIHEAP State Plan, section 1.9 (included and excluded income sources) #page 5-7" href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5 - - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200, pages 49-51 + - title: "Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200 #page 49-51" href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 - - title: Florida FY2025 LIHEAP State Plan, Section 1.9, pages 5-7 + - title: "Florida FY2025 LIHEAP State Plan, Section 1.9 #page 5-7" href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=5 diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py index 68df65f16aa..0eb7f8ffcba 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py @@ -7,9 +7,10 @@ class fl_liheap_eligible(Variable): definition_period = YEAR label = "Florida LIHEAP regular heating eligibility" defined_for = StateCode.FL - # Plan pages 5 and 8; manual pages 4, 23, 41, 43, 47 and 50. reference = ( + # PDF pages 5, 8 "https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5", + # PDF pages 4, 23, 41, 43, 47, 50 "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=4", ) diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py index fb49a903046..20936d2a3e6 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py @@ -9,9 +9,9 @@ class fl_liheap(Variable): unit = USD label = "Florida LIHEAP regular heating assistance" defined_for = StateCode.FL - # Manual pages 8-9, 23-24 and 43; plan page 9. reference = ( "https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf", + # PDF pages 8-9, 23-24, 43 "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=8", "https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=9", "https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf", diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py index ce7ae95b69a..5569163a5df 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py @@ -8,10 +8,13 @@ class fl_liheap_countable_income(Variable): unit = USD label = "Florida LIHEAP annual countable household income" defined_for = StateCode.FL - # Plan section 1.9, pages 5-7; manual pages 49-51. + # Plan section 1.9 reference = ( + # PDF pages 5-7 "https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5", + # PDF pages 49-51 "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49", + # PDF pages 5-7 "https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=5", ) From ddce33d862b04d017eafbdd39a1a41555a9cf764 Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 8 Oct 2026 00:43:16 -0400 Subject: [PATCH 7/9] Fix Florida LIHEAP minor income and published payment bands --- .../fl/commerce/liheap/income/adult_age.yaml | 13 ++++-- .../liheap/income/nonstudent_min_age.yaml | 12 ----- .../liheap/payment/published_band_size.yaml | 12 +++++ .../payment/published_band_upper_bound.yaml | 17 +++++++ policyengine_us/programs.yaml | 3 +- .../states/fl/commerce/liheap/fl_liheap.yaml | 46 +++++++++++++++++++ .../income/fl_liheap_countable_income.yaml | 44 +++++++----------- .../eligibility/fl_liheap_income_limit.py | 3 +- .../states/fl/commerce/liheap/fl_liheap.py | 8 ++++ .../income/fl_liheap_countable_income.py | 18 ++++---- 10 files changed, 118 insertions(+), 58 deletions(-) delete mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_size.yaml create mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_upper_bound.yaml diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml index 0094f3774e4..738498137ae 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/adult_age.yaml @@ -1,6 +1,7 @@ description: Florida uses this threshold as the age at which all members' income counts under the Low Income Home Energy Assistance Program. -# Manual 1200.01D counts earned and unearned income alike from this age, -# whether or not the member is a full-time student. +# The statewide application and portal FAQ exclude all minors' income. +# The March 2025 application metadata does not establish a policy-change date; +# FY2025 uses this current rule as a backfilled assumption. values: 2024-10-01: 18 metadata: @@ -8,9 +9,11 @@ metadata: period: year label: Florida LIHEAP adult income age reference: - - title: Florida FY2026 LIHEAP State Plan, section 1.9 (earnings under 18; superseded by manual 1200.01D) + - title: Florida FY2026 LIHEAP State Plan, section 1.9 href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=6 - - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01D - href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 + - title: Florida statewide LIHEAP application, Monthly Household Income + href: https://storage.googleapis.com/florida-liheap-relief-static-assets/Florida_LIHEAP_Application_English.pdf#page=1 + - title: Florida statewide LIHEAP portal, What qualifies as income + href: https://floridaliheap.com/faq - title: Florida FY2025 LIHEAP State Plan, Section 1.9 href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=6 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml deleted file mode 100644 index 96c1e3e0e6f..00000000000 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/nonstudent_min_age.yaml +++ /dev/null @@ -1,12 +0,0 @@ -description: Florida uses this threshold as the age at which nonstudents' income counts under the Low Income Home Energy Assistance Program. -# Follows manual 1200.01D for earned and unearned income; plan item 1.9 leaves -# under-18 earnings unchecked (see fl_liheap_countable_income). -values: - 2024-10-01: 16 -metadata: - unit: year - period: year - label: Florida LIHEAP nonstudent income minimum age - reference: - - title: Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200.01D - href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_size.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_size.yaml new file mode 100644 index 00000000000..ff7cf996a9f --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_size.yaml @@ -0,0 +1,12 @@ +description: Florida uses a separately published regular heating payment-band row for this household size under the Low Income Home Energy Assistance Program. +# Zero disables the separate row before the FY2026 matrix. +values: + 2024-10-01: 0 + 2025-10-01: 13 +metadata: + unit: person + period: year + label: Florida LIHEAP separately published payment-band household size + reference: + - title: Florida FY2026 LIHEAP Payment Matrix, thirteen-person row + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_upper_bound.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_upper_bound.yaml new file mode 100644 index 00000000000..0f3780f5489 --- /dev/null +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_upper_bound.yaml @@ -0,0 +1,17 @@ +description: Florida publishes these regular heating payment-band upper bounds for the separately specified household size under the Low Income Home Energy Assistance Program. +# Preserve the operative matrix's payment row independently of eligibility. +# Its printed maximum is inconsistent with the 60%-SMI eligibility rule. +first: + 2025-10-01: 78_843 +second: + 2025-10-01: 110_380 +third: + 2025-10-01: 134_033 +metadata: + unit: currency-USD + period: year + label: Florida LIHEAP separately published payment-band upper bounds + propagate_metadata_to_children: true + reference: + - title: Florida FY2026 LIHEAP Payment Matrix, thirteen-person row + href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index e0aa26257e3..646678b058a 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -828,8 +828,7 @@ programs: full_name: Florida Low Income Home Energy Assistance Program variable: fl_liheap parameter_prefix: gov.states.fl.commerce.liheap - verified_years: "2025-2026" - notes: FY2025-FY2026 regular heating only, from the published payment matrices. Crisis, cooling, weatherization and equipment assistance are out of scope. + notes: FY2025-FY2026 regular heating schedules are checked. FY2026 large-household eligibility conflicts with contract wording; above-range categorical awards remain estimates. The current minor-income rule is backfilled to FY2025 without a confirmed change date. Crisis, cooling, weatherization and equipment assistance are out of scope. - state: IL status: complete name: Illinois LIHEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml index cb634e208e6..dfd5435a327 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml @@ -831,3 +831,49 @@ fl_liheap_income_limit: 32_155 fl_liheap_eligible: true fl_liheap: 350 + +# Statewide application p1 and portal FAQ exclude all minor income regardless +# of school attendance. The adult with the same 34_000 income is over the +# FY2026 one-person limit 32_155; minors qualify for the 350 first-band base. +- name: Case 23, minor nonstudent income is excluded through the final benefit. + period: 2026 + input: + state_code: [FL, FL, FL] + age: [16, 17, 18] + immigration_status: [CITIZEN, CITIZEN, CITIZEN] + is_full_time_student: [false, false, false] + employment_income: [30_000, 30_000, 30_000] + social_security: [4_000, 4_000, 4_000] + ssi: [0, 0, 0] + tanf: [0, 0, 0] + snap: [0, 0, 0] + heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY] + heating_expense: [1_200, 1_200, 1_200] + output: + fl_liheap_countable_income: [0, 0, 34_000] + fl_liheap_income_limit: [32_155, 32_155, 32_155] + fl_liheap_eligible: [true, true, false] + fl_liheap: [350, 350, 0] + +# FY2026 printed size-13 payment row: first band ends at 78_843, second at +# 110_380 and third at 134_033. Eligibility remains the plan's 60%-SMI rule, +# 94_610. SNAP receipt waives the income eligibility test, allowing checks of +# the higher printed band boundaries without inventing a higher income limit. +- name: Case 24, thirteen-person published payment bands remain distinct from income eligibility. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [FL, FL, FL, FL, FL, FL, FL, FL, FL] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN] + fl_liheap_household_size: [13, 13, 13, 13, 13, 13, 13, 13, 13] + fl_liheap_countable_income: [60_000, 78_843, 78_843.01, 94_610, 94_610.01, 110_380, 110_380.01, 134_033, 134_033.01] + snap: [0, 0, 0, 0, 0, 1, 1, 1, 1] + tanf: [0, 0, 0, 0, 0, 0, 0, 0, 0] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0] + heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY] + heating_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + output: + fl_liheap_income_limit: [94_610, 94_610, 94_610, 94_610, 94_610, 94_610, 94_610, 94_610, 94_610] + fl_liheap_eligible: [true, true, true, true, false, true, true, true, true] + fl_liheap: [350, 350, 300, 300, 0, 300, 250, 250, 200] diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml index b54a6afbf74..1bbd9c36495 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml @@ -118,14 +118,10 @@ output: fl_liheap_countable_income: 30_000 -# Manual 1200.01D, page 49, applies one age and student test to all income: -# members 18 and older count, and so do 16- and 17-year-olds who do not attend -# school full time. The model follows it over plan item 1.9, page 6, which -# leaves earned income of a child under 18 unchecked. Plan page 7 counts net -# gambling and periodic estate receipts. At 15 the young member's 6_000 wages -# + 1_200 self-employment + 1_200 Social Security + 900 estate + (600 - 100) -# gambling = 9_800 is excluded, leaving the parent's 12_000. -- name: Case 5, a 15-year-old nonstudent's income is excluded under the manual's age and student test. +# The statewide application, Monthly Household Income (page 1), excludes +# all minors' income. The FY2026 plan endorses this portal in section 1.10. +# The young member's 9_800 total income is excluded, leaving parent income. +- name: Case 5, a 15-year-old nonstudent's income is excluded under the statewide application. period: 2026 absolute_error_margin: 0.01 input: @@ -161,11 +157,9 @@ output: fl_liheap_countable_income: 12_000 -# Manual 1200.01D, page 49, counts all income of a 16- or 17-year-old who does -# not attend school full time; the model follows it over plan item 1.9, page 6. -# Parent 12_000 + young member 6_000 wages + 1_200 self-employment + 1_200 -# Social Security + 900 estate + (600 - 100) gambling = 21_800. -- name: Case 6, a 16-year-old nonstudent's earned and unearned income both count. +# The statewide application page 1 and portal FAQ exclude all minors' income, +# without a student exception. Only the parent's 12_000 counts. +- name: Case 6, a 16-year-old nonstudent's earned and unearned income are both excluded. period: 2026 absolute_error_margin: 0.01 input: @@ -199,12 +193,10 @@ members: [person1, person2] state_code: FL output: - fl_liheap_countable_income: 21_800 + fl_liheap_countable_income: 12_000 -# Manual 1200.01D, page 49, counts a 16- or 17-year-old's income only when the -# member does not attend school full time. The young member's 6_000 wages + -# 1_200 self-employment + 1_200 Social Security + 900 estate + (600 - 100) -# gambling = 9_800 is excluded, leaving the parent's 12_000. +# The statewide application page 1 and portal FAQ exclude all minors' income, +# without a student exception. Only the parent's 12_000 counts. - name: Case 7, a 16-year-old full-time student's earned and unearned income are both excluded. period: 2026 absolute_error_margin: 0.01 @@ -241,11 +233,9 @@ output: fl_liheap_countable_income: 12_000 -# Manual 1200.01D, page 49, counts all income of a 16- or 17-year-old who does -# not attend school full time; the model follows it over plan item 1.9, page 6. -# Parent 12_000 + young member 6_000 wages + 1_200 self-employment + 1_200 -# Social Security + 900 estate + (600 - 100) gambling = 21_800. -- name: Case 8, a 17-year-old nonstudent's earned and unearned income both count. +# The statewide application page 1 and portal FAQ exclude all minors' income, +# without a student exception. Only the parent's 12_000 counts. +- name: Case 8, a 17-year-old nonstudent's earned and unearned income are both excluded. period: 2026 absolute_error_margin: 0.01 input: @@ -279,12 +269,10 @@ members: [person1, person2] state_code: FL output: - fl_liheap_countable_income: 21_800 + fl_liheap_countable_income: 12_000 -# Manual 1200.01D, page 49, counts a 16- or 17-year-old's income only when the -# member does not attend school full time. The young member's 6_000 wages + -# 1_200 self-employment + 1_200 Social Security + 900 estate + (600 - 100) -# gambling = 9_800 is excluded, leaving the parent's 12_000. +# The statewide application page 1 and portal FAQ exclude all minors' income, +# without a student exception. Only the parent's 12_000 counts. - name: Case 9, a 17-year-old full-time student's earned and unearned income are both excluded. period: 2026 absolute_error_margin: 0.01 diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py index c72b93e24b3..a4fc3f34da2 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py @@ -42,7 +42,8 @@ def formula(spm_unit, period, parameters): # says to follow DEO's current benefit matrix (page 9), so FY2026 # follows the matrix. # The printed FY2026 size-13 maximum, $157,686, equals 100% of SMI. - # Follow the matrix's explicit 60% rule instead ($94,610). The federal + # Apply the plan's explicit 60% eligibility rule ($94,610); payment + # bands remain the published matrix row in fl_liheap. The federal # table restricts its 150%-FPG large-household exception to # cooling/crisis. Larger sizes follow the federal factors without table # verification. diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py index 20936d2a3e6..50d8e1b28fd 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py @@ -52,6 +52,14 @@ def formula(spm_unit, period, parameters): guideline * band_fpg_rate.third, np.floor(limit * fraction.third + 0.5), ) + # The operative matrix publishes a distinct FY2026 size-13 row. + # Its band cutoffs govern payments even though its printed maximum + # conflicts with the plan's 60%-SMI eligibility ceiling. + published_row = size == p.payment.published_band_size + published = p.payment.published_band_upper_bound + first = where(published_row, published.first, first) + second = where(published_row, published.second, second) + third = where(published_row, published.third, third) # The FY2025 second guideline band starts "At least 75%" of the # guideline, so the first guideline band excludes its upper bound. in_first = where(fpg_based, income < first, income <= first) diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py index 5569163a5df..6a15de059a1 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.py @@ -16,22 +16,20 @@ class fl_liheap_countable_income(Variable): "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49", # PDF pages 5-7 "https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=5", + "https://storage.googleapis.com/florida-liheap-relief-static-assets/Florida_LIHEAP_Application_English.pdf#page=1", + "https://floridaliheap.com/faq", ) def formula(spm_unit, period, parameters): p = parameters(period).gov.states.fl.commerce.liheap.income person = spm_unit.members age = person("age", period) - # Plan vs manual conflict; this follows the manual for earned and - # unearned income alike. Manual 1200.01D (PDF p. 49): "Any income of a - # household member 18 and older will be counted ... Income for any - # persons ages 16 and 17 who do not attend school full time will be - # counted." The FY2025 and FY2026 plans, item 1.9 (p. 6), leave - # "Earned income of a child under the age of 18" unchecked, which - # would exclude a 16- or 17-year-old nonstudent's earnings. - income_counted = (age >= p.adult_age) | ( - (age >= p.nonstudent_min_age) & ~person("is_full_time_student", period) - ) + # The current statewide application excludes all minors' income, + # matching the portal FAQ. The FY2026 plan endorses this portal in + # section 1.10. This supersedes the older manual's nonstudent exception. + # The application was created in March 2025 but supplies no rule-change + # effective date; applying it throughout FY2025 is a backfilled assumption. + income_counted = age >= p.adult_age # Use existing net business inputs without another expense deduction. # Plan section 1.8 counts gross income and manual 1200.01B counts "the # gross amount of income", so each source is floored at zero and a From 7c5688f9ec8f2bda6cc42c6560838bff4f90ab16 Mon Sep 17 00:00:00 2001 From: Ziming Date: Sun, 11 Oct 2026 23:45:52 -0400 Subject: [PATCH 8/9] Use the shared SMI helper for the Florida 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/fl_liheap_income_limit.py | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py index a4fc3f34da2..567759ab0ea 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_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, smi from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg @@ -22,18 +22,13 @@ def formula(spm_unit, period, parameters): p = parameters(period).gov.states.fl.commerce.liheap.eligibility size = spm_unit("fl_liheap_household_size", period) state = spm_unit.household("state_code_str", period) - size_smi = smi(size, state, period, parameters) if p.truncates_smi_limit: - four_person_smi = parameters(period).gov.hhs.smi.amount[state] - size_share = size_smi / four_person_smi - # This floor order reconciles all FY2026 matrix rows for sizes 1-12; - # the sources do not expressly prescribe the rounding order. - four_person_limit = np.floor(four_person_smi * p.smi_rate) - smi_limit = np.floor(four_person_limit * size_share) + # The HHS floor order reconciles all FY2026 matrix rows, sizes 1-12. + smi_limit = liheap_smi_limit(size, state, p.smi_rate, period, parameters) else: # The FY2025 matrix rounds this unrounded amount, and each band # cutoff derived from it, to the nearest dollar. - smi_limit = size_smi * p.smi_rate + smi_limit = smi(size, state, period, parameters) * p.smi_rate # Source conflict: manual 900.01D (page 39) sets 60% of SMI for sizes # 1-8 and 150% of the poverty guideline for 9 or more, while 1100.03D # (page 47) and 1200.01A (page 49) set 150% of the guideline for every From f2f6f3f706b764649b6cf8e08b8e6826e4358952 Mon Sep 17 00:00:00 2001 From: Ziming Date: Thu, 15 Oct 2026 16:11:37 -0400 Subject: [PATCH 9/9] Address review round 1 on Florida LIHEAP - C1: From 15 people the FY2026 limit (60% of SMI) is below the federal 110%-of-poverty floor, so protected househol - S1: Size 13 takes its limit from the 60% rule but its bands from the printed 100%-SMI row - S2: Six parameter descriptions fall outside the house template; two describe a student-status rule the head no lon - S3: Two small pieces of logic have no test that would fail if they broke 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 ++ .../eligibility/categorical_programs.yaml | 2 +- .../commerce/liheap/eligibility/smi_rate.yaml | 4 + .../liheap/income/sources/earned.yaml | 2 +- .../liheap/income/sources/household.yaml | 2 +- .../liheap/income/sources/unearned.yaml | 16 ++- .../commerce/liheap/payment/base_amount.yaml | 5 + .../liheap/payment/published_band_size.yaml | 12 -- .../payment/published_band_upper_bound.yaml | 17 --- .../commerce/liheap/payment/supplement.yaml | 4 +- policyengine_us/programs.yaml | 2 +- .../fl_liheap_categorically_eligible.yaml | 21 --- .../eligibility/fl_liheap_eligible.yaml | 45 ++++--- .../eligibility/fl_liheap_income_limit.yaml | 53 +------- .../states/fl/commerce/liheap/fl_liheap.yaml | 124 ++++++++---------- .../liheap/fl_liheap_household_size.yaml | 16 +-- .../income/fl_liheap_countable_income.yaml | 65 --------- .../liheap/eligibility/fl_liheap_eligible.py | 40 ++++-- .../eligibility/fl_liheap_income_limit.py | 19 ++- .../states/fl/commerce/liheap/fl_liheap.py | 16 +-- .../liheap/fl_liheap_household_size.py | 3 +- 21 files changed, 170 insertions(+), 309 deletions(-) create mode 100644 policyengine_us/parameters/gov/hhs/liheap/fpg_floor.yaml delete mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_size.yaml delete mode 100644 policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_upper_bound.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/fl/commerce/liheap/eligibility/categorical_programs.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml index 9c55ff02076..c19e4b4620d 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/categorical_programs.yaml @@ -1,4 +1,4 @@ -description: Florida waives the LIHEAP income test for households receiving a positive amount from any of these programs. +description: Florida uses receipt of these programs as categorical income eligibility under the Low Income Home Energy Assistance Program. values: # The FY2025 plan also lists means-tested veterans programs, which no # existing input identifies. diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml index a2a621ea4f0..c8ace75bc94 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/eligibility/smi_rate.yaml @@ -1,4 +1,6 @@ description: Florida limits annual heating-assistance income to this fraction of state median income under the Low Income Home Energy Assistance Program. +# The draft FFY2027 plan (section 2.1) keeps 60% of state median income for +# sizes 1-8 and 9 or more. values: 2024-10-01: 0.6 metadata: @@ -12,3 +14,5 @@ metadata: href: https://liheapch.acf.gov/delivery/income_eligibility.htm - title: Florida FY2025 LIHEAP State Plan, Section 2.1 href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=8 + - title: Florida draft FFY2027 LIHEAP State Plan, section 2.1 + href: https://www.floridajobs.org/docs/lwdb-resources/lwdb-grants-management/economic-self-sufficiency-organizations/esso-grant-agreements/liheap/2026-liheap/draft-ffy2027_comm_liheap_model-plan-template_watermark.pdf#page=8 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml index 93ae9a29cf3..83487aabc26 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/earned.yaml @@ -1,4 +1,4 @@ -description: Florida counts these earned income sources subject to its age and student-status rule under the Low Income Home Energy Assistance Program. +description: Florida uses these sources as earned income of members aged 18 and older under the Low Income Home Energy Assistance Program. # Existing net business inputs have no additional LIHEAP expense deduction. values: 2024-10-01: diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml index 597d1c05825..f6b636a93bb 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/household.yaml @@ -1,4 +1,4 @@ -description: Florida counts these household cash grants once under the Low Income Home Energy Assistance Program. +description: Florida uses these household cash benefits as countable income under the Low Income Home Energy Assistance Program. values: 2024-10-01: # The take-up-gated TANF total includes Florida Temporary Cash Assistance diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml index 582c1afc7f0..d8e77d795ce 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/income/sources/unearned.yaml @@ -1,4 +1,4 @@ -description: Florida counts these person-level unearned income sources subject to its age and student-status rule under the Low Income Home Energy Assistance Program. +description: Florida uses these sources as unearned income of members aged 18 and older under the Low Income Home Energy Assistance Program. values: 2024-10-01: - social_security @@ -26,7 +26,13 @@ values: - alimony_income # Net gambling and lottery winnings are counted in the formula, with losses # offsetting only winnings. -# The FY2025 and FY2026 plans exclude unemployment compensation and cash gifts. +# The FY2025 and FY2026 plans (section 1.9) and the portal FAQ exclude +# unemployment compensation and cash gifts. +# Florida Optional State Supplementation (fl_oss) is not listed: it pays +# residents of assisted living facilities, adult family-care homes and mental +# health residential treatment facilities, whom manual 900.02B (page 39, +# residence paid through a state residential program) and 900.03B(1) (page +# 40, institutions) exclude from LIHEAP households. # Pension/SSA subcomponents are not counted twice; TANF is counted once in # household.yaml. metadata: @@ -36,7 +42,9 @@ metadata: reference: - title: "Florida FY2026 LIHEAP State Plan, section 1.9 (included and excluded income sources) #page 5-7" href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5 - - title: "Florida LIHEAP Policies and Procedures Manual, May 27 2022, part 1200 #page 49-51" - href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=49 + - title: "Florida LIHEAP Policies and Procedures Manual, May 27 2022, parts 900 and 1200 #page 39-40,49-51" + href: https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=39 - title: "Florida FY2025 LIHEAP State Plan, Section 1.9 #page 5-7" href: https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=5 + - title: Florida statewide LIHEAP portal, What qualifies as income + href: https://floridaliheap.com/faq diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/base_amount.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/base_amount.yaml index e47bd84436c..86b0325477c 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/base_amount.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/base_amount.yaml @@ -1,4 +1,7 @@ description: Florida provides this base regular heating payment by income band under the Low Income Home Energy Assistance Program. +# The draft FFY2027 plan (section 2.6) estimates a $200 minimum and $700 +# maximum, matching these bases with all additions; no FY2027 matrix is +# published. 1: 2024-10-01: 1_000 2025-10-01: 350 @@ -23,3 +26,5 @@ metadata: href: https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf - title: Florida FY2026 LIHEAP Payment Matrix, home energy benefit href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf + - title: Florida draft FFY2027 LIHEAP State Plan, section 2.6 + href: https://www.floridajobs.org/docs/lwdb-resources/lwdb-grants-management/economic-self-sufficiency-organizations/esso-grant-agreements/liheap/2026-liheap/draft-ffy2027_comm_liheap_model-plan-template_watermark.pdf#page=9 diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_size.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_size.yaml deleted file mode 100644 index ff7cf996a9f..00000000000 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_size.yaml +++ /dev/null @@ -1,12 +0,0 @@ -description: Florida uses a separately published regular heating payment-band row for this household size under the Low Income Home Energy Assistance Program. -# Zero disables the separate row before the FY2026 matrix. -values: - 2024-10-01: 0 - 2025-10-01: 13 -metadata: - unit: person - period: year - label: Florida LIHEAP separately published payment-band household size - reference: - - title: Florida FY2026 LIHEAP Payment Matrix, thirteen-person row - href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_upper_bound.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_upper_bound.yaml deleted file mode 100644 index 0f3780f5489..00000000000 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/published_band_upper_bound.yaml +++ /dev/null @@ -1,17 +0,0 @@ -description: Florida publishes these regular heating payment-band upper bounds for the separately specified household size under the Low Income Home Energy Assistance Program. -# Preserve the operative matrix's payment row independently of eligibility. -# Its printed maximum is inconsistent with the 60%-SMI eligibility rule. -first: - 2025-10-01: 78_843 -second: - 2025-10-01: 110_380 -third: - 2025-10-01: 134_033 -metadata: - unit: currency-USD - period: year - label: Florida LIHEAP separately published payment-band upper bounds - propagate_metadata_to_children: true - reference: - - title: Florida FY2026 LIHEAP Payment Matrix, thirteen-person row - href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf diff --git a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/supplement.yaml b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/supplement.yaml index 02d4e8481e0..5b63a52e9e6 100644 --- a/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/supplement.yaml +++ b/policyengine_us/parameters/gov/states/fl/commerce/liheap/payment/supplement.yaml @@ -1,4 +1,4 @@ -description: Florida adds this amount once for each applicable vulnerable household category to the regular heating payment under the Low Income Home Energy Assistance Program. +description: Florida provides this additional heating payment once for each vulnerable household category present under the Low Income Home Energy Assistance Program. elderly: 2024-10-01: 100 disabled: @@ -15,3 +15,5 @@ metadata: href: https://liheapch.acf.gov/docs/2026/benefits-matricies/FL_BenefitMatrix_Heat-Cool_2026.pdf - title: Florida FY2025 LIHEAP Payment Matrix, additional assistance href: https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf + - title: Florida FY2026 LIHEAP State Plan, section 2.4 + href: https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=9 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index e730212c5fb..51b131d634a 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -828,7 +828,7 @@ programs: full_name: Florida Low Income Home Energy Assistance Program variable: fl_liheap parameter_prefix: gov.states.fl.commerce.liheap - notes: FY2025-FY2026 regular heating schedules are checked. FY2026 large-household eligibility conflicts with contract wording; above-range categorical awards remain estimates. The current minor-income rule is backfilled to FY2025 without a confirmed change date. Crisis, cooling, weatherization and equipment assistance are out of scope. + notes: FY2025-FY2026 regular heating schedules are checked. FY2026 large-household eligibility conflicts with contract wording; above-range awards for categorical or 110%-poverty-floor households remain estimates. The current minor-income rule is backfilled to FY2025 without a confirmed change date. Crisis, cooling, weatherization and equipment assistance are out of scope. - state: IL status: complete name: Illinois LIHEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.yaml index 5fa6cfd2af2..5ce7c88d98d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_categorically_eligible.yaml @@ -10,14 +10,11 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 100_000 spm_units: spm_unit: members: [person1] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 1_200 households: @@ -36,18 +33,14 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 person2: age: 8 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 spm_units: spm_unit: members: [person1, person2] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 3_600 snap: 0 households: @@ -67,19 +60,15 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 12_000 person2: age: 8 immigration_status: CITIZEN - is_full_time_student: false ssi: 1_200 spm_units: spm_unit: members: [person1, person2] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -100,14 +89,11 @@ person1: age: 67 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 is_ssi_eligible: true spm_units: spm_unit: members: [person1] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -126,13 +112,10 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 spm_units: spm_unit: members: [person1] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 1_200 households: @@ -161,15 +144,11 @@ spm_units: spm_unit1: members: [person1, person2] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 2_160 takes_up_tanf_if_eligible: true snap: 0 spm_unit2: members: [person3, person4] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 2_160 takes_up_tanf_if_eligible: false snap: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.yaml index 119faf62242..1b671ea64bb 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.yaml @@ -15,9 +15,6 @@ immigration_status: - CITIZEN - CITIZEN - is_full_time_student: - - false - - false ssi: - 0 - 0 @@ -61,11 +58,6 @@ - CITIZEN - CITIZEN - CITIZEN - is_full_time_student: - - false - - false - - false - - false ssi: - 0 - 0 @@ -120,11 +112,6 @@ - UNDOCUMENTED - CITIZEN - CITIZEN - is_full_time_student: - - false - - false - - false - - false ssi: - 0 - 0 @@ -188,12 +175,6 @@ - CITIZEN - CITIZEN - CITIZEN - is_full_time_student: - - false - - false - - false - - false - - false ssi: - 0 - 0 @@ -273,9 +254,6 @@ immigration_status: - CITIZEN - CITIZEN - is_full_time_student: - - false - - false ssi: - 0 - 0 @@ -315,7 +293,6 @@ state_code: [FL, FL, FL, FL] age: [40, 40, 40, 40] immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN] - is_full_time_student: [false, false, false, false] ssi: [0, 0, 0, 0] fl_tca: [0, 0, 0, 0] snap: [0, 0, 0, 0] @@ -325,3 +302,25 @@ heating_expense: [1_200, 1_200, 1_200, 1_200] output: fl_liheap_eligible: [true, false, true, false] + +# Outside rent, has_heating_expense is the heating obligation (manual 900.01A, +# page 39; 1100.04A, page 47). It defaults to true when the primary fuel's bill +# is positive; an explicit false is respected even with a 1_200 gas bill. +# Part-time Florida minimum-wage earnings: 20 x (39 x 14 + 13 x 15) = 14_820, +# under the 32_155 one-person limit. +- name: Case 7, an explicit absence of a heating obligation outside rent is respected despite a fuel bill. + period: 2026 + input: + state_code: [FL, FL] + age: [40, 40] + immigration_status: [CITIZEN, CITIZEN] + ssi: [0, 0] + fl_tca: [0, 0] + snap: [0, 0] + employment_income: [14_820, 14_820] + heating_type: [NATURAL_GAS, NATURAL_GAS] + gas_expense: [1_200, 1_200] + heat_expense_included_in_rent: [false, false] + has_heating_expense: [true, false] + output: + fl_liheap_eligible: [true, false] diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.yaml index d7dce72fe6c..2728086240c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.yaml @@ -1,6 +1,7 @@ # FY2026 matrix, sizes 1-12 as printed. Its stated 60% of state median income -# rule gives 94_610 at size 13; the printed 157_686 is treated as an anomaly, -# not a separate 100% of state median income category. +# rule gives floor(61_837 x 1.53) = 94_610 at size 13, which the model uses for +# both the limit and the payment bands; the printed 157_686 is 100% of state +# median income and exceeds the federal ceiling of 42 U.S.C. 8624(b)(2)(B). - name: Case 1, all twelve consistent published heating limits match the current matrix. period: 2026 @@ -44,19 +45,6 @@ - CITIZEN - CITIZEN - CITIZEN - is_full_time_student: - - false - - false - - false - - false - - false - - false - - false - - false - - false - - false - - false - - false ssi: - 0 - 0 @@ -109,32 +97,6 @@ - 0 - 0 - 0 - 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 - heating_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 fl_liheap_household_size: - 1 - 2 @@ -172,8 +134,6 @@ - 40 immigration_status: - CITIZEN - is_full_time_student: - - false ssi: - 0 fl_tca: @@ -182,10 +142,6 @@ - 0 employment_income: - 0 - heating_type: - - NATURAL_GAS - heating_expense: - - 1_200 fl_liheap_household_size: - 13 output: @@ -207,13 +163,10 @@ state_code: [FL, FL, FL, FL, FL, FL, FL, FL] age: [40, 40, 40, 40, 40, 40, 40, 40] immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN] - is_full_time_student: [false, false, false, false, false, false, false, false] ssi: [0, 0, 0, 0, 0, 0, 0, 0] fl_tca: [0, 0, 0, 0, 0, 0, 0, 0] snap: [0, 0, 0, 0, 0, 0, 0, 0] employment_income: [0, 0, 0, 0, 0, 0, 0, 0] - heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] - heating_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] fl_liheap_household_size: [1, 2, 4, 6, 8, 9, 12, 20] output: fl_liheap_income_limit: [30_587.856, 39_999.504, 58_822.8, 77_646.096, 81_175.464, 87_150, 111_360, 175_920] diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml index dfd5435a327..6332e00fd67 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap.yaml @@ -2,9 +2,10 @@ # FY2025 matrix: bases of 1_000/750/550/400. Both years add 100 for a member # age 60 or older, 100 for a disabled member and 150 for a child age 5 or # younger, each once per household. Manual page 23 bases the payment on the -# matrix, not the actual bill. Categorical households with income above the -# table receive the smallest base as a documented estimate; the sources give -# no express instruction for that case. +# matrix, not the actual bill. Categorical households, and households of 15 or +# more protected by the federal 110% poverty floor, with income above the table +# receive the smallest base as a documented estimate; the sources give no +# express instruction for that case. - name: Case 1, published income band tops include exact amounts and the next cent starts the next band. period: 2026 @@ -36,15 +37,6 @@ - CITIZEN - CITIZEN - CITIZEN - is_full_time_student: - - false - - false - - false - - false - - false - - false - - false - - false ssi: - 0 - 0 @@ -127,11 +119,6 @@ - CITIZEN - CITIZEN - CITIZEN - is_full_time_student: - - false - - false - - false - - false ssi: - 0 - 0 @@ -180,12 +167,10 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 person2: age: 5 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 spm_units: spm_unit: @@ -207,12 +192,10 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 person2: age: 6 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 spm_units: spm_unit: @@ -234,29 +217,24 @@ person1: age: 65 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 is_disabled: true person2: age: 64 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 is_disabled: true person3: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 person4: age: 5 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 person5: age: 3 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 spm_units: spm_unit: @@ -280,13 +258,11 @@ person1: age: 40 immigration_status: UNDOCUMENTED - is_full_time_student: false ssi: 0 employment_income: 30_000 person2: age: 5 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 spm_units: spm_unit: @@ -312,8 +288,6 @@ - 40 immigration_status: - CITIZEN - is_full_time_student: - - false ssi: - 0 fl_tca: @@ -341,8 +315,6 @@ - 40 immigration_status: - CITIZEN - is_full_time_student: - - false ssi: - 0 fl_tca: @@ -379,10 +351,6 @@ - CITIZEN - CITIZEN - CITIZEN - is_full_time_student: - - false - - false - - false ssi: - 0 - 0 @@ -438,11 +406,6 @@ - CITIZEN - CITIZEN - CITIZEN - is_full_time_student: - - false - - false - - false - - false ssi: - 0 - 0 @@ -490,7 +453,6 @@ state_code: [FL, FL] age: [40, 40] immigration_status: [CITIZEN, CITIZEN] - is_full_time_student: [false, false] ssi: [0, 0] fl_tca: [0, 0] snap: [0, 0] @@ -513,12 +475,10 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 person2: age: 5.5 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 spm_units: spm_unit: @@ -546,7 +506,6 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 10_000 spm_units: @@ -579,7 +538,6 @@ state_code: [FL, FL, FL, FL, FL, FL, FL, FL] age: [40, 40, 40, 40, 40, 40, 40, 40] immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN] - is_full_time_student: [false, false, false, false, false, false, false, false] ssi: [0, 0, 0, 0, 0, 0, 0, 0] fl_tca: [0, 0, 0, 0, 0, 0, 0, 0] snap: [0, 0, 0, 0, 0, 0, 0, 0] @@ -638,7 +596,6 @@ state_code: [FL, FL, FL, FL, FL, FL, FL, FL] age: [40, 40, 40, 40, 40, 40, 40, 40] immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN] - is_full_time_student: [false, false, false, false, false, false, false, false] ssi: [0, 0, 0, 0, 0, 0, 0, 0] fl_tca: [0, 0, 0, 0, 0, 0, 0, 0] snap: [0, 0, 0, 0, 0, 0, 0, 0] @@ -660,7 +617,6 @@ state_code: [FL, FL, FL, FL, FL, FL] age: [40, 40, 40, 40, 40, 40] immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN] - is_full_time_student: [false, false, false, false, false, false] ssi: [0, 0, 0, 0, 0, 0] fl_tca: [0, 0, 0, 0, 0, 0] snap: [0, 0, 0, 0, 0, 0] @@ -683,7 +639,6 @@ state_code: [FL, FL] age: [50, 62] immigration_status: [CITIZEN, CITIZEN] - is_full_time_student: [false, false] ssi: [0, 0] fl_tca: [0, 0] snap: [0, 0] @@ -708,7 +663,6 @@ state_code: [FL, FL] age: [40, 40] immigration_status: [CITIZEN, CITIZEN] - is_full_time_student: [false, false] ssi: [0, 0] fl_tca: [0, 0] snap: [0, 0] @@ -736,14 +690,12 @@ person1: age: 66 immigration_status: CITIZEN - is_full_time_student: false is_disabled: true ssi: 0 social_security: 14_400 person2: age: 4 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 spm_units: spm_unit: @@ -775,7 +727,6 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 14_820 spm_units: @@ -810,7 +761,6 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 14_820 spm_units: @@ -855,25 +805,55 @@ fl_liheap_eligible: [true, true, false] fl_liheap: [350, 350, 0] -# FY2026 printed size-13 payment row: first band ends at 78_843, second at -# 110_380 and third at 134_033. Eligibility remains the plan's 60%-SMI rule, -# 94_610. SNAP receipt waives the income eligibility test, allowing checks of -# the higher printed band boundaries without inventing a higher income limit. -- name: Case 24, thirteen-person published payment bands remain distinct from income eligibility. +# FY2026, size 13: the matrix footnote's 60% of state median income gives +# floor(61_837 x 1.53) = 94_610; the printed 157_686 row (100% of state median +# income) is not followed. Band tops round half up: 94_610 x 50% = 47_305, +# x 70% = 66_227 and x 85% = 80_418.5, so 80_419. 60_000 is in band 2 (300), as +# at sizes 12 (46_379-64_929) and 14 (48_234-67_526). Income above 94_610 is +# ineligible without receipt (the 110% floor for 13 is 89_815); SNAP receipt +# waives the test and income above the table pays the 200 base. +- name: Case 24, thirteen-person bands follow the sixty percent state median income limit. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [FL, FL, FL, FL, FL, FL, FL, FL, FL, FL] + age: [40, 40, 40, 40, 40, 40, 40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN] + fl_liheap_household_size: [13, 13, 13, 13, 13, 13, 13, 13, 13, 13] + fl_liheap_countable_income: [47_305, 47_305.01, 60_000, 66_227, 66_227.01, 80_419, 80_419.01, 94_610, 94_610.01, 100_000] + snap: [0, 0, 0, 0, 0, 0, 0, 0, 0, 1] + tanf: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY] + heating_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + output: + fl_liheap_income_limit: [94_610, 94_610, 94_610, 94_610, 94_610, 94_610, 94_610, 94_610, 94_610, 94_610] + fl_liheap_eligible: [true, true, true, true, true, true, true, true, false, true] + fl_liheap: [350, 300, 300, 300, 250, 250, 200, 200, 0, 200] + +# Federal 110% poverty floor: 42 U.S.C. 8624(b)(2)(B), repeated in the accepted +# FY2026 plan assurances (page 45) and contract G(8)e, bars excluding a +# household on income below 110 percent of the poverty level. Period 2026 uses +# the 2025 guideline: size 15 is 15_650 + 14 x 5_500 = 92_650 (floor 101_915), +# size 16 is 98_150 (floor 107_965). The FY2026 limits are +# floor(61_837 x 1.59) = 98_320 and floor(61_837 x 1.62) = 100_175. Incomes +# between each limit and floor stay eligible in band 4 (200); 101_915.01 is +# excluded. +- name: Case 25, households of fifteen or more below 110 percent of the poverty guideline remain income eligible. period: 2026 absolute_error_margin: 0.01 input: - state_code: [FL, FL, FL, FL, FL, FL, FL, FL, FL] - age: [40, 40, 40, 40, 40, 40, 40, 40, 40] - immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN, CITIZEN] - fl_liheap_household_size: [13, 13, 13, 13, 13, 13, 13, 13, 13] - fl_liheap_countable_income: [60_000, 78_843, 78_843.01, 94_610, 94_610.01, 110_380, 110_380.01, 134_033, 134_033.01] - snap: [0, 0, 0, 0, 0, 1, 1, 1, 1] - tanf: [0, 0, 0, 0, 0, 0, 0, 0, 0] - ssi: [0, 0, 0, 0, 0, 0, 0, 0, 0] - heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY, ELECTRICITY] - heating_expense: [1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200, 1_200] + state_code: [FL, FL, FL, FL] + age: [40, 40, 40, 40] + immigration_status: [CITIZEN, CITIZEN, CITIZEN, CITIZEN] + ssi: [0, 0, 0, 0] + fl_tca: [0, 0, 0, 0] + snap: [0, 0, 0, 0] + employment_income: [100_000, 105_000, 101_914.99, 101_915.01] + fl_liheap_household_size: [15, 16, 15, 15] + heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] + heating_expense: [1_200, 1_200, 1_200, 1_200] output: - fl_liheap_income_limit: [94_610, 94_610, 94_610, 94_610, 94_610, 94_610, 94_610, 94_610, 94_610] - fl_liheap_eligible: [true, true, true, true, false, true, true, true, true] - fl_liheap: [350, 350, 300, 300, 0, 300, 250, 250, 200] + fl_liheap_income_limit: [98_320, 100_175, 98_320, 98_320] + fl_liheap_eligible: [true, true, true, false] + fl_liheap: [200, 200, 200, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap_household_size.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap_household_size.yaml index 017e9c8a447..280170da862 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap_household_size.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/fl_liheap_household_size.yaml @@ -1,4 +1,4 @@ -# Florida independent household income and categorical-income components; no final program eligibility or award is asserted. +# Florida LIHEAP household size counts citizens and qualified noncitizens; no final program eligibility or award is asserted. # Manual pages 41-42 count citizens and qualified noncitizens and exclude # nonqualified people from the size; SNAP-specific waiting periods do not apply. @@ -9,33 +9,26 @@ person1: age: 40 immigration_status: LEGAL_PERMANENT_RESIDENT - is_full_time_student: false ssi: 0 person2: age: 8 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 person3: age: 35 immigration_status: ASYLEE - is_full_time_student: false ssi: 0 person4: age: 22 immigration_status: DACA - is_full_time_student: false ssi: 0 person5: age: 39 immigration_status: UNDOCUMENTED - is_full_time_student: false ssi: 0 spm_units: spm_unit: members: [person1, person2, person3, person4, person5] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -46,7 +39,8 @@ fl_liheap_household_size: 3 # Manual pages 41-42 require at least one qualified member for final program -# eligibility; this size component returns 0. +# eligibility; this size component returns 0, and the income limit is masked to +# 0 (the SMI size factor would otherwise treat size 0 as one person, 32_155). - name: Case 2, only nonqualified members produce a zero household size. period: 2026 input: @@ -54,13 +48,10 @@ person1: age: 40 immigration_status: UNDOCUMENTED - is_full_time_student: false ssi: 0 spm_units: spm_unit: members: [person1] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -69,3 +60,4 @@ state_code: FL output: fl_liheap_household_size: 0 + fl_liheap_income_limit: 0 diff --git a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml index 1bbd9c36495..53a1e49f87e 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/fl/commerce/liheap/income/fl_liheap_countable_income.yaml @@ -10,7 +10,6 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 12_000 self_employment_income: 2_400 @@ -20,8 +19,6 @@ spm_units: spm_unit: members: [person1] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -42,7 +39,6 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 12_000 self_employment_income: -6_000 @@ -50,8 +46,6 @@ spm_units: spm_unit: members: [person1] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -70,15 +64,12 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 1_200 self_employment_income: -6_000 spm_units: spm_unit: members: [person1] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -99,7 +90,6 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 30_000 unemployment_compensation: 6_000 @@ -107,8 +97,6 @@ spm_units: spm_unit: members: [person1] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 6_000 households: @@ -129,7 +117,6 @@ person1: age: 45 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 12_000 person2: @@ -146,8 +133,6 @@ spm_units: spm_unit: members: [person1, person2] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -167,7 +152,6 @@ person1: age: 45 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 12_000 person2: @@ -184,8 +168,6 @@ spm_units: spm_unit: members: [person1, person2] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -205,7 +187,6 @@ person1: age: 45 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 12_000 person2: @@ -222,8 +203,6 @@ spm_units: spm_unit: members: [person1, person2] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -243,7 +222,6 @@ person1: age: 45 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 12_000 person2: @@ -260,8 +238,6 @@ spm_units: spm_unit: members: [person1, person2] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -281,7 +257,6 @@ person1: age: 45 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 12_000 person2: @@ -298,8 +273,6 @@ spm_units: spm_unit: members: [person1, person2] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -321,7 +294,6 @@ person1: age: 45 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 12_000 person2: @@ -338,8 +310,6 @@ spm_units: spm_unit: members: [person1, person2] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -360,7 +330,6 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 30_000 rental_income: 1_200 @@ -369,8 +338,6 @@ spm_units: spm_unit: members: [person1] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -394,7 +361,6 @@ person1: age: 67 immigration_status: CITIZEN - is_full_time_student: false ssi: 1_200 social_security: 18_000 pension_income: 2_400 @@ -412,8 +378,6 @@ spm_units: spm_unit: members: [person1] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -434,15 +398,12 @@ person1: age: 67 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 social_security: 24_000 medicare_part_b_premium: 2_400 spm_units: spm_unit: members: [person1] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -463,20 +424,16 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 6_000 person2: age: 8 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 social_security: 1_200 spm_units: spm_unit: members: [person1, person2] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 3_600 snap: 0 households: @@ -497,20 +454,16 @@ person1: age: 40 immigration_status: UNDOCUMENTED - is_full_time_student: false ssi: 0 employment_income: 20_000 person2: age: 8 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 social_security: 1_200 spm_units: spm_unit: members: [person1, person2] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -531,15 +484,12 @@ person1: age: 40 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 gambling_winnings: 40_000 gambling_losses: 30_000 spm_units: spm_unit: members: [person1] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -559,15 +509,12 @@ state_code: [FL, FL, FL] age: [40, 40, 40] immigration_status: [CITIZEN, CITIZEN, CITIZEN] - is_full_time_student: [false, false, false] ssi: [0, 0, 0] fl_tca: [0, 0, 0] snap: [0, 0, 0] employment_income: [1_200, 1_200, 1_200] gambling_winnings: [4_000, 4_000, 4_000] gambling_losses: [4_000, 5_000, 10_000] - heating_type: [NATURAL_GAS, NATURAL_GAS, NATURAL_GAS] - gas_expense: [1_200, 1_200, 1_200] output: fl_liheap_countable_income: [1_200, 1_200, 1_200] @@ -583,21 +530,17 @@ person1: age: 58 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 military_retirement_pay: 24_000 taxable_private_pension_income: 6_000 person2: age: 63 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 railroad_benefits: 18_000 spm_units: spm_unit: members: [person1, person2] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -620,7 +563,6 @@ person1: age: 45 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 employment_income: 30_000 farm_operations_income: -2_500 @@ -628,7 +570,6 @@ person2: age: 44 immigration_status: CITIZEN - is_full_time_student: false ssi: 0 self_employment_income: -8_000 partnership_s_corp_income: -1_000 @@ -638,8 +579,6 @@ spm_units: spm_unit: members: [person1, person2] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 0 snap: 0 households: @@ -667,15 +606,11 @@ spm_units: spm_unit1: members: [person1, person2, person3] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 3_636 takes_up_tanf_if_eligible: true snap: 0 spm_unit2: members: [person4, person5, person6] - heating_type: NATURAL_GAS - gas_expense: 1_200 fl_tca: 3_636 takes_up_tanf_if_eligible: false snap: 0 diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py index 0eb7f8ffcba..ffd8e2095ad 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_eligible.py @@ -1,4 +1,5 @@ from policyengine_us.model_api import * +from policyengine_us.variables.gov.hhs.tax_unit_fpg import fpg class fl_liheap_eligible(Variable): @@ -8,17 +9,34 @@ class fl_liheap_eligible(Variable): label = "Florida LIHEAP regular heating eligibility" defined_for = StateCode.FL reference = ( - # PDF pages 5, 8 + "https://www.law.cornell.edu/uscode/text/42/8624", + # PDF pages 5, 8, 45 "https://liheapch.acf.gov/docs/2026/state-plans/FL_Plan_2026.pdf#page=5", - # PDF pages 4, 23, 41, 43, 47, 50 + # PDF pages 4, 23, 39, 41, 43, 47-48, 50-51 "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=4", + # PDF pages 12-13 + "https://www.pbcgov.com/pubInf/Agenda/20260106/3E2.pdf#page=12", ) def formula(spm_unit, period, parameters): + p = parameters(period).gov.states.fl.commerce.liheap.eligibility + p_hhs = parameters(period).gov.hhs.liheap size = spm_unit("fl_liheap_household_size", period) income = spm_unit("fl_liheap_countable_income", period) - # The matrices print the maximum income value in whole dollars. + # The matrices print the maximum income value in whole dollars. Manual + # 1200.03A (page 51) and contract G(8)a annualize 30 days of income + # against it; the portal instead compares one month with monthly + # maximums rounded to whole dollars, which differ from this annual + # value by at most $6 a year either way ($2,680 for one person is $5 + # more lenient than 32,155 / 12 = 2,679.58; $3,504 for two is $1 + # stricter). limit = np.floor(spm_unit("fl_liheap_income_limit", period) + 0.5) + state_group = spm_unit.household("state_group_str", period) + guideline = fpg(size, state_group, period, parameters, year_lag=p.fpg_year_lag) + # 42 U.S.C. 8624(b)(2)(B), the FY2026 plan assurances (page 45) and + # contract G(8)e bar excluding a household on income below 110% of the + # poverty level; from 15 people this floor exceeds the FY2026 limit. + income_eligible = (income <= limit) | (income < guideline * p_hhs.fpg_floor) categorical = spm_unit("fl_liheap_categorically_eligible", period) heating_expense = spm_unit("heating_expense", period) heat_in_rent = spm_unit("heat_expense_included_in_rent", period) @@ -29,15 +47,20 @@ def formula(spm_unit, period, parameters): # arrangement. Actual verification and account ownership are absent # from existing inputs. In subsidized heat-in-rent housing, a positive # heating expense represents the household's verified excess charge. - # A direct bill or reported obligation is otherwise residual liability. + # Outside rent, has_heating_expense (true by default when the primary + # fuel's bill is positive) is the obligation, and an explicit false is + # respected. # Source conflict: manual 900.01A (page 39) allows "non-subsidized rent # that includes utilities", but 1100.04B(1) (page 47) asks for a # landlord statement that "Home energy costs are not included in their - # rent". This follows 900.01A, page 4 and both plans. + # rent". This follows 900.01A, 100.01 (page 4) and FY2025 plan item + # 2.3's utilities-in-rent answer (page 9); the same FY2025 item repeats + # the landlord statement under Renters (page 8), and the FY2026 plan + # answers No to all three renter rows (page 8). responsible = where( heat_in_rent, ~subsidized | (heating_expense > 0), - spm_unit("has_heating_expense", period) | (heating_expense > 0), + spm_unit("has_heating_expense", period), ) heating_type = spm_unit("heating_type", period) types = heating_type.possible_values @@ -46,8 +69,5 @@ def formula(spm_unit, period, parameters): heat_in_rent | (heating_type != types.UNSPECIFIED) ) return ( - (size > 0) - & ((income <= limit) | categorical) - & responsible - & fuel_qualifies + (size > 0) & (income_eligible | categorical) & responsible & fuel_qualifies ) diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py index 567759ab0ea..1c88a8949e7 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/eligibility/fl_liheap_income_limit.py @@ -16,6 +16,8 @@ class fl_liheap_income_limit(Variable): "https://liheapch.acf.gov/delivery/income_eligibility.htm", "https://liheapch.acf.gov/docs/2025/state-plans/FL_Plan_2025.pdf#page=8", "https://liheapch.acf.gov/docs/2025/benefits-matricies/FL_BenefitMatrix_2025.pdf", + "https://www.law.cornell.edu/uscode/text/42/8624", + "https://floridaliheap.com/faq", ) def formula(spm_unit, period, parameters): @@ -36,12 +38,17 @@ def formula(spm_unit, period, parameters): # (page 8) and matrix use 60% of SMI for every size, and the manual # says to follow DEO's current benefit matrix (page 9), so FY2026 # follows the matrix. - # The printed FY2026 size-13 maximum, $157,686, equals 100% of SMI. - # Apply the plan's explicit 60% eligibility rule ($94,610); payment - # bands remain the published matrix row in fl_liheap. The federal - # table restricts its 150%-FPG large-household exception to - # cooling/crisis. Larger sizes follow the federal factors without table - # verification. + # The printed FY2026 size-13 row tops out at $157,686, which is 100% of + # SMI (103,063 x 1.53), and the portal FAQ prints $13,141 a month + # (157,686 / 12). The matrix footnote says 60% of SMI, plan item 2.1 + # (page 8) lists 60% for sizes 1-9, and 42 U.S.C. 8624(b)(2)(B) caps + # eligibility at the greater of 150% of poverty and 60% of SMI, which + # the printed row exceeds. Size 13 therefore uses 60% of SMI ($94,610) + # for both this limit and the payment bands. The federal table + # restricts its 150%-FPG large-household exception to cooling/crisis. + # Larger sizes follow the federal factors without table verification; + # from 15 people the 110%-of-poverty floor in fl_liheap_eligible + # exceeds this limit. state_group = spm_unit.household("state_group_str", period) guideline = fpg(size, state_group, period, parameters, year_lag=p.fpg_year_lag) limit = max_(smi_limit, guideline * p.fpg_rate) diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py index 50d8e1b28fd..3e1a5e7312e 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap.py @@ -52,14 +52,6 @@ def formula(spm_unit, period, parameters): guideline * band_fpg_rate.third, np.floor(limit * fraction.third + 0.5), ) - # The operative matrix publishes a distinct FY2026 size-13 row. - # Its band cutoffs govern payments even though its printed maximum - # conflicts with the plan's 60%-SMI eligibility ceiling. - published_row = size == p.payment.published_band_size - published = p.payment.published_band_upper_bound - first = where(published_row, published.first, first) - second = where(published_row, published.second, second) - third = where(published_row, published.third, third) # The FY2025 second guideline band starts "At least 75%" of the # guideline, so the first guideline band excludes its upper bound. in_first = where(fpg_based, income < first, income <= first) @@ -68,9 +60,11 @@ def formula(spm_unit, period, parameters): [1, 2, 3], default=4, ) - # Categorical households above the printed maximum use the smallest - # base as an estimate; sources waive the income test but do not supply - # an above-range benefit band. Receipt alone never selects a top band. + # Categorical households and households protected by the federal + # 110%-of-poverty floor (fl_liheap_eligible) can have income above the + # maximum income value. They receive the band 4 (smallest) base as an + # estimate; the sources do not supply an above-range benefit band. + # Receipt alone never selects a higher-paying band. base = p.payment.base_amount[band] age = spm_unit.members("age", period) elderly = spm_unit.any(age >= p.payment.elderly_min_age) diff --git a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py index 79fa1149218..06db9d841f5 100644 --- a/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py +++ b/policyengine_us/variables/gov/states/fl/commerce/liheap/fl_liheap_household_size.py @@ -7,7 +7,8 @@ class fl_liheap_household_size(Variable): definition_period = YEAR label = "Florida LIHEAP household size" defined_for = StateCode.FL - reference = "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=42" + # PDF pages 41-42 + reference = "https://liheapch.acf.gov/sites/default/files/webfiles/docs/2023/manuals/FL_PolicyManual_2023.pdf#page=41" # Manual 900.04A (page 41) makes a household eligible when "at least one # member, preferably an adult," qualifies, but 900.04D.1 denies the