Skip to content
1 change: 1 addition & 0 deletions changelog.d/or-liheap-cooling.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Pay Oregon's LIHEAP standard payment from the matrix cooling column to households with no heating cost, including homes without heating, that pay an electric bill for cooling. Stop Oregon's LIHEAP income limits at the federal maximum, the greater of HHS's 60% state median income amount and 150% of the poverty guideline.
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
description: Oregon provides this annual regular heating payment for electricity by region, household size and income band under the Low Income Home Energy Assistance Program.
description: Oregon provides this annual regular heating or cooling payment for electricity by region, household size and income band under the Low Income Home Energy Assistance Program.
# The COOLING Electricity column equals the HEATING Electricity column in every
# row of the PY2025 to PY2027 matrices, so this table pays both.
1:
1:
1:
Expand Down Expand Up @@ -175,7 +177,7 @@ description: Oregon provides this annual regular heating payment for electricity
metadata:
unit: currency-USD
period: year
label: Oregon LIHEAP electricity heating payment
label: Oregon LIHEAP electricity heating or cooling payment
breakdown:
- range(1, 3)
- range(1, 7)
Expand Down
2 changes: 1 addition & 1 deletion policyengine_us/programs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1330,7 +1330,7 @@ programs:
variable: or_liheap
parameter_prefix: gov.states.or.ohcs.liheap
verified_years: "2025-2027"
notes: PY2025-PY2027 regular heating only, with the two county regions, five fuel columns and the roomer half payment. Crisis, cooling, Fuel Payment, equipment and OEAP assistance are out of scope.
notes: PY2025-PY2027 regular heating or cooling standard payment, one a year, with the two county regions, five fuel columns, the cooling column for households with no heating cost and the roomer half payment. A cooling payment on top of a heating payment, crisis, Fuel Payment, equipment and OEAP assistance are not modeled.
- state: SC
status: partial
name: South Carolina LIHEAP
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,34 @@
# 110% x (15,960 + 5,680 x 19) = 136,268 for 20 people, above the 134,937
# PY2027 60% SMI limit.
or_liheap_eligible: [true, false]

- name: Case 6, a household with no heating cost qualifies on an electric bill when it pays for cooling.
period: 2026
input:
state_code: [OR, OR, OR, OR, OR, OR, OR]
heating_type: [NONE, NONE, NONE, UNSPECIFIED, NONE, NONE, WOOD]
# The wood-heated home gathers its own wood, so it has no heating cost.
has_heating_expense: [false, false, false, false, false, false, false]
has_cooling_expense: [true, false, true, true, true, true, true]
# About $125 a month for electricity.
pre_subsidy_electricity_expense: [1_500, 1_500, 0, 1_500, 1_500, 1_500, 1_500]
receives_housing_assistance: [false, false, false, false, true, false, false]
# About 30 hours a week at the Portland-metro minimum wage, as in Case 1;
# the sixth household is one dollar above the size 1 limit of $38,384,
# HHS's FY2026 60% state median income amount (page 77 prints $38,385).
or_liheap_countable_income: [26_000, 26_000, 26_000, 26_000, 26_000, 38_385, 26_000]
output:
# "Eligible" households are "responsible for their home heating or cooling
# expenses" (page 90), and the matrices pay electric cooling from their
# COOLING column (pages 78-81):
# 1. no heating, pays for cooling on an electric bill: eligible;
# 2. no cooling cost: no energy burden to demonstrate, ineligible;
# 3. no electric bill: ineligible;
# 4. UNSPECIFIED fuel: ineligible;
# 5. subsidized housing paying its own electric bill: "pays a vendor
# directly for utility costs" is an energy burden (page 93), eligible;
# 6. income above the limit: ineligible;
# 7. wood heat with no heating cost, pays for cooling on an electric
# bill: the benefit goes to the energy type it is applied to
# (page 67), eligible.
or_liheap_eligible: [true, false, false, false, true, false, true]
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,36 @@
# https://www.oregon.gov/ohcs/energy-weatherization/Documents/PY25%20EA%20Operations%20%20Policy%20Manual-%20FINAL%209-27-24.pdf#page=57
# PY2027, page 71:
# https://www.oregon.gov/ohcs/energy-weatherization/Documents/manuals/09-23-2027-PY-2027-EA-Operations-Policy-Manual.pdf#page=71
- name: Case 1, published FY2026 annual income limits for the odd household sizes 1 to 11.
# Each limit stops at the federal maximum of 42 U.S.C. 8624(b)(2)(B): the greater
# of HHS's 60% state median income amount (60% of the four-person median, rounded
# down, times the size factor, rounded down) and 150% of the prior year's poverty
# guideline. HHS's FY2026 amounts for Oregon, sizes 1 to 6: $38,384, $50,194,
# $62,005, $73,816, $85,626, $97,437:
# https://acf.gov/sites/default/files/documents/ocs/COMM_LIHEAP_IM2025-02_SMIStateTable_Att4.pdf#page=2
- name: Case 1, FY2026 annual income limits for the odd household sizes 1 to 11 stop at the federal maximum.
period: 2026
input:
state_code: [OR, OR, OR, OR, OR, OR]
spm_unit_size: [1, 3, 5, 7, 9, 11]
output:
# FY2026 page 77: size 1 $38,385, size 3 $62,006, size 5 $85,627, size 7
# $99,652, size 9 $104,081, size 11 $108,510.
or_liheap_income_limit: [38_385, 62_006, 85_627, 99_652, 104_081, 108_510]
# FY2026 page 77 prints size 1 $38,385, size 3 $62,006, size 5 $85,627,
# size 7 $99,652, size 9 $104,081, size 11 $108,510. HHS's amount is $1
# lower at each: 73,816 x 1.35 = 99,651 at size 7, x 1.41 = 104,080 at
# size 9 and x 1.47 = 108,509 at size 11.
or_liheap_income_limit: [38_384, 62_005, 85_626, 99_651, 104_080, 108_509]

- name: Case 2, published PY2025 annual income limits for the odd household sizes 1 to 11.
- name: Case 2, PY2025 annual income limits for the odd household sizes 1 to 11 stop at the federal maximum.
period: 2025
input:
state_code: [OR, OR, OR, OR, OR, OR]
spm_unit_size: [1, 3, 5, 7, 9, 11]
output:
# PY2025 page 57: size 1 $36,811, size 3 $59,464, size 5 $82,117, size 7
# $95,567, size 9 $99,814, size 11 $104,062.
or_liheap_income_limit: [36_811, 59_464, 82_117, 95_567, 99_814, 104_062]
# PY2025 page 57 prints size 1 $36,811, size 3 $59,464, size 5 $82,117,
# size 7 $95,567, size 9 $99,814, size 11 $104,062. The FY2025 four-person
# median is $117,983; 60% rounded down is $70,789, and the size factors
# 0.52, 0.84, 1.16, 1.35, 1.41 and 1.47 give $36,810, $59,462, $82,115,
# $95,565, $99,812 and $104,059.
or_liheap_income_limit: [36_810, 59_462, 82_115, 95_565, 99_812, 104_059]

- name: Case 3, published PY2027 annual income limits at the sizes whose 60 percent product is a whole dollar.
period: 2027
Expand All @@ -36,15 +47,31 @@
# PY2027 page 71: size 2 $52,734, size 3 $65,142, size 6 $102,366, size 8
# $107,019, size 10 $111,672, size 12 $116,325. The FY2027 four-person
# median is $129,250, so these products are exact dollars (129,250 x 0.68
# x 0.6 = 52,734) and a float32 ceiling would add $1 to each.
# x 0.6 = 52,734) and a float32 ceiling would add $1 to each. They equal
# the federal maximum, so none is lowered.
or_liheap_income_limit: [52_734, 65_142, 102_366, 107_019, 111_672, 116_325]

- name: Case 4, published FY2026 annual income limits for the even household sizes 2 to 12.
- name: Case 4, FY2026 annual income limits for the even household sizes 2 to 12 stop at the federal maximum.
period: 2026
input:
state_code: [OR, OR, OR, OR, OR, OR]
spm_unit_size: [2, 4, 6, 8, 10, 12]
output:
# FY2026 page 77: size 2 $50,196, size 4 $73,817, size 6 $97,438, size 8
# $101,867, size 10 $106,296, size 12 $110,725.
or_liheap_income_limit: [50_196, 73_817, 97_438, 101_867, 106_296, 110_725]
# FY2026 page 77 prints size 2 $50,196, size 4 $73,817, size 6 $97,438,
# size 8 $101,867, size 10 $106,296, size 12 $110,725. HHS's amount is $2
# lower at size 2 and $1 lower at sizes 4 to 10 (73,816 x 1.38 = 101,866;
# x 1.44 = 106,295). At size 12, 150% of the 2025 guideline (76,150 x 1.5 =
# 114,225) is the higher federal maximum, so the printed amount stays.
or_liheap_income_limit: [50_194, 73_816, 97_437, 101_866, 106_295, 110_725]

