Skip to content

DC and Illinois LIHEAP payments raise errors beside out-of-state households, and Louisiana's school readiness credit raises one for a family beside a second tax unit, where each should return every household's own amount #10788

Open

Description

@MaxGhenis

Three formulas return an amount for one household with one tax unit and raise an error once other households or tax units share the simulation.

  • dc_liheap_payment raises ParameterNotFoundError when a simulation holds a DC household that qualifies for LIHEAP and any household outside DC.
  • il_liheap_base_payment raises the same error when a simulation holds an Illinois household and any household outside Illinois. il_liheap adds it, il_ihwap_categorically_eligible adds a list that names il_liheap, and il_ihwap_eligible reads that result. All three raise too when the Illinois household qualifies for LIHEAP.
  • la_school_readiness_credit raises ValueError when a simulation holds a Louisiana tax unit, a second tax unit in any state, and more people than tax units. Two Louisiana families trigger it, and so does one Louisiana household of three people in two tax units. la_school_readiness_credit_non_refundable adds it and raises too, and la_school_readiness_credit_refundable does when a Louisiana tax unit passes its income test.

For example, for a single adult in DC with no income and $2,000 of heating costs, dc_liheap_payment returns $904 for 2026. Add a single adult in New York to the same simulation, and the same call raises ParameterNotFoundError for gov.states.dc.doee.liheap.payment.electricity.0.

The model still computes household_net_income for a simulation that mixes these households. A caller meets the errors by requesting one of these variables under those conditions: for the three-person Louisiana household, in a test that pairs households from two states, or over a dataset that spans states.

Why they raise

policyengine-core 3.34.2, the version main's uv.lock pins, computes a variable's defined_for mask first. If any member passes, it runs the formula over the whole population and applies the mask to the result afterwards. So every lookup and every product inside a formula has to work for every household and tax unit in the simulation.

What the code should do

Each formula should return the same amount for each household whatever other households or tax units share the simulation. vt_renter_credit does this for its county tables: before the lookup, it gives every household outside Vermont a county the tables hold.

Reproduction

This script runs each variable for one household and then for the same household beside a single adult in New York, at commit 3f6e604:

from policyengine_us import Simulation

YEAR = 2026
GROUPS = ["households", "tax_units", "spm_units", "families"]


def simulate(*households):
    situation = {"people": {}, "marital_units": {}, **{group: {} for group in GROUPS}}
    for i, (state, people, tax_unit_inputs) in enumerate(households):
        members = [f"person_{i}_{j}" for j in range(len(people))]
        for name, person in zip(members, people):
            situation["people"][name] = {k: {YEAR: v} for k, v in person.items()}
            situation["marital_units"][f"marital_unit_{name}"] = {"members": [name]}
        for group in GROUPS:
            situation[group][f"{group}_{i}"] = {"members": members}
        situation["households"][f"households_{i}"]["state_code"] = {YEAR: state}
        for k, v in tax_unit_inputs.items():
            situation["tax_units"][f"tax_units_{i}"][k] = {YEAR: v}
    return Simulation(situation=situation)


adult = {"age": 40, "heating_expense_person": 2_000}
parent, child = {"age": 30}, {"age": 3, "la_quality_rating_of_child_care_facility": 5}
dc, il, ny = [(state, [adult], {}) for state in ("DC", "IL", "NY")]
la = ("LA", [parent, child], {"la_refundable_cdcc": 50})

for variable, home in [
    ("dc_liheap_payment", dc),
    ("il_liheap_base_payment", il),
    ("il_liheap", il),
    ("il_ihwap_eligible", il),
    ("la_school_readiness_credit", la),
]:
    print(variable, "alone:", simulate(home).calculate(variable, YEAR))
    try:
        both = simulate(home, ny).calculate(variable, YEAR)
        print(variable, "with a New York household:", both)
    except Exception as error:
        print(variable, "with a New York household:", type(error).__name__, error)

It prints:

dc_liheap_payment alone: [904.]
dc_liheap_payment with a New York household: ParameterNotFoundError The parameter 'gov.states.dc.doee.liheap.payment.electricity.0' was not found in the 2026-01-01 tax and benefit system.
il_liheap_base_payment alone: [910.]
il_liheap_base_payment with a New York household: ParameterNotFoundError The parameter 'gov.states.il.dceo.liheap.payment.matrix.all_electric.0' was not found in the 2026-01-01 tax and benefit system.
il_liheap alone: [910.]
il_liheap with a New York household: ParameterNotFoundError The parameter 'gov.states.il.dceo.liheap.payment.matrix.all_electric.0' was not found in the 2026-01-01 tax and benefit system.
il_ihwap_eligible alone: [ True]
il_ihwap_eligible with a New York household: ParameterNotFoundError The parameter 'gov.states.il.dceo.liheap.payment.matrix.all_electric.0' was not found in the 2026-01-01 tax and benefit system.
la_school_readiness_credit alone: [100.]
la_school_readiness_credit with a New York household: ValueError operands could not be broadcast together with shapes (2,) (3,) 

We ran it with policyengine-core 3.33.2, which orders the mask and the formula the same way as 3.34.2.

In further runs, dc_liheap_payment returns [904. 904.] for two DC households that both qualify and [0. 0.] for a DC household over the income limit beside the New York one. la_school_readiness_credit raises the same ValueError for a Louisiana family beside a single Louisiana adult, in a second household or in a second tax unit of the family's household. It returns [0. 0.] for two single Louisiana adults. For an Illinois household over the income limit beside the New York one, il_liheap_base_payment still raises, while il_liheap returns [0. 0.].

Fix

For DC and Illinois, clip the income level to the matrix's rows before the lookup (1 to 10 in DC, 1 to 4 in Illinois), as nv_liheap does for its size key. policyengine-core still zeroes the out-of-state rows through defined_for. For Louisiana, multiply by tax_unit.project(child_care_credit), which gives each person their own tax unit's credit. With those three changes applied as a reform, one simulation of DC, Illinois, New York and Louisiana households runs, and every household keeps the amount it gets alone.

Related

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions