Housing assistance#

Housing assistance is a payment that varies with income, household size, rental expenses, local economic conditions, and other factors.

How earnings affect a one-person household’s housing assistance#

Consider a single person household in San Francisco, California with $2,000 monthly earned income and $1,000 monthly rent. They would be eligible for $400 per month in housing assistance.

from policyengine_us import IndividualSim
import pandas as pd
import plotly.express as px

sim_emp = IndividualSim(year=2022)
sim_emp.add_person(name="person", employment_income=2000 * 12)
sim_emp.add_spm_unit(
    name="spm_unit",
    members=["person"],
    hud_gross_rent=1_000 * 12,
    # 2022 AMI.
    # https://sfmohcd.org/sites/default/files/Documents/MOH/BMR%20Ownership/2022%20AMI-IncomeLimits.pdf
    ami=138550,
    # 2019 1BR payment standard.
    # https://sfmohcd.org/sites/default/files/Documents/MOH/Asset%20Management/2019%20AMI_RentLimits-HMFA.pdf
    pha_payment_standard=2748 * 12,
)

print(
    "Housing assistance: ",
    round(sim_emp.calc("housing_assistance")[0] / 12),
)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/policyengine_core/simulations/simulation_builder.py in init_variable_values(self, entity, instance_object, instance_id)
    548             try:
--> 549                 entity.check_variable_defined_for_entity(variable_name)
    550             except (

/opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/policyengine_core/entities/entity.py in check_variable_defined_for_entity(self, variable_name)
     51             )
---> 52             raise ValueError(message)

ValueError: You tried to compute the variable 'ami' for the entity 'spm_units';
however the variable 'ami' is defined for 'households'.
Learn more about entities in our documentation:
<https://openfisca.org/doc/coding-the-legislation/50_entities.html>.

During handling of the above exception, another exception occurred:

SituationParsingError                     Traceback (most recent call last)
/tmp/ipykernel_2520/768002617.py in <cell line: 0>()
     19 print(
     20     "Housing assistance: ",
---> 21     round(sim_emp.calc("housing_assistance")[0] / 12),
     22 )

/opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/policyengine_core/simulations/individual_sim.py in calc(self, var, period, target, index, map_to, reform)
    238         """
    239         if not hasattr(self, "simulation"):
--> 240             self.build()
    241 
    242         if self.parametric_vary and reform is None:

/opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/policyengine_core/simulations/individual_sim.py in build(self)
     86                     ] = members
     87             # Add missing entities with specified default roles
---> 88         self.simulation = Simulation(
     89             tax_benefit_system=self.system,
     90             situation=self.situation_data,

/opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/policyengine_core/simulations/simulation.py in __init__(self, tax_benefit_system, populations, situation, dataset, reform, trace)
    146             builder = SimulationBuilder()
    147             builder.default_period = self.default_input_period
--> 148             builder.build_from_dict(self.tax_benefit_system, situation, self)
    149             self.has_axes = builder.has_axes
    150 

/opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/policyengine_core/simulations/simulation_builder.py in build_from_dict(self, tax_benefit_system, input_dict, simulation)
     84             for key in input_dict.keys()
     85         ):
---> 86             simulation = self.build_from_entities(
     87                 tax_benefit_system, input_dict, simulation
     88             )

/opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/policyengine_core/simulations/simulation_builder.py in build_from_entities(self, tax_benefit_system, input_dict, simulation)
    174             instances_json = input_dict.get(entity_class.plural)
    175             if instances_json is not None:
--> 176                 self.add_group_entity(
    177                     self.persons_plural,
    178                     persons_ids,

/opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/policyengine_core/simulations/simulation_builder.py in add_group_entity(self, persons_plural, persons_ids, entity, instances_json)
    483                     self.roles[entity.plural][person_index] = person_role
    484 
--> 485             self.init_variable_values(entity, variables_json, instance_id)
    486 
    487         if persons_to_allocate:

/opt/hostedtoolcache/Python/3.11.9/x64/lib/python3.11/site-packages/policyengine_core/simulations/simulation_builder.py in init_variable_values(self, entity, instance_object, instance_id)
    551                 ValueError
    552             ) as e:  # The variable is defined for another entity
--> 553                 raise SituationParsingError(path_in_json, e.args[0])
    554             except VariableNotFoundError as e:  # The variable doesn't exist
    555                 raise SituationParsingError(path_in_json, str(e), code=404)

SituationParsingError: {'spm_units': {'spm_unit': {'ami': "You tried to compute the variable 'ami' for the entity 'spm_units'; however the variable 'ami' is defined for 'households'. Learn more about entities in our documentation: <https://openfisca.org/doc/coding-the-legislation/50_entities.html>."}}}

What if their earnings change? They receive their full rent of $1,000 if they have zero earnings, and the benefit phases out at 30 cents per dollar of earnings, until it fully phases out at $3,333 monthly earnings.

sim_emp.vary("employment_income", max=4_000 * 12, step=120)

import plotly.express as px

LABELS = dict(
    employment_income="Monthly employment income",
    dividend_income="Monthly dividend income",
    income="Monthly income",
    income_source="Income source",
    housing_cost="Monthly housing cost",
    housing_assistance="Monthly housing assistance",
    hap_mtr="Marginal tax rate from HAP",
    housing_assistance_mtr="Marginal tax rate from housing assistance",
    hap="Housing assistance payment (if eligible)",
)

emp_df_full = pd.DataFrame(
    dict(
        employment_income=sim_emp.calc("employment_income")[0] / 12,
        hap=sim_emp.calc("hud_hap")[0] / 12,
        housing_assistance=sim_emp.calc("housing_assistance")[0] / 12,
        hap_mtr=-sim_emp.deriv("hud_hap", "employment_income"),
        housing_assistance_mtr=-sim_emp.deriv(
            "housing_assistance", "employment_income"
        ),
    )
)

fig = px.line(
    emp_df_full,
    "employment_income",
    "housing_assistance",
    labels=LABELS,
    title="Housing assistance for a one-person household in California with $1,000 monthly housing costs",
)
fig.update_layout(xaxis_tickformat="$,", yaxis_tickformat="$,")
fig.show()

We can also view their marginal tax rate from the program, revealing housing assistance’s 30% phase-out rate.

fig = px.line(
    emp_df_full,
    "employment_income",
    "housing_assistance_mtr",
    labels=LABELS,
    title="Housing assistance MTR for a one-person household in California with $1,000 monthly housing costs",
)
fig.update_layout(
    xaxis_tickformat="$,", yaxis_tickformat=".0%", yaxis_range=[0, 1]
)
fig.show()