- name: Case 5, PY2027 limits whose 60 percent product ends in 50 cents stop at the federal maximum.
period: 2027
input:
state_code: [OR, OR, OR]
spm_unit_size: [7, 9, 11]
output:
# The FY2027 four-person median is $129,250, and 60% of it is $77,550.
# Times the size factors 1.35, 1.41 and 1.47 that is $104,692.50,
# $109,345.50 and $113,998.50. Rounded up they would be $104,693,
# $109,346 and $113,999; HHS rounds down, so each limit is $1 lower.
or_liheap_income_limit: [104_692, 109_345, 113_998]
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@
# 14,400 + pension 600 (page 44) + interest 201, counted because it is
# over $200 a year (pages 43 and 50) = 15,201.
or_liheap_countable_income: 15_201
# 60% of state median income, size 2: $50,196 (page 77).
or_liheap_income_limit: 50_196
# 60% of state median income, size 2: HHS's FY2026 amount is $50,194
# (page 77 prints $50,196).
or_liheap_income_limit: 50_194
# Deschutes County is in the Region 2 list (page 80).
or_liheap_region: 2
# Size 2, second row "$12,550 $25,098" (page 80). Counting the child's
Expand Down Expand Up @@ -78,8 +79,9 @@
# Income earned by a member aged 18 or over who is enrolled in high
# school is not counted (pages 41 and 53); there is no other income.
or_liheap_countable_income: 0
# 60% of state median income, size 1: $38,385 (page 77).
or_liheap_income_limit: 38_385
# 60% of state median income, size 1: HHS's FY2026 amount is $38,384
# (page 77 prints $38,385).
or_liheap_income_limit: 38_384
# Size 1, first row "$0 $9,597" (page 78). Counting the 13,100 of wages
# would give the second row instead.
or_liheap_income_band: 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@
# size 12 (page 81): $750, $750, $645, $460
or_liheap: [750, 655, 545, 390, 750, 725, 605, 435, 750, 750, 645, 460, 750, 750, 645, 460, 750, 750, 645, 460, 750, 750, 645, 460]

- name: Case 11, the published income limit includes the dollar above the last matrix row.
- name: Case 11, the income limit stops at the federal maximum, a dollar below the top of the last matrix row.
period: 2026
input:
state_code: [OR, OR, OR]
Expand All @@ -225,18 +225,17 @@
heating_type: [ELECTRICITY, ELECTRICITY, ELECTRICITY]
# About $125 a month for electric heat.
pre_subsidy_electricity_expense: [1_500, 1_500, 1_500]
# The last size 2 matrix row ends at $50,195 (page 78) while the size 2
# income limit is $50,196 (page 77): the top of the row, the limit itself
# and one dollar above the limit.
or_liheap_countable_income: [50_195, 50_196, 50_197]
# The last size 2 matrix row ends at $50,195 (page 78) and page 77 prints
# a size 2 limit of $50,196. Both are above HHS's FY2026 60% state median
# income amount for two people, $50,194, the most 42 U.S.C. 8624(b)(2)(B)
# allows: the limit itself, the top of the row and the printed limit.
or_liheap_countable_income: [50_194, 50_195, 50_196]
output:
# Size 2 limit $50,196 (page 77): $50,195 and $50,196 are within it,
# $50,197 is not.
or_liheap_eligible: [true, true, false]
# $50,194 is within the limit; $50,195 and $50,196 are not.
or_liheap_eligible: [true, false, false]
# Region 1, size 2, "$37,647 $50,195", Electricity $275 (page 78). The
# eligible household at exactly $50,196 is paid from that same last row.
# The ineligible household receives $0.
or_liheap: [275, 275, 0]
# ineligible households receive $0.
or_liheap: [275, 0, 0]

- name: Case 12, income bands include their printed tops and change at the next dollar.
period: 2026
Expand Down Expand Up @@ -439,7 +438,8 @@
or_liheap: [310, 0]

# PY2026 manual, page 44: rental-property income and pensions paid to surviving
# dependents count separately from wages. Page 77 gives the $38,385 size-one limit.
# dependents count separately from wages. The size-one limit is $38,384, HHS's
# FY2026 60% state median income amount (page 77 prints $38,385).
# https://www.oregon.gov/ohcs/energy-weatherization/Documents/2026%20Final%20Energy%20Assistance%20Intake%20Operations%20%26%20Policy%20Manual.pdf#page=44
- name: Case 19, farm rental income puts a gas-heating household over the income limit.
period: 2026
Expand All @@ -455,9 +455,9 @@
heating_type: NATURAL_GAS
gas_expense: 1_200
output:
# $30,000 wages + $10,000 farm rents exceeds $38,385.
# $30,000 wages + $10,000 farm rents exceeds $38,384.
or_liheap_countable_income: 40_000
or_liheap_income_limit: 38_385
or_liheap_income_limit: 38_384
or_liheap_eligible: false
or_liheap: 0

Expand All @@ -475,9 +475,9 @@
heating_type: NATURAL_GAS
gas_expense: 1_200
output:
# $30,000 wages + $10,000 survivor pension exceeds $38,385.
# $30,000 wages + $10,000 survivor pension exceeds $38,384.
or_liheap_countable_income: 40_000
or_liheap_income_limit: 38_385
or_liheap_income_limit: 38_384
or_liheap_eligible: false
or_liheap: 0

Expand Down Expand Up @@ -540,8 +540,9 @@
fuel_oil_expense: [0, 0, 2_400, 2_400]
or_liheap_countable_income: [9_400, 0, 20_000, 20_000]
output:
# PY2025 size 1 limit $36,811 (page 57).
or_liheap_income_limit: [36_811, 36_811, 36_811, 36_811]
# PY2025 size 1 limit: page 57 prints $36,811; the federal maximum is
# $36,810 (60% of $117,983, rounded down, x 0.52).
or_liheap_income_limit: [36_810, 36_810, 36_810, 36_810]
# PY2025 size 1 rows "$0 $9,203", "$9,204 $18,406", "$18,407 $27,609"
# (page 58).
or_liheap_income_band: [2, 1, 3, 3]
Expand Down Expand Up @@ -598,3 +599,61 @@
# Gas $380 and Heating Oil $535 in the last row. Sizes 7 to 12 print the
# size 6 amounts.
or_liheap: [750, 615, 515, 370, 380, 535]

# The matrices carry a COOLING Electricity column beside the HEATING columns,
# equal to the HEATING Electricity column in every row (PY2025 pages 58-59,
# PY2026 pages 78-81, PY2027 pages 72-75). Countable income of $26,000 is about
# 30 hours a week at the Portland-metro minimum wage, and about $125 a month
# pays the electric bill.
- name: Case 26, a household with no heating cost that pays for electric cooling receives the cooling column.
period: 2026
input:
state_code: [OR, OR, OR, OR]
county: [MULTNOMAH_COUNTY_OR, DESCHUTES_COUNTY_OR, MULTNOMAH_COUNTY_OR, DESCHUTES_COUNTY_OR]
spm_unit_size: [1, 1, 1, 1]
heating_type: [NONE, NONE, NONE, WOOD]
# The wood-heated home gathers its own wood, so it has no heating cost.
has_heating_expense: [false, false, false, false]
has_cooling_expense: [true, true, false, true]
pre_subsidy_electricity_expense: [1_500, 1_500, 1_500, 1_500]
or_liheap_countable_income: [26_000, 26_000, 26_000, 26_000]
output:
# PY2026 size 1 row "$19,194 $28,789" (page 78).
or_liheap_income_band: [3, 3, 3, 3]
# COOLING Electricity: Region 1 $300 (page 78), Region 2 $335 (page 80).
# Without a cooling cost the home is ineligible, $0. The wood-heated home
# applies its benefit to electricity, so it is paid from the COOLING
# column rather than Wood/Pellets (page 67).
or_liheap: [300, 335, 0, 335]

- name: Case 27, program year 2025 pays the PY2025 cooling column.
period: 2025
input:
state_code: OR
county: DESCHUTES_COUNTY_OR
spm_unit_size: 1
heating_type: NONE
has_cooling_expense: true
pre_subsidy_electricity_expense: 1_500
or_liheap_countable_income: 26_000
output:
# PY2025 size 1 row "$18,407 $27,609" (page 58); Region 2 COOLING
# Electricity $270 (page 59).
or_liheap_income_band: 3
or_liheap: 270

- name: Case 28, program year 2027 pays the PY2027 cooling column.
period: 2027
input:
state_code: OR
county: MULTNOMAH_COUNTY_OR
spm_unit_size: 1
heating_type: NONE
has_cooling_expense: true
pre_subsidy_electricity_expense: 1_500
or_liheap_countable_income: 26_000
output:
# PY2027 size 1 row "$20,164 $30,245" (page 72); Region 1 COOLING
# Electricity $310 (page 72).
or_liheap_income_band: 3
or_liheap: 310
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ class or_liheap_eligible(Variable):
value_type = bool
entity = SPMUnit
definition_period = YEAR
label = "Oregon LIHEAP regular heating eligibility"
label = "Oregon LIHEAP regular heating or cooling eligibility"
defined_for = StateCode.OR
# Manual PDF pages 11, 30, 36, 61, 77, 93-94; OAR 813-200-0020(1)(b).
# Manual PDF pages 11,30,36,61,67,77-81,90,93-94; OAR 813-200-0020(1)(b).
reference = (
"https://www.oregon.gov/ohcs/energy-weatherization/Documents/2026%20Final%20Energy%20Assistance%20Intake%20Operations%20%26%20Policy%20Manual.pdf#page=61",
"https://www.oregon.gov/ohcs/energy-weatherization/Documents/2026%20Final%20Energy%20Assistance%20Intake%20Operations%20%26%20Policy%20Manual.pdf#page=11",
"https://www.law.cornell.edu/regulations/oregon/Or-Admin-Code-SS-813-200-0020",
"https://www.law.cornell.edu/uscode/text/42/8624",
)
Expand All @@ -20,7 +20,9 @@ class or_liheap_eligible(Variable):
"813-200-0020(1)(b) and the manual's energy burden table: subsidized housing "
"with heat included in rent is excluded even when the household pays a "
"separate non-heating utility bill. The Oregon sources do not prescribe a "
"SNAP immigration test. No asset or categorical income test applies. "
"SNAP immigration test. A household with no heating cost, including a home "
"without heating, qualifies on an electric bill when it pays for cooling. No "
"asset or categorical income test applies. "
"Institutional residence, tribal duplicate awards, documentation and "
"application timing are not modeled. A zero benefit above size 12 or for an "
"unknown county denotes unsupported schedule coverage, not a legal denial."
Expand All @@ -33,7 +35,21 @@ def formula(spm_unit, period, parameters):
subsidized = spm_unit(
"receives_housing_assistance", period
) | spm_unit.household("is_in_public_housing", period)
heating_type = spm_unit("heating_type", period)
heating_cost = spm_unit("has_heating_expense", period) | heat_in_rent
# Eligible households are responsible for home heating or cooling costs
# (page 90). A household with no heating cost, including a home without
# heating, qualifies on an electric bill it pays for cooling; the
# benefit then follows the energy type it is applied to (page 67), and
# the matrix pays electric cooling from its COOLING column. heating_type
# is a required input, so UNSPECIFIED does not qualify.
cooling_only = (
(heating_type != heating_type.possible_values.UNSPECIFIED)
& ~heating_cost
& spm_unit("has_cooling_expense", period)
& (spm_unit("pre_subsidy_electricity_expense", period) > 0)
)
energy_cost = heating_cost | cooling_only
income = spm_unit("or_liheap_countable_income", period)
limit = spm_unit("or_liheap_income_limit", period)
# 42 U.S.C. 8624(b)(2)(B) bars excluding a household on income below 110%
Expand All @@ -44,4 +60,4 @@ def formula(spm_unit, period, parameters):
guideline = fpg(size, state_group, period, parameters, year_lag=p.fpg_year_lag)
floor = np.round(guideline * p_hhs.fpg_floor, 2)
income_eligible = (income <= limit) | (income < floor)
return heating_cost & ~(heat_in_rent & subsidized) & income_eligible
return energy_cost & ~(heat_in_rent & subsidized) & income_eligible
Loading
Loading