From 5a710617fdd8f77ff3e3be66fee7ee60d3fe9572 Mon Sep 17 00:00:00 2001 From: Ziming Date: Sun, 8 Nov 2026 22:01:40 -0500 Subject: [PATCH 1/2] Pay California HEAP to households whose only energy cost is cooling Co-Authored-By: Claude Opus 5.5 --- changelog.d/ca-liheap-cooling.fixed.md | 1 + policyengine_us/programs.yaml | 2 +- .../csd/liheap/ca_liheap_utility_heating.yaml | 50 ++++++++++++++ .../ca_liheap_utility_eligible.yaml | 69 +++++++++++++++++++ .../csd/liheap/ca_liheap_utility_heating.py | 4 +- .../eligibility/ca_liheap_utility_eligible.py | 15 +++- 6 files changed, 136 insertions(+), 5 deletions(-) create mode 100644 changelog.d/ca-liheap-cooling.fixed.md diff --git a/changelog.d/ca-liheap-cooling.fixed.md b/changelog.d/ca-liheap-cooling.fixed.md new file mode 100644 index 00000000000..054198176c4 --- /dev/null +++ b/changelog.d/ca-liheap-cooling.fixed.md @@ -0,0 +1 @@ +Pay California's regular HEAP utility benefit to households with no heating that pay an electric bill for cooling. diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index a7cef450f8b..95392d1eb81 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -1027,7 +1027,7 @@ programs: variable: ca_liheap_utility_heating parameter_prefix: gov.states.ca.csd.liheap verified_years: "2026" - notes: FY2026 regular utility heating on gas and electric accounts, Madera propane and fuel-oil heating, and statewide wood heating from an approved delivery cost; other liquid-fuel service areas and vendor-service supplements are not modeled. Crisis, cooling, weatherization and equipment assistance are out of scope. + notes: FY2026 regular utility benefit on gas and electric accounts, which also pays cooling-only households on their electric bill, plus Madera propane and fuel-oil heating and statewide wood heating from an approved delivery cost; other liquid-fuel service areas and vendor-service supplements are not modeled. Crisis, weatherization and equipment assistance are out of scope. - state: CO status: partial name: Colorado LEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/ca_liheap_utility_heating.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/ca_liheap_utility_heating.yaml index 8d54dbb9977..baabc5583c8 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/ca_liheap_utility_heating.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/ca_liheap_utility_heating.yaml @@ -5430,3 +5430,53 @@ ca_liheap_energy_burden_group: [1, 4, 3, 0, 0] ca_liheap_madera_priority_points: [8, 20, 18, 0, 0] ca_liheap_utility_heating: [0, 702, 0, 702, 434] + +# Plan p. 55: HEAP payments on electric bills from July 1 to October 31 are +# cooling, paid from the same county table (pp. 182-184) with no bill cap. A +# desert home in Imperial County with no heating pays an electric bill for air +# conditioning. One adult with wages of $12,000 ($1,000 a month) is poverty +# group 1: Imperial pays $621. The same home without a cooling cost gets $0. +- name: Case 17, a home without heating receives the county utility amount for cooling. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 40 + is_citizen_or_legal_immigrant: true + ssi: 0 + employment_income_before_lsr: 12_000 + person2: + age: 40 + is_citizen_or_legal_immigrant: true + ssi: 0 + employment_income_before_lsr: 12_000 + spm_units: + spm_unit1: + members: [person1] + heating_type: NONE + has_cooling_expense: true + pre_subsidy_electricity_expense: 1_200 + snap: 0 + tanf: 0 + ca_state_supplement: 0 + spm_unit2: + members: [person2] + heating_type: NONE + has_cooling_expense: false + pre_subsidy_electricity_expense: 1_200 + snap: 0 + tanf: 0 + ca_state_supplement: 0 + households: + household1: + members: [person1] + state_code: CA + county: IMPERIAL_COUNTY_CA + household2: + members: [person2] + state_code: CA + county: IMPERIAL_COUNTY_CA + output: + ca_liheap_income_group: [1, 1] + ca_liheap_utility_heating: [621, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.yaml index 782cce03ab4..9e5d2e225b3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.yaml @@ -345,3 +345,72 @@ county: ALAMEDA_COUNTY_CA output: ca_liheap_utility_eligible: [true, true, true, true, true, true, false, false, false, false] + +# Plan p. 55 reports HEAP payments on electric bills from July 1 to October 31 +# as cooling, within the one HEAP benefit a year (guide p. 193). A home without +# heating qualifies on its electric bill when it pays for cooling. Without an +# electric bill it has no account to credit, and UNSPECIFIED fuel still gets +# nothing. Wages of 40,500 exceed the 39,979.92 one-person limit (p. 184). +- name: Case 4, a home without heating qualifies on its electric bill when it pays for cooling. + period: 2026 + input: + people: + person1: {age: 35, is_citizen_or_legal_immigrant: true, ssi: 0} + person2: {age: 35, is_citizen_or_legal_immigrant: true, ssi: 0} + person3: {age: 35, is_citizen_or_legal_immigrant: true, ssi: 0} + person4: + age: 35 + is_citizen_or_legal_immigrant: true + ssi: 0 + employment_income_before_lsr: 40_500 + spm_units: + spm_unit1: + members: [person1] + heating_type: NONE + has_cooling_expense: true + pre_subsidy_electricity_expense: 1_200 + snap: 0 + tanf: 0 + ca_state_supplement: 0 + spm_unit2: + members: [person2] + heating_type: NONE + has_cooling_expense: true + snap: 0 + tanf: 0 + ca_state_supplement: 0 + spm_unit3: + members: [person3] + heating_type: UNSPECIFIED + has_cooling_expense: true + pre_subsidy_electricity_expense: 1_200 + snap: 0 + tanf: 0 + ca_state_supplement: 0 + spm_unit4: + members: [person4] + heating_type: NONE + has_cooling_expense: true + pre_subsidy_electricity_expense: 1_200 + snap: 0 + tanf: 0 + ca_state_supplement: 0 + households: + household1: + members: [person1] + state_code: CA + county: IMPERIAL_COUNTY_CA + household2: + members: [person2] + state_code: CA + county: IMPERIAL_COUNTY_CA + household3: + members: [person3] + state_code: CA + county: IMPERIAL_COUNTY_CA + household4: + members: [person4] + state_code: CA + county: IMPERIAL_COUNTY_CA + output: + ca_liheap_utility_eligible: [true, false, false, false] diff --git a/policyengine_us/variables/gov/states/ca/csd/liheap/ca_liheap_utility_heating.py b/policyengine_us/variables/gov/states/ca/csd/liheap/ca_liheap_utility_heating.py index b996436f671..914987bca7f 100644 --- a/policyengine_us/variables/gov/states/ca/csd/liheap/ca_liheap_utility_heating.py +++ b/policyengine_us/variables/gov/states/ca/csd/liheap/ca_liheap_utility_heating.py @@ -6,7 +6,9 @@ class ca_liheap_utility_heating(Variable): entity = SPMUnit definition_period = YEAR unit = USD - label = "California LIHEAP regular gas or electric utility heating benefit" + label = ( + "California LIHEAP regular gas or electric utility heating or cooling benefit" + ) defined_for = "ca_liheap_utility_eligible" # PDF pages 182-184 reference = "https://www.csd.ca.gov/Shared%20Documents/2026-Final-LIHEAP-State-Plan.pdf#page=182" diff --git a/policyengine_us/variables/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.py b/policyengine_us/variables/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.py index 7b044a7b560..c0196e7c7de 100644 --- a/policyengine_us/variables/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.py +++ b/policyengine_us/variables/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.py @@ -5,10 +5,10 @@ class ca_liheap_utility_eligible(Variable): value_type = bool entity = SPMUnit definition_period = YEAR - label = "California LIHEAP utility heating eligibility" + label = "California LIHEAP utility heating or cooling eligibility" defined_for = StateCode.CA reference = ( - # PDF pages 9,182-184,193,202-203,205,219,221,264-265,279,363 + # PDF pages 9,55,182-184,193,202-203,205,219,221,264-265,279,363 "https://www.csd.ca.gov/Shared%20Documents/2026-Final-LIHEAP-State-Plan.pdf#page=9", # PDF pages 189,233-234 "https://maderacap.org/wp-content/uploads/2025/10/Agenda-and-Packet-for-October-9-2025-Updated.pdf#page=189", @@ -27,7 +27,16 @@ def formula(spm_unit, period, parameters): ) # The applicant names the utility account that takes the benefit, so a # home heated with another fuel qualifies on a gas or electric bill. - utility_account = utility_heat | (known_fuel & (utility_bills > 0)) + # HEAP payments on electric bills count as cooling, so a home without + # heating qualifies on its electric bill when it pays for cooling. + cooling_only = ( + (heating_type == types.NONE) + & spm_unit("has_cooling_expense", period) + & (spm_unit("pre_subsidy_electricity_expense", period) > 0) + ) + utility_account = ( + utility_heat | (known_fuel & (utility_bills > 0)) | cooling_only + ) # A household receives one HEAP or WPO payment per program year. other_component = ( add( From 736a4ef3675ef19050500ab160a21ab9b0d9e593 Mon Sep 17 00:00:00 2001 From: Ziming Date: Mon, 9 Nov 2026 23:06:08 -0500 Subject: [PATCH 2/2] Pay California HEAP to any reported heating type on a utility bill Follow the CSD guide over the national cooling-only contract: a household responsible for energy costs names the gas or electric account that takes the benefit, so a home heated with another fuel or without heating qualifies on either bill with no cooling-cost condition. UNSPECIFIED still does not qualify. Update the two main cases whose results change. Co-Authored-By: Claude Opus 5.5 --- changelog.d/ca-liheap-cooling.fixed.md | 2 +- policyengine_us/programs.yaml | 2 +- .../csd/liheap/ca_liheap_utility_heating.yaml | 14 +++++++----- .../ca_liheap_utility_eligible.yaml | 22 ++++++++++++------- .../eligibility/ca_liheap_utility_eligible.py | 21 +++++++----------- 5 files changed, 33 insertions(+), 28 deletions(-) diff --git a/changelog.d/ca-liheap-cooling.fixed.md b/changelog.d/ca-liheap-cooling.fixed.md index 054198176c4..0b4368d5acd 100644 --- a/changelog.d/ca-liheap-cooling.fixed.md +++ b/changelog.d/ca-liheap-cooling.fixed.md @@ -1 +1 @@ -Pay California's regular HEAP utility benefit to households with no heating that pay an electric bill for cooling. +Pay California's regular HEAP utility benefit to households with no heating that pay a gas or electric bill. diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 95392d1eb81..eea6ca7bc5c 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -1027,7 +1027,7 @@ programs: variable: ca_liheap_utility_heating parameter_prefix: gov.states.ca.csd.liheap verified_years: "2026" - notes: FY2026 regular utility benefit on gas and electric accounts, which also pays cooling-only households on their electric bill, plus Madera propane and fuel-oil heating and statewide wood heating from an approved delivery cost; other liquid-fuel service areas and vendor-service supplements are not modeled. Crisis, weatherization and equipment assistance are out of scope. + notes: FY2026 regular utility benefit on gas and electric accounts, which also pays homes without heating on either bill, plus Madera propane and fuel-oil heating and statewide wood heating from an approved delivery cost; other liquid-fuel service areas and vendor-service supplements are not modeled. Crisis, weatherization and equipment assistance are out of scope. - state: CO status: partial name: Colorado LEAP diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/ca_liheap_utility_heating.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/ca_liheap_utility_heating.yaml index baabc5583c8..8e2444a6588 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/ca_liheap_utility_heating.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/ca_liheap_utility_heating.yaml @@ -5254,7 +5254,8 @@ # bill. A wood home with an approved delivery takes that instead. Madera burden is # 2,040 / 12,000 = 17%, for 10 + 10 = 20 points: with a $12 propane bill WPO pays # 411 + 105 = 516; with no propane bill WPO pays nothing (contract 8.1.5.2, p. 364) -# and HEAP pays 702. NONE and UNSPECIFIED get $0 even with a utility bill. +# and HEAP pays 702. A home without heating takes HEAP on its electric bill +# (Alameda group 1, 484); UNSPECIFIED gets $0 even with a utility bill. - name: Case 15, another fuel takes the utility benefit only where no other component pays. period: 2026 absolute_error_margin: 0.01 @@ -5350,7 +5351,9 @@ state_code: CA county: ALAMEDA_COUNTY_CA output: - ca_liheap_utility_heating: [716, 716, 0, 0, 702, 0, 0] + # Was 0, now 484 for spm_unit6: the guide asks only for responsibility for + # energy costs (p. 193). + ca_liheap_utility_heating: [716, 716, 0, 0, 702, 484, 0] ca_liheap_wood_heating: [0, 0, 600, 0, 0, 0, 0] ca_liheap_covered_liquid_heating: [0, 0, 0, 516, 0, 0, 0] @@ -5435,8 +5438,9 @@ # cooling, paid from the same county table (pp. 182-184) with no bill cap. A # desert home in Imperial County with no heating pays an electric bill for air # conditioning. One adult with wages of $12,000 ($1,000 a month) is poverty -# group 1: Imperial pays $621. The same home without a cooling cost gets $0. -- name: Case 17, a home without heating receives the county utility amount for cooling. +# group 1: Imperial pays $621. The guide has no cooling-cost condition, so the +# same home without a cooling cost also receives $621 on its electric bill. +- name: Case 17, a home without heating receives the county utility amount on its electric bill. period: 2026 absolute_error_margin: 0.01 input: @@ -5479,4 +5483,4 @@ county: IMPERIAL_COUNTY_CA output: ca_liheap_income_group: [1, 1] - ca_liheap_utility_heating: [621, 0] + ca_liheap_utility_heating: [621, 621] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.yaml index 9e5d2e225b3..868d96e0fc4 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.yaml @@ -213,7 +213,9 @@ - false # Guide p. 221: the applicant names the utility account that takes the benefit, so -# any known fuel qualifies on a gas or electric bill. NONE and UNSPECIFIED do not. +# any known fuel qualifies on a gas or electric bill. So does a home without heating, +# since the guide asks only for responsibility for energy costs (p. 193). UNSPECIFIED +# does not. # A wood home with an approved delivery takes that payment instead (contract # 8.1.2, p. 363). Heat in rent gives a propane home no utility account. - name: Case 3, a gas or electric bill opens the utility benefit to every known fuel. @@ -344,14 +346,17 @@ state_code: CA county: ALAMEDA_COUNTY_CA output: - ca_liheap_utility_eligible: [true, true, true, true, true, true, false, false, false, false] + # Was false, now true for spm_unit7: a home without heating qualifies on its + # electric bill. + ca_liheap_utility_eligible: [true, true, true, true, true, true, true, false, false, false] # Plan p. 55 reports HEAP payments on electric bills from July 1 to October 31 # as cooling, within the one HEAP benefit a year (guide p. 193). A home without -# heating qualifies on its electric bill when it pays for cooling. Without an -# electric bill it has no account to credit, and UNSPECIFIED fuel still gets -# nothing. Wages of 40,500 exceed the 39,979.92 one-person limit (p. 184). -- name: Case 4, a home without heating qualifies on its electric bill when it pays for cooling. +# heating qualifies on an electric bill it pays for cooling, and on a gas bill +# alone without any cooling cost: the guide asks only that the household be +# responsible for energy costs (p. 193). UNSPECIFIED fuel still gets nothing. +# Wages of 40,500 exceed the 39,979.92 one-person limit (p. 184). +- name: Case 4, a home without heating qualifies on a gas or electric bill. period: 2026 input: people: @@ -375,7 +380,8 @@ spm_unit2: members: [person2] heating_type: NONE - has_cooling_expense: true + has_cooling_expense: false + gas_expense: 600 snap: 0 tanf: 0 ca_state_supplement: 0 @@ -413,4 +419,4 @@ state_code: CA county: IMPERIAL_COUNTY_CA output: - ca_liheap_utility_eligible: [true, false, false, false] + ca_liheap_utility_eligible: [true, true, false, false] diff --git a/policyengine_us/variables/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.py b/policyengine_us/variables/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.py index c0196e7c7de..56d80b09073 100644 --- a/policyengine_us/variables/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.py +++ b/policyengine_us/variables/gov/states/ca/csd/liheap/eligibility/ca_liheap_utility_eligible.py @@ -21,22 +21,17 @@ def formula(spm_unit, period, parameters): utility_heat = np.isin( heating_type.decode_to_str(), p.eligibility.utility_heating_fuels ) - known_fuel = (heating_type != types.NONE) & (heating_type != types.UNSPECIFIED) + # heating_type is a required input; UNSPECIFIED does not qualify. + heating_type_reported = heating_type != types.UNSPECIFIED utility_bills = add( spm_unit, period, ["pre_subsidy_electricity_expense", "gas_expense"] ) - # The applicant names the utility account that takes the benefit, so a - # home heated with another fuel qualifies on a gas or electric bill. - # HEAP payments on electric bills count as cooling, so a home without - # heating qualifies on its electric bill when it pays for cooling. - cooling_only = ( - (heating_type == types.NONE) - & spm_unit("has_cooling_expense", period) - & (spm_unit("pre_subsidy_electricity_expense", period) > 0) - ) - utility_account = ( - utility_heat | (known_fuel & (utility_bills > 0)) | cooling_only - ) + # The household must be responsible for energy costs (guide 1.1 and + # 1.6), and the applicant names the utility account that takes the + # benefit. So a home heated with another fuel, or without heating, + # qualifies on a gas or electric bill. HEAP payments on electric bills + # from July 1 to October 31 are reported as cooling. + utility_account = utility_heat | (heating_type_reported & (utility_bills > 0)) # A household receives one HEAP or WPO payment per program year. other_component = ( add(