Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/liheap-smi-helper.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Share one HHS state median income helper for the LIHEAP income limits in Mississippi, Maryland and New Jersey, and reuse the SMI household-size factor in Colorado CCAP.
26 changes: 23 additions & 3 deletions policyengine_us/variables/gov/hhs/hhs_smi.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,37 @@
from policyengine_us.model_api import *


def smi(unit_size, state, period, parameters):
def smi_size_factor(unit_size, period, parameters):
"""Return the 45 CFR 96.85(b) share of the four-person SMI for a size."""
p = parameters(period).gov.hhs.smi
size_threshold = p.additional_person_threshold
capped_size = clip(unit_size - 1, 0, size_threshold - 1)
extra_persons = max_(unit_size - size_threshold, 0)
size_adjustment = (
return (
p.household_size_adjustment.first_person
+ p.household_size_adjustment.second_to_sixth_person * capped_size
+ p.household_size_adjustment.additional_person * extra_persons
)
return p.amount[state] * size_adjustment


def smi(unit_size, state, period, parameters):
p = parameters(period).gov.hhs.smi
return p.amount[state] * smi_size_factor(unit_size, period, parameters)


def liheap_smi_limit(unit_size, state, rate, period, parameters):
"""Return the LIHEAP limit at a rate of the size-adjusted SMI.

HHS multiplies the rate times the four-person SMI by the size factor
(LIHEAP IM 2025-02, Attachment 4). Flooring the four-person amount, then
the size-adjusted amount, reproduces the published tables; HHS does not
state a rounding order. Pass an earlier date as the period to use a prior
year's SMI.
"""
four_person_smi = parameters(period).gov.hhs.smi.amount[state]
four_person_limit = np.floor(four_person_smi * rate)
size_factor = smi_size_factor(unit_size, period, parameters)
return np.floor(four_person_limit * size_factor)


class hhs_smi(Variable):
Expand Down
15 changes: 2 additions & 13 deletions policyengine_us/variables/gov/states/co/ccap/co_ccap_smi.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from policyengine_us.model_api import *
from policyengine_us.variables.gov.hhs.hhs_smi import smi


class co_ccap_smi(Variable):
Expand All @@ -18,16 +19,4 @@ def formula(spm_unit, period, parameters):
instant_str = f"{year}-10-01"
else:
instant_str = f"{year - 1}-10-01"
four_person_smi = parameters(instant_str).gov.hhs.smi.amount[state_code]
adjustment_mapping = parameters(
instant_str
).gov.hhs.smi.household_size_adjustment
first_person_rate = adjustment_mapping.first_person
second_to_sixth_additional_rate = adjustment_mapping.second_to_sixth_person
seven_or_more_additional_rate = adjustment_mapping.additional_person
size_adjustment = (
first_person_rate
+ second_to_sixth_additional_rate * (min_(size, 6) - 1)
+ seven_or_more_additional_rate * max_(size - 6, 0)
)
return four_person_smi * size_adjustment / MONTHS_IN_YEAR
return smi(size, state_code, instant_str, parameters) / MONTHS_IN_YEAR
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from policyengine_us.model_api import *
from policyengine_us.variables.gov.hhs.hhs_smi import smi
from policyengine_us.variables.gov.hhs.hhs_smi import liheap_smi_limit


class md_meap_income_limit(Variable):
Expand Down Expand Up @@ -28,10 +28,6 @@ def formula(spm_unit, period, parameters):
p = parameters(period).gov.states.md.dhs.meap.eligibility
size = max_(spm_unit("md_meap_household_size", period), 1)
state = spm_unit.household("state_code_str", period)
base = parameters(period).gov.hhs.smi.amount[state]
factor = smi(size, state, period, parameters) / base
# ACF LIHEAP IM 2025-02 Attachment 4 (page 5) floors 60% of the
# four-person SMI, then floors each size's share of it.
smi_limit = np.floor(factor * np.floor(base * p.smi_rate))
smi_limit = liheap_smi_limit(size, state, p.smi_rate, period, parameters)
poverty_limit = spm_unit("md_meap_fpg", period) * p.fpg_rate
return where(size >= p.smi_min_size, smi_limit, poverty_limit)
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from policyengine_us.model_api import *
from policyengine_us.variables.gov.hhs.hhs_smi import liheap_smi_limit


class ms_liheap_income_limit(Variable):
Expand All @@ -18,16 +19,6 @@ def formula(spm_unit, period, parameters):
p = parameters(period).gov.states.ms.mdhs.liheap
size = spm_unit("ms_liheap_household_size", period)
state = spm_unit.household("state_code_str", period)
federal = parameters(period).gov.hhs.smi
# The published FY2026 limits truncate the four-person 60% SMI amount
# first, then the household-adjusted amount. This reproduces all 20 rows;
# it is a reconciled numerical pattern, not an explicit rounding rule.
four_person_limit = np.floor(federal.amount[state] * p.income_limit)
adjustment = federal.household_size_adjustment
threshold = federal.additional_person_threshold
size_share = (
adjustment.first_person
+ adjustment.second_to_sixth_person * clip(size - 1, 0, threshold - 1)
+ adjustment.additional_person * max_(size - threshold, 0)
)
return where(size > 0, np.floor(four_person_limit * size_share), 0)
# The HHS floor order reproduces all 20 published FY2026 rows.
limit = liheap_smi_limit(size, state, p.income_limit, period, parameters)
return where(size > 0, limit, 0)
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from policyengine_us.model_api import *
from policyengine_us.variables.gov.hhs.hhs_smi import smi
from policyengine_us.variables.gov.hhs.hhs_smi import liheap_smi_limit


class nj_liheap_income_limit(Variable):
Expand Down Expand Up @@ -32,15 +32,11 @@ def formula(spm_unit, period, parameters):
p = parameters(period).gov.states.nj.dca.liheap.eligibility
size = max_(spm_unit("nj_liheap_household_size", period), 1)
state = spm_unit.household("state_code_str", period)
smi_parameters = parameters(period).gov.hhs.smi
base = smi_parameters.amount[state]

def monthly_limit(table_size):
factor = smi(table_size, state, period, parameters) / base
# ACF LIHEAP IM 2025-02 Attachment 4 (page 2) floors 60% of the
# four-person SMI, then floors each size's share of it; the fact
# sheet's monthly column rounds that half up (not half to even).
annual = np.floor(factor * np.floor(base * p.smi_rate))
annual = liheap_smi_limit(table_size, state, p.smi_rate, period, parameters)
# The fact sheet's monthly column rounds the HHS annual limit half
# up (not half to even).
return np.floor(annual / MONTHS_IN_YEAR + 0.5)

monthly = monthly_limit(min_(size, p.max_table_size))
Expand Down
Loading