diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29bb2d..401c11d8695 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,4 @@ +- bump: minor + changes: + added: + - Exclude students from the SNAP unit with certain exceptions. diff --git a/policyengine_us/parameters/gov/usda/snap/max_allotment.yaml b/policyengine_us/parameters/gov/usda/snap/max_allotment.yaml index d5911077dc0..5c8f0ce5720 100644 --- a/policyengine_us/parameters/gov/usda/snap/max_allotment.yaml +++ b/policyengine_us/parameters/gov/usda/snap/max_allotment.yaml @@ -7,6 +7,8 @@ main: - range(1, 9) label: SNAP max allotment CONTIGUOUS_US: + 0: + 2020-10-01: 0 1: 2020-10-01: 204 2021-01-01: 234 @@ -64,6 +66,8 @@ main: 2023-10-01: 1_751 2024-10-01: 1_756 AK_URBAN: + 0: + 2020-10-01: 0 1: 2020-10-01: 251 2021-01-01: 289 @@ -121,6 +125,8 @@ main: 2023-10-01: 2_246 2024-10-01: 2_265 AK_RURAL_1: + 0: + 2020-10-01: 0 1: 2020-10-01: 320 2021-01-01: 368 @@ -178,6 +184,8 @@ main: 2023-10-01: 2_865 2024-10-01: 2_888 AK_RURAL_2: + 0: + 2020-10-01: 0 1: 2020-10-01: 390 2021-01-01: 448 @@ -235,6 +243,8 @@ main: 2023-10-01: 3_487 2024-10-01: 3_516 GU: + 0: + 2020-10-01: 0 1: 2020-10-01: 300 2021-01-01: 345 @@ -292,6 +302,8 @@ main: 2023-10-01: 2_581 2024-10-01: 2_588 HI: + 0: + 2020-10-01: 0 1: 2020-10-01: 375 2021-01-01: 432 @@ -349,6 +361,8 @@ main: 2023-10-01: 3_166 2024-10-01: 3_102 VI: + 0: + 2020-10-01: 0 1: 2020-10-01: 262 2021-01-01: 301 diff --git a/policyengine_us/parameters/gov/usda/snap/student/age_threshold.yaml b/policyengine_us/parameters/gov/usda/snap/student/age_threshold.yaml new file mode 100644 index 00000000000..a6ab4af1377 --- /dev/null +++ b/policyengine_us/parameters/gov/usda/snap/student/age_threshold.yaml @@ -0,0 +1,23 @@ +description: The United States includes students in the Supplemental Nutrition Assistance Program unit if their age falls into the following range. +brackets: + - threshold: + 2018-01-01: 0 + amount: + 2018-01-01: true + - threshold: + 2018-01-01: 18 + amount: + 2018-01-01: false + - threshold: + 2018-01-01: 50 + amount: + 2018-01-01: true + +metadata: + type: single_amount + threshold_unit: year + amount_unit: bool + label: SNAP student age eligibility threshold + reference: + - title: 7 U.S. Code § 2015 - Eligibility disqualifications (e)(1) + href: https://www.law.cornell.edu/uscode/text/7/2015 diff --git a/policyengine_us/parameters/gov/usda/snap/student/working_hours_threshold.yaml b/policyengine_us/parameters/gov/usda/snap/student/working_hours_threshold.yaml new file mode 100644 index 00000000000..d3998056be2 --- /dev/null +++ b/policyengine_us/parameters/gov/usda/snap/student/working_hours_threshold.yaml @@ -0,0 +1,12 @@ +description: The United States includes students who work more than this number of weekly hours in the Supplemental Nutrition Assistance Program unit. + +values: + 2005-01-01: 20 + +metadata: + unit: hours + label: SNAP student working hours threshold + period: week + reference: + - title: 7 U.S. Code § 2015 - Eligibility disqualifications (e)(4) + href: https://www.law.cornell.edu/uscode/text/7/2015 diff --git a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml index 4db1352a744..6699ea6fb28 100644 --- a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/is_snap_eligible.yaml @@ -27,3 +27,75 @@ meets_snap_categorical_eligibility: false output: is_snap_eligible: true + +- name: One ineligible student present + period: 2022 + input: + people: + person1: + is_snap_ineligible_student: false + person2: + is_snap_ineligible_student: false + person3: + is_snap_ineligible_student: true + spm_units: + spm_unit1: + members: [person1, person2, person3] + meets_snap_gross_income_test: true + meets_snap_net_income_test: true + meets_snap_asset_test: true + meets_snap_categorical_eligibility: false + output: + is_snap_eligible: true + +- name: Every person ineligible + period: 2022 + input: + people: + person1: + is_snap_ineligible_student: true + person2: + is_snap_ineligible_student: true + person3: + is_snap_ineligible_student: true + spm_units: + spm_unit1: + members: [person1, person2, person3] + meets_snap_gross_income_test: true + meets_snap_net_income_test: true + meets_snap_asset_test: true + meets_snap_categorical_eligibility: false + output: + is_snap_eligible: false + +- name: Single ineligible student + period: 2022 + input: + people: + person1: + is_snap_ineligible_student: true + spm_units: + spm_unit1: + members: [person1] + meets_snap_gross_income_test: true + meets_snap_net_income_test: true + meets_snap_asset_test: true + meets_snap_categorical_eligibility: false + output: + is_snap_eligible: false + +- name: Single eligible person + period: 2022 + input: + people: + person1: + is_snap_ineligible_student: false + spm_units: + spm_unit1: + members: [person1] + meets_snap_gross_income_test: true + meets_snap_net_income_test: true + meets_snap_asset_test: true + meets_snap_categorical_eligibility: false + output: + is_snap_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/student/is_snap_ineligible_student.yaml b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/student/is_snap_ineligible_student.yaml new file mode 100644 index 00000000000..d803d1c2a96 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/usda/snap/eligibility/student/is_snap_ineligible_student.yaml @@ -0,0 +1,21 @@ +- name: Student who does not meet any exemption criteria + period: 2022 + input: + is_full_time_student: true + age: 18 + is_disabled: false + weekly_hours_worked: false + is_parent: false + output: + is_snap_ineligible_student: true + +- name: Student who is under the age limit + period: 2022 + input: + is_full_time_student: true + age: 17 + is_disabled: false + weekly_hours_worked: false + is_parent: false + output: + is_snap_ineligible_student: false diff --git a/policyengine_us/tests/policy/baseline/gov/usda/snap/snap_unit.yaml b/policyengine_us/tests/policy/baseline/gov/usda/snap/snap_unit.yaml new file mode 100644 index 00000000000..d93b2db0c7c --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/usda/snap/snap_unit.yaml @@ -0,0 +1,50 @@ +- name: Household with one ineligible studnet + period: 2022 + input: + people: + person1: + is_snap_ineligible_student: false + person2: + is_snap_ineligible_student: false + person3: + is_snap_ineligible_student: true + spm_units: + spm_unit1: + members: [person1, person2, person3] + spm_unit_size: 3 + output: + snap_unit_size: 2 + +- name: Everybody eligible + period: 2022 + input: + people: + person1: + is_snap_ineligible_student: false + person2: + is_snap_ineligible_student: false + person3: + is_snap_ineligible_student: false + spm_units: + spm_unit1: + members: [person1, person2, person3] + spm_unit_size: 3 + output: + snap_unit_size: 3 + +- name: No eligible people + period: 2022 + input: + people: + person1: + is_snap_ineligible_student: true + person2: + is_snap_ineligible_student: true + person3: + is_snap_ineligible_student: true + spm_units: + spm_unit1: + members: [person1, person2, person3] + spm_unit_size: 3 + output: + snap_unit_size: 0 diff --git a/policyengine_us/variables/gov/usda/snap/eligibility/is_snap_eligible.py b/policyengine_us/variables/gov/usda/snap/eligibility/is_snap_eligible.py index 1ab897fc458..bb526d3b3c0 100644 --- a/policyengine_us/variables/gov/usda/snap/eligibility/is_snap_eligible.py +++ b/policyengine_us/variables/gov/usda/snap/eligibility/is_snap_eligible.py @@ -21,4 +21,10 @@ def formula(spm_unit, period, parameters): categorical_eligibility = spm_unit( "meets_snap_categorical_eligibility", period ) - return normal_eligibility | categorical_eligibility + person = spm_unit.members + eligible_person_present = spm_unit.any( + ~person("is_snap_ineligible_student", period) + ) + return ( + normal_eligibility | categorical_eligibility + ) & eligible_person_present diff --git a/policyengine_us/variables/gov/usda/snap/eligibility/student/is_snap_ineligible_student.py b/policyengine_us/variables/gov/usda/snap/eligibility/student/is_snap_ineligible_student.py new file mode 100644 index 00000000000..40e6d2db4af --- /dev/null +++ b/policyengine_us/variables/gov/usda/snap/eligibility/student/is_snap_ineligible_student.py @@ -0,0 +1,28 @@ +from policyengine_us.model_api import * + + +class is_snap_ineligible_student(Variable): + value_type = bool + entity = Person + label = "Is an ineligible student for SNAP" + documentation = "Whether this person is an ineligible student for SNAP and can not be counted towards the household size" + definition_period = YEAR + reference = "https://www.law.cornell.edu/uscode/text/7/2015" + + def formula(person, period, parameters): + student = person( + "is_full_time_student", period + ) # part-time students should also count + age = person("age", period) + p = parameters(period).gov.usda.snap.student + age_eligible = p.age_threshold.calc(age) + disabled = person("is_disabled", period) + hours_worked = person("weekly_hours_worked", period) + hours_eligible = hours_worked >= p.working_hours_threshold + # The parental status applies to children under 6 (under 12 + # if no care available) + # Single parents with children under 12 are also eligible + is_parent = person("is_parent", period) + return student & ~( + age_eligible | disabled | hours_eligible | is_parent + ) diff --git a/policyengine_us/variables/gov/usda/snap/income/deductions/snap_standard_deduction.py b/policyengine_us/variables/gov/usda/snap/income/deductions/snap_standard_deduction.py index 30021d9da76..420061a5c73 100644 --- a/policyengine_us/variables/gov/usda/snap/income/deductions/snap_standard_deduction.py +++ b/policyengine_us/variables/gov/usda/snap/income/deductions/snap_standard_deduction.py @@ -15,5 +15,6 @@ def formula(spm_unit, period, parameters): period ).gov.usda.snap.income.deductions.standard state_group = spm_unit.household("state_group_str", period) - capped_household_size = min_(spm_unit("spm_unit_size", period), 6) + unit = spm_unit("snap_unit_size", period) + capped_household_size = clip(unit, 1, 6) return standard_deductions[state_group][capped_household_size] diff --git a/policyengine_us/variables/gov/usda/snap/income/snap_fpg.py b/policyengine_us/variables/gov/usda/snap/income/snap_fpg.py index 2f8905ea20e..ca0144bc512 100644 --- a/policyengine_us/variables/gov/usda/snap/income/snap_fpg.py +++ b/policyengine_us/variables/gov/usda/snap/income/snap_fpg.py @@ -12,7 +12,7 @@ class snap_fpg(Variable): definition_period = MONTH def formula(spm_unit, period, parameters): - n = spm_unit("spm_unit_size", period.this_year) + n = spm_unit("snap_unit_size", period.this_year) state_group = spm_unit.household("state_group_str", period.this_year) year = period.start.year month = period.start.month diff --git a/policyengine_us/variables/gov/usda/snap/snap_max_allotment.py b/policyengine_us/variables/gov/usda/snap/snap_max_allotment.py index c4b5c790153..79f92ce0180 100644 --- a/policyengine_us/variables/gov/usda/snap/snap_max_allotment.py +++ b/policyengine_us/variables/gov/usda/snap/snap_max_allotment.py @@ -14,11 +14,11 @@ def formula(spm_unit, period, parameters): MAX_HOUSEHOLD_SIZE_UNDER_MAIN = 8 snap_region = spm_unit.household("snap_region_str", period) household_size = min_( - spm_unit("spm_unit_size", period), MAX_HOUSEHOLD_SIZE_UNDER_MAIN + spm_unit("snap_unit_size", period), MAX_HOUSEHOLD_SIZE_UNDER_MAIN ) additional_members = max_( 0, - spm_unit("spm_unit_size", period) - MAX_HOUSEHOLD_SIZE_UNDER_MAIN, + spm_unit("snap_unit_size", period) - MAX_HOUSEHOLD_SIZE_UNDER_MAIN, ) main_allotment = max_allotments.main[snap_region][household_size] additional_allotment = ( diff --git a/policyengine_us/variables/gov/usda/snap/snap_min_allotment.py b/policyengine_us/variables/gov/usda/snap/snap_min_allotment.py index 28296918c74..82b9a51c412 100644 --- a/policyengine_us/variables/gov/usda/snap/snap_min_allotment.py +++ b/policyengine_us/variables/gov/usda/snap/snap_min_allotment.py @@ -23,7 +23,7 @@ def formula(spm_unit, period, parameters): ] # Minimum benefits only apply to households up to a certain size. - size = spm_unit("spm_unit_size", period) + size = spm_unit("snap_unit_size", period) eligible = size <= snap.min_allotment.maximum_household_size min_allotment = eligible * min_allotment.rate * relevant_max_allotment diff --git a/policyengine_us/variables/gov/usda/snap/snap_unit.py b/policyengine_us/variables/gov/usda/snap/snap_unit.py new file mode 100644 index 00000000000..0bb83609ba8 --- /dev/null +++ b/policyengine_us/variables/gov/usda/snap/snap_unit.py @@ -0,0 +1,15 @@ +from policyengine_us.model_api import * + + +class snap_unit_size(Variable): + value_type = int + entity = SPMUnit + label = "SNAP unit" + definition_period = YEAR + + def formula(spm_unit, period, parameters): + unit_size = spm_unit("spm_unit_size", period) + person = spm_unit.members + ineligible_student = person("is_snap_ineligible_student", period) + ineligible_students = spm_unit.sum(ineligible_student) + return max_(unit_size - ineligible_students, 0) diff --git a/policyengine_us/variables/household/demographic/person/is_father.py b/policyengine_us/variables/household/demographic/person/is_father.py index d7b8ae421e2..75caa06d1aa 100644 --- a/policyengine_us/variables/household/demographic/person/is_father.py +++ b/policyengine_us/variables/household/demographic/person/is_father.py @@ -12,5 +12,5 @@ def formula(person, period, parameters): # male and has some children in their own household (provided in the # CPS). female = person("is_female", period) - has_children = person("own_children_in_household", period) > 0 + has_children = person("is_parent", period) return ~female & has_children diff --git a/policyengine_us/variables/household/demographic/person/is_mother.py b/policyengine_us/variables/household/demographic/person/is_mother.py index a86ed689129..1394af0eaa8 100644 --- a/policyengine_us/variables/household/demographic/person/is_mother.py +++ b/policyengine_us/variables/household/demographic/person/is_mother.py @@ -14,6 +14,6 @@ def formula(person, period, parameters): # (provided in the CPS). # 2. Breastfeeding (user-input). female = person("is_female", period) - has_children = person("own_children_in_household", period) > 0 + has_children = person("is_parent", period) breastfeeding = person("is_breastfeeding", period) return breastfeeding | (female & has_children) diff --git a/policyengine_us/variables/household/demographic/person/is_parent.py b/policyengine_us/variables/household/demographic/person/is_parent.py new file mode 100644 index 00000000000..fcb5d85dcf8 --- /dev/null +++ b/policyengine_us/variables/household/demographic/person/is_parent.py @@ -0,0 +1,11 @@ +from policyengine_us.model_api import * + + +class is_parent(Variable): + value_type = bool + entity = Person + label = "Is a parent" + definition_period = YEAR + + def formula(person, period, parameters): + return person("own_children_in_household", period) > 0