From b2942e33bce918e23bb2832cdde48f0a99509a4e Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 2 Oct 2026 09:55:17 -0400 Subject: [PATCH 1/3] Give children and couples who cannot share a bedroom their own bedrooms in the LHA size criteria UC Regs 2013 Sch 4 para 12(6), (6A), (8), (9)(c)-(d) and HB Regs 2006 / HB (SPC) Regs 2006 reg 13D(3)(za), (zb), (ba) and closing words. A child who cannot share is taken out of the children's pairing and has a room of their own; a couple one of whom cannot share has a bedroom each. Housing Benefit allows these bedrooms only where the dwelling has them (num_bedrooms; assumed where not reported). New input cannot_reasonably_share_bedroom_due_to_disability. In force from 4 December 2013 (children) and 1 April 2017 (couples); attendance allowance only at the higher rate before 27 January 2025 (SI 2025/3). Fixes #2069. Co-Authored-By: Claude Opus 5.5 --- changelog.d/2069.fixed.md | 1 + .../cannot_share_bedroom/child/benefits.yaml | 14 + .../cannot_share_bedroom/child/in_effect.yaml | 10 + .../couple_member/benefits.yaml | 26 + .../couple_member/in_effect.yaml | 10 + .../family/LHA_cannot_share_bedroom.yaml | 895 ++++++++++++++++++ ...est_lha_bedroom_young_people_properties.py | 228 ++++- .../gov/dwp/LHA_additional_bedrooms.py | 10 +- .../variables/gov/dwp/LHA_allowed_bedrooms.py | 83 +- .../gov/dwp/LHA_cannot_share_bedrooms.py | 61 ++ ...housing_benefit_LHA_additional_bedrooms.py | 4 +- .../housing_benefit_LHA_allowed_bedrooms.py | 130 +-- ...using_benefit_LHA_cannot_share_bedrooms.py | 87 ++ .../dwp/is_child_who_cannot_share_bedroom.py | 42 + ...mber_of_couple_who_cannot_share_bedroom.py | 46 + ...ceives_higher_rate_attendance_allowance.py | 13 + ...sonably_share_bedroom_due_to_disability.py | 25 + .../household/demographic/num_bedrooms.py | 5 + 18 files changed, 1582 insertions(+), 108 deletions(-) create mode 100644 changelog.d/2069.fixed.md create mode 100644 policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/child/benefits.yaml create mode 100644 policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/child/in_effect.yaml create mode 100644 policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/couple_member/benefits.yaml create mode 100644 policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/couple_member/in_effect.yaml create mode 100644 policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_cannot_share_bedroom.yaml create mode 100644 policyengine_uk/variables/gov/dwp/LHA_cannot_share_bedrooms.py create mode 100644 policyengine_uk/variables/gov/dwp/housing_benefit_LHA_cannot_share_bedrooms.py create mode 100644 policyengine_uk/variables/gov/dwp/is_child_who_cannot_share_bedroom.py create mode 100644 policyengine_uk/variables/gov/dwp/is_member_of_couple_who_cannot_share_bedroom.py create mode 100644 policyengine_uk/variables/gov/dwp/receives_higher_rate_attendance_allowance.py create mode 100644 policyengine_uk/variables/household/demographic/cannot_reasonably_share_bedroom_due_to_disability.py diff --git a/changelog.d/2069.fixed.md b/changelog.d/2069.fixed.md new file mode 100644 index 0000000000..fa082240a6 --- /dev/null +++ b/changelog.d/2069.fixed.md @@ -0,0 +1 @@ +- Give children and couples who cannot share a bedroom because of disability their own bedrooms in the LHA size criteria (UC Regs 2013 Sch 4 para 12(6), (6A), (8), (9)(c)-(d); HB Regs 2006 and HB (SPC) Regs 2006 reg 13D(3)(za), (zb), (ba)), with the new input `cannot_reasonably_share_bedroom_due_to_disability`. A child under 16 on the DLA care component at the middle or highest rate who cannot reasonably share is left out of the children's pairing and has a room of their own, so a room is added only where the child would otherwise share. A couple one of whom cannot share (on attendance allowance, armed forces independence payment, DLA care at the middle or highest rate or PIP daily living; attendance allowance only at the higher rate before 27 January 2025) has a bedroom each: for Universal Credit the renter's couple, for Housing Benefit any couple of occupiers. Housing Benefit allows these bedrooms only where the dwelling has them (`num_bedrooms`; assumed where not reported). In force from 4 December 2013 (children) and 1 April 2017 (couples). diff --git a/policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/child/benefits.yaml b/policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/child/benefits.yaml new file mode 100644 index 0000000000..e3238513b3 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/child/benefits.yaml @@ -0,0 +1,14 @@ +description: Disability benefits a child must receive to be a child who cannot share a bedroom in the LHA size criteria. The law also lists the care component of child disability payment at the middle or highest rate (Scotland), which the model has no variable for. +values: + 2013-12-04: + - dla_sc_middle_plus +metadata: + unit: list + label: LHA child who cannot share qualifying benefits + reference: + - title: Universal Credit Regulations 2013 Sch 4 para 12(6)(a) + href: https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12 + - title: Housing Benefit Regulations 2006 reg 2(1), "child who cannot share a bedroom" + href: https://www.legislation.gov.uk/uksi/2006/213/regulation/2 + - title: Housing Benefit (State Pension Credit) Regulations 2006 reg 2(1), "child who cannot share a bedroom" + href: https://www.legislation.gov.uk/uksi/2006/214/regulation/2 diff --git a/policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/child/in_effect.yaml b/policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/child/in_effect.yaml new file mode 100644 index 0000000000..55b3650c3c --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/child/in_effect.yaml @@ -0,0 +1,10 @@ +description: Whether a child who cannot share a bedroom because of disability has a bedroom of their own in the LHA size criteria. +values: + 2011-04-01: false + 2013-12-04: true +metadata: + unit: bool + label: LHA bedroom for a child who cannot share in effect + reference: + - title: Housing Benefit and Universal Credit (Size Criteria) (Miscellaneous Amendments) Regulations 2013 (SI 2013/2828) regs 2(4), 3(3) and 4(3) + href: https://www.legislation.gov.uk/uksi/2013/2828/made diff --git a/policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/couple_member/benefits.yaml b/policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/couple_member/benefits.yaml new file mode 100644 index 0000000000..b14a739956 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/couple_member/benefits.yaml @@ -0,0 +1,26 @@ +description: Disability benefits a member of a couple must receive to be a member of a couple who cannot share a bedroom in the LHA size criteria. Attendance allowance counted only at the higher rate until 27 January 2025. The law also lists the Scottish devolved equivalents (child disability payment, adult disability payment, Scottish adult disability living allowance, pension age disability payment), which the model has no variables for. +values: + 2017-04-01: + - receives_higher_rate_attendance_allowance + - armed_forces_independence_payment + - dla_sc_middle_plus + - pip_dl + 2025-01-27: + - attendance_allowance + - armed_forces_independence_payment + - dla_sc_middle_plus + - pip_dl +metadata: + unit: list + label: LHA member of a couple who cannot share qualifying benefits + reference: + - title: Universal Credit Regulations 2013 Sch 4 para 12(6A)(a) + href: https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12 + - title: Universal Credit Regulations 2013 reg 2, "attendance allowance" (includes armed forces independence payment) + href: https://www.legislation.gov.uk/uksi/2013/376/regulation/2 + - title: Housing Benefit Regulations 2006 reg 2(1), "member of a couple who cannot share a bedroom" + href: https://www.legislation.gov.uk/uksi/2006/213/regulation/2 + - title: Housing Benefit (State Pension Credit) Regulations 2006 reg 2(1), "member of a couple who cannot share a bedroom" + href: https://www.legislation.gov.uk/uksi/2006/214/regulation/2 + - title: Social Security (Miscellaneous Amendments) Regulations 2025 (SI 2025/3) regs 3-5 (attendance allowance at any rate from 27 January 2025) + href: https://www.legislation.gov.uk/uksi/2025/3/made diff --git a/policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/couple_member/in_effect.yaml b/policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/couple_member/in_effect.yaml new file mode 100644 index 0000000000..0e654a8864 --- /dev/null +++ b/policyengine_uk/parameters/gov/dwp/LHA/cannot_share_bedroom/couple_member/in_effect.yaml @@ -0,0 +1,10 @@ +description: Whether a couple one of whom cannot share a bedroom with the other because of disability has a bedroom each in the LHA size criteria. +values: + 2011-04-01: false + 2017-04-01: true +metadata: + unit: bool + label: LHA bedroom for a member of a couple who cannot share in effect + reference: + - title: Housing Benefit and Universal Credit (Size Criteria) (Miscellaneous Amendments) Regulations 2017 (SI 2017/213) regs 4, 5 and 6 + href: https://www.legislation.gov.uk/uksi/2017/213/made diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_cannot_share_bedroom.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_cannot_share_bedroom.yaml new file mode 100644 index 0000000000..7714ec1467 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_cannot_share_bedroom.yaml @@ -0,0 +1,895 @@ +# Bedrooms for children and couples who cannot share a bedroom because of +# disability in the LHA size criteria. +# +# Universal Credit (UC Regs 2013 Sch 4 para 12): the disabled child condition +# (12(6)) is met where the renter, or another member of their extended +# benefit unit, is responsible for a child who would otherwise be expected to +# share a bedroom, receives the DLA (or CDP) care component at the middle or +# highest rate, and is not reasonably able to share a room with another child +# because of their disability. The renter then has as many additional +# bedrooms as are necessary for each such child to have their own (12(8)). +# The disabled person condition (12(6A)) is met where the renter would +# otherwise share a bedroom with a joint renter, receives a qualifying +# disability benefit and cannot reasonably share with the joint renter; it +# gives one additional bedroom (12(9)(d)). From 4 December 2013 (children) +# and 1 April 2017 (couples); attendance allowance counted only at the +# higher rate until 27 January 2025 (SI 2025/3). +# +# Housing Benefit (HB Regs 2006 and HB (SPC) Regs 2006 reg 13D(3)): the +# categories are, in order, (za) a member of a couple who cannot share a +# bedroom, (zb) a member of a couple who can share a bedroom (the partner of +# one who cannot), (a) a couple, (b) a person who is not a child, (ba) a child +# who cannot share a bedroom, (c) two children of the same sex, (d) two +# children under 10, (e) a child; "but the claimant is only entitled to a +# bedroom in respect of a child who cannot share a bedroom or a member of a +# couple who cannot share a bedroom if there is a bedroom in the dwelling +# occupied as the home that is additional to those to which the claimant +# would be entitled if the child or the member of the couple were able to +# share a bedroom" (as amended by SI 2017/213 regs 4(4) and 5(3)). +# Definitions in reg 2(1). The model reads the dwelling's bedrooms from +# num_bedrooms and assumes the bedroom exists where that is not reported (0). +# +# Examples from the DWP Advice for Decision Making chapter F3 (F3135-F3138) +# and HB circular A21/2013 Annex 2. The cannot-share judgement is the input +# cannot_reasonably_share_bedroom_due_to_disability. + +- name: ADM F3135 example 1, two disabled sisters who would share each have a room + period: 2026 + input: + people: + joanne: + age: 40 + is_household_head: true + kevin: + age: 42 + daughter_10: + age: 10 + is_male: false + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + daughter_8: + age: 8 + is_male: false + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + son_13: + age: 13 + is_male: true + benunits: + family: + members: [joanne, kevin, daughter_10, daughter_8, son_13] + households: + home: + members: [joanne, kevin, daughter_10, daughter_8, son_13] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + is_child_who_cannot_share_bedroom: [false, false, true, true, false] + # The couple, the son and the two girls who would otherwise share: the + # girls each have their own room, one additional bedroom (para 12(8)). + LHA_cannot_share_bedrooms: 1 + LHA_additional_bedrooms: 1 + LHA_allowed_bedrooms: 4 + LHA_category: E + # HB reg 13D(3)(ba): each girl is a child who cannot share. + housing_benefit_LHA_cannot_share_bedrooms: 1 + housing_benefit_LHA_allowed_bedrooms: 4 + +- name: ADM F3135 example 2, a disabled boy who need not share gets no extra room + period: 2026 + input: + people: + ankush: + age: 40 + is_household_head: true + rita: + age: 38 + son_7: + age: 7 + is_male: true + son_9: + age: 9 + is_male: true + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + son_11: + age: 11 + is_male: true + benunits: + family: + members: [ankush, rita, son_7, son_9, son_11] + households: + home: + members: [ankush, rita, son_7, son_9, son_11] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # The 7- and 11-year-olds share and the 9-year-old has his own room, as + # he would anyway: 3 bedrooms, none additional. + LHA_cannot_share_bedrooms: 0 + LHA_allowed_bedrooms: 3 + housing_benefit_LHA_cannot_share_bedrooms: 0 + housing_benefit_LHA_allowed_bedrooms: 3 + +- name: ADM F3135 example 3, a child the decision maker finds able to share gets no room + period: 2026 + input: + people: + carol: + age: 35 + is_household_head: true + jason: + age: 36 + daughter_5: + age: 5 + is_male: false + daughter_7: + age: 7 + is_male: false + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: false + benunits: + family: + members: [carol, jason, daughter_5, daughter_7] + households: + home: + members: [carol, jason, daughter_5, daughter_7] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + is_child_who_cannot_share_bedroom: [false, false, false, false] + LHA_allowed_bedrooms: 2 + housing_benefit_LHA_allowed_bedrooms: 2 + +- name: ADM F3136, a couple able to share a bedroom gets no extra room + period: 2026 + input: + people: + david: + age: 60 + is_household_head: true + pip_dl: 5_000 + cannot_reasonably_share_bedroom_due_to_disability: false + kathy: + age: 58 + benunits: + couple: + members: [david, kathy] + households: + home: + members: [david, kathy] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + LHA_allowed_bedrooms: 1 + housing_benefit_LHA_allowed_bedrooms: 1 + +- name: A member of a couple who cannot share with the other has a second bedroom + period: 2026 + input: + people: + claimant: + age: 60 + is_household_head: true + pip_dl: 5_000 + cannot_reasonably_share_bedroom_due_to_disability: true + partner: + age: 58 + benunits: + couple: + members: [claimant, partner] + households: + home: + members: [claimant, partner] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + is_member_of_couple_who_cannot_share_bedroom: [true, false] + # UC para 12(6A) and (9)(d): one additional bedroom. + LHA_cannot_share_bedrooms: 1 + LHA_allowed_bedrooms: 2 + LHA_category: C + # HB reg 13D(3)(za) and (zb): one bedroom for each member. + housing_benefit_LHA_cannot_share_bedrooms: 1 + housing_benefit_LHA_allowed_bedrooms: 2 + housing_benefit_LHA_category: C + +- name: Both members of a couple unable to share have one additional bedroom between them + period: 2026 + input: + people: + tony: + age: 50 + is_household_head: true + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + mike: + age: 52 + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + benunits: + couple: + members: [tony, mike] + households: + home: + members: [tony, mike] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC para 12(9)(d): one bedroom if one or both joint renters satisfy the + # condition. HB (za): one bedroom each. + LHA_allowed_bedrooms: 2 + housing_benefit_LHA_allowed_bedrooms: 2 + +- name: ADM F3138, the disabled person and overnight care conditions each give a bedroom + period: 2026 + input: + people: + claimant: + age: 50 + is_household_head: true + dla_sc_middle_plus: true + has_non_resident_overnight_carer: true + cannot_reasonably_share_bedroom_due_to_disability: true + partner: + age: 52 + benunits: + couple: + members: [claimant, partner] + households: + home: + members: [claimant, partner] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC para 12(9)(a) and (d). + LHA_additional_bedrooms: 2 + LHA_allowed_bedrooms: 3 + # HB reg 13D(3)(za)-(zb) and (3A)(a). + housing_benefit_LHA_additional_bedrooms: 1 + housing_benefit_LHA_cannot_share_bedrooms: 1 + housing_benefit_LHA_allowed_bedrooms: 3 + +- name: A21/2013 Annex 2 example 1, a disabled only child needs no extra room + period: 2026 + input: + people: + claimant: + age: 40 + is_household_head: true + partner: + age: 40 + child: + age: 6 + is_male: true + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + benunits: + family: + members: [claimant, partner, child] + households: + home: + members: [claimant, partner, child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + num_bedrooms: 3 + output: + LHA_cannot_share_bedrooms: 0 + LHA_allowed_bedrooms: 2 + housing_benefit_LHA_cannot_share_bedrooms: 0 + housing_benefit_LHA_allowed_bedrooms: 2 + +- name: A21/2013 Annex 2 example 2, two disabled daughters each have a room + period: 2026 + input: + people: + claimant: + age: 45 + is_household_head: true + partner: + age: 45 + daughter_11: + age: 11 + is_male: false + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + daughter_13: + age: 13 + is_male: false + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + benunits: + family: + members: [claimant, partner, daughter_11, daughter_13] + households: + home: + members: [claimant, partner, daughter_11, daughter_13] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + num_bedrooms: 3 + output: + LHA_allowed_bedrooms: 3 + housing_benefit_LHA_cannot_share_bedrooms: 1 + housing_benefit_LHA_allowed_bedrooms: 3 + +- name: A21/2013 Annex 2 example 3, the non-disabled brothers share + period: 2026 + input: + people: + claimant: + age: 45 + is_household_head: true + partner: + age: 45 + son_7: + age: 7 + is_male: true + son_9: + age: 9 + is_male: true + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + son_15: + age: 15 + is_male: true + benunits: + family: + members: [claimant, partner, son_7, son_9, son_15] + households: + home: + members: [claimant, partner, son_7, son_9, son_15] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + num_bedrooms: 4 + output: + # The couple, the disabled son, and the sons aged 7 and 15 together. + LHA_cannot_share_bedrooms: 0 + LHA_allowed_bedrooms: 3 + housing_benefit_LHA_cannot_share_bedrooms: 0 + housing_benefit_LHA_allowed_bedrooms: 3 + +- name: A21/2013 Annex 2 example 4, no Housing Benefit room beyond the dwelling's bedrooms + period: 2026 + input: + people: + claimant: + age: 40 + is_household_head: true + partner: + age: 40 + son_7: + age: 7 + is_male: true + son_9: + age: 9 + is_male: true + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + benunits: + family: + members: [claimant, partner, son_7, son_9] + households: + home: + members: [claimant, partner, son_7, son_9] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + num_bedrooms: 2 + output: + # HB: the boys would share if able to, so the claimant would be entitled + # to 2 bedrooms, and the dwelling has no third (reg 13D(3), closing + # words): the two-bedroom rate. + housing_benefit_LHA_cannot_share_bedrooms: 0 + housing_benefit_LHA_allowed_bedrooms: 2 + housing_benefit_LHA_category: C + # UC has no such condition. + LHA_cannot_share_bedrooms: 1 + LHA_allowed_bedrooms: 3 + LHA_category: D + +- name: The same family in a three-bedroom dwelling has the Housing Benefit room + period: 2026 + input: + people: + claimant: + age: 40 + is_household_head: true + partner: + age: 40 + son_7: + age: 7 + is_male: true + son_9: + age: 9 + is_male: true + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + benunits: + family: + members: [claimant, partner, son_7, son_9] + households: + home: + members: [claimant, partner, son_7, son_9] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + num_bedrooms: 3 + output: + housing_benefit_LHA_cannot_share_bedrooms: 1 + housing_benefit_LHA_allowed_bedrooms: 3 + +- name: A21/2013 Annex 2 example 5, five bedrooms in the criteria but the four-bedroom rate + period: 2026 + input: + people: + claimant: + age: 40 + is_household_head: true + partner: + age: 40 + son_12: + age: 12 + is_male: true + daughter_9: + age: 9 + is_male: false + daughter_3: + age: 3 + is_male: false + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + daughter_4: + age: 4 + is_male: false + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + benunits: + family: + members: [claimant, partner, son_12, daughter_9, daughter_3, daughter_4] + households: + home: + members: [claimant, partner, son_12, daughter_9, daughter_3, daughter_4] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + num_bedrooms: 5 + output: + # Able to share: the couple, the son, and the three girls in two rooms + # (4). Otherwise: the couple, the son, the 9-year-old, and each disabled + # daughter (5). + housing_benefit_LHA_cannot_share_bedrooms: 1 + housing_benefit_LHA_allowed_bedrooms: 5 + housing_benefit_LHA_category: E + LHA_allowed_bedrooms: 5 + LHA_category: E + +- name: A21/2013 Annex 2 example 6, a disabled boy who would share with his brother + period: 2026 + input: + people: + claimant: + age: 40 + is_household_head: true + partner: + age: 40 + son_12: + age: 12 + is_male: true + son_10: + age: 10 + is_male: true + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + benunits: + family: + members: [claimant, partner, son_12, son_10] + households: + home: + members: [claimant, partner, son_12, son_10] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + num_bedrooms: 4 + output: + housing_benefit_LHA_allowed_bedrooms: 3 + housing_benefit_LHA_category: D + LHA_allowed_bedrooms: 3 + +- name: Two disabled girls who could each be alone but not both have a room each + period: 2026 + input: + people: + renter: + age: 40 + is_household_head: true + daughter_12: + age: 12 + is_male: false + daughter_8: + age: 8 + is_male: false + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + daughter_6: + age: 6 + is_male: false + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + benunits: + family: + members: [renter, daughter_12, daughter_8, daughter_6] + households: + home: + members: [renter, daughter_12, daughter_8, daughter_6] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # Without the condition the girls need 2 rooms, and either disabled girl + # could be the one alone. With it, no disabled girl shares: 3 rooms + # (UC para 12(8), each such child has their own bedroom; HB reg + # 13D(3)(ba) for each). + LHA_cannot_share_bedrooms: 1 + LHA_allowed_bedrooms: 4 + housing_benefit_LHA_cannot_share_bedrooms: 1 + housing_benefit_LHA_allowed_bedrooms: 4 + +- name: A non-dependant's disabled child who would share with the renter's child + period: 2026 + input: + people: + renter: + age: 48 + is_household_head: true + is_male: false + daughter_9: + age: 9 + is_male: false + older_daughter: + age: 26 + is_male: false + granddaughter: + age: 8 + is_male: false + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + benunits: + renter_family: + members: [renter, daughter_9] + daughter_family: + members: [older_daughter, granddaughter] + households: + home: + members: [renter, daughter_9, older_daughter, granddaughter] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # The granddaughter is in the renter's extended benefit unit and her + # mother is responsible for her (UC para 12(6), "another member of their + # extended benefit unit"); in HB she is an occupier (13D(3)(ba)). The + # girls would share; instead each has a room. The daughter's own claim + # counts only her own child, who would not share. + LHA_cannot_share_bedrooms: [1, 0] + LHA_allowed_bedrooms: [4, 2] + housing_benefit_LHA_cannot_share_bedrooms: [1, 0] + housing_benefit_LHA_allowed_bedrooms: [4, 2] + +- name: A disabled foster child gives only the foster parent's bedroom + period: 2026 + input: + people: + renter: + age: 45 + is_household_head: true + daughter_8: + age: 8 + is_male: false + foster_child: + age: 7 + is_male: false + is_looked_after_by_local_authority: true + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + benunits: + family: + members: [renter, daughter_8, foster_child] + households: + home: + members: [renter, daughter_8, foster_child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # No one is responsible for a looked-after child (UC reg 4(6)(a)) and + # she does not occupy the dwelling (HB reg 21(3)), so she is in neither + # size criteria; the renter has the foster parent's bedroom. + is_child_who_cannot_share_bedroom: [false, false, true] + LHA_cannot_share_bedrooms: 0 + LHA_allowed_bedrooms: 3 + housing_benefit_LHA_cannot_share_bedrooms: 0 + housing_benefit_LHA_allowed_bedrooms: 3 + +- name: A disabled child placed for adoption counts in Universal Credit only + period: 2026 + input: + people: + adopter: + age: 45 + is_household_head: true + son_8: + age: 8 + is_male: true + placed_child: + age: 6 + is_male: true + is_placed_for_adoption: true + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + benunits: + family: + members: [adopter, son_8, placed_child] + households: + home: + members: [adopter, son_8, placed_child] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC: the adopter is responsible for the child (reg 4A(1)(c)); the boys + # would share, so the disabled child condition adds a room, and the + # foster parent condition another (para 12(4)(b)). + LHA_cannot_share_bedrooms: 1 + LHA_allowed_bedrooms: 4 + # HB: a child placed for adoption does not occupy the dwelling (reg + # 21(3)); the adopter is a qualifying parent or carer. + housing_benefit_LHA_cannot_share_bedrooms: 0 + housing_benefit_LHA_allowed_bedrooms: 3 + +- name: A non-dependant couple who cannot share have two Housing Benefit bedrooms + period: 2026 + input: + people: + renter: + age: 62 + is_household_head: true + son: + age: 34 + pip_dl: 5_000 + cannot_reasonably_share_bedroom_due_to_disability: true + son_partner: + age: 33 + benunits: + renter_family: + members: [renter] + son_family: + members: [son, son_partner] + households: + home: + members: [renter, son, son_partner] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # HB reg 13D(3)(za)-(zb): any couple of occupiers, not only the + # claimant's, has a bedroom each where a member cannot share. + housing_benefit_LHA_cannot_share_bedrooms: [1, 1] + housing_benefit_LHA_allowed_bedrooms: [3, 2] + # UC: each non-dependant already has a bedroom (para 10(1)(c); ADM F3112 + # note 1), and the disabled person condition is the renter's (12(6A)); + # the son's own claim gets it as a renter. + LHA_cannot_share_bedrooms: [0, 1] + LHA_allowed_bedrooms: [3, 2] + +- name: Housing Benefit room for a disabled child needs one beyond the overnight carer's + period: 2026 + input: + people: + claimant: + age: 40 + is_household_head: true + partner: + age: 40 + son_7: + age: 7 + is_male: true + son_9: + age: 9 + is_male: true + dla_sc_middle_plus: true + has_non_resident_overnight_carer: true + cannot_reasonably_share_bedroom_due_to_disability: true + benunits: + family: + members: [claimant, partner, son_7, son_9] + households: + home: + members: [claimant, partner, son_7, son_9] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + num_bedrooms: 3 + output: + # Able to share: the couple, the boys, and the overnight carer (3). The + # dwelling has no fourth bedroom. + housing_benefit_LHA_additional_bedrooms: 1 + housing_benefit_LHA_cannot_share_bedrooms: 0 + housing_benefit_LHA_allowed_bedrooms: 3 + LHA_allowed_bedrooms: 4 + +- name: Lower-rate attendance allowance qualified a couple member only from 27 January 2025 + period: 2024 + input: + people: + claimant: + age: 70 + is_household_head: true + aa_category: LOWER + cannot_reasonably_share_bedroom_due_to_disability: true + partner: + age: 68 + benunits: + couple: + members: [claimant, partner] + households: + home: + members: [claimant, partner] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # UC para 12(6A)(a)(ii) and HB reg 2(1) read "attendance allowance at the + # higher rate" until SI 2025/3. + is_member_of_couple_who_cannot_share_bedroom: [false, false] + LHA_allowed_bedrooms: 1 + housing_benefit_LHA_allowed_bedrooms: 1 + +- name: Lower-rate attendance allowance qualifies a couple member from 2026 + period: 2026 + input: + people: + claimant: + age: 70 + is_household_head: true + aa_category: LOWER + cannot_reasonably_share_bedroom_due_to_disability: true + partner: + age: 68 + benunits: + couple: + members: [claimant, partner] + households: + home: + members: [claimant, partner] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + is_member_of_couple_who_cannot_share_bedroom: [true, false] + LHA_allowed_bedrooms: 2 + housing_benefit_LHA_allowed_bedrooms: 2 + +- name: Higher-rate attendance allowance qualified a couple member before 2025 + period: 2024 + input: + people: + claimant: + age: 70 + is_household_head: true + aa_category: HIGHER + cannot_reasonably_share_bedroom_due_to_disability: true + partner: + age: 68 + benunits: + couple: + members: [claimant, partner] + households: + home: + members: [claimant, partner] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + is_member_of_couple_who_cannot_share_bedroom: [true, false] + LHA_allowed_bedrooms: 2 + housing_benefit_LHA_allowed_bedrooms: 2 + +- name: No couple bedroom before 1 April 2017 + period: 2016 + input: + people: + claimant: + age: 60 + is_household_head: true + pip_dl: 5_000 + cannot_reasonably_share_bedroom_due_to_disability: true + partner: + age: 58 + benunits: + couple: + members: [claimant, partner] + households: + home: + members: [claimant, partner] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + is_member_of_couple_who_cannot_share_bedroom: [false, false] + LHA_cannot_share_bedrooms: 0 + housing_benefit_LHA_cannot_share_bedrooms: 0 + +- name: The cannot-share judgement needs a qualifying disability benefit + period: 2026 + input: + people: + claimant: + age: 40 + is_household_head: true + cannot_reasonably_share_bedroom_due_to_disability: true + partner: + age: 40 + son_7: + age: 7 + is_male: true + son_9: + age: 9 + is_male: true + dla_sc_middle_plus: false + cannot_reasonably_share_bedroom_due_to_disability: true + benunits: + family: + members: [claimant, partner, son_7, son_9] + households: + home: + members: [claimant, partner, son_7, son_9] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + is_child_who_cannot_share_bedroom: [false, false, false, false] + is_member_of_couple_who_cannot_share_bedroom: [false, false, false, false] + LHA_allowed_bedrooms: 2 + housing_benefit_LHA_allowed_bedrooms: 2 + +- name: A person aged 16 or over is not a child who cannot share + period: 2026 + input: + people: + renter: + age: 45 + is_household_head: true + son_16: + age: 16 + is_male: true + current_education: UPPER_SECONDARY + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + son_8: + age: 8 + is_male: true + benunits: + family: + members: [renter, son_16, son_8] + households: + home: + members: [renter, son_16, son_8] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # The 16-year-old has his own bedroom as a qualifying young person (UC + # para 10(1)(b)) or a person who is not a child (HB 13D(3)(b)). + is_child_who_cannot_share_bedroom: [false, false, false] + LHA_allowed_bedrooms: 3 + housing_benefit_LHA_allowed_bedrooms: 3 diff --git a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py index e80ae2d80e..8f4c57d5c3 100644 --- a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py +++ b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py @@ -2,8 +2,9 @@ 9-12; HB Regs 2006 and HB (SPC) Regs 2006 reg 13D and reg 21(3)): the bedrooms of benefit-unit members aged 16 or over who are not the claimant or partner, the children of other families in the household, foster and -adoption placements, and the additional bedrooms for foster parents and -overnight care. +adoption placements, the additional bedrooms for foster parents and +overnight care, and the bedrooms for children and couples who cannot share +a bedroom because of disability. Invariants, for any generated population of households: @@ -60,7 +61,26 @@ - one additional bedroom if anyone so counted, or a foster child of the family, has overnight care, and one if the family fosters, has a child placed for adoption, or has an approved foster parent between - placements. + placements; + - children who cannot share a bedroom because of disability: the brute + force forbids them any room-mate, and the difference from the + unrestricted pairing is additional (UC para 12(6), (8); HB reg + 13D(3)(ba)); + - couples one of whom cannot share: one more bedroom for the family's own + couple (UC para 12(6A), (9)(d); HB 13D(3)(za)-(zb)) and, under HB, for + each occupier couple of a non-dependant's, boarder's or lodger's family + counted by the household head's family; + - under HB, the bedrooms for people who cannot share count only so far + as the dwelling's reported bedrooms exceed the count if everyone could + share (reg 13D(3), closing words); unreported (0) means no limit. +6. A child who cannot share: flagging one more child under 16 as unable to + share (with a qualifying benefit) raises any family's bedrooms by nothing + or one under each scheme, and changes only the families whose size + criteria count that child. +7. The dwelling's bedrooms: with them reported, every family's Housing + Benefit bedrooms lie between the count if everyone could share and the + count with them unreported, and exceed the dwelling's bedrooms only + where the count if everyone could share already does. """ from itertools import combinations @@ -82,9 +102,16 @@ EDUCATION = ["NOT_IN_EDUCATION", "UPPER_SECONDARY", "UPPER_SECONDARY", "TERTIARY"] KINDS = ["own", "own", "own", "foster", "adopted"] OVERNIGHT = st.sampled_from([False, False, False, True]) - -# (age, is_male, kind, has overnight care) -child = st.tuples(st.integers(0, 15), st.booleans(), st.sampled_from(KINDS), OVERNIGHT) +CANNOT_SHARE = st.sampled_from([False, False, False, True]) + +# (age, is_male, kind, has overnight care, cannot share a bedroom) +child = st.tuples( + st.integers(0, 15), + st.booleans(), + st.sampled_from(KINDS), + OVERNIGHT, + CANNOT_SHARE, +) @st.composite @@ -96,6 +123,9 @@ def family(draw, role): ), children=draw(st.lists(child, min_size=0, max_size=4)), between_placements=draw(st.sampled_from([False, False, False, True])), + # The first adult cannot share a bedroom with a partner because of + # disability (this matters only where there are two adults). + couple_cannot_share=draw(CANNOT_SHARE), ) @@ -152,12 +182,16 @@ def child_scenario(draw, kinds): draw(st.booleans()), draw(st.sampled_from(kinds)), False, + False, ) return population, (h, f), added -def child_inputs(age, male, kind, overnight): +def child_inputs(age, male, kind, overnight, cannot_share): inputs = {"age": age, "is_male": male, "is_household_head": False} + if cannot_share: + inputs["dla_sc_middle_plus"] = True + inputs["cannot_reasonably_share_bedroom_due_to_disability"] = True if kind == "foster": inputs["is_looked_after_by_local_authority"] = True if kind == "adopted": @@ -168,11 +202,21 @@ def child_inputs(age, male, kind, overnight): return inputs -def build(population, added=None, supply_roles=True, added_child=None): +def build( + population, + added=None, + supply_roles=True, + added_child=None, + num_bedrooms=None, + flagged_child=None, +): """One situation for the population, optionally with a person aged 16 to 19 added to family ``added[0]`` = (household, family) with attributes ``added[1]``, or a child ``added_child[1]`` = (age, is_male, kind, - overnight) added to family ``added_child[0]``. + overnight, cannot share) added to family ``added_child[0]``. + ``num_bedrooms`` lists each household's bedrooms (unreported if None). + ``flagged_child`` = (household, family, index) marks that child as unable + to share a bedroom. Returns the situation and, per benefit unit, its (household, family).""" people, benunits, homes, keys = {}, {}, {}, [] @@ -194,7 +238,15 @@ def build(population, added=None, supply_roles=True, added_child=None): ids.append(pid) if fam["between_placements"]: people[ids[0]]["is_approved_foster_parent_without_placement"] = True + if fam["couple_cannot_share"]: + people[ids[0]]["pip_dl"] = 5_000 + people[ids[0]]["cannot_reasonably_share_bedroom_due_to_disability"] = ( + True + ) children = list(fam["children"]) + if flagged_child is not None and flagged_child[:2] == (h, f): + i = flagged_child[2] + children[i] = children[i][:4] + (True,) if added_child is not None and added_child[0] == (h, f): children.append(added_child[1]) for i, spec in enumerate(children): @@ -225,6 +277,8 @@ def build(population, added=None, supply_roles=True, added_child=None): "tenure_type": "RENT_PRIVATELY", "brma": "MAIDSTONE", } + if num_bedrooms is not None: + homes[f"h{h}"]["num_bedrooms"] = num_bedrooms[h] situation = {"people": people, "benunits": benunits, "households": homes} return situation, keys @@ -238,9 +292,13 @@ def bedrooms(situation): def fewest_rooms_for_children(children): """Brute force: the fewest rooms holding the children two to a room, - where a pair must be of the same sex or both under 10.""" + where a pair must be of the same sex or both under 10, and a child who + cannot share because of disability (a third element that is true) shares + with no one.""" def can_share(a, b): + if (len(a) > 2 and a[2]) or (len(b) > 2 and b[2]): + return False return a[1] == b[1] or (a[0] < 10 and b[0] < 10) def best(remaining): @@ -260,7 +318,7 @@ def fosters(fam): """Whether the family meets the foster parent condition (UC) or has a qualifying parent or carer (HB) through its children or adults.""" return fam["between_placements"] or any( - kind in ("foster", "adopted") for _, _, kind, _ in fam["children"] + c[2] in ("foster", "adopted") for c in fam["children"] ) @@ -390,16 +448,49 @@ def test_a_foster_child_adds_only_the_foster_parents_bedroom(case): assert np.array_equal(hb_after - hb_before, expected_uc) +@st.composite +def reference_scenario(draw): + population, target, person = draw(scenario()) + # Each household's bedrooms: unreported (0) or 1 to 6, as in the FRS. + num_bedrooms = draw( + st.lists( + st.sampled_from([0, 0, 1, 2, 3, 4, 5, 6]), + min_size=len(population), + max_size=len(population), + ) + ) + return population, target, person, num_bedrooms + + +def pairable(children): + """(age, is_male) of each child, for the pairing without the condition.""" + return [c[:2] for c in children] + + +def with_flags(children): + """(age, is_male, cannot share) of each child.""" + return [(c[0], c[1], c[4]) for c in children] + + +def couple_cannot_share(fam): + """The family is a couple one of whom cannot share a bedroom with the + other (UC Sch 4 para 12(6A); HB reg 2(1)).""" + return len(fam["adults"]) == 2 and fam["couple_cannot_share"] + + @PROPERTY_SETTINGS -@given(scenario()) +@given(reference_scenario()) def test_bedrooms_match_a_reference_count_of_the_size_criteria(case): - population, target, person = case - situation, keys = build(population, (target, person)) + population, target, person, num_bedrooms = case + situation, keys = build(population, (target, person), num_bedrooms=num_bedrooms) sim, uc, hb = bedrooms(situation) uc_additional = np.asarray(sim.calculate("LHA_additional_bedrooms", YEAR)) hb_additional = np.asarray( sim.calculate("housing_benefit_LHA_additional_bedrooms", YEAR) ) + hb_cannot_share = np.asarray( + sim.calculate("housing_benefit_LHA_cannot_share_bedrooms", YEAR) + ) added = flags(person) # 5. Reference count. for b, (h, f) in enumerate(keys): @@ -424,6 +515,9 @@ def test_bedrooms_match_a_reference_count_of_the_size_criteria(case): hb_overnight = True uc_foster = fosters(fam) or (is_target and added["fostered"]) hb_carer = fosters(fam) or (is_target and added["placed"]) + # Couples who cannot share: UC only the renter's (para 12(6A)); HB + # every occupier couple (reg 13D(3)(za)-(zb)). + uc_couples = hb_couples = int(couple_cannot_share(fam)) if fam["role"] == "head": for g, other in enumerate(families[1:], start=1): joins = target == (h, g) @@ -447,17 +541,105 @@ def test_bedrooms_match_a_reference_count_of_the_size_criteria(case): c[3] for c in other["children"] if c[2] == "own" ) hb_overnight |= joins and added["overnight"] and not added["placed"] - expected_uc_additional = int(uc_overnight) + int(uc_foster) + hb_couples += int(couple_cannot_share(other)) + # Children who cannot share: the fewest rooms in which none of them + # shares, beyond the rooms the children would otherwise need (UC + # para 12(6) and (8); HB reg 13D(3)(ba)). + uc_child_rooms = fewest_rooms_for_children(pairable(uc_children)) + uc_disabled_child_rooms = ( + fewest_rooms_for_children(with_flags(uc_children)) - uc_child_rooms + ) + hb_child_rooms = fewest_rooms_for_children(pairable(hb_children)) + hb_disabled_child_rooms = ( + fewest_rooms_for_children(with_flags(hb_children)) - hb_child_rooms + ) + expected_uc_additional = ( + int(uc_overnight) + int(uc_foster) + uc_disabled_child_rooms + uc_couples + ) expected_hb_additional = int(hb_overnight) + int(hb_carer) assert uc_additional[b] == expected_uc_additional assert hb_additional[b] == expected_hb_additional - assert uc[b] == ( - uc_rooms - + fewest_rooms_for_children([c[:2] for c in uc_children]) - + expected_uc_additional + assert uc[b] == uc_rooms + uc_child_rooms + expected_uc_additional + # HB: the bedrooms if everyone could share, plus those for people who + # cannot share, so far as the dwelling has bedrooms beyond the first + # (reg 13D(3), closing words). + able_to_share = hb_rooms + hb_child_rooms + expected_hb_additional + extra = hb_disabled_child_rooms + hb_couples + if num_bedrooms[h] > 0: + extra = min(extra, max(num_bedrooms[h] - able_to_share, 0)) + assert hb_cannot_share[b] == extra + assert hb[b] == able_to_share + extra + + +@st.composite +def flagged_child_scenario(draw): + """A population and one of its children under 16 to flag as unable to + share a bedroom (with a qualifying benefit).""" + population = draw(st.lists(household(), min_size=1, max_size=4)) + children = [ + (h, f, i) + for h, families in enumerate(population) + for f, fam in enumerate(families) + for i in range(len(fam["children"])) + ] + if not children: + population[0][0]["children"].append((5, True, "own", False, False)) + children = [(0, 0, len(population[0][0]["children"]) - 1)] + return population, draw(st.sampled_from(children)) + + +@PROPERTY_SETTINGS +@given(flagged_child_scenario()) +def test_a_child_who_cannot_share_adds_at_most_one_bedroom(case): + population, flagged = case + before, keys = build(population) + after, _ = build(population, flagged_child=flagged) + _, uc_before, hb_before = bedrooms(before) + _, uc_after, hb_after = bedrooms(after) + # 6. One more child who cannot share raises any family's bedrooms by + # nothing or one under each scheme, with the dwelling's bedrooms + # unreported: each such child takes a room, but the others then need at + # most one fewer. + for change in (uc_after - uc_before, hb_after - hb_before): + assert np.all((change == 0) | (change == 1)) + # Only families whose size criteria count the child change: the child's + # own family and, for a child of a non-dependant (UC) or of any occupier + # family (HB), the household head's family. + h, f, i = flagged + kind = population[h][f]["children"][i][2] + role = population[h][f]["role"] + for b, key in enumerate(keys): + uc_counts = (key == (h, f) and kind != "foster") or ( + key == (h, 0) and role == "non_dependant" and kind != "foster" ) - assert hb[b] == ( - hb_rooms - + fewest_rooms_for_children([c[:2] for c in hb_children]) - + expected_hb_additional + hb_counts = (key == (h, f) and kind == "own") or ( + key == (h, 0) + and role in ("non_dependant", "boarder", "lodger") + and kind == "own" ) + if not uc_counts: + assert uc_after[b] == uc_before[b] + if not hb_counts: + assert hb_after[b] == hb_before[b] + + +@PROPERTY_SETTINGS +@given(reference_scenario()) +def test_housing_benefit_rooms_never_exceed_the_dwelling_beyond_the_base(case): + population, target, person, num_bedrooms = case + unreported, keys = build(population, (target, person)) + reported, _ = build(population, (target, person), num_bedrooms=num_bedrooms) + sim, _, hb_free = bedrooms(unreported) + able = hb_free - np.asarray( + sim.calculate("housing_benefit_LHA_cannot_share_bedrooms", YEAR) + ) + _, _, hb = bedrooms(reported) + # 7. The dwelling's bedrooms only ever withhold the bedrooms for people + # who cannot share: the HB count lies between the count if everyone + # could share and the count with the dwelling unreported, and exceeds + # the dwelling's bedrooms only where the count if everyone could share + # already does. + beds = np.array([num_bedrooms[h] for h, _ in keys]) + assert np.all(able <= hb) and np.all(hb <= hb_free) + reported_beds = beds > 0 + assert np.all(hb[reported_beds] <= np.maximum(able, beds)[reported_beds]) diff --git a/policyengine_uk/variables/gov/dwp/LHA_additional_bedrooms.py b/policyengine_uk/variables/gov/dwp/LHA_additional_bedrooms.py index 3d580ea582..03ee8a9a6e 100644 --- a/policyengine_uk/variables/gov/dwp/LHA_additional_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/LHA_additional_bedrooms.py @@ -14,9 +14,8 @@ class LHA_additional_bedrooms(Variable): "however many children are placed. The extended benefit unit " "includes the household head's non-dependants and their children. " "Before 1 April 2017 only the renter could meet the overnight care " - "condition. " - "The disabled child and disabled person conditions, for children or " - "couples who cannot share a bedroom, are not modelled." + "condition. Plus the bedrooms for children and couples who cannot " + "share a bedroom because of disability (see LHA_cannot_share_bedrooms)." ) definition_period = YEAR reference = ( @@ -59,4 +58,7 @@ def formula(benunit, period, parameters): # Para 12(9)(a)-(b): one bedroom for each condition met. overnight_room = benunit.any(own) | (others > 0) foster_room = benunit("lha_renter_meets_foster_parent_condition", period) - return 1.0 * overnight_room + 1.0 * foster_room + # Para 12(8) and (9)(c)-(d): the disabled child and disabled person + # conditions. + cannot_share = benunit("LHA_cannot_share_bedrooms", period) + return 1.0 * overnight_room + 1.0 * foster_room + cannot_share diff --git a/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py b/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py index c210ae2f12..d4c86ecfd7 100644 --- a/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py @@ -26,7 +26,9 @@ def rooms_for_children(boys_under_10, older_boys, girls_under_10, older_girls): return over_10_rooms + under_10_rooms -def bedrooms_for_children(benunit, period, own_child=None, other_child=None): +def bedrooms_for_children( + benunit, period, own_child=None, other_child=None, cannot_share=None +): """Bedrooms for the children under 16 in the family's size criteria. Children must share rooms in pairs unless they are opposite-sex and one @@ -38,6 +40,11 @@ def bedrooms_for_children(benunit, period, own_child=None, other_child=None): other families of the household who count in the household head's family's size criteria, such as a non-dependant's child; they share rooms with the head family's own children. + + ``cannot_share`` marks children who have a bedroom of their own because + of disability. Each counted child so marked has their own room and the + other children are paired as usual: the fewest rooms in which no marked + child shares. """ person = benunit.members age = person("age", period) @@ -47,6 +54,8 @@ def bedrooms_for_children(benunit, period, own_child=None, other_child=None): if own_child is None: own_child = under_16 own_child = own_child & under_16 + if cannot_share is None: + cannot_share = np.zeros_like(under_16) head_family = benunit.any(person("is_household_head", period)) def count(group): @@ -56,12 +65,45 @@ def count(group): others = person.household.sum(other_child & under_16 & group) return own + head_family * benunit.max(others) - return rooms_for_children( - count(under_10 & male), - count(~under_10 & male), - count(under_10 & ~male), - count(~under_10 & ~male), + sharing = ~cannot_share + return count(cannot_share) + rooms_for_children( + count(sharing & under_10 & male), + count(sharing & ~under_10 & male), + count(sharing & under_10 & ~male), + count(sharing & ~under_10 & ~male), + ) + + +def universal_credit_size_criteria_people(benunit, period): + """The people who count in a family's Universal Credit size criteria + beyond its own members aged 16 or over: ``responsible``, the children and + qualifying young persons the renter is responsible for (UC Regs 2013 regs + 4, 4A and 5); and ``non_dependant``, the household head's non-dependants + from other families and their children (Sch 4 para 9(1)(c) and 9(2)). + """ + person = benunit.members + # UC Regs 2013 reg 4: a person is responsible for a child or qualifying + # young person who normally lives with them, but no one is responsible + # for one looked after by a local authority (reg 4(6)(a)), such as a + # foster child, unless reg 4A applies (for example a child placed for + # adoption). + responsible = person( + "is_child_or_qualifying_young_person_for_universal_credit", period ) + no_one_responsible = ( + person("is_child_for_universal_credit", period) + | person("is_qualifying_young_person_for_universal_credit", period) + ) & ~responsible + # Para 9(1)(c) and 9(2): people of other families in the household who + # are non-dependants of the household head's family. Para 9(2)(g) + # excludes a child or qualifying young person no one in the extended + # benefit unit is responsible for, such as a non-dependant's foster + # child; a non-dependant's own child is their responsibility (reg 4(2)) + # and so is a non-dependant too. + non_dependant = ( + person("is_non_dependant_of_household_head", period) & ~no_one_responsible + ) + return responsible, non_dependant class LHA_allowed_bedrooms(Variable): @@ -73,8 +115,10 @@ class LHA_allowed_bedrooms(Variable): "unit: one bedroom for the renter or couple, one for each member of " "the benefit unit aged 16 or over who is not the claimant or partner, " "one for each non-dependant aged 16 or over, the children's bedrooms, " - "and the additional bedrooms for overnight care and foster parents " - "(see LHA_additional_bedrooms). A qualifying young person the renter " + "and the additional bedrooms for overnight care, foster parents, and " + "children and couples who cannot share a bedroom because of " + "disability (see LHA_additional_bedrooms). A qualifying young person " + "the renter " "is responsible for has their own bedroom; any other member aged 16 " "or over is a non-dependant and has one too, except a qualifying " "young person no one is responsible for, such as one looked after by " @@ -104,18 +148,10 @@ def formula(benunit, period, parameters): # A child is a person under 16 (WRA 2012 s.40). aged_16_or_over = person("age", period) >= 16 family_member = aged_16_or_over & ~person("is_claimant_or_partner", period) - # UC Regs 2013 reg 4: a person is responsible for a child or - # qualifying young person who normally lives with them, but no one is - # responsible for one looked after by a local authority (reg 4(6)(a)), - # such as a foster child, unless reg 4A applies (for example a child - # placed for adoption). - responsible = person( - "is_child_or_qualifying_young_person_for_universal_credit", period + responsible, non_dependant = universal_credit_size_criteria_people( + benunit, period ) qualifying = person("is_qualifying_young_person_for_universal_credit", period) - no_one_responsible = ( - person("is_child_for_universal_credit", period) | qualifying - ) & ~responsible # UC Regs 2013 Sch 4 para 10(1)(b): a qualifying young person for # whom the renter is responsible (regs 4 and 5). qualifying_young_person = family_member & responsible @@ -127,15 +163,8 @@ def formula(benunit, period, parameters): family_member & ~qualifying & ~person("is_lha_foster_child", period) ) family_rooms = benunit.sum(qualifying_young_person | family_non_dependant) - # Para 9(1)(c) and 9(2): people of other families in the household - # who are non-dependants of the household head's family. Para 9(2)(g) - # excludes a child or qualifying young person no one in the extended - # benefit unit is responsible for, such as a non-dependant's foster - # child; a non-dependant's own child is their responsibility (reg - # 4(2)) and so is a non-dependant too. - non_dependant = ( - person("is_non_dependant_of_household_head", period) & ~no_one_responsible - ) + # Para 9(1)(c) and 9(2): the household head's non-dependants from + # other families (see universal_credit_size_criteria_people). head_family = benunit.any(person("is_household_head", period)) # Para 10(1)(c): a non-dependant who is not a child. non_dependants = head_family * benunit.max( diff --git a/policyengine_uk/variables/gov/dwp/LHA_cannot_share_bedrooms.py b/policyengine_uk/variables/gov/dwp/LHA_cannot_share_bedrooms.py new file mode 100644 index 0000000000..3a88dbced5 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/LHA_cannot_share_bedrooms.py @@ -0,0 +1,61 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.gov.dwp.LHA_allowed_bedrooms import ( + bedrooms_for_children, + universal_credit_size_criteria_people, +) + + +class LHA_cannot_share_bedrooms(Variable): + value_type = float + entity = BenUnit + label = "Universal Credit bedrooms for children and couples who cannot share" + documentation = ( + "Additional bedrooms in the Universal Credit size criteria under the " + "disabled child and disabled person conditions. Disabled child " + "condition: each child in the renter's extended benefit unit who " + "cannot share a bedroom because of disability (see " + "is_child_who_cannot_share_bedroom) has a bedroom of their own and " + "the other children share as usual; the additional bedrooms are as " + "many as that takes beyond the rooms the children would otherwise " + "need, so none where such a child would not otherwise share. Where " + "several such children could each have a room alone but not all at " + "once, the model gives them all their own rooms. Disabled person " + "condition: one bedroom if the renter or a joint renter cannot share " + "a bedroom with the other because of disability (see " + "is_member_of_couple_who_cannot_share_bedroom). Unlike Housing " + "Benefit, Universal Credit does not require the home to have the " + "extra bedroom." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/10", + ) + + def formula(benunit, period, parameters): + person = benunit.members + responsible, non_dependant = universal_credit_size_criteria_people( + benunit, period + ) + + def children(cannot_share=None): + return bedrooms_for_children( + benunit, + period, + own_child=responsible, + other_child=non_dependant, + cannot_share=cannot_share, + ) + + # UC Regs 2013 Sch 4 para 12(6) and (8): as many additional bedrooms + # as are necessary for each child in the extended benefit unit who + # would otherwise be expected to share, and is not reasonably able + # to, to have their own bedroom. + cannot_share = person("is_child_who_cannot_share_bedroom", period) + disabled_child_rooms = children(cannot_share) - children() + # Para 12(6A) and (9)(d): one additional bedroom if the renter or a + # joint renter satisfies the disabled person condition. + disabled_person_room = benunit.any( + person("is_member_of_couple_who_cannot_share_bedroom", period) + ) + return disabled_child_rooms + 1.0 * disabled_person_room diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py index d43048fd17..da205b3ab7 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_additional_bedrooms.py @@ -19,7 +19,9 @@ class housing_benefit_LHA_additional_bedrooms(Variable): "the dwelling for " "the carer or the foster child; the model does not observe the " "dwelling's bedrooms and assumes there is one. Children and couples " - "who cannot share a bedroom because of disability are not modelled." + "who cannot share a bedroom because of disability have their own " + "bedrooms in the main size criteria (see " + "housing_benefit_LHA_cannot_share_bedrooms)." ) definition_period = YEAR reference = ( diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py index bbf65f77cb..21247d785c 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_allowed_bedrooms.py @@ -4,6 +4,70 @@ ) +def housing_benefit_occupiers(benunit, period): + """Person masks for a family's Housing Benefit size criteria: + ``occupier``, everyone who occupies the dwelling as their home rather + than being placed with a family as a foster child or for adoption; and + ``other_occupier``, the occupiers of other families who count in the + household head's family's size criteria (a non-dependant's, boarder's or + lodger's family, but not a sharer of the rent's).""" + person = benunit.members + # HB Regs 2006 reg 21(3): a child or young person placed with the + # claimant or partner as a foster child or for adoption does not occupy + # the claimant's dwelling. Reg 21(3) names only placements with the + # claimant or partner; the model follows DWP in treating a child placed + # with anyone in the household as no occupier (LHA Guidance Manual para + # 2.033; HB circular A21/2013 para 22), as Universal Credit does (UC Sch 4 + # para 9(2)(g)). + occupier = ~person("is_child_or_young_person_placed_with_family", period) + head_family = person.benunit.any(person("is_household_head", period)) + sharer = person.benunit("liable_for_share_of_household_rent", period) + # Reg 13D(3) for occupiers outside the family, as defined in 13D(12): + # everyone in a non-dependant's, boarder's or lodger's family, including + # their children. + other_occupier = ~head_family & ~sharer & occupier + return occupier, other_occupier + + +def housing_benefit_bedrooms_if_able_to_share(benunit, period): + """Bedrooms in the Housing Benefit size criteria (HB Regs 2006 reg + 13D(3)-(3B)) as if every child and every member of a couple could share + a bedroom. The reg 13D(3) proviso compares the dwelling's bedrooms with + this entitlement.""" + person = benunit.members + # A child is a person under 16 (HB Regs 2006 reg 2(1)). + aged_16_or_over = person("age", period) >= 16 + occupier, other_occupier = housing_benefit_occupiers(benunit, period) + # HB Regs 2006 reg 13D(3)(b): a person who is not a child, here a member + # of the benefit unit other than the claimant or partner. + family_rooms = benunit.sum( + aged_16_or_over & ~person("is_claimant_or_partner", period) & occupier + ) + # Reg 13D(3)(a): a couple, here the claimant and partner of an occupier's + # family, has one bedroom; (b): every other occupier aged 16 or over has + # their own. Each member of a couple counts as half, and only where both + # members are counted, so the halves always pair. + counted = other_occupier & aged_16_or_over + claimant_or_partner = counted & person("is_claimant_or_partner", period) + couple = (person.benunit.sum(claimant_or_partner) == 2) & person.benunit( + "is_couple", period + ) + occupier_rooms = counted * where(claimant_or_partner & couple, 0.5, 1) + is_head_family = benunit.any(person("is_household_head", period)) + other_occupiers = is_head_family * benunit.max(person.household.sum(occupier_rooms)) + # Reg 13D(3)(c)-(e): the occupiers' children, the claimant's own and other + # families', share rooms with each other. + children = bedrooms_for_children( + benunit, + period, + own_child=occupier, + other_child=other_occupier, + ) + # Reg 13D(3A) and (3B): additional bedrooms. + additional = benunit("housing_benefit_LHA_additional_bedrooms", period) + return 1 + family_rooms + other_occupiers + children + additional + + class housing_benefit_LHA_allowed_bedrooms(Variable): value_type = float entity = BenUnit @@ -20,13 +84,15 @@ class housing_benefit_LHA_allowed_bedrooms(Variable): "bedroom, such as a young person in full-time education; so do a " "householder's boarder or lodger and a non-dependant, but a sharer " "of the rent does not. A couple, such as a non-dependant and their " - "partner, has one bedroom between them. The children of a " - "non-dependant, boarder or " - "lodger are occupiers too, and share rooms with the claimant's " - "children. A child or young person placed with a family in the " - "household as a foster child or for adoption is not an occupier. A " - "sharer's, boarder's or lodger's own claim counts only their own " - "family." + "partner, has one bedroom between them, or one each if a member " + "cannot share a bedroom with the other because of disability. The " + "children of a non-dependant, boarder or lodger are occupiers too, " + "and share rooms with the claimant's children, except that a child " + "who cannot share because of disability has a room of their own (see " + "housing_benefit_LHA_cannot_share_bedrooms). A child or young person " + "placed with a family in the household as a foster child or for " + "adoption is not an occupier. A sharer's, boarder's or lodger's own " + "claim counts only their own family." ) definition_period = YEAR reference = ( @@ -39,50 +105,8 @@ class housing_benefit_LHA_allowed_bedrooms(Variable): ) def formula(benunit, period, parameters): - person = benunit.members - # A child is a person under 16 (HB Regs 2006 reg 2(1)). - aged_16_or_over = person("age", period) >= 16 - # HB Regs 2006 reg 21(3): a child or young person placed with the - # claimant or partner as a foster child or for adoption does not - # occupy the claimant's dwelling. Reg 21(3) names only placements - # with the claimant or partner; the model follows DWP in treating a - # child placed with anyone in the household as no occupier (LHA - # Guidance Manual para 2.033; HB circular A21/2013 para 22), as - # Universal Credit does (UC Sch 4 para 9(2)(g)). - occupier = ~person("is_child_or_young_person_placed_with_family", period) - # HB Regs 2006 reg 13D(3)(b): a person who is not a child, here a - # member of the benefit unit other than the claimant or partner. - family_rooms = benunit.sum( - aged_16_or_over & ~person("is_claimant_or_partner", period) & occupier - ) - head_family = person.benunit.any(person("is_household_head", period)) - sharer = person.benunit("liable_for_share_of_household_rent", period) - # Reg 13D(3) for occupiers outside the family, as defined in 13D(12): - # everyone in a non-dependant's, boarder's or lodger's family, - # including their children. - other_occupier = ~head_family & ~sharer & occupier - # Reg 13D(3)(a): a couple, here the claimant and partner of an - # occupier's family, has one bedroom; (b): every other occupier aged - # 16 or over has their own. Each member of a couple counts as half, - # and only where both members are counted, so the halves always pair. - counted = other_occupier & aged_16_or_over - claimant_or_partner = counted & person("is_claimant_or_partner", period) - couple = (person.benunit.sum(claimant_or_partner) == 2) & person.benunit( - "is_couple", period - ) - occupier_rooms = counted * where(claimant_or_partner & couple, 0.5, 1) - is_head_family = benunit.any(person("is_household_head", period)) - other_occupiers = is_head_family * benunit.max( - person.household.sum(occupier_rooms) - ) - # Reg 13D(3)(c)-(e): the occupiers' children, the claimant's own and - # other families', share rooms with each other. - children = bedrooms_for_children( - benunit, - period, - own_child=occupier, - other_child=other_occupier, + # Reg 13D(3)(za), (zb) and (ba): the further bedrooms for children and + # members of couples who cannot share, where the dwelling has them. + return housing_benefit_bedrooms_if_able_to_share(benunit, period) + benunit( + "housing_benefit_LHA_cannot_share_bedrooms", period ) - # Reg 13D(3A) and (3B): additional bedrooms. - additional = benunit("housing_benefit_LHA_additional_bedrooms", period) - return 1 + family_rooms + other_occupiers + children + additional diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_cannot_share_bedrooms.py b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_cannot_share_bedrooms.py new file mode 100644 index 0000000000..b379ba161e --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/housing_benefit_LHA_cannot_share_bedrooms.py @@ -0,0 +1,87 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.gov.dwp.LHA_allowed_bedrooms import ( + bedrooms_for_children, +) +from policyengine_uk.variables.gov.dwp.housing_benefit_LHA_allowed_bedrooms import ( + housing_benefit_bedrooms_if_able_to_share, + housing_benefit_occupiers, +) + + +class housing_benefit_LHA_cannot_share_bedrooms(Variable): + value_type = float + entity = BenUnit + label = "Housing Benefit bedrooms for children and couples who cannot share" + documentation = ( + "Bedrooms in the Housing Benefit size criteria beyond those the " + "claimant would have if every occupier could share. Each occupier " + "child who cannot share a bedroom because of disability (see " + "is_child_who_cannot_share_bedroom) has a room of their own and the " + "other children share as usual, so there is no extra room where such " + "a child would not otherwise share. A couple of occupiers, the " + "claimant's own or another family's, has a bedroom each instead of " + "one between them if either member cannot share with the other (see " + "is_member_of_couple_who_cannot_share_bedroom). These bedrooms count " + "only so far as the dwelling has bedrooms beyond the claimant's " + "entitlement if everyone could share, including the additional " + "bedrooms for overnight care and qualifying parents or carers. The " + "dwelling's bedrooms are read from num_bedrooms; where that is 0, " + "meaning not reported (the Family Resources Survey records at least " + "one bedroom for every household, and the released enhanced FRS does " + "not carry it), the model assumes the dwelling has the bedrooms." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2006/213/regulation/13D", + "https://www.legislation.gov.uk/uksi/2006/214/regulation/13D", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/2", + "https://www.legislation.gov.uk/uksi/2006/214/regulation/2", + "https://www.legislation.gov.uk/uksi/2017/213/made", + ) + + def formula(benunit, period, parameters): + person = benunit.members + occupier, other_occupier = housing_benefit_occupiers(benunit, period) + + def children(cannot_share=None): + return bedrooms_for_children( + benunit, + period, + own_child=occupier, + other_child=other_occupier, + cannot_share=cannot_share, + ) + + # HB Regs 2006 reg 13D(3)(ba): a child who cannot share a bedroom is + # a category of their own, ahead of the pairs in (c) and (d). + cannot_share = person("is_child_who_cannot_share_bedroom", period) + child_rooms = children(cannot_share) - children() + # Reg 13D(3)(za) and (zb), ahead of (a): each member of a couple one + # of whom cannot share has their own bedroom, so the couple has two + # rather than one. The claimant's own couple: + member = person("is_member_of_couple_who_cannot_share_bedroom", period) + own_couple_room = benunit.any(member & occupier) + # Occupier couples of other families, counted for the household + # head's family (see housing_benefit_bedrooms_if_able_to_share): one + # further bedroom per couple, however many of its members qualify. + aged_16_or_over = person("age", period) >= 16 + counted = ( + other_occupier & aged_16_or_over & person("is_claimant_or_partner", period) + ) + couple = (person.benunit.sum(counted) == 2) & person.benunit( + "is_couple", period + ) + qualifying = counted & couple & member + per_couple = qualifying / max_(person.benunit.sum(qualifying), 1) + is_head_family = benunit.any(person("is_household_head", period)) + other_couple_rooms = is_head_family * benunit.max( + person.household.sum(per_couple) + ) + extra = child_rooms + 1.0 * own_couple_room + other_couple_rooms + # Reg 13D(3), closing words: only if the dwelling has a bedroom + # additional to those to which the claimant would be entitled if the + # child or the member of the couple were able to share. + able_to_share = housing_benefit_bedrooms_if_able_to_share(benunit, period) + bedrooms = benunit.max(person.household("num_bedrooms", period)) + spare = max_(bedrooms - able_to_share, 0) + return where(bedrooms > 0, min_(extra, spare), extra) diff --git a/policyengine_uk/variables/gov/dwp/is_child_who_cannot_share_bedroom.py b/policyengine_uk/variables/gov/dwp/is_child_who_cannot_share_bedroom.py new file mode 100644 index 0000000000..8eb3bd3027 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/is_child_who_cannot_share_bedroom.py @@ -0,0 +1,42 @@ +from policyengine_uk.model_api import * + + +class is_child_who_cannot_share_bedroom(Variable): + value_type = bool + entity = Person + label = "Child who cannot share a bedroom (LHA size criteria)" + documentation = ( + "A child under 16 who receives the care component of Disability " + "Living Allowance at the middle or highest rate and is not reasonably " + "able to share a bedroom with another child because of their " + "disability (cannot_reasonably_share_bedroom_due_to_disability). " + "This is the child in the Universal Credit disabled child condition " + "and the Housing Benefit 'child who cannot share a bedroom', in force " + "from 4 December 2013. The law also accepts the care component of " + "child disability payment (Scotland), which the model counts only " + "where the data record it as Disability Living Allowance. Whether " + "the child would otherwise be expected to share is decided in the " + "size criteria (see LHA_cannot_share_bedrooms and " + "housing_benefit_LHA_cannot_share_bedrooms)." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/2", + "https://www.legislation.gov.uk/uksi/2006/214/regulation/2", + "https://www.legislation.gov.uk/uksi/2013/2828/made", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.dwp.LHA.cannot_share_bedroom.child + # UC Regs 2013 reg 2 and HB Regs 2006 reg 2(1): a child is a person + # under 16. + child = person("age", period) < 16 + # UC Sch 4 para 12(6)(a); HB reg 2(1), "child who cannot share a + # bedroom", (a). + qualifying_benefit = add(person, period, p.benefits) > 0 + # Para 12(6)(b); reg 2(1), (b). + cannot_share = person( + "cannot_reasonably_share_bedroom_due_to_disability", period + ) + return p.in_effect & child & qualifying_benefit & cannot_share diff --git a/policyengine_uk/variables/gov/dwp/is_member_of_couple_who_cannot_share_bedroom.py b/policyengine_uk/variables/gov/dwp/is_member_of_couple_who_cannot_share_bedroom.py new file mode 100644 index 0000000000..37a56d2e89 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/is_member_of_couple_who_cannot_share_bedroom.py @@ -0,0 +1,46 @@ +from policyengine_uk.model_api import * + + +class is_member_of_couple_who_cannot_share_bedroom(Variable): + value_type = bool + entity = Person + label = "Member of a couple who cannot share a bedroom (LHA size criteria)" + documentation = ( + "The claimant or partner of a couple who receives a qualifying " + "disability benefit (attendance allowance or armed forces " + "independence payment, the care component of Disability Living " + "Allowance at the middle or highest rate, or the daily living " + "component of Personal Independence Payment) and is not reasonably " + "able to share a bedroom with the other member of the couple because " + "of their disability (cannot_reasonably_share_bedroom_due_to_" + "disability). This is the renter in the Universal Credit disabled " + "person condition and the Housing Benefit 'member of a couple who " + "cannot share a bedroom', in force from 1 April 2017. Attendance " + "allowance counted only at the higher rate until 27 January 2025. " + "The law also lists the Scottish devolved equivalents, which the " + "model counts only where the data record them as the DWP benefits." + ) + definition_period = YEAR + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/2", + "https://www.legislation.gov.uk/uksi/2006/214/regulation/2", + "https://www.legislation.gov.uk/uksi/2017/213/made", + "https://www.legislation.gov.uk/uksi/2025/3/made", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.dwp.LHA.cannot_share_bedroom.couple_member + # UC Sch 4 para 12(6A): the renter and a joint renter; HB Regs 2006 + # reg 2(1): a member of a couple. + couple_member = person("is_claimant_or_partner", period) & person.benunit( + "is_couple", period + ) + # Para 12(6A)(a); reg 2(1), "member of a couple who cannot share a + # bedroom", (a). + qualifying_benefit = add(person, period, p.benefits) > 0 + # Para 12(6A)(b); reg 2(1), (b). + cannot_share = person( + "cannot_reasonably_share_bedroom_due_to_disability", period + ) + return p.in_effect & couple_member & qualifying_benefit & cannot_share diff --git a/policyengine_uk/variables/gov/dwp/receives_higher_rate_attendance_allowance.py b/policyengine_uk/variables/gov/dwp/receives_higher_rate_attendance_allowance.py new file mode 100644 index 0000000000..2654ca1ffb --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/receives_higher_rate_attendance_allowance.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class receives_higher_rate_attendance_allowance(Variable): + label = "Receives attendance allowance at the higher rate" + entity = Person + definition_period = YEAR + value_type = bool + reference = "https://www.legislation.gov.uk/ukpga/1992/4/section/65" + + def formula(person, period, parameters): + category = person("aa_category", period) + return category == category.possible_values.HIGHER diff --git a/policyengine_uk/variables/household/demographic/cannot_reasonably_share_bedroom_due_to_disability.py b/policyengine_uk/variables/household/demographic/cannot_reasonably_share_bedroom_due_to_disability.py new file mode 100644 index 0000000000..0f336105d4 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/cannot_reasonably_share_bedroom_due_to_disability.py @@ -0,0 +1,25 @@ +from policyengine_uk.model_api import * + + +class cannot_reasonably_share_bedroom_due_to_disability(Variable): + value_type = bool + entity = Person + label = "not reasonably able to share a bedroom because of disability" + documentation = ( + "Whether the decision maker (Universal Credit) or the local authority " + "(Housing Benefit) is satisfied that, because of their disability, " + "this person is not reasonably able to share a bedroom: for a child " + "under 16, with another child; for a member of a couple, with the " + "other member of the couple. With a qualifying disability benefit, " + "this gives the child or the couple a bedroom of their own in the LHA " + "size criteria (see is_child_who_cannot_share_bedroom and " + "is_member_of_couple_who_cannot_share_bedroom). The survey data do " + "not record this judgement, so it is false unless supplied." + ) + definition_period = YEAR + default_value = False + reference = ( + "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/12", + "https://www.legislation.gov.uk/uksi/2006/213/regulation/2", + "https://www.legislation.gov.uk/uksi/2006/214/regulation/2", + ) diff --git a/policyengine_uk/variables/household/demographic/num_bedrooms.py b/policyengine_uk/variables/household/demographic/num_bedrooms.py index d30aff8b2a..3a1f95d512 100644 --- a/policyengine_uk/variables/household/demographic/num_bedrooms.py +++ b/policyengine_uk/variables/household/demographic/num_bedrooms.py @@ -6,4 +6,9 @@ class num_bedrooms(Variable): value_type = int entity = Household label = "The number of bedrooms in the house" + documentation = ( + "The number of bedrooms in the dwelling (Family Resources Survey " + "BEDROOM6, which is at least 1 and top-coded at 6). 0, the default, " + "means not reported." + ) definition_period = YEAR From a7ffd848b07d1b17033eed78650cf8ad3cbb927b Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 2 Oct 2026 10:25:35 -0400 Subject: [PATCH 2/3] Test couples with both members unable to share The property generator now flags none, the first or both adults of a couple, so the Housing Benefit count of one further bedroom per occupier couple, however many members qualify, is exercised. Adds the matching YAML case for a non-dependant couple. Co-Authored-By: Claude Opus 5.5 --- .../family/LHA_cannot_share_bedroom.yaml | 32 +++++++++++++++++++ ...est_lha_bedroom_young_people_properties.py | 16 ++++------ 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_cannot_share_bedroom.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_cannot_share_bedroom.yaml index 7714ec1467..394dda7cad 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_cannot_share_bedroom.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/LHA_cannot_share_bedroom.yaml @@ -691,6 +691,38 @@ LHA_cannot_share_bedrooms: [0, 1] LHA_allowed_bedrooms: [3, 2] +- name: A non-dependant couple who both cannot share still have two Housing Benefit bedrooms + period: 2026 + input: + people: + renter: + age: 62 + is_household_head: true + son: + age: 34 + pip_dl: 5_000 + cannot_reasonably_share_bedroom_due_to_disability: true + son_partner: + age: 33 + dla_sc_middle_plus: true + cannot_reasonably_share_bedroom_due_to_disability: true + benunits: + renter_family: + members: [renter] + son_family: + members: [son, son_partner] + households: + home: + members: [renter, son, son_partner] + rent: 10_400 + tenure_type: RENT_PRIVATELY + brma: MAIDSTONE + output: + # Reg 13D(3)(za) for each member: one bedroom each, so one more for the + # couple, not two. + housing_benefit_LHA_cannot_share_bedrooms: [1, 1] + housing_benefit_LHA_allowed_bedrooms: [3, 2] + - name: Housing Benefit room for a disabled child needs one beyond the overnight carer's period: 2026 input: diff --git a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py index 8f4c57d5c3..5fb6ad8e6b 100644 --- a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py +++ b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py @@ -123,9 +123,9 @@ def family(draw, role): ), children=draw(st.lists(child, min_size=0, max_size=4)), between_placements=draw(st.sampled_from([False, False, False, True])), - # The first adult cannot share a bedroom with a partner because of - # disability (this matters only where there are two adults). - couple_cannot_share=draw(CANNOT_SHARE), + # How many adults, from the first, cannot share a bedroom with a + # partner because of disability (this matters only for two adults). + couple_cannot_share=draw(st.sampled_from([0, 0, 0, 1, 2])), ) @@ -238,11 +238,9 @@ def build( ids.append(pid) if fam["between_placements"]: people[ids[0]]["is_approved_foster_parent_without_placement"] = True - if fam["couple_cannot_share"]: - people[ids[0]]["pip_dl"] = 5_000 - people[ids[0]]["cannot_reasonably_share_bedroom_due_to_disability"] = ( - True - ) + for pid in ids[: fam["couple_cannot_share"]]: + people[pid]["pip_dl"] = 5_000 + people[pid]["cannot_reasonably_share_bedroom_due_to_disability"] = True children = list(fam["children"]) if flagged_child is not None and flagged_child[:2] == (h, f): i = flagged_child[2] @@ -475,7 +473,7 @@ def with_flags(children): def couple_cannot_share(fam): """The family is a couple one of whom cannot share a bedroom with the other (UC Sch 4 para 12(6A); HB reg 2(1)).""" - return len(fam["adults"]) == 2 and fam["couple_cannot_share"] + return len(fam["adults"]) == 2 and fam["couple_cannot_share"] > 0 @PROPERTY_SETTINGS From 4e7cfd70acecdcb6b582d2e305a7b1d65d67bf28 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 3 Oct 2026 07:50:32 -0400 Subject: [PATCH 3/3] Pin the exact effect of a couple who cannot share Invariant 8 always builds a couple from another family and flags one or both members: the couple's own claim gains one bedroom under each scheme, and the household head's family one Housing Benefit bedroom when the couple are occupiers it counts. A mutation that counted a bedroom per qualifying member survived the reference test's 25 examples; this test pins it. Co-Authored-By: Claude Opus 5.5 --- ...est_lha_bedroom_young_people_properties.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py index 5fb6ad8e6b..29c1e94509 100644 --- a/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py +++ b/policyengine_uk/tests/test_lha_bedroom_young_people_properties.py @@ -81,6 +81,11 @@ Benefit bedrooms lie between the count if everyone could share and the count with them unreported, and exceed the dwelling's bedrooms only where the count if everyone could share already does. +8. Couples: flagging one or both members of another family's couple as + unable to share gives the couple's own claim exactly one more bedroom + under each scheme, and the household head's family exactly one more + Housing Benefit bedroom (none under UC) if the couple are occupiers it + counts; no other family changes. """ from itertools import combinations @@ -641,3 +646,47 @@ def test_housing_benefit_rooms_never_exceed_the_dwelling_beyond_the_base(case): assert np.all(able <= hb) and np.all(hb <= hb_free) reported_beds = beds > 0 assert np.all(hb[reported_beds] <= np.maximum(able, beds)[reported_beds]) + + +@st.composite +def couple_scenario(draw): + """A population with a couple from another family (a sharer's, + boarder's, lodger's or non-dependant's) in one household, and how many + of the couple's members to flag as unable to share a bedroom.""" + population = draw(st.lists(household(), min_size=1, max_size=3)) + h = draw(st.integers(0, len(population) - 1)) + couple = draw(st.sampled_from(ROLES).flatmap(family)) + couple["adults"] = draw( + st.lists(st.tuples(st.integers(20, 85), OVERNIGHT), min_size=2, max_size=2) + ) + couple["couple_cannot_share"] = 0 + population[h] = population[h] + [couple] + return population, (h, len(population[h]) - 1), draw(st.sampled_from([1, 2])) + + +@PROPERTY_SETTINGS +@given(couple_scenario()) +def test_a_couple_who_cannot_share_adds_one_bedroom_however_many_qualify(case): + population, (h, f), members = case + before, keys = build(population) + population[h][f]["couple_cannot_share"] = members + after, _ = build(population) + _, uc_before, hb_before = bedrooms(before) + _, uc_after, hb_after = bedrooms(after) + # 8. Flagging one or both members of a couple from another family, with + # a qualifying benefit, gives the couple's own claim one more bedroom + # under each scheme (UC para 12(6A), (9)(d); HB reg 13D(3)(za)-(zb)), + # and the household head's family one more HB bedroom if the couple are + # occupiers it counts (a non-dependant's, boarder's or lodger's family, + # 13D(12)), but no more UC bedroom (the disabled person condition is the + # renter's own). No other family changes. The dwelling's bedrooms are + # unreported. + role = population[h][f]["role"] + expected_uc = np.zeros(len(keys)) + expected_hb = np.zeros(len(keys)) + own = keys.index((h, f)) + expected_uc[own] = expected_hb[own] = 1 + if role in ("non_dependant", "boarder", "lodger"): + expected_hb[keys.index((h, 0))] = 1 + assert np.array_equal(uc_after - uc_before, expected_uc) + assert np.array_equal(hb_after - hb_before, expected_hb)