diff --git a/changelog.d/hub-review-followups-2026-10-25.changed.md b/changelog.d/hub-review-followups-2026-10-25.changed.md new file mode 100644 index 0000000000..504ff14594 --- /dev/null +++ b/changelog.d/hub-review-followups-2026-10-25.changed.md @@ -0,0 +1 @@ +Corrected comments, docstrings and citations from reviews of recently merged pull requests, and added property tests for claimant roles and the Childcare Offer for Wales income sources. diff --git a/policyengine_uk/parameters/gov/dwp/housing_benefit/non_dep_deduction/remunerative_work_hours.yaml b/policyengine_uk/parameters/gov/dwp/housing_benefit/non_dep_deduction/remunerative_work_hours.yaml index 0fecad832e..a481b43cca 100644 --- a/policyengine_uk/parameters/gov/dwp/housing_benefit/non_dep_deduction/remunerative_work_hours.yaml +++ b/policyengine_uk/parameters/gov/dwp/housing_benefit/non_dep_deduction/remunerative_work_hours.yaml @@ -10,3 +10,5 @@ metadata: href: https://www.legislation.gov.uk/uksi/2006/213/regulation/6 - title: The Housing Benefit (Persons who have attained the qualifying age for state pension credit) Regulations 2006 reg. 6(1) href: https://www.legislation.gov.uk/uksi/2006/214/regulation/6 + - title: The Housing Benefit Regulations (Northern Ireland) 2006 reg. 6(1) + href: https://www.legislation.gov.uk/nisr/2006/405/regulation/6 diff --git a/policyengine_uk/parameters/gov/economic_assumptions/README.md b/policyengine_uk/parameters/gov/economic_assumptions/README.md index b0db05c16f..a7ef5d5f4f 100644 --- a/policyengine_uk/parameters/gov/economic_assumptions/README.md +++ b/policyengine_uk/parameters/gov/economic_assumptions/README.md @@ -237,9 +237,9 @@ and so do the Universal Credit standard allowance and health element indices built from it. Housing Benefit non-dependant deduction income bands, DfE funding rates, the Childcare Offer for Wales hourly rate and the council tax high value surcharge follow the lagged calendar-year CPI projection through -`indices.obr.lagged_cpi`, pending their own uprating models. The deduction amounts continue to follow September CPI. -The savings credit threshold and Housing -Benefit pension-age allowances are within the s150 review, under +`indices.obr.lagged_cpi`, pending their own uprating models. The deduction +amounts continue to follow September CPI. The savings credit threshold and +Housing Benefit pension-age allowances are within the s150 review, under [s150(1)(l) and (h)](https://www.legislation.gov.uk/ukpga/1992/5/section/150), even though their increases in practice are linked to earnings rather than September CPI. diff --git a/policyengine_uk/parameters/gov/welsh_government/childcare_offer/income/gross_income_sources.yaml b/policyengine_uk/parameters/gov/welsh_government/childcare_offer/income/gross_income_sources.yaml index f1f1afff81..1e14c7c83a 100644 --- a/policyengine_uk/parameters/gov/welsh_government/childcare_offer/income/gross_income_sources.yaml +++ b/policyengine_uk/parameters/gov/welsh_government/childcare_offer/income/gross_income_sources.yaml @@ -53,8 +53,9 @@ values: # Maternity Grant, lump_sum_income) or to the household (cost of living # payments, the energy rebate, bill credits), because question 10 works out # annual income from regular pay and the bonuses a parent expects, read here - # as a test of recurring income (question 9's "all payments" points the - # other way); and loans, which are repaid (student_loans). + # as a test of recurring income (question 8's current-tax-year test and + # question 9's "all payments" point the other way); and loans, which are + # repaid (student_loans). # # Data limits, immaterial near £100,000: for a Welsh student the FRS # education_grants residual can still hold Childcare Grant and Disabled diff --git a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/housing_benefit_non_dep_remunerative_work.yaml b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/housing_benefit_non_dep_remunerative_work.yaml index e12d79e9f4..bb515785bf 100644 --- a/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/housing_benefit_non_dep_remunerative_work.yaml +++ b/policyengine_uk/tests/policy/baseline/finance/benefit/family/housing_benefit/non_dep_deduction/housing_benefit_non_dep_remunerative_work.yaml @@ -3,7 +3,7 @@ # A non-worker or 15-hour worker with banding income pays 20.40 * 52 = # 1,060.80 in 2026-27. One with no banding income keeps the scale's zero: # the gate only lowers a deduction until #2007 models the exemptions in -# reg 74(6)-(10) and reg 55(6)-(9) with the flat charge. +# reg 74(6), (7), (8) and (10) and reg 55(6)-(9) with the flat charge. # A 16-hour worker's wages of 9,000 alone are below 192/week; adding # IIDB or child maintenance of 12,000 gives 403.85/week, so 105.20 * 52. diff --git a/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/september_cpi_uprating.yaml b/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/september_cpi_uprating.yaml index a2f8077707..236b9110f1 100644 --- a/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/september_cpi_uprating.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/social_security_scotland/september_cpi_uprating.yaml @@ -6,7 +6,7 @@ # Annual model periods use the fiscal-year parameter amounts. - name: Scottish Child Payment projects to 29.20 a week in April 2027 - # 28.20 x 1.035 = 29.187 -> 29.20, then 29.20 x 365.25 / 7 = 1523.614286. + # 28.20 x 1.035 = 29.187 -> 29.20, then 29.20 x 52 = 1518.40. period: 2027 absolute_error_margin: 0.01 input: diff --git a/policyengine_uk/tests/policy/baseline/gov/welsh_government/childcare_offer/wales_childcare_offer.yaml b/policyengine_uk/tests/policy/baseline/gov/welsh_government/childcare_offer/wales_childcare_offer.yaml index ea2e486d61..71d1e84660 100644 --- a/policyengine_uk/tests/policy/baseline/gov/welsh_government/childcare_offer/wales_childcare_offer.yaml +++ b/policyengine_uk/tests/policy/baseline/gov/welsh_government/childcare_offer/wales_childcare_offer.yaml @@ -484,8 +484,8 @@ wales_childcare_offer: 0 # current_education's default formula puts an 18- or 19-year-old in tertiary -# education when nothing is supplied, so a parent of that age -# counts as a parent in education (guidance para 71). Survey data supplies +# education when nothing is supplied, so a parent of that age counts as a +# parent in education (guidance para 71). Survey data supplies # current_education, so this default matters only for household calculations. - name: A 19-year-old lone parent with no education input counts as in education by default period: 2026 diff --git a/policyengine_uk/tests/test_child_and_adult_definitions_properties.py b/policyengine_uk/tests/test_child_and_adult_definitions_properties.py index 24140408c9..bc6ee58fbf 100644 --- a/policyengine_uk/tests/test_child_and_adult_definitions_properties.py +++ b/policyengine_uk/tests/test_child_and_adult_definitions_properties.py @@ -44,6 +44,13 @@ claimant's child either way (the any-age gap). Before/after families share one Simulation to avoid repeated model construction, but occupy distinct households and benefit units. +5. Supplied claimant/partner role vectors, valid or not, are returned + unchanged on any benefit unit. Widening an unflagged member's gap below + the claimant, with the claimant, flags, other members and HBAI adulthood + fixed, never makes a member the presumption excludes the partner. + Identified parents take priority over the presumption; no other presumed + child is a claimant or partner. The two-other-parents exception to the + head being the claimant is kept. Scope: these are tests of the ENCODED statutory limbs, not full entitlement. Education status proxies full-time non-advanced study: hours, employment @@ -249,21 +256,36 @@ def claimants_or_partners(family, any_age_gap=20): # couple instead. Age ties go to the earlier member. any_age_gap=None is # the rule without the any-age gap, kept for the differential test. n = len(family) - adult = [not hbai_dependent_child(p, family) for p in family] - ages = [p["age"] for p in family] - heads = [i for i in range(n) if family[i]["is_benunit_head"] and adult[i]] - adults = [i for i in range(n) if adult[i]] - if not adults: + claimant, adult, presumed = presumption(family, any_age_gap) + if claimant is None: return [False] * n - pool = heads or adults - claimant = max(pool, key=lambda i: (ages[i], -i)) + ages = [p["age"] for p in family] parent = [adult[i] and family[i]["is_parent"] for i in range(n)] other_parents = [i for i in range(n) if parent[i] and i != claimant] if len(other_parents) >= 2 and not parent[claimant]: couple = sorted(other_parents, key=lambda i: (-ages[i], i))[:2] return [i in couple for i in range(n)] + pool = other_parents or [ + i for i in range(n) if adult[i] and i != claimant and not presumed[i] + ] + partner = max(pool, key=lambda i: (ages[i], -i)) if pool else None + return [i == claimant or i == partner for i in range(n)] + + +def presumption(family, any_age_gap=20): + # The reference claimant (None without an HBAI adult), HBAI adulthood and + # the members presumed to be the claimant's child, before identified + # parents take priority. + n = len(family) + adult = [not hbai_dependent_child(p, family) for p in family] + ages = [p["age"] for p in family] + heads = [i for i in range(n) if family[i]["is_benunit_head"] and adult[i]] + adults = [i for i in range(n) if adult[i]] + if not adults: + return None, adult, [False] * n + claimant = max(heads or adults, key=lambda i: (ages[i], -i)) young_child = any(ages[j] < 20 and ages[claimant] - ages[j] >= 16 for j in range(n)) - flag_unexplained = parent[claimant] and not young_child + flag_unexplained = family[claimant]["is_parent"] and not young_child def presumed_child(i): gap = ages[claimant] - ages[i] @@ -271,11 +293,7 @@ def presumed_child(i): return True return (ages[i] < 20 or flag_unexplained) and gap >= 16 - pool = other_parents or [ - i for i in adults if i != claimant and not presumed_child(i) - ] - partner = max(pool, key=lambda i: (ages[i], -i)) if pool else None - return [i == claimant or i == partner for i in range(n)] + return claimant, adult, [presumed_child(i) for i in range(n)] def legacy_child_or_young_person(p, claimant): @@ -992,3 +1010,88 @@ def test_wide_gap_couples_by_head_order_and_with_supplied_roles(): supplied = simulate(families) supplied.set_input("is_claimant_or_partner", YEAR, [True] * 4) np.testing.assert_array_equal(supplied.calculate("is_couple", YEAR), [True] * 2) + + +@PROPERTY_SETTINGS +@given(arbitrary_benefit_units(), st.data()) +def test_supplied_roles_are_returned_unchanged(families, data): + # Any Boolean role vector, valid or not, on any benefit unit: a supplied + # is_claimant_or_partner is an input, so the model never recomputes it. + size = sum(map(len, families)) + roles = data.draw(st.lists(st.booleans(), min_size=size, max_size=size)) + sim = simulate(families) + sim.set_input("is_claimant_or_partner", YEAR, roles) + assert_values(sim, "is_claimant_or_partner", roles) + + +@st.composite +def widened_gap_pairs(draw): + # An adult head (the claimant), flagged or not, an unflagged member aged + # 20+ at least 16 years younger, and any other members. The pair is the + # unit before and after the member is made younger, holding everything + # else fixed. + pairs = [] + for _ in range(draw(st.integers(1, 8))): + claimant_age = draw(st.integers(36, 90)) + family = [ + person(claimant_age, is_parent=draw(st.booleans()), is_benunit_head=True) + ] + for _ in range(draw(st.integers(0, 3))): + family.append( + person( + draw(st.integers(0, claimant_age)), + current_education=draw(st.sampled_from(EDUCATIONS)), + is_in_approved_training=draw(st.booleans()), + is_parent=draw(st.booleans()), + ) + ) + j = draw(st.integers(1, len(family))) + age = draw(st.integers(20, claimant_age - 16)) + family.insert(j, person(age)) + younger = [dict(p) for p in family] + younger[j]["age"] = draw(st.integers(0, age - 1)) + pairs.append((family, younger, j)) + return pairs + + +@PROPERTY_SETTINGS +@given(widened_gap_pairs()) +def test_a_wider_gap_never_makes_a_presumed_child_the_partner(pairs): + # Metamorphic: an unflagged member made younger, so their gap below the + # claimant widens, with the claimant, every flag, every other member and + # everyone's HBAI adulthood fixed. A member the presumption excludes from + # the partner pool stays excluded, so never becomes the partner. The + # two-other-parents exception is not asserted away: it never selects an + # unflagged member either. + cases = [] + for family, younger, j in pairs: + before, after = presumption(family), presumption(younger) + held = before[0] == after[0] == 0 and before[1] == after[1] + if held and before[2][j]: + cases.append((family, younger, j)) + if not cases: + return + combined = [unit for family, younger, _ in cases for unit in (family, younger)] + roles = simulate(combined).calculate("is_claimant_or_partner", YEAR) + offset = 0 + for family, younger, j in cases: + assert presumption(younger)[2][j] + assert not roles[offset + j], family + assert not roles[offset + len(family) + j], younger + offset += 2 * len(family) + + +@PROPERTY_SETTINGS +@given(arbitrary_benefit_units()) +def test_effective_children_and_partners_are_disjoint(families): + # An identified parent (an HBAI adult flagged is_parent) takes priority + # over the presumption. Every other presumed child is an effective child, + # and no effective child is ever the partner (nor the claimant). + roles = simulate(families).calculate("is_claimant_or_partner", YEAR) + offset = 0 + for family in families: + _, adult, presumed = presumption(family) + for i, p in enumerate(family): + effective_child = presumed[i] and not (adult[i] and p["is_parent"]) + assert not (effective_child and roles[offset + i]), family + offset += len(family) diff --git a/policyengine_uk/tests/test_council_tax_reduction_pensioner_earnings_disregard_properties.py b/policyengine_uk/tests/test_council_tax_reduction_pensioner_earnings_disregard_properties.py index c40751e943..dafb5dd601 100644 --- a/policyengine_uk/tests/test_council_tax_reduction_pensioner_earnings_disregard_properties.py +++ b/policyengine_uk/tests/test_council_tax_reduction_pensioner_earnings_disregard_properties.py @@ -370,8 +370,9 @@ def test_housing_benefit_disregard_is_the_statutory_weekly_sum_in_every_year( def test_disabled_working_age_single_gets_the_special_hb_disregard(): # The property above reads special eligibility from the model, so pin it - # here. With no hours the additional sum cannot apply, and tax credits - # have ended by 2026, so the working tax credit route is off. + # here. A disabled working-age single has the disability premium, which + # gives the £20 (SI 2006/213 Sch 4 para 3); with no hours the additional + # sum cannot apply. unit = dict( shape="single", nation="ENGLAND", diff --git a/policyengine_uk/tests/test_salary_sacrifice_ni_measure_properties.py b/policyengine_uk/tests/test_salary_sacrifice_ni_measure_properties.py index 5251df7f06..d737326620 100644 --- a/policyengine_uk/tests/test_salary_sacrifice_ni_measure_properties.py +++ b/policyengine_uk/tests/test_salary_sacrifice_ni_measure_properties.py @@ -13,8 +13,9 @@ the model's own liability variables, so it does not depend on the formulas under test. -Invariants, for any pay, salary sacrifice, age, statutory sick pay and -employer pension contribution: +Invariants, for any pay, salary sacrifice, age, statutory sick pay, employer +pension contribution, full-year Category A director status and apprentice +status: 1. Difference of complete liabilities: the employee measure equals ni_class_1_employee with the excess less ni_class_1_employee diff --git a/policyengine_uk/tests/test_statutory_paternity_pay_income_tax_properties.py b/policyengine_uk/tests/test_statutory_paternity_pay_income_tax_properties.py index e57c68a792..ad32b4fee5 100644 --- a/policyengine_uk/tests/test_statutory_paternity_pay_income_tax_properties.py +++ b/policyengine_uk/tests/test_statutory_paternity_pay_income_tax_properties.py @@ -291,6 +291,9 @@ def test_income_tax_never_falls_as_statutory_paternity_pay_rises(population): ) + results.family_total("CB_HITC", i, LOW) ) + # This margin relies on even-pound base incomes: PAY's + # 388.64 -> 389 pair cannot cross a personal-allowance + # taper-rounding step. assert gain <= family["high"] - family["low"] + TOLERANCE, family diff --git a/policyengine_uk/tests/test_wales_childcare_offer_income_sources.py b/policyengine_uk/tests/test_wales_childcare_offer_income_sources.py index fd3ae03c78..3508c8ffed 100644 --- a/policyengine_uk/tests/test_wales_childcare_offer_income_sources.py +++ b/policyengine_uk/tests/test_wales_childcare_offer_income_sources.py @@ -14,8 +14,8 @@ would just lose its case; the lists are also pinned to the reviewed sources below, so a removal fails here too. Each source is also checked not to add another listed source (through adds or subtracts, at any depth), which would -count it twice. That check does not see a formula that reads another listed -source. +count it twice, and the sources are checked to expand to disjoint variables. +Those checks do not see a formula that reads another listed source. """ from pathlib import Path @@ -156,3 +156,18 @@ def test_each_list_holds_the_reviewed_sources_once(name): def test_no_listed_source_adds_another(entity, source): listed = {other for _, other in CASES} assert not components(source) & listed + + +def test_listed_sources_expand_to_disjoint_variables(): + # Each source's closure is the source and every variable it adds or + # subtracts, at any depth. Overlapping closures would count the shared + # income twice, even where neither source adds the other. + closures = {source: {source} | components(source) for _, source in CASES} + overlaps = { + (first, second): sorted(closures[first] & closures[second]) + for i, first in enumerate(closures) + for second in list(closures)[i + 1 :] + if closures[first] & closures[second] + } + assert len(closures) == 40 + assert not overlaps, overlaps diff --git a/policyengine_uk/utils/pensions.py b/policyengine_uk/utils/pensions.py index 8b5ecb3ff0..be14ef3e7c 100644 --- a/policyengine_uk/utils/pensions.py +++ b/policyengine_uk/utils/pensions.py @@ -27,9 +27,9 @@ def relevant_uk_earnings(person, period, hmrc): """FA 2004 s.189(2) relevant UK earnings. Employment income chargeable to income tax is net taxable earnings - (ITEPA 2003 ss.9(2) and 11): pay and employer-paid taxable SSP/SMP, which - the model holds separately from wages, less the allowable expenses in - s.327(3). s.327(5) also lists the deductions for pension contributions + (ITEPA 2003 ss.9(2) and 11): pay and employer-paid taxable SSP, SMP and + SPP (employment_benefits), which the model holds separately from wages, + less the allowable expenses in s.327(3). s.327(5) also lists the deductions for pension contributions (FA 2004 ss.188-194), but those are the relief that these earnings limit, so they are not taken off; the limit would otherwise depend on itself. Trading income is the profit chargeable under ITTOIA 2005 Part 2, before diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income.py b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income.py index 7d393e0df8..49c6d82786 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income.py @@ -136,7 +136,7 @@ class housing_benefit_applicable_income(Variable): "https://www.legislation.gov.uk/uksi/2006/214/regulation/23", "https://www.legislation.gov.uk/uksi/2006/214/regulation/29", "https://www.legislation.gov.uk/nisr/2006/405/regulation/22", - "https://www.legislation.gov.uk/nisr/2006/405/schedule/6/paragraph/64", + "https://www.legislation.gov.uk/nisr/2009/276/regulation/3/made", "https://www.legislation.gov.uk/nisr/2006/405/schedule/6/paragraph/32", "https://www.legislation.gov.uk/nisr/2006/406/regulation/21", "https://www.legislation.gov.uk/nisr/2006/406/regulation/27", @@ -147,7 +147,6 @@ class housing_benefit_applicable_income(Variable): "https://www.legislation.gov.uk/uksi/2006/213/regulation/40", "https://www.legislation.gov.uk/uksi/2006/213/schedule/5/paragraph/16", "https://www.legislation.gov.uk/uksi/2006/213/schedule/5/paragraph/47", - "https://www.legislation.gov.uk/uksi/2006/214/regulation/29", "https://www.legislation.gov.uk/uksi/2006/214/regulation/26", "https://www.legislation.gov.uk/uksi/2006/214/regulation/27", "https://www.legislation.gov.uk/nisr/2006/406/regulation/24", diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income_disregard.py b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income_disregard.py index 7de5c94a3d..ed00e229ab 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income_disregard.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_applicable_income_disregard.py @@ -42,7 +42,9 @@ class housing_benefit_applicable_income_disregard(Variable): def formula(benunit, period, parameters): p = parameters(period).gov.dwp.housing_benefit.means_test.income_disregard net_earnings = benunit("housing_benefit_net_earnings", period) - # Working age Sch 4 paras 4, 7 and 10; pension age Sch 4 paras 2 and 7. + # Working age Sch 4 paras 3, 4, 7 and 10; pension age Sch 4 paras 2, + # 4, 5 and 7. Other full-amount £20 routes are pre-assessed + # (housing_benefit_special_earnings_disregard_eligible). standard = benunit("housing_benefit_special_earnings_disregard", period) # Working age Sch 4 para 18 (NI Sch 5 para 18), from 5 October 2026: # specified or temporary accommodation. It applies alongside paras 3 diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_special_earnings_disregard_eligible.py b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_special_earnings_disregard_eligible.py index f1735a222f..47569ec0e2 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_special_earnings_disregard_eligible.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/applicable_income/housing_benefit_special_earnings_disregard_eligible.py @@ -3,7 +3,7 @@ class housing_benefit_special_earnings_disregard_eligible(Variable): """Implement the working-age premium route and the modelled pension-age - paragraph 5 qualifying routes. + carer (paragraph 4) and paragraph 5 qualifying routes. GB SI 2006/214 Sch. 4 and NI SR 2006/406 Sch. 5 para. 5(1)(a) list the following receipts implemented here: diff --git a/policyengine_uk/variables/gov/dwp/housing_benefit/non_dep_deduction/household_benefits_individual_non_dep_deduction.py b/policyengine_uk/variables/gov/dwp/housing_benefit/non_dep_deduction/household_benefits_individual_non_dep_deduction.py index 36d14136a9..2bec92df06 100644 --- a/policyengine_uk/variables/gov/dwp/housing_benefit/non_dep_deduction/household_benefits_individual_non_dep_deduction.py +++ b/policyengine_uk/variables/gov/dwp/housing_benefit/non_dep_deduction/household_benefits_individual_non_dep_deduction.py @@ -33,11 +33,12 @@ def formula(person, period, parameters): # instead of an income band. Reg 6(6) excludes income-related # recipients from remunerative work. The gate only lowers a # deduction: a non-worker with no banding income keeps the scale's - # zero, because the exemptions in reg 74(6)-(10) and reg 55(6)-(9) - # (a claimant on Attendance Allowance; a non-dependant on Pension - # Credit, or under 25 on Income Support; a full-time student) are not - # modelled, and the income-related benefits are not banding income, - # so many such non-dependants have none. + # zero, because the exemptions in reg 74(6), (7), (8) and (10), + # and reg 55(6)-(9) (a claimant on Attendance Allowance; a + # non-dependant on Pension Credit, or under 25 on Income Support; + # a full-time student) are not modelled, and the income-related + # benefits are not banding income, so many such non-dependants + # have none. # #2007 adds the exemptions and the flat charge together. deduction = where( in_remunerative_work,