Clean vehicle credit#

The clean vehicle tax credit was introduced in 2008 and overhauled in the Inflation Reduction Act (IRA), signed in August 2022.

Examples#

Consider a filer in Massachusetts who buys a new Tesla Model Y—the top-selling clean vehicle as of July 2022—in 2023. Under pre-IRA law, they would not be eligible as Tesla has surpassed its manufacturer cap, but the IRA repeals that cap. The IRA adds new requirements to the credit around the manufacturer retail selling price, battery components and elements countries of origin, and filer income. It leaves the credit non-refundable, producing a phase-in.

from policyengine_us import IndividualSim

import pandas as pd
import plotly.express as px


def make(adults, children, purchased_qualifying_new_clean_vehicle):
    sim = IndividualSim(year=2023)
    # Specify age 25 to qualify for EITC.
    sim.add_person(name="head", age=25, is_tax_unit_head=True)
    members = ["head"]
    if adults == 2:
        sim.add_person(name="spouse")
        members.append("spouse")
    for i in range(children):
        child = "child{}".format(i)
        sim.add_person(name=child, age=5)
        members.append(child)
    sim.add_tax_unit(
        name="tax_unit",
        members=members,
        purchased_qualifying_new_clean_vehicle=purchased_qualifying_new_clean_vehicle,
        new_clean_vehicle_battery_capacity=70,
        # Assume it meets both requirements.
        new_clean_vehicle_battery_critical_minerals_extracted_in_trading_partner_country=0.5,
        new_clean_vehicle_battery_components_made_in_north_america=0.5,
        new_clean_vehicle_classification="SUV",
        new_clean_vehicle_msrp=65_990,  # Per Google.
        premium_tax_credit=0,
    )
    sim.add_spm_unit(name="spm_unit", members=members)
    sim.add_household(name="household", members=members, state_code="MA")
    sim.vary("employment_income", max=350_000)
    return pd.DataFrame(
        dict(
            employment_income=sim.calc("employment_income")[0],
            spm_unit_net_income=sim.calc("spm_unit_net_income")[0],
            credit=sim.calc("new_clean_vehicle_credit")[0],
            adults=adults,
            children=children,
            purchased_qualifying_new_clean_vehicle=purchased_qualifying_new_clean_vehicle,
        )
    )


l = []
for adults in [1, 2]:
    for children in range(0, 4):
        for purchased_qualifying_new_clean_vehicle in [True, False]:
            l.append(
                make(adults, children, purchased_qualifying_new_clean_vehicle)
            )

df = pd.concat(l)

wide = df.pivot(
    index=["employment_income", "adults", "children"],
    columns="purchased_qualifying_new_clean_vehicle",
    values="spm_unit_net_income",
).reset_index()
wide["benefit"] = wide[True] - wide[False]

LABELS = dict(
    employment_income="Employment income",
    benefit="Net clean vehicle credit",
    children="Children",
    adults="Adults",
)

fig = px.line(
    wide,
    "employment_income",
    "benefit",
    color="children",
    animation_frame="adults",
    labels=LABELS,
    title="Net benefit from clean vehicle credit under Inflation Reduction Act",
)
fig.update_layout(xaxis_tickformat="$,", yaxis_tickformat="$,")
fig.show()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/individual_sim.py:261, in IndividualSim.calc(self, var, period, target, index, map_to, reform)
    260 try:
--> 261     result = self.simulation.calculate(var, period)
    262 except:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:684, in Simulation._run_formula(self, variable, population, period)
    683     for added_variable in adds_list:
--> 684         values = values + self.calculate(
    685             added_variable, period, map_to=variable.entity.key
    686         )
    687 if variable.subtracts is not None:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py:34, in spm_unit_benefits.formula(spm_unit, period, parameters)
     33     BENEFITS.append("spm_unit_capped_housing_subsidy")
---> 34 return add(spm_unit, period, BENEFITS)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/commons/formulas.py:225, in add(entity, period, variables, options)
    211 """Sums a list of variables.
    212 
    213 Args:
   (...)
    223     ArrayLike: The result of the operation.
    224 """
--> 225 return for_each_variable(
    226     entity, period, variables, agg_func="add", options=options
    227 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/commons/formulas.py:184, in for_each_variable(entity, period, variables, agg_func, group_agg_func, options)
    182 elif variable_entity.is_person:
    183     values = group_agg_func(
--> 184         entity.members(variable, period, options=options)
    185     )
    186 elif entity.entity.is_person:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi.py:16, in ssi.formula(person, period, parameters)
     15     return 0
---> 16 return max_(0, person("uncapped_ssi", period))

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/uncapped_ssi.py:13, in uncapped_ssi.formula(person, period, parameters)
     12 def formula(person, period, parameters):
---> 13     amount = person("ssi_amount_if_eligible", period)
     14     meets_resource_test = person("meets_ssi_resource_test", period)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi_amount_if_eligible.py:16, in ssi_amount_if_eligible.formula(person, period, parameters)
     13 ssi = parameters(period).gov.ssa.ssi.amount
     14 return (
     15     where(
---> 16         person("ssi_claim_is_joint", period),
     17         ssi.couple,
     18         ssi.individual,
     19     )
     20     * MONTHS_IN_YEAR
     21 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi_claim_is_joint.py:12, in ssi_claim_is_joint.formula(person, period, parameters)
     11 eligible = person("is_ssi_aged_blind_disabled", period)
---> 12 both_eligible = person.marital_unit.sum(eligible) == 2
     13 income_is_deemed = (
     14     person("ssi_income_deemed_from_ineligible_spouse", period) > 0
     15 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/projector.py:28, in Projector.__getattr__.<locals>.projector_function(*args, **kwargs)
     27 result = reference_attr(*args, **kwargs)
---> 28 return self.transform_and_bubble_up(result)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/projector.py:37, in Projector.transform_and_bubble_up(self, result)
     36 def transform_and_bubble_up(self, result):
---> 37     transformed_result = self.transform(result)
     38     if self.parent is None:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/entity_to_person_projector.py:19, in EntityToPersonProjector.transform(self, result)
     18 def transform(self, result: ArrayLike) -> ArrayLike:
---> 19     return self.reference_entity.project(result)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/group_population.py:335, in GroupPopulation.project(self, array, role)
    334 def project(self, array: ArrayLike, role: Role = None) -> ArrayLike:
--> 335     self.check_array_compatible_with_entity(array)
    336     self.entity.check_role_validity(role)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:60, in Population.check_array_compatible_with_entity(self, array)
     59 if not self.count == array.size:
---> 60     raise ValueError(
     61         "Input {} is not a valid value for the entity {} (size = {} != {} = count)".format(
     62             array, self.entity.key, array.size, self.count
     63         )
     64     )

ValueError: Input [0. 0. 0. ... 0. 0. 0.] is not a valid value for the entity marital_unit (size = 7001 != 7002 = count)

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/individual_sim.py:264, in IndividualSim.calc(self, var, period, target, index, map_to, reform)
    263 try:
--> 264     result = self.sim.calculate_add(var, period)
    265 except:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:597, in Simulation.calculate_add(self, variable_name, period, decode_enums)
    591     raise ValueError(
    592         "Unable to sum constant variable '{}' over period {}: only variables defined daily, monthly, or yearly can be summed over time.".format(
    593             variable.name, period
    594         )
    595     )
--> 597 return sum(
    598     self.calculate(variable_name, sub_period)
    599     for sub_period in period.get_subperiods(variable.definition_period)
    600 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:598, in <genexpr>(.0)
    591     raise ValueError(
    592         "Unable to sum constant variable '{}' over period {}: only variables defined daily, monthly, or yearly can be summed over time.".format(
    593             variable.name, period
    594         )
    595     )
    597 return sum(
--> 598     self.calculate(variable_name, sub_period)
    599     for sub_period in period.get_subperiods(variable.definition_period)
    600 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:684, in Simulation._run_formula(self, variable, population, period)
    683     for added_variable in adds_list:
--> 684         values = values + self.calculate(
    685             added_variable, period, map_to=variable.entity.key
    686         )
    687 if variable.subtracts is not None:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py:34, in spm_unit_benefits.formula(spm_unit, period, parameters)
     33     BENEFITS.append("spm_unit_capped_housing_subsidy")
---> 34 return add(spm_unit, period, BENEFITS)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/commons/formulas.py:225, in add(entity, period, variables, options)
    211 """Sums a list of variables.
    212 
    213 Args:
   (...)
    223     ArrayLike: The result of the operation.
    224 """
--> 225 return for_each_variable(
    226     entity, period, variables, agg_func="add", options=options
    227 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/commons/formulas.py:184, in for_each_variable(entity, period, variables, agg_func, group_agg_func, options)
    182 elif variable_entity.is_person:
    183     values = group_agg_func(
--> 184         entity.members(variable, period, options=options)
    185     )
    186 elif entity.entity.is_person:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi.py:16, in ssi.formula(person, period, parameters)
     15     return 0
---> 16 return max_(0, person("uncapped_ssi", period))

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/uncapped_ssi.py:13, in uncapped_ssi.formula(person, period, parameters)
     12 def formula(person, period, parameters):
---> 13     amount = person("ssi_amount_if_eligible", period)
     14     meets_resource_test = person("meets_ssi_resource_test", period)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi_amount_if_eligible.py:16, in ssi_amount_if_eligible.formula(person, period, parameters)
     13 ssi = parameters(period).gov.ssa.ssi.amount
     14 return (
     15     where(
---> 16         person("ssi_claim_is_joint", period),
     17         ssi.couple,
     18         ssi.individual,
     19     )
     20     * MONTHS_IN_YEAR
     21 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    480 self._check_for_cycle(variable.name, period)
--> 481 array = self._run_formula(variable, population, period)
    483 # If no result, use the default value and cache it

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi_claim_is_joint.py:12, in ssi_claim_is_joint.formula(person, period, parameters)
     11 eligible = person("is_ssi_aged_blind_disabled", period)
---> 12 both_eligible = person.marital_unit.sum(eligible) == 2
     13 income_is_deemed = (
     14     person("ssi_income_deemed_from_ineligible_spouse", period) > 0
     15 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/projector.py:28, in Projector.__getattr__.<locals>.projector_function(*args, **kwargs)
     27 result = reference_attr(*args, **kwargs)
---> 28 return self.transform_and_bubble_up(result)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/projector.py:37, in Projector.transform_and_bubble_up(self, result)
     36 def transform_and_bubble_up(self, result):
---> 37     transformed_result = self.transform(result)
     38     if self.parent is None:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/entity_to_person_projector.py:19, in EntityToPersonProjector.transform(self, result)
     18 def transform(self, result: ArrayLike) -> ArrayLike:
---> 19     return self.reference_entity.project(result)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/group_population.py:335, in GroupPopulation.project(self, array, role)
    334 def project(self, array: ArrayLike, role: Role = None) -> ArrayLike:
--> 335     self.check_array_compatible_with_entity(array)
    336     self.entity.check_role_validity(role)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:60, in Population.check_array_compatible_with_entity(self, array)
     59 if not self.count == array.size:
---> 60     raise ValueError(
     61         "Input {} is not a valid value for the entity {} (size = {} != {} = count)".format(
     62             array, self.entity.key, array.size, self.count
     63         )
     64     )

ValueError: Input [0. 0. 0. ... 0. 0. 0.] is not a valid value for the entity marital_unit (size = 7001 != 7002 = count)

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
Cell In[1], line 51
     48     for children in range(0, 4):
     49         for purchased_qualifying_new_clean_vehicle in [True, False]:
     50             l.append(
---> 51                 make(adults, children, purchased_qualifying_new_clean_vehicle)
     52             )
     54 df = pd.concat(l)
     56 wide = df.pivot(
     57     index=["employment_income", "adults", "children"],
     58     columns="purchased_qualifying_new_clean_vehicle",
     59     values="spm_unit_net_income",
     60 ).reset_index()

Cell In[1], line 37, in make(adults, children, purchased_qualifying_new_clean_vehicle)
     32 sim.add_household(name="household", members=members, state_code="MA")
     33 sim.vary("employment_income", max=350_000)
     34 return pd.DataFrame(
     35     dict(
     36         employment_income=sim.calc("employment_income")[0],
---> 37         spm_unit_net_income=sim.calc("spm_unit_net_income")[0],
     38         credit=sim.calc("new_clean_vehicle_credit")[0],
     39         adults=adults,
     40         children=children,
     41         purchased_qualifying_new_clean_vehicle=purchased_qualifying_new_clean_vehicle,
     42     )
     43 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/individual_sim.py:266, in IndividualSim.calc(self, var, period, target, index, map_to, reform)
    264         result = self.sim.calculate_add(var, period)
    265     except:
--> 266         result = self.simulation.calculate_divide(var, period)
    267 if (
    268     target is not None
    269     and target not in self.situation_data[entity.plural]
    270 ):
    271     map_to = self.get_entity(target).key

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:634, in Simulation.calculate_divide(self, variable_name, period, decode_enums)
    630     return (
    631         self.calculate(variable_name, period=computation_period) / 12.0
    632     )
    633 elif period.unit == periods.YEAR:
--> 634     return self.calculate(variable_name, period)
    636 raise ValueError(
    637     "Unable to divide the value of '{}' to match period {}.".format(
    638         variable_name, period
    639     )
    640 )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    315 self.tracer.record_calculation_start(
    316     variable_name, period, self.branch_name
    317 )
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:
    322         result = result.decode_to_str()

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    479 try:
    480     self._check_for_cycle(variable.name, period)
--> 481     array = self._run_formula(variable, population, period)
    483     # If no result, use the default value and cache it
    484     if array is None:
    485         # Check if the variable has a previously defined value

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:684, in Simulation._run_formula(self, variable, population, period)
    682     values = 0
    683     for added_variable in adds_list:
--> 684         values = values + self.calculate(
    685             added_variable, period, map_to=variable.entity.key
    686         )
    687 if variable.subtracts is not None:
    688     if isinstance(variable.subtracts, str):

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    315 self.tracer.record_calculation_start(
    316     variable_name, period, self.branch_name
    317 )
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:
    322         result = result.decode_to_str()

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    479 try:
    480     self._check_for_cycle(variable.name, period)
--> 481     array = self._run_formula(variable, population, period)
    483     # If no result, use the default value and cache it
    484     if array is None:
    485         # Check if the variable has a previously defined value

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    719     array = formula(population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/household/income/spm_unit/spm_unit_benefits.py:34, in spm_unit_benefits.formula(spm_unit, period, parameters)
     32 if not parameters(period).gov.hud.abolition:
     33     BENEFITS.append("spm_unit_capped_housing_subsidy")
---> 34 return add(spm_unit, period, BENEFITS)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/commons/formulas.py:225, in add(entity, period, variables, options)
    205 def add(
    206     entity: Population,
    207     period: Period,
    208     variables: List[str],
    209     options: List[str] = None,
    210 ):
    211     """Sums a list of variables.
    212 
    213     Args:
   (...)
    223         ArrayLike: The result of the operation.
    224     """
--> 225     return for_each_variable(
    226         entity, period, variables, agg_func="add", options=options
    227     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/commons/formulas.py:184, in for_each_variable(entity, period, variables, agg_func, group_agg_func, options)
    181     values = entity(variable, period, options=options)
    182 elif variable_entity.is_person:
    183     values = group_agg_func(
--> 184         entity.members(variable, period, options=options)
    185     )
    186 elif entity.entity.is_person:
    187     raise ValueError(
    188         f"You requested to aggregate {variable} (defined for {variable_entity.plural}) to {entity.entity.plural}, but this is not yet implemented."
    189     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    130     return self.simulation.calculate_divide(
    131         variable_name, period, **calculate_kwargs
    132     )
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    315 self.tracer.record_calculation_start(
    316     variable_name, period, self.branch_name
    317 )
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:
    322         result = result.decode_to_str()

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    479 try:
    480     self._check_for_cycle(variable.name, period)
--> 481     array = self._run_formula(variable, population, period)
    483     # If no result, use the default value and cache it
    484     if array is None:
    485         # Check if the variable has a previously defined value

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    719     array = formula(population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi.py:16, in ssi.formula(person, period, parameters)
     14 if parameters(period).gov.ssa.ssi.abolish_ssi:
     15     return 0
---> 16 return max_(0, person("uncapped_ssi", period))

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    130     return self.simulation.calculate_divide(
    131         variable_name, period, **calculate_kwargs
    132     )
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    315 self.tracer.record_calculation_start(
    316     variable_name, period, self.branch_name
    317 )
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:
    322         result = result.decode_to_str()

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    479 try:
    480     self._check_for_cycle(variable.name, period)
--> 481     array = self._run_formula(variable, population, period)
    483     # If no result, use the default value and cache it
    484     if array is None:
    485         # Check if the variable has a previously defined value

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    719     array = formula(population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/uncapped_ssi.py:13, in uncapped_ssi.formula(person, period, parameters)
     12 def formula(person, period, parameters):
---> 13     amount = person("ssi_amount_if_eligible", period)
     14     meets_resource_test = person("meets_ssi_resource_test", period)
     15     eligible = person("is_ssi_eligible_individual", period)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    130     return self.simulation.calculate_divide(
    131         variable_name, period, **calculate_kwargs
    132     )
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    315 self.tracer.record_calculation_start(
    316     variable_name, period, self.branch_name
    317 )
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:
    322         result = result.decode_to_str()

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    479 try:
    480     self._check_for_cycle(variable.name, period)
--> 481     array = self._run_formula(variable, population, period)
    483     # If no result, use the default value and cache it
    484     if array is None:
    485         # Check if the variable has a previously defined value

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    719     array = formula(population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi_amount_if_eligible.py:16, in ssi_amount_if_eligible.formula(person, period, parameters)
     12 def formula(person, period, parameters):
     13     ssi = parameters(period).gov.ssa.ssi.amount
     14     return (
     15         where(
---> 16             person("ssi_claim_is_joint", period),
     17             ssi.couple,
     18             ssi.individual,
     19         )
     20         * MONTHS_IN_YEAR
     21     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:134, in Population.__call__(self, variable_name, period, options)
    130     return self.simulation.calculate_divide(
    131         variable_name, period, **calculate_kwargs
    132     )
    133 else:
--> 134     return self.simulation.calculate(
    135         variable_name, period, **calculate_kwargs
    136     )

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:320, in Simulation.calculate(self, variable_name, period, map_to, decode_enums)
    315 self.tracer.record_calculation_start(
    316     variable_name, period, self.branch_name
    317 )
    319 try:
--> 320     result = self._calculate(variable_name, period)
    321     if isinstance(result, EnumArray) and decode_enums:
    322         result = result.decode_to_str()

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:481, in Simulation._calculate(self, variable_name, period)
    479 try:
    480     self._check_for_cycle(variable.name, period)
--> 481     array = self._run_formula(variable, population, period)
    483     # If no result, use the default value and cache it
    484     if array is None:
    485         # Check if the variable has a previously defined value

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/simulations/simulation.py:721, in Simulation._run_formula(self, variable, population, period)
    719     array = formula(population, period)
    720 else:
--> 721     array = formula(population, period, parameters_at)
    723 return array

File ~/work/policyengine-us/policyengine-us/policyengine_us/variables/gov/ssa/ssi/ssi_claim_is_joint.py:12, in ssi_claim_is_joint.formula(person, period, parameters)
     10 def formula(person, period, parameters):
     11     eligible = person("is_ssi_aged_blind_disabled", period)
---> 12     both_eligible = person.marital_unit.sum(eligible) == 2
     13     income_is_deemed = (
     14         person("ssi_income_deemed_from_ineligible_spouse", period) > 0
     15     )
     16     return both_eligible | income_is_deemed

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/projector.py:28, in Projector.__getattr__.<locals>.projector_function(*args, **kwargs)
     26 def projector_function(*args, **kwargs):
     27     result = reference_attr(*args, **kwargs)
---> 28     return self.transform_and_bubble_up(result)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/projector.py:37, in Projector.transform_and_bubble_up(self, result)
     36 def transform_and_bubble_up(self, result):
---> 37     transformed_result = self.transform(result)
     38     if self.parent is None:
     39         return transformed_result

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/projectors/entity_to_person_projector.py:19, in EntityToPersonProjector.transform(self, result)
     18 def transform(self, result: ArrayLike) -> ArrayLike:
---> 19     return self.reference_entity.project(result)

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/group_population.py:335, in GroupPopulation.project(self, array, role)
    334 def project(self, array: ArrayLike, role: Role = None) -> ArrayLike:
--> 335     self.check_array_compatible_with_entity(array)
    336     self.entity.check_role_validity(role)
    337     if role is None:

File /opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/policyengine_core/populations/population.py:60, in Population.check_array_compatible_with_entity(self, array)
     58 def check_array_compatible_with_entity(self, array: numpy.ndarray) -> None:
     59     if not self.count == array.size:
---> 60         raise ValueError(
     61             "Input {} is not a valid value for the entity {} (size = {} != {} = count)".format(
     62                 array, self.entity.key, array.size, self.count
     63             )
     64         )

ValueError: Input [0. 0. 0. ... 0. 0. 0.] is not a valid value for the entity marital_unit (size = 7001 != 7002 = count)

Since the used clean vehicle credit is also non-refundable, filers must have a certain level of income to benefit from it. For example, a single filer purchasing a used clean vehicle will receive the full benefit when they earn $48,000, and then lose the entire benefit when they earn $75,000. Married filers and those with children have to earn more to receive the full benefit, and continue to receive the benefit with higher incomes before losing it.

def make_used(adults, children, purchased_qualifying_used_clean_vehicle):
    sim = IndividualSim(year=2023)
    # Specify age 25 to qualify for EITC.
    sim.add_person(name="head", age=25, is_tax_unit_head=True)
    members = ["head"]
    if adults == 2:
        sim.add_person(name="spouse")
        members.append("spouse")
    for i in range(children):
        child = "child{}".format(i)
        sim.add_person(name=child, age=5)
        members.append(child)
    sim.add_tax_unit(
        name="tax_unit",
        members=members,
        purchased_qualifying_used_clean_vehicle=purchased_qualifying_used_clean_vehicle,
        used_clean_vehicle_sale_price=20_000,
        premium_tax_credit=0,
    )
    sim.add_spm_unit(name="spm_unit", members=members)
    sim.add_household(name="household", members=members, state_code="MA")
    sim.vary("employment_income", max=350_000)
    return pd.DataFrame(
        dict(
            employment_income=sim.calc("employment_income")[0],
            spm_unit_net_income=sim.calc("spm_unit_net_income")[0],
            credit=sim.calc("used_clean_vehicle_credit")[0],
            adults=adults,
            children=children,
            purchased_qualifying_used_clean_vehicle=purchased_qualifying_used_clean_vehicle,
        )
    )


l = []
for adults in [1, 2]:
    for children in range(0, 4):
        for purchased_qualifying_used_clean_vehicle in [True, False]:
            l.append(
                make_used(
                    adults,
                    children,
                    purchased_qualifying_used_clean_vehicle,
                )
            )

df = pd.concat(l)

wide = df.pivot(
    index=["employment_income", "adults", "children"],
    columns="purchased_qualifying_used_clean_vehicle",
    values="spm_unit_net_income",
).reset_index()

wide["benefit"] = wide[True] - wide[False]

LABELS = dict(
    employment_income="Employment income",
    benefit="Net used clean vehicle credit",
    children="Children",
    adults="Adults",
)

fig = px.line(
    wide,
    "employment_income",
    "benefit",
    color="children",
    animation_frame="adults",
    labels=LABELS,
    title="Net benefit from used clean vehicle credit under Inflation Reduction Act",
)
fig.update_layout(xaxis_tickformat="$,", yaxis_tickformat="$,")
fig.show()