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()

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()