diff --git a/CLAUDE.md b/CLAUDE.md index e4984ad72..d231aa23d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,4 +28,38 @@ - Organized to match the same structure as parameters/ - Comments should include relevant regulatory citations and calculation logic - **tests/**: Test cases for validating correct implementation of policies -- For government departments, use the correct department for the policy (e.g., education policies under DfE, not DWP) \ No newline at end of file +- For government departments, use the correct department for the policy (e.g., education policies under DfE, not DWP) + +## Lessons Learned from Variable Refactoring (2025-05-31) + +### Issue: Refactoring Script Bug +A refactoring script was used to split multi-variable Python files into single-variable files. The script had a systematic bug: when a file contained multiple Variable classes and one had the same name as the file, that variable was dropped entirely. + +### Variables Dropped During Refactoring +21 variables were dropped, including: +- Wrapper variables: `jsa_income`, `esa_income`, `jsa_contrib`, `esa_contrib`, `afcs`, `bsp`, `iidb` +- Calculated variables: `benefit_cap`, `carers_allowance`, `income_support`, `maternity_allowance`, `sda`, `tax_credits` +- Core variables: `child_benefit`, `allowances`, `marriage_allowance`, `stamp_duty_land_tax`, `vat`, `land_transaction_tax`, `attendance_allowance`, `council_tax_benefit`, `business_rates`, `tax`, `total_wealth`, `private_school_vat`, `bi_phaseout` + +### Enum Recovery Errors +During recovery, enums were recreated based on assumptions rather than checking original code, leading to incorrect values: +- **TenureType**: Missing `OWNED_OUTRIGHT` and `OWNED_WITH_MORTGAGE` (consolidated into `OWNER_OCCUPIED`) +- **AccommodationType**: Wrong labels (e.g., "House - detached" vs "Detached house") +- **EducationType**: Wrong capitalization ("Lower secondary" vs "Lower Secondary") +- **FamilyType**: Shortened labels (missing ", with children" suffix) +- **EmploymentStatus**: Complete restructure (missing FT_/PT_ prefixes) +- **MinimumWageCategory**: Wrong format ("18-20" vs "18 to 20", "Over 24" vs "25 or over") +- **CouncilTaxBand**: Added incorrect "Band " prefix +- **StatePensionType**: Wrong capitalization ("Basic" vs "basic") + +### Best Practices for Refactoring Recovery +1. **Always check original code** - Never rely on assumptions or context clues +2. **Use git history systematically** - `git show :path/to/file` to see original content +3. **Verify enum values exactly** - Even small differences in labels can break functionality +4. **Test incrementally** - Run tests after each fix to ensure progress +5. **Document the recovery process** - Track what was fixed for future reference + +### OpenFisca-Specific Patterns +- Use `.possible_values` to access enum values, not direct imports +- Import helper functions like `find_freeze_start` when needed +- Functions like `ceil` should be `np.ceil` in OpenFisca context \ No newline at end of file diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29bb..5179ac3f4 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,5 @@ +- bump: patch + changes: + changed: + - Refactored all Variable files to follow single-responsibility principle with one Variable class per file. + - Split approximately 70 multi-Variable Python files into individual files, improving code organization and maintainability. \ No newline at end of file diff --git a/docs/book/index.ipynb b/docs/book/index.ipynb index d317623a1..a48f3d4a0 100644 --- a/docs/book/index.ipynb +++ b/docs/book/index.ipynb @@ -58,14 +58,16 @@ "\n", "df = sim.calculate_dataframe(\n", " [\n", - " \"household_id\", # If the first variable is household level, the dataframe will project everything to households. Same for people.\n", + " \"household_id\", # If the first variable is household level, the dataframe will project everything to households. Same for people.\n", " \"income_tax\",\n", " \"region\",\n", " ],\n", - " period=2025\n", + " period=2025,\n", ")\n", "\n", - "df.groupby(\"region\").income_tax.sum().sort_values(ascending=False)/1e9 # Weights automatically applied" + "df.groupby(\"region\").income_tax.sum().sort_values(\n", + " ascending=False\n", + ") / 1e9 # Weights automatically applied" ] }, { @@ -115,7 +117,10 @@ "\n", "baseline = Microsimulation(dataset=ENHANCED_FRS)\n", "reformed = Microsimulation(dataset=ENHANCED_FRS, reform=reform)\n", - "revenue = reformed.calculate(\"gov_balance\", 2025).sum() - baseline.calc(\"gov_balance\", 2025).sum()\n", + "revenue = (\n", + " reformed.calculate(\"gov_balance\", 2025).sum()\n", + " - baseline.calc(\"gov_balance\", 2025).sum()\n", + ")\n", "f\"Revenue: £{round(revenue / 1e+9, 1)}bn\"" ] } diff --git a/docs/book/usage/getting-started.ipynb b/docs/book/usage/getting-started.ipynb index 2ecd3856d..5a4b74188 100644 --- a/docs/book/usage/getting-started.ipynb +++ b/docs/book/usage/getting-started.ipynb @@ -175,7 +175,9 @@ "source": [ "from policyengine_uk import Microsimulation\n", "\n", - "sim = Microsimulation(dataset=\"hf://policyengine/policyengine-uk-data/enhanced_frs_2022_23.h5\")\n", + "sim = Microsimulation(\n", + " dataset=\"hf://policyengine/policyengine-uk-data/enhanced_frs_2022_23.h5\"\n", + ")\n", "\n", "# The hf:// points to the private data-\n", "# hf:// <- go get the data from huggingface\n", @@ -211,7 +213,9 @@ "source": [ "ENHANCED_FRS = \"hf://policyengine/policyengine-uk-data/enhanced_frs_2022_23.h5\"\n", "\n", - "baseline = Microsimulation(dataset=ENHANCED_FRS) # Enhanced FRS 2022 by default\n", + "baseline = Microsimulation(\n", + " dataset=ENHANCED_FRS\n", + ") # Enhanced FRS 2022 by default\n", "reformed = Microsimulation(dataset=ENHANCED_FRS, reform=increase_basic_rate)\n", "\n", "revenue = (\n", diff --git a/find_missing_enums.py b/find_missing_enums.py new file mode 100644 index 000000000..506d7b876 --- /dev/null +++ b/find_missing_enums.py @@ -0,0 +1,49 @@ +import os +import re +import ast + + +def find_undefined_names(directory): + """Find all potentially undefined names in Python files.""" + undefined = set() + + for root, dirs, files in os.walk(directory): + for file in files: + if file.endswith(".py"): + filepath = os.path.join(root, file) + try: + with open(filepath, "r") as f: + content = f.read() + + # Look for patterns like "possible_values = SomeName" or "SomeName.VALUE" + enum_refs = re.findall( + r"possible_values\s*=\s*(\w+)", content + ) + enum_refs.extend( + re.findall( + r"(\w+)\.(?:NONE|LOWER|HIGHER|MIDDLE|STANDARD|ENHANCED|MALE|FEMALE|SINGLE|COUPLE)", + content, + ) + ) + + for name in enum_refs: + if name not in [ + "self", + "person", + "household", + "benunit", + "parameters", + ]: + # Check if it's defined in the file + if f"class {name}" not in content: + undefined.add((name, filepath)) + + except Exception as e: + print(f"Error processing {filepath}: {e}") + + return undefined + + +undefined_names = find_undefined_names("policyengine_uk/variables/") +for name, filepath in sorted(undefined_names): + print(f"{name} used in {filepath}") diff --git a/policyengine_uk/parameters/gov/dwp/tax_credits/min_benefit.yaml b/policyengine_uk/parameters/gov/dwp/tax_credits/min_benefit.yaml index f5883eeb2..e80291692 100644 --- a/policyengine_uk/parameters/gov/dwp/tax_credits/min_benefit.yaml +++ b/policyengine_uk/parameters/gov/dwp/tax_credits/min_benefit.yaml @@ -7,8 +7,7 @@ metadata: propagate_metadata_to_children: true reference: - href: https://www.legislation.gov.uk/uksi/2002/2008/regulation/9 - name: The Tax Credits (Income Thresholds and Determination of Rates) Regulations - 2002 + name: The Tax Credits (Income Thresholds and Determination of Rates) Regulations 2002 unit: currency-USD values: 2002-08-01: 26 diff --git a/policyengine_uk/tests/microsimulation/test_validity.py b/policyengine_uk/tests/microsimulation/test_validity.py index f38baf6b3..3da6ae62a 100644 --- a/policyengine_uk/tests/microsimulation/test_validity.py +++ b/policyengine_uk/tests/microsimulation/test_validity.py @@ -1,4 +1,4 @@ -from policyengine import Simulation +from policyengine_uk import Simulation import pytest YEARS = range(2024, 2026) diff --git a/policyengine_uk/variables/contrib/labour/attends_private_school.py b/policyengine_uk/variables/contrib/labour/attends_private_school.py new file mode 100644 index 000000000..88971d517 --- /dev/null +++ b/policyengine_uk/variables/contrib/labour/attends_private_school.py @@ -0,0 +1,81 @@ +from policyengine_uk.model_api import * + + +def interpolate_percentile(param, percentile): + if str(percentile) in param: + return param[str(percentile)] + else: + idx = percentile - (percentile % 5) + p1 = idx + p2 = idx + 5 + v1 = param[str(idx)] + v2 = param[str(idx + 5)] + return v1 + (v2 - v1) * (percentile - p1) / (p2 - p1) + + +class attends_private_school(Variable): + label = "attends private school" + entity = Person + definition_period = YEAR + value_type = bool + + def formula(person, period, parameters): + if not hasattr(person.simulation, "dataset"): + return 0 + household = person.household + # To ensure that our model matches + # total number of students actually enrolled + + ps_vat_params = parameters(period).gov.simulation.private_school_vat + private_school_attendance_rate = ( + ps_vat_params.private_school_attendance_rate + ) + + population_adjustment_factor = ps_vat_params.private_school_factor + + person = household.members + + is_child = person("is_child", period) + + taxes = household.sum( + person("income_tax", period) + person("national_insurance", period) + ) + + net_income = ( + household("household_market_income", period) + + household("household_benefits", period) + - taxes + ) + + household_weight = household("household_weight", period) + weighted_income = MicroSeries(net_income, weights=household_weight) + + if household_weight.sum() < 1e6: + return 0 + + percentile = np.zeros_like(weighted_income).astype(numpy.int64) + mask = household_weight > 0 + + percentile[mask] = ( + weighted_income[mask] + .percentile_rank() + .clip(0, 100) + .values.astype(numpy.int64) + ) + # STUDENT_POPULATION_ADJUSTMENT_FACTOR = 0.78 + STUDENT_POPULATION_ADJUSTMENT_FACTOR = population_adjustment_factor + + p_attends_private_school = ( + np.array( + [ + interpolate_percentile(private_school_attendance_rate, p) + for p in percentile + ] + ) + * STUDENT_POPULATION_ADJUSTMENT_FACTOR + * is_child + ) + + value = random(person) < p_attends_private_school + + return value diff --git a/policyengine_uk/variables/contrib/labour/private_school_vat.py b/policyengine_uk/variables/contrib/labour/private_school_vat.py index 755cfb699..33b641d45 100644 --- a/policyengine_uk/variables/contrib/labour/private_school_vat.py +++ b/policyengine_uk/variables/contrib/labour/private_school_vat.py @@ -1,73 +1,4 @@ from policyengine_uk.model_api import * -from policyengine_uk.variables.gov.hmrc.tax import household_tax - - -class attends_private_school(Variable): - label = "attends private school" - entity = Person - definition_period = YEAR - value_type = bool - - def formula(person, period, parameters): - if not hasattr(person.simulation, "dataset"): - return 0 - household = person.household - # To ensure that our model matches - # total number of students actually enrolled - - ps_vat_params = parameters(period).gov.simulation.private_school_vat - private_school_attendance_rate = ( - ps_vat_params.private_school_attendance_rate - ) - - population_adjustment_factor = ps_vat_params.private_school_factor - - person = household.members - - is_child = person("is_child", period) - - taxes = household.sum( - person("income_tax", period) + person("national_insurance", period) - ) - - net_income = ( - household("household_market_income", period) - + household("household_benefits", period) - - taxes - ) - - household_weight = household("household_weight", period) - weighted_income = MicroSeries(net_income, weights=household_weight) - - if household_weight.sum() < 1e6: - return 0 - - percentile = np.zeros_like(weighted_income).astype(numpy.int64) - mask = household_weight > 0 - - percentile[mask] = ( - weighted_income[mask] - .percentile_rank() - .clip(0, 100) - .values.astype(numpy.int64) - ) - # STUDENT_POPULATION_ADJUSTMENT_FACTOR = 0.78 - STUDENT_POPULATION_ADJUSTMENT_FACTOR = population_adjustment_factor - - p_attends_private_school = ( - np.array( - [ - interpolate_percentile(private_school_attendance_rate, p) - for p in percentile - ] - ) - * STUDENT_POPULATION_ADJUSTMENT_FACTOR - * is_child - ) - - value = random(person) < p_attends_private_school - - return value class private_school_vat(Variable): @@ -94,15 +25,3 @@ def formula(household, period, parameters): * private_school_vat_rate * private_school_vat_basis ) - - -def interpolate_percentile(param, percentile): - if str(percentile) in param: - return param[str(percentile)] - else: - idx = percentile - (percentile % 5) - p1 = idx - p2 = idx + 5 - v1 = param[str(idx)] - v2 = param[str(idx + 5)] - return v1 + (v2 - v1) * (percentile - p1) / (p2 - p1) diff --git a/policyengine_uk/variables/contrib/policyengine/budget_change.py b/policyengine_uk/variables/contrib/policyengine/budget_change.py deleted file mode 100644 index 26a7efffe..000000000 --- a/policyengine_uk/variables/contrib/policyengine/budget_change.py +++ /dev/null @@ -1,378 +0,0 @@ -from policyengine_uk.model_api import * - - -class pre_budget_change_household_benefits(Variable): - value_type = float - entity = Household - label = "household benefits" - documentation = "Total value of benefits received by household" - definition_period = YEAR - unit = GBP - adds = [ - "child_benefit", - "esa_income", - "housing_benefit", - "income_support", - "jsa_income", - "pension_credit", - "universal_credit", - "working_tax_credit", - "child_tax_credit", - "attendance_allowance", - "afcs", - "bsp", - "carers_allowance", - "dla", - "esa_contrib", - "iidb", - "incapacity_benefit", - "jsa_contrib", - "pip", - "sda", - "state_pension", - "maternity_allowance", - "statutory_sick_pay", - "statutory_maternity_pay", - "ssmg", - "basic_income", - "epg_subsidy", - "cost_of_living_support_payment", - "energy_bills_rebate", - ] - - def formula(household, period, parameters): - contrib = parameters(period).gov.contrib - uprating = contrib.benefit_uprating - benefits = pre_budget_change_household_benefits.adds - if contrib.abolish_council_tax: - benefits = [ - benefit - for benefit in benefits - if benefit != "council_tax_benefit" - ] - general_benefits = add( - household, - period, - [ - benefit - for benefit in benefits - if benefit not in ["basic_income"] - ], - ) - non_sp_benefits = add( - household, - period, - [ - benefit - for benefit in benefits - if benefit not in ["state_pension", "basic_income"] - ], - ) - return ( - add(household, period, benefits) - + general_benefits * uprating.all - + non_sp_benefits * uprating.non_sp - ) - - -class pre_budget_change_household_tax(Variable): - value_type = float - entity = Household - label = "household taxes" - documentation = "Total taxes owed by the household" - definition_period = YEAR - unit = GBP - adds = [ - "expected_sdlt", - "expected_ltt", - "expected_lbtt", - "corporate_sdlt", - "business_rates", - "council_tax", - "domestic_rates", - "fuel_duty", - "tv_licence", - "wealth_tax", - "non_primary_residence_wealth_tax", - "income_tax", - "national_insurance", - "LVT", - "carbon_tax", - "vat_change", - "capital_gains_tax", - ] - - def formula(household, period, parameters): - if parameters(period).gov.contrib.abolish_council_tax: - return add( - household, - period, - [ - tax - for tax in pre_budget_change_household_tax.adds - if tax not in ["council_tax"] - ], - ) - else: - return add(household, period, pre_budget_change_household_tax.adds) - - -class pre_budget_change_household_net_income(Variable): - label = "household net income" - documentation = "household net income" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - adds = ["household_market_income", "pre_budget_change_household_benefits"] - subtracts = ["pre_budget_change_household_tax"] - - -class pre_budget_change_ons_household_income_decile(Variable): - label = "pre-budget change household income decile (ONS matched)" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - income = household("pre_budget_change_household_net_income", period) - equivalisation = household("household_equivalisation_bhc", period) - if household.simulation.dataset is not None: - household_weight = household("household_weight", period) - weighted_income = MicroSeries( - income / equivalisation, weights=household_weight - ) - decile = weighted_income.decile_rank().values - else: - upper_bounds = [ - 16000.0, - 20700.0, - 24100.0, - 27200.0, - 31800.0, - 37200.0, - 45200.0, - 53300.0, - 68500.0, - np.inf, - ] - - equivalised_income = income / equivalisation - decile = np.select( - [equivalised_income <= upper_bounds[i] for i in range(10)], - list(range(1, 11)), - ) - # Set negatives to -1. - # This avoids the bottom decile summing to a negative number, - # which would flip the % change in the interface. - return where(income < 0, -1, decile) - - -class nhs_budget_change(Variable): - label = "NHS budget change" - entity = Household - definition_period = YEAR - value_type = float - - def formula(household, period, parameters): - budget_increase = ( - parameters(period).gov.contrib.policyengine.budget.nhs * 1e9 - ) - if budget_increase == 0: - return 0 - decile = household( - "pre_budget_change_ons_household_income_decile", period - ) - weight = household("household_weight", period) - DECILE_INCIDENCE = { - 1: 0.095, - 2: 0.118, - 3: 0.126, - 4: 0.117, - 5: 0.104, - 6: 0.104, - 7: 0.092, - 8: 0.089, - 9: 0.085, - 10: 0.07, - } - - decile = pd.Series(decile) - - budget_increase_per_decile = { - i: budget_increase * DECILE_INCIDENCE[i] for i in range(1, 11) - } - if household.simulation.dataset is not None: - households_per_decile = ( - pd.Series(weight).groupby(decile).sum().to_dict() - ) - else: - households_per_decile = {i: 28e5 for i in range(1, 11)} - - average_per_decile = { - i: budget_increase_per_decile[i] / households_per_decile[i] - for i in range(1, 11) - } - - return decile.map(average_per_decile).replace({np.nan: 0}) - - -class education_budget_change(Variable): - label = "education budget change" - entity = Household - definition_period = YEAR - value_type = float - - def formula(household, period, parameters): - - budget_increase = ( - parameters(period).gov.contrib.policyengine.budget.education * 1e9 - ) - if budget_increase == 0: - return 0 - decile = household( - "pre_budget_change_ons_household_income_decile", period - ) - weight = household("household_weight", period) - DECILE_INCIDENCE = { - 1: 0.114, - 2: 0.146, - 3: 0.122, - 4: 0.125, - 5: 0.119, - 6: 0.085, - 7: 0.088, - 8: 0.076, - 9: 0.077, - 10: 0.046, - } - - decile = pd.Series(decile) - budget_increase_per_decile = { - i: budget_increase * DECILE_INCIDENCE[i] for i in range(1, 11) - } - if household.simulation.dataset is not None: - households_per_decile = ( - pd.Series(weight).groupby(decile).sum().to_dict() - ) - else: - households_per_decile = {i: 28e5 for i in range(1, 11)} - - average_per_decile = { - i: budget_increase_per_decile[i] / households_per_decile[i] - for i in range(1, 11) - } - - return decile.map(average_per_decile).replace({np.nan: 0}) - - -class other_public_spending_budget_change(Variable): - label = "non- budget change" - entity = Household - definition_period = YEAR - value_type = float - - def formula(household, period, parameters): - - budget_increase = ( - parameters( - period - ).gov.contrib.policyengine.budget.other_public_spending - * 1e9 - ) - if budget_increase == 0: - return 0 - decile = household( - "pre_budget_change_ons_household_income_decile", period - ) - weight = household("household_weight", period) - DECILE_INCIDENCE = { - 1: 0.114, - 2: 0.146, - 3: 0.122, - 4: 0.125, - 5: 0.119, - 6: 0.085, - 7: 0.088, - 8: 0.076, - 9: 0.077, - 10: 0.046, - } - - decile = pd.Series(decile) - budget_increase_per_decile = { - i: budget_increase * DECILE_INCIDENCE[i] for i in range(1, 11) - } - if household.simulation.dataset is not None: - households_per_decile = ( - pd.Series(weight).groupby(decile).sum().to_dict() - ) - else: - households_per_decile = {i: 28e5 for i in range(1, 11)} - - average_per_decile = { - i: budget_increase_per_decile[i] / households_per_decile[i] - for i in range(1, 11) - } - - return decile.map(average_per_decile).replace({np.nan: 0}) - - -class corporate_incident_tax_revenue_change(Variable): - label = "corporate-incident tax revenue change" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - share = household("shareholding", period) - revenue_change = parameters( - period - ).gov.contrib.policyengine.budget.corporate_incident_tax_change - return revenue_change * share * 1e9 - - -class high_income_incident_tax_change(Variable): - label = "high income-incident tax revenue change" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - if household.simulation.dataset is None: - return 0 - - total_income = household.members("total_income", period) - high_income = household.sum(max_(total_income - 100e3, 0)) - weight = household("household_weight", period) - share = high_income / (high_income * weight).sum() - revenue_change = parameters( - period - ).gov.contrib.policyengine.budget.high_income_incident_tax_change - return revenue_change * 1e9 * share - - -class consumer_incident_tax_revenue_change(Variable): - label = "consumer-incident tax revenue change" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - consumption = household("consumption", period) - if ( - household.simulation.dataset is not None - and household("consumption", period).sum() != 0 - ): - weight = household("household_weight", period) - share = consumption / (consumption * weight).sum() - else: - share = consumption / 846e9 - revenue_change = parameters( - period - ).gov.contrib.policyengine.budget.consumer_incident_tax_change - return revenue_change * share * 1e9 diff --git a/policyengine_uk/variables/contrib/policyengine/consumer_incident_tax_revenue_change.py b/policyengine_uk/variables/contrib/policyengine/consumer_incident_tax_revenue_change.py new file mode 100644 index 000000000..59c34668b --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/consumer_incident_tax_revenue_change.py @@ -0,0 +1,24 @@ +from policyengine_uk.model_api import * + + +class consumer_incident_tax_revenue_change(Variable): + label = "consumer-incident tax revenue change" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + consumption = household("consumption", period) + if ( + household.simulation.dataset is not None + and household("consumption", period).sum() != 0 + ): + weight = household("household_weight", period) + share = consumption / (consumption * weight).sum() + else: + share = consumption / 846e9 + revenue_change = parameters( + period + ).gov.contrib.policyengine.budget.consumer_incident_tax_change + return revenue_change * share * 1e9 diff --git a/policyengine_uk/variables/contrib/policyengine/corporate_incident_tax_revenue_change.py b/policyengine_uk/variables/contrib/policyengine/corporate_incident_tax_revenue_change.py new file mode 100644 index 000000000..a2bced148 --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/corporate_incident_tax_revenue_change.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class corporate_incident_tax_revenue_change(Variable): + label = "corporate-incident tax revenue change" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + share = household("shareholding", period) + revenue_change = parameters( + period + ).gov.contrib.policyengine.budget.corporate_incident_tax_change + return revenue_change * share * 1e9 diff --git a/policyengine_uk/variables/contrib/policyengine/education_budget_change.py b/policyengine_uk/variables/contrib/policyengine/education_budget_change.py new file mode 100644 index 000000000..d1e3c28a1 --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/education_budget_change.py @@ -0,0 +1,50 @@ +from policyengine_uk.model_api import * + + +class education_budget_change(Variable): + label = "education budget change" + entity = Household + definition_period = YEAR + value_type = float + + def formula(household, period, parameters): + + budget_increase = ( + parameters(period).gov.contrib.policyengine.budget.education * 1e9 + ) + if budget_increase == 0: + return 0 + decile = household( + "pre_budget_change_ons_household_income_decile", period + ) + weight = household("household_weight", period) + DECILE_INCIDENCE = { + 1: 0.114, + 2: 0.146, + 3: 0.122, + 4: 0.125, + 5: 0.119, + 6: 0.085, + 7: 0.088, + 8: 0.076, + 9: 0.077, + 10: 0.046, + } + + decile = pd.Series(decile) + budget_increase_per_decile = { + i: budget_increase * DECILE_INCIDENCE[i] for i in range(1, 11) + } + if household.simulation.dataset is not None: + households_per_decile = ( + pd.Series(weight).groupby(decile).sum().to_dict() + ) + else: + households_per_decile = {i: 28e5 for i in range(1, 11)} + + average_per_decile = { + i: budget_increase_per_decile[i] / households_per_decile[i] + for i in range(1, 11) + } + + return decile.map(average_per_decile).replace({np.nan: 0}) diff --git a/policyengine_uk/variables/contrib/policyengine/employer_ni/adjusted_employer_cost.py b/policyengine_uk/variables/contrib/policyengine/employer_ni/adjusted_employer_cost.py new file mode 100644 index 000000000..50638e40d --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/employer_ni/adjusted_employer_cost.py @@ -0,0 +1,46 @@ +from policyengine_uk.model_api import * + + +class adjusted_employer_cost(Variable): + label = "employer cost" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + employment_income = person("employment_income", period) + benefits = add( + person, + period, + [ + "household_statutory_sick_pay", + "household_statutory_maternity_pay", + "household_statutory_paternity_pay", + ], + ) + employer_pension_contributions = person( + "employer_pension_contributions", period + ) + ni_class_1_income = ( + employment_income + benefits + employer_pension_contributions + ) + + # Calculate employer cost + parameters = parameters(period) + class_1 = parameters.gov.hmrc.national_insurance.class_1 + r_r = class_1.rates.employer + t_r = class_1.thresholds.secondary_threshold * WEEKS_IN_YEAR + p_r = ( + parameters.gov.contrib.policyengine.employer_ni.exempt_employer_pension_contributions + ) + pen_con_subtracted_r = employer_pension_contributions + if p_r: + pen_con_subtracted_r = employer_pension_contributions + else: + pen_con_subtracted_r = 0 + + employer_ni = r_r * max_( + 0, ni_class_1_income - pen_con_subtracted_r - t_r + ) + return ni_class_1_income + employer_ni diff --git a/policyengine_uk/variables/contrib/policyengine/employer_ni/baseline_employer_cost.py b/policyengine_uk/variables/contrib/policyengine/employer_ni/baseline_employer_cost.py new file mode 100644 index 000000000..bca502fa5 --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/employer_ni/baseline_employer_cost.py @@ -0,0 +1,56 @@ +from policyengine_uk.model_api import * + + +class baseline_employer_cost(Variable): + label = "baseline employer cost" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + prior_employment_income = person( + "employment_income_before_lsr", period + ) + employment_income_behavioral_response = person( + "employment_income_behavioral_response", period + ) + benefits = add( + person, + period, + [ + "household_statutory_sick_pay", + "household_statutory_maternity_pay", + "household_statutory_paternity_pay", + ], + ) + employer_pension_contributions = person( + "employer_pension_contributions", period + ) + ni_class_1_income = ( + prior_employment_income + + employment_income_behavioral_response + + benefits + + employer_pension_contributions + ) + + # Calculate baseline employer cost + baseline_parameters = parameters(period).baseline + baseline_class_1 = ( + baseline_parameters.gov.hmrc.national_insurance.class_1 + ) + r_b = baseline_class_1.rates.employer + t_b = baseline_class_1.thresholds.secondary_threshold * WEEKS_IN_YEAR + p_b = ( + baseline_parameters.gov.contrib.policyengine.employer_ni.exempt_employer_pension_contributions + ) + pen_con_subtracted_b = employer_pension_contributions + if p_b: + pen_con_subtracted_b = employer_pension_contributions + else: + pen_con_subtracted_b = 0 + + baseline_employer_ni = r_b * max_( + 0, ni_class_1_income - pen_con_subtracted_b - t_b + ) + return ni_class_1_income + baseline_employer_ni diff --git a/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_cost.py b/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_cost.py new file mode 100644 index 000000000..cc1e7fcae --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_cost.py @@ -0,0 +1,26 @@ +from policyengine_uk.model_api import * + + +class employer_cost(Variable): + label = "employer cost" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + benefits = add( + person, + period, + [ + "household_statutory_sick_pay", + "household_statutory_maternity_pay", + "household_statutory_paternity_pay", + ], + ) + return ( + person("employment_income", period) + + person("ni_employer", period) + + person("employer_pension_contributions", period) + + benefits + ) diff --git a/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_fixed_employer_cost_change.py b/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_fixed_employer_cost_change.py index bc10a0236..02370bb78 100644 --- a/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_fixed_employer_cost_change.py +++ b/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_fixed_employer_cost_change.py @@ -1,219 +1,6 @@ from policyengine_uk.model_api import * -class employer_cost(Variable): - label = "employer cost" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - def formula(person, period, parameters): - benefits = add( - person, - period, - [ - "household_statutory_sick_pay", - "household_statutory_maternity_pay", - "household_statutory_paternity_pay", - ], - ) - return ( - person("employment_income", period) - + person("ni_employer", period) - + person("employer_pension_contributions", period) - + benefits - ) - - -class baseline_employer_cost(Variable): - label = "baseline employer cost" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - def formula(person, period, parameters): - prior_employment_income = person( - "employment_income_before_lsr", period - ) - employment_income_behavioral_response = person( - "employment_income_behavioral_response", period - ) - benefits = add( - person, - period, - [ - "household_statutory_sick_pay", - "household_statutory_maternity_pay", - "household_statutory_paternity_pay", - ], - ) - employer_pension_contributions = person( - "employer_pension_contributions", period - ) - ni_class_1_income = ( - prior_employment_income - + employment_income_behavioral_response - + benefits - + employer_pension_contributions - ) - - # Calculate baseline employer cost - baseline_parameters = parameters(period).baseline - baseline_class_1 = ( - baseline_parameters.gov.hmrc.national_insurance.class_1 - ) - r_b = baseline_class_1.rates.employer - t_b = baseline_class_1.thresholds.secondary_threshold * WEEKS_IN_YEAR - p_b = ( - baseline_parameters.gov.contrib.policyengine.employer_ni.exempt_employer_pension_contributions - ) - pen_con_subtracted_b = employer_pension_contributions - if p_b: - pen_con_subtracted_b = employer_pension_contributions - else: - pen_con_subtracted_b = 0 - - baseline_employer_ni = r_b * max_( - 0, ni_class_1_income - pen_con_subtracted_b - t_b - ) - return ni_class_1_income + baseline_employer_ni - - -class adjusted_employer_cost(Variable): - label = "employer cost" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - def formula(person, period, parameters): - employment_income = person("employment_income", period) - benefits = add( - person, - period, - [ - "household_statutory_sick_pay", - "household_statutory_maternity_pay", - "household_statutory_paternity_pay", - ], - ) - employer_pension_contributions = person( - "employer_pension_contributions", period - ) - ni_class_1_income = ( - employment_income + benefits + employer_pension_contributions - ) - - # Calculate employer cost - parameters = parameters(period) - class_1 = parameters.gov.hmrc.national_insurance.class_1 - r_r = class_1.rates.employer - t_r = class_1.thresholds.secondary_threshold * WEEKS_IN_YEAR - p_r = ( - parameters.gov.contrib.policyengine.employer_ni.exempt_employer_pension_contributions - ) - pen_con_subtracted_r = employer_pension_contributions - if p_r: - pen_con_subtracted_r = employer_pension_contributions - else: - pen_con_subtracted_r = 0 - - employer_ni = r_r * max_( - 0, ni_class_1_income - pen_con_subtracted_r - t_r - ) - return ni_class_1_income + employer_ni - - -class employer_ni_response_consumer_incidence(Variable): - label = "price response to employer NI reform" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - def formula(person, period, parameters): - emp_ni = parameters(period).gov.contrib.policyengine.employer_ni - if emp_ni.consumer_incidence == 0 or emp_ni.employee_incidence == 1: - # If consumer incidence is zero, or if the employee incidence is 100%, then there is no capital incidence.: - return 0 - - if not hasattr(person.simulation, "dataset"): - # In single-household simulations, we can't automatically put revenue into price increases because we don't know the revenue. - return 0 - - person_weight = person("person_weight", period) - baseline_employer_cost = person("baseline_employer_cost", period) - employer_cost = person("adjusted_employer_cost", period) - change_in_employer_cost = employer_cost - baseline_employer_cost - amount_paid_by_employers = ( - person_weight * change_in_employer_cost - ).sum() - - consumption = ( - person.household("consumption", period) - / person.household.nb_persons() - ) - total_consumption = (consumption * person_weight).sum() - share_of_total_consumption = consumption / total_consumption - - value = ( - amount_paid_by_employers - * share_of_total_consumption - * consumer_incidence - ) - - if total_consumption == 0: - return 0 - - return value - - -class employer_ni_response_capital_incidence(Variable): - label = "capital response to employer NI reform" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - def formula(person, period, parameters): - emp_ni = parameters(period).gov.contrib.policyengine.employer_ni - if emp_ni.capital_incidence == 0 or emp_ni.employee_incidence == 1: - # If capital incidence is zero, or if the employee incidence is 100%, then there is no capital incidence.: - return 0 - - if not hasattr(person.simulation, "dataset"): - # In single-household simulations, we can't automatically put revenue into price increases because we don't know the revenue. - return 0 - - person_weight = person("person_weight", period) - baseline_employer_cost = person("baseline_employer_cost", period) - employer_cost = person("adjusted_employer_cost", period) - change_in_employer_cost = employer_cost - baseline_employer_cost - amount_paid_by_employers = ( - person_weight * change_in_employer_cost - ).sum() - - wealth = ( - person.household("corporate_wealth", period) - / person.household.nb_persons() - ) - total_wealth = (wealth * person_weight).sum() - share_of_total_wealth = wealth / total_wealth - - value = ( - amount_paid_by_employers - * share_of_total_wealth - * capital_incidence - ) - - if total_wealth == 0: - return 0 - - return value - - class employer_ni_fixed_employer_cost_change(Variable): label = "employer NI reform incidence" entity = Person diff --git a/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_response_capital_incidence.py b/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_response_capital_incidence.py new file mode 100644 index 000000000..5bfba9b83 --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_response_capital_incidence.py @@ -0,0 +1,45 @@ +from policyengine_uk.model_api import * + + +class employer_ni_response_capital_incidence(Variable): + label = "capital response to employer NI reform" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + emp_ni = parameters(period).gov.contrib.policyengine.employer_ni + if emp_ni.capital_incidence == 0 or emp_ni.employee_incidence == 1: + # If capital incidence is zero, or if the employee incidence is 100%, then there is no capital incidence.: + return 0 + + if not hasattr(person.simulation, "dataset"): + # In single-household simulations, we can't automatically put revenue into price increases because we don't know the revenue. + return 0 + + person_weight = person("person_weight", period) + baseline_employer_cost = person("baseline_employer_cost", period) + employer_cost = person("adjusted_employer_cost", period) + change_in_employer_cost = employer_cost - baseline_employer_cost + amount_paid_by_employers = ( + person_weight * change_in_employer_cost + ).sum() + + wealth = ( + person.household("corporate_wealth", period) + / person.household.nb_persons() + ) + total_wealth = (wealth * person_weight).sum() + share_of_total_wealth = wealth / total_wealth + + value = ( + amount_paid_by_employers + * share_of_total_wealth + * capital_incidence + ) + + if total_wealth == 0: + return 0 + + return value diff --git a/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_response_consumer_incidence.py b/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_response_consumer_incidence.py new file mode 100644 index 000000000..212973f35 --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/employer_ni/employer_ni_response_consumer_incidence.py @@ -0,0 +1,45 @@ +from policyengine_uk.model_api import * + + +class employer_ni_response_consumer_incidence(Variable): + label = "price response to employer NI reform" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + emp_ni = parameters(period).gov.contrib.policyengine.employer_ni + if emp_ni.consumer_incidence == 0 or emp_ni.employee_incidence == 1: + # If consumer incidence is zero, or if the employee incidence is 100%, then there is no capital incidence.: + return 0 + + if not hasattr(person.simulation, "dataset"): + # In single-household simulations, we can't automatically put revenue into price increases because we don't know the revenue. + return 0 + + person_weight = person("person_weight", period) + baseline_employer_cost = person("baseline_employer_cost", period) + employer_cost = person("adjusted_employer_cost", period) + change_in_employer_cost = employer_cost - baseline_employer_cost + amount_paid_by_employers = ( + person_weight * change_in_employer_cost + ).sum() + + consumption = ( + person.household("consumption", period) + / person.household.nb_persons() + ) + total_consumption = (consumption * person_weight).sum() + share_of_total_consumption = consumption / total_consumption + + value = ( + amount_paid_by_employers + * share_of_total_consumption + * consumer_incidence + ) + + if total_consumption == 0: + return 0 + + return value diff --git a/policyengine_uk/variables/contrib/policyengine/high_income_incident_tax_change.py b/policyengine_uk/variables/contrib/policyengine/high_income_incident_tax_change.py new file mode 100644 index 000000000..de7cbe3bc --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/high_income_incident_tax_change.py @@ -0,0 +1,22 @@ +from policyengine_uk.model_api import * + + +class high_income_incident_tax_change(Variable): + label = "high income-incident tax revenue change" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + if household.simulation.dataset is None: + return 0 + + total_income = household.members("total_income", period) + high_income = household.sum(max_(total_income - 100e3, 0)) + weight = household("household_weight", period) + share = high_income / (high_income * weight).sum() + revenue_change = parameters( + period + ).gov.contrib.policyengine.budget.high_income_incident_tax_change + return revenue_change * 1e9 * share diff --git a/policyengine_uk/variables/contrib/policyengine/nhs_budget_change.py b/policyengine_uk/variables/contrib/policyengine/nhs_budget_change.py new file mode 100644 index 000000000..7899add8a --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/nhs_budget_change.py @@ -0,0 +1,50 @@ +from policyengine_uk.model_api import * + + +class nhs_budget_change(Variable): + label = "NHS budget change" + entity = Household + definition_period = YEAR + value_type = float + + def formula(household, period, parameters): + budget_increase = ( + parameters(period).gov.contrib.policyengine.budget.nhs * 1e9 + ) + if budget_increase == 0: + return 0 + decile = household( + "pre_budget_change_ons_household_income_decile", period + ) + weight = household("household_weight", period) + DECILE_INCIDENCE = { + 1: 0.095, + 2: 0.118, + 3: 0.126, + 4: 0.117, + 5: 0.104, + 6: 0.104, + 7: 0.092, + 8: 0.089, + 9: 0.085, + 10: 0.07, + } + + decile = pd.Series(decile) + + budget_increase_per_decile = { + i: budget_increase * DECILE_INCIDENCE[i] for i in range(1, 11) + } + if household.simulation.dataset is not None: + households_per_decile = ( + pd.Series(weight).groupby(decile).sum().to_dict() + ) + else: + households_per_decile = {i: 28e5 for i in range(1, 11)} + + average_per_decile = { + i: budget_increase_per_decile[i] / households_per_decile[i] + for i in range(1, 11) + } + + return decile.map(average_per_decile).replace({np.nan: 0}) diff --git a/policyengine_uk/variables/contrib/policyengine/other_public_spending_budget_change.py b/policyengine_uk/variables/contrib/policyengine/other_public_spending_budget_change.py new file mode 100644 index 000000000..b6f6649c8 --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/other_public_spending_budget_change.py @@ -0,0 +1,53 @@ +from policyengine_uk.model_api import * + + +class other_public_spending_budget_change(Variable): + label = "non- budget change" + entity = Household + definition_period = YEAR + value_type = float + + def formula(household, period, parameters): + + budget_increase = ( + parameters( + period + ).gov.contrib.policyengine.budget.other_public_spending + * 1e9 + ) + if budget_increase == 0: + return 0 + decile = household( + "pre_budget_change_ons_household_income_decile", period + ) + weight = household("household_weight", period) + DECILE_INCIDENCE = { + 1: 0.114, + 2: 0.146, + 3: 0.122, + 4: 0.125, + 5: 0.119, + 6: 0.085, + 7: 0.088, + 8: 0.076, + 9: 0.077, + 10: 0.046, + } + + decile = pd.Series(decile) + budget_increase_per_decile = { + i: budget_increase * DECILE_INCIDENCE[i] for i in range(1, 11) + } + if household.simulation.dataset is not None: + households_per_decile = ( + pd.Series(weight).groupby(decile).sum().to_dict() + ) + else: + households_per_decile = {i: 28e5 for i in range(1, 11)} + + average_per_decile = { + i: budget_increase_per_decile[i] / households_per_decile[i] + for i in range(1, 11) + } + + return decile.map(average_per_decile).replace({np.nan: 0}) diff --git a/policyengine_uk/variables/contrib/policyengine/pre_budget_change_household_benefits.py b/policyengine_uk/variables/contrib/policyengine/pre_budget_change_household_benefits.py new file mode 100644 index 000000000..56f03f58e --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/pre_budget_change_household_benefits.py @@ -0,0 +1,75 @@ +from policyengine_uk.model_api import * + + +class pre_budget_change_household_benefits(Variable): + value_type = float + entity = Household + label = "household benefits" + documentation = "Total value of benefits received by household" + definition_period = YEAR + unit = GBP + adds = [ + "child_benefit", + "esa_income", + "housing_benefit", + "income_support", + "jsa_income", + "pension_credit", + "universal_credit", + "working_tax_credit", + "child_tax_credit", + "attendance_allowance", + "afcs", + "bsp", + "carers_allowance", + "dla", + "esa_contrib", + "iidb", + "incapacity_benefit", + "jsa_contrib", + "pip", + "sda", + "state_pension", + "maternity_allowance", + "statutory_sick_pay", + "statutory_maternity_pay", + "ssmg", + "basic_income", + "epg_subsidy", + "cost_of_living_support_payment", + "energy_bills_rebate", + ] + + def formula(household, period, parameters): + contrib = parameters(period).gov.contrib + uprating = contrib.benefit_uprating + benefits = pre_budget_change_household_benefits.adds + if contrib.abolish_council_tax: + benefits = [ + benefit + for benefit in benefits + if benefit != "council_tax_benefit" + ] + general_benefits = add( + household, + period, + [ + benefit + for benefit in benefits + if benefit not in ["basic_income"] + ], + ) + non_sp_benefits = add( + household, + period, + [ + benefit + for benefit in benefits + if benefit not in ["state_pension", "basic_income"] + ], + ) + return ( + add(household, period, benefits) + + general_benefits * uprating.all + + non_sp_benefits * uprating.non_sp + ) diff --git a/policyengine_uk/variables/contrib/policyengine/pre_budget_change_household_net_income.py b/policyengine_uk/variables/contrib/policyengine/pre_budget_change_household_net_income.py new file mode 100644 index 000000000..fa941047b --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/pre_budget_change_household_net_income.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class pre_budget_change_household_net_income(Variable): + label = "household net income" + documentation = "household net income" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + adds = ["household_market_income", "pre_budget_change_household_benefits"] + subtracts = ["pre_budget_change_household_tax"] diff --git a/policyengine_uk/variables/contrib/policyengine/pre_budget_change_household_tax.py b/policyengine_uk/variables/contrib/policyengine/pre_budget_change_household_tax.py new file mode 100644 index 000000000..76195ace1 --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/pre_budget_change_household_tax.py @@ -0,0 +1,43 @@ +from policyengine_uk.model_api import * + + +class pre_budget_change_household_tax(Variable): + value_type = float + entity = Household + label = "household taxes" + documentation = "Total taxes owed by the household" + definition_period = YEAR + unit = GBP + adds = [ + "expected_sdlt", + "expected_ltt", + "expected_lbtt", + "corporate_sdlt", + "business_rates", + "council_tax", + "domestic_rates", + "fuel_duty", + "tv_licence", + "wealth_tax", + "non_primary_residence_wealth_tax", + "income_tax", + "national_insurance", + "LVT", + "carbon_tax", + "vat_change", + "capital_gains_tax", + ] + + def formula(household, period, parameters): + if parameters(period).gov.contrib.abolish_council_tax: + return add( + household, + period, + [ + tax + for tax in pre_budget_change_household_tax.adds + if tax not in ["council_tax"] + ], + ) + else: + return add(household, period, pre_budget_change_household_tax.adds) diff --git a/policyengine_uk/variables/contrib/policyengine/pre_budget_change_ons_household_income_decile.py b/policyengine_uk/variables/contrib/policyengine/pre_budget_change_ons_household_income_decile.py new file mode 100644 index 000000000..f3a96d489 --- /dev/null +++ b/policyengine_uk/variables/contrib/policyengine/pre_budget_change_ons_household_income_decile.py @@ -0,0 +1,42 @@ +from policyengine_uk.model_api import * + + +class pre_budget_change_ons_household_income_decile(Variable): + label = "pre-budget change household income decile (ONS matched)" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + income = household("pre_budget_change_household_net_income", period) + equivalisation = household("household_equivalisation_bhc", period) + if household.simulation.dataset is not None: + household_weight = household("household_weight", period) + weighted_income = MicroSeries( + income / equivalisation, weights=household_weight + ) + decile = weighted_income.decile_rank().values + else: + upper_bounds = [ + 16000.0, + 20700.0, + 24100.0, + 27200.0, + 31800.0, + 37200.0, + 45200.0, + 53300.0, + 68500.0, + np.inf, + ] + + equivalised_income = income / equivalisation + decile = np.select( + [equivalised_income <= upper_bounds[i] for i in range(10)], + list(range(1, 11)), + ) + # Set negatives to -1. + # This avoids the bottom decile summing to a negative number, + # which would flip the % change in the interface. + return where(income < 0, -1, decile) diff --git a/policyengine_uk/variables/contrib/ubi_center/basic_income/bi_household_phaseout.py b/policyengine_uk/variables/contrib/ubi_center/basic_income/bi_household_phaseout.py new file mode 100644 index 000000000..b6df7e5ba --- /dev/null +++ b/policyengine_uk/variables/contrib/ubi_center/basic_income/bi_household_phaseout.py @@ -0,0 +1,39 @@ +from policyengine_uk.model_api import * +import warnings + + +class bi_household_phaseout(Variable): + label = "Basic income phase-out (household)" + documentation = ( + "Reduction in basic income from household-level phase-outs." + ) + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + income = person("total_income", period) + household = person.household + household_income = household.sum(income) + bi = parameters(period).gov.contrib.ubi_center.basic_income + remaining_bi = person("bi_maximum", period) - person( + "bi_individual_phaseout", period + ) # Basic income remaining after individual-level phaseouts + + household_bi = household.sum(remaining_bi) + income_over_threshold = max_( + household_income - bi.phase_out.household.threshold, + 0, + ) + uncapped_deduction = ( + bi.phase_out.household.rate * income_over_threshold + ) + capped_deduction = min_(household_bi, uncapped_deduction) + + warnings.filterwarnings("ignore") + + percent_reduction = where( + household_bi > 0, capped_deduction / household_bi, 0 + ) + return percent_reduction * remaining_bi diff --git a/policyengine_uk/variables/contrib/ubi_center/basic_income/bi_individual_phaseout.py b/policyengine_uk/variables/contrib/ubi_center/basic_income/bi_individual_phaseout.py new file mode 100644 index 000000000..2e974dbf5 --- /dev/null +++ b/policyengine_uk/variables/contrib/ubi_center/basic_income/bi_individual_phaseout.py @@ -0,0 +1,25 @@ +from policyengine_uk.model_api import * +import warnings + + +class bi_individual_phaseout(Variable): + label = "Basic income phase-out (individual)" + documentation = ( + "Reduction in basic income from individual-level phase-outs." + ) + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + income = person("total_income", period) + bi = parameters(period).gov.contrib.ubi_center.basic_income + max_bi = person("bi_maximum", period) + income_over_threshold = max_( + income - bi.phase_out.individual.threshold, 0 + ) + uncapped_deduction = ( + bi.phase_out.individual.rate * income_over_threshold + ) + return min_(max_bi, uncapped_deduction) diff --git a/policyengine_uk/variables/contrib/ubi_center/basic_income/bi_phaseout.py b/policyengine_uk/variables/contrib/ubi_center/basic_income/bi_phaseout.py index 84f172bc9..9e8032e9c 100644 --- a/policyengine_uk/variables/contrib/ubi_center/basic_income/bi_phaseout.py +++ b/policyengine_uk/variables/contrib/ubi_center/basic_income/bi_phaseout.py @@ -1,65 +1,4 @@ from policyengine_uk.model_api import * -import warnings - - -class bi_individual_phaseout(Variable): - label = "Basic income phase-out (individual)" - documentation = ( - "Reduction in basic income from individual-level phase-outs." - ) - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - def formula(person, period, parameters): - income = person("total_income", period) - bi = parameters(period).gov.contrib.ubi_center.basic_income - max_bi = person("bi_maximum", period) - income_over_threshold = max_( - income - bi.phase_out.individual.threshold, 0 - ) - uncapped_deduction = ( - bi.phase_out.individual.rate * income_over_threshold - ) - return min_(max_bi, uncapped_deduction) - - -class bi_household_phaseout(Variable): - label = "Basic income phase-out (household)" - documentation = ( - "Reduction in basic income from household-level phase-outs." - ) - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - def formula(person, period, parameters): - income = person("total_income", period) - household = person.household - household_income = household.sum(income) - bi = parameters(period).gov.contrib.ubi_center.basic_income - remaining_bi = person("bi_maximum", period) - person( - "bi_individual_phaseout", period - ) # Basic income remaining after individual-level phaseouts - - household_bi = household.sum(remaining_bi) - income_over_threshold = max_( - household_income - bi.phase_out.household.threshold, - 0, - ) - uncapped_deduction = ( - bi.phase_out.household.rate * income_over_threshold - ) - capped_deduction = min_(household_bi, uncapped_deduction) - - warnings.filterwarnings("ignore") - - percent_reduction = where( - household_bi > 0, capped_deduction / household_bi, 0 - ) - return percent_reduction * remaining_bi class bi_phaseout(Variable): diff --git a/policyengine_uk/variables/gov/dwp/BRMA_LHA_rate.py b/policyengine_uk/variables/gov/dwp/BRMA_LHA_rate.py new file mode 100644 index 000000000..a8eae300e --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/BRMA_LHA_rate.py @@ -0,0 +1,63 @@ +from policyengine_uk.model_api import * +import pandas as pd +import warnings +from policyengine_core.model_api import * +from policyengine_uk.variables.gov.dwp.LHA_category import ( + find_freeze_start, + time_shift_dataset, +) + +warnings.filterwarnings("ignore") + + +class BRMA_LHA_rate(Variable): + value_type = float + entity = BenUnit + label = "LHA rate" + documentation = "Local Housing Allowance rate" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + brma = benunit.value_from_first_person( + benunit.members.household("BRMA", period).decode_to_str() + ) + category = benunit("LHA_category", period).decode_to_str() + + from policyengine_uk.parameters.gov.dwp.LHA import lha_list_of_rents + + parameters = benunit.simulation.tax_benefit_system.parameters + lha = parameters.gov.dwp.LHA + + # We first need to know what time period to collect rents from. If LHA is frozen, we need to look earlier + # than the current time period. + + frozen = lha.freeze(period) + if frozen: + # Find the first year of the current freeze + freeze_start = find_freeze_start(lha.freeze, period.start) + lha_period = int(freeze_start[:4]) # Get year + else: + lha_period = int(period.start.year) + + private_rent_index = parameters.gov.indices.private_rent_index + lha_list_of_rents = time_shift_dataset( + lha_list_of_rents.copy(), lha_period, private_rent_index + ) + + percentile = lha.percentile(period) + + lha_rates = lha_list_of_rents.groupby( + ["brma", "lha_category"] + ).weekly_rent.quantile(percentile) + + lha_lookup_table = pd.DataFrame( + { + "brma": brma, + "lha_category": category, + } + ) + lha_lookup_table["weekly_rent"] = lha_lookup_table.apply( + lambda x: lha_rates.loc[x.brma, x.lha_category], axis=1 + ) + return lha_lookup_table.weekly_rent.values * 52 diff --git a/policyengine_uk/variables/gov/dwp/CTC_child_element.py b/policyengine_uk/variables/gov/dwp/CTC_child_element.py new file mode 100644 index 000000000..ff8c12db2 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/CTC_child_element.py @@ -0,0 +1,25 @@ +from policyengine_uk.model_api import * + + +class CTC_child_element(Variable): + value_type = float + entity = BenUnit + label = "Child Tax Credit child element" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 9" + unit = GBP + + def formula(benunit, period, parameters): + person = benunit.members + CTC = parameters(period).gov.dwp.tax_credits.child_tax_credit + is_child_for_CTC = person("is_child_for_CTC", period) + is_CTC_child_limit_exempt = person("is_CTC_child_limit_exempt", period) + exempt_child = is_child_for_CTC & is_CTC_child_limit_exempt + exempt_children = benunit.sum(exempt_child) + child_limit = CTC.limit.child_count + spaces_left = max_(0, child_limit - exempt_children) + non_exempt_children = min_( + spaces_left, benunit.sum(is_child_for_CTC) - exempt_children + ) + children = exempt_children + non_exempt_children + return CTC.elements.child_element * children diff --git a/policyengine_uk/variables/gov/dwp/CTC_disabled_child_element.py b/policyengine_uk/variables/gov/dwp/CTC_disabled_child_element.py new file mode 100644 index 000000000..835aff2e3 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/CTC_disabled_child_element.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * + + +class CTC_disabled_child_element(Variable): + value_type = float + entity = BenUnit + label = "CTC entitlement from disabled child elements" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 9" + unit = GBP + + def formula(benunit, period, parameters): + person = benunit.members + is_child_for_CTC = person("is_child_for_CTC", period) + is_disabled_for_benefits = person("is_disabled_for_benefits", period) + is_disabled_child = is_child_for_CTC & is_disabled_for_benefits + disabled_children = benunit.sum(is_disabled_child) + CTC = parameters(period).gov.dwp.tax_credits.child_tax_credit + amount = CTC.elements.dis_child_element * disabled_children + return benunit("is_CTC_eligible", period) * amount diff --git a/policyengine_uk/variables/gov/dwp/CTC_family_element.py b/policyengine_uk/variables/gov/dwp/CTC_family_element.py new file mode 100644 index 000000000..6d4b74d0a --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/CTC_family_element.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class CTC_family_element(Variable): + value_type = float + entity = BenUnit + label = "CTC entitlement in the Family Element" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 9" + unit = GBP + defined_for = "is_CTC_eligible" + + def formula(benunit, period, parameters): + return parameters( + period + ).gov.dwp.tax_credits.child_tax_credit.elements.family_element diff --git a/policyengine_uk/variables/gov/dwp/CTC_maximum_rate.py b/policyengine_uk/variables/gov/dwp/CTC_maximum_rate.py new file mode 100644 index 000000000..e6e954727 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/CTC_maximum_rate.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class CTC_maximum_rate(Variable): + value_type = float + entity = BenUnit + label = "Maximum Child Tax Credit" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 9" + unit = GBP + + adds = [ + "CTC_family_element", + "CTC_child_element", + "CTC_disabled_child_element", + "CTC_severely_disabled_child_element", + ] diff --git a/policyengine_uk/variables/gov/dwp/CTC_severely_disabled_child_element.py b/policyengine_uk/variables/gov/dwp/CTC_severely_disabled_child_element.py new file mode 100644 index 000000000..865b9779e --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/CTC_severely_disabled_child_element.py @@ -0,0 +1,26 @@ +from policyengine_uk.model_api import * + + +class CTC_severely_disabled_child_element(Variable): + value_type = float + entity = BenUnit + label = "CTC entitlement from severely disabled child elements" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 9" + unit = GBP + defined_for = "is_CTC_eligible" + + def formula(benunit, period, parameters): + person = benunit.members + is_child_for_CTC = person("is_child_for_CTC", period) + is_severely_disabled_for_benefits = person( + "is_severely_disabled_for_benefits", period + ) + is_severely_disabled_child = ( + is_child_for_CTC & is_severely_disabled_for_benefits + ) + severely_disabled_children = benunit.sum(is_severely_disabled_child) + CTC = parameters(period).gov.dwp.tax_credits.child_tax_credit + return ( + CTC.elements.severe_dis_child_element * severely_disabled_children + ) diff --git a/policyengine_uk/variables/gov/dwp/ESA_contrib.py b/policyengine_uk/variables/gov/dwp/ESA_contrib.py deleted file mode 100644 index c81bd5f93..000000000 --- a/policyengine_uk/variables/gov/dwp/ESA_contrib.py +++ /dev/null @@ -1,29 +0,0 @@ -from policyengine_uk.model_api import * - - -class esa_contrib(Variable): - value_type = float - entity = Person - label = "ESA (contribution-based)" - definition_period = YEAR - unit = GBP - - adds = ["esa_contrib_reported"] - - -class esa_contrib_reported(Variable): - value_type = float - entity = Person - label = "Employment and Support Allowance (contribution-based) (reported)" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" - - -class esa(Variable): - label = "ESA" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - adds = ["esa_contrib", "esa_income"] diff --git a/policyengine_uk/variables/gov/dwp/JSA_income.py b/policyengine_uk/variables/gov/dwp/JSA_income.py deleted file mode 100644 index decc3122b..000000000 --- a/policyengine_uk/variables/gov/dwp/JSA_income.py +++ /dev/null @@ -1,30 +0,0 @@ -from policyengine_uk.model_api import * - - -class jsa_income_reported(Variable): - value_type = float - entity = Person - label = "JSA (income-based) (reported amount)" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" - - -class jsa_income(Variable): - value_type = float - entity = BenUnit - label = "JSA (income-based)" - definition_period = YEAR - unit = GBP - - def formula(benunit, period, parameters): - return benunit("jsa_income_reported", period) - - -class jsa(Variable): - value_type = float - entity = BenUnit - label = "Amount of Jobseeker's Allowance for this family" - definition_period = YEAR - unit = GBP - adds = ["jsa_income", "jsa_contrib"] diff --git a/policyengine_uk/variables/gov/dwp/LHA.py b/policyengine_uk/variables/gov/dwp/LHA.py deleted file mode 100644 index cf5ea423a..000000000 --- a/policyengine_uk/variables/gov/dwp/LHA.py +++ /dev/null @@ -1,248 +0,0 @@ -from policyengine_uk.model_api import * -import pandas as pd -import warnings -from policyengine_core.model_api import * - -warnings.filterwarnings("ignore") - - -class LHA_eligible(Variable): - value_type = bool - entity = BenUnit - label = "Eligibility for Local Housing Allowance" - documentation = ( - "Whether benefit unit is eligible for Local Housing Allowance" - ) - definition_period = YEAR - - def formula(benunit, period, parameters): - renting = benunit("benunit_is_renting", period) - anyone_in_social_housing = benunit.any( - benunit.members("in_social_housing", period) - ) - return renting & ~anyone_in_social_housing - - -class LHA_allowed_bedrooms(Variable): - value_type = float - entity = BenUnit - label = "The number of bedrooms covered by LHA for the benefit unit" - definition_period = YEAR - reference = "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/10/2021-04-06" - - def formula(benunit, period, parameters): - """ - LHA allows for one room for: - a) The benefit unit adult(s) - b) Each person over 16 outside the benefit unit - but within the household - Children must share rooms in pairs unless they are - opposite-sex and one is over 10. The number of bedrooms - allowed under LHA rules is the minimum number of bedrooms - required to allocate people satisfying these rules. - """ - person = benunit.members - age = person("age", period) - male = person("is_male", period) - under_16 = age < 16 - under_10 = age < 10 - child_over_10 = ~under_10 & under_16 - # One room each for over-16s outside the benefit unit - non_dependants = benunit.max( - person.household.sum(~under_16) - ) - benunit.sum(~under_16) - boys_under_10 = benunit.sum(under_10 & male) - boys_over_10 = benunit.sum(child_over_10 & male) - girls_under_10 = benunit.sum(under_10 & ~male) - girls_over_10 = benunit.sum(child_over_10 & ~male) - # First, have over-10s share where possible - over_10_rooms = (boys_over_10 + 1) // 2 + (girls_over_10 + 1) // 2 - # There may children over 10 still not sharing - space_for_boy_under_10 = boys_over_10 % 2 - space_for_girl_under_10 = girls_over_10 % 2 - # Have those spaces filled where possible by children under 10 - left_over_boys_under_10 = max_( - boys_under_10 - space_for_boy_under_10, 0 - ) - left_over_girls_under_10 = max_( - girls_under_10 - space_for_girl_under_10, 0 - ) - # The remaining children must share in pairs - under_10_rooms = ( - left_over_boys_under_10 + left_over_girls_under_10 + 1 - ) // 2 - return 1 + non_dependants + over_10_rooms + under_10_rooms - - -class LHA_cap(Variable): - value_type = float - entity = BenUnit - label = "Applicable amount for LHA" - documentation = "Applicable amount for Local Housing Allowance" - definition_period = YEAR - unit = GBP - - def formula(benunit, period, parameters): - rent = benunit("benunit_rent", period) - cap = benunit("BRMA_LHA_rate", period) - return min_(rent, cap) - - -class LHACategory(Enum): - A = "Shared" - B = "1 Bed" - C = "2 Bed" - D = "3 Bed" - E = "4+ Bed" - - -class LHA_category(Variable): - value_type = Enum - entity = BenUnit - label = "LHA category for the benefit unit, taking into account LHA rules on the number of LHA-covered bedrooms" - definition_period = YEAR - possible_values = LHACategory - default_value = LHACategory.C - - def formula(benunit, period, parameters): - num_rooms = benunit("LHA_allowed_bedrooms", period.this_year) - person = benunit.members - household = person.household - is_shared = benunit.any( - household("is_shared_accommodation", period.this_year) - ) - num_adults_in_hh = benunit.max( - household.sum(person("is_adult", period)) - ) - eldest_adult_age_in_hh = benunit.max( - household.max(person("age", period)) - ) - has_children = benunit.any(person("is_child", period)) - # Households with only one adult, if under 35, can only claim shared if without children: - # https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/28 - can_only_claim_shared = ( - (num_adults_in_hh == 1) - & (eldest_adult_age_in_hh < 35) - & ~has_children - ) - return select( - [ - is_shared | can_only_claim_shared, - num_rooms == 1, - num_rooms == 2, - num_rooms == 3, - num_rooms > 3, - ], - [ - LHACategory.A, - LHACategory.B, - LHACategory.C, - LHACategory.D, - LHACategory.E, - ], - ) - - -def time_shift_dataset( - df: pd.DataFrame, year: int, private_rent_index: Parameter -) -> pd.DataFrame: - """Check if we have rows of data for the given year. If so, remove all other years. If not, select the latest year rows and uprate using the private rent index. - - Args: - df (pd.DataFrame): The List of Rents. - year (int): The requests year. - private_rent_index (Parameter): The private rent index. - - Returns: - pd.DataFrame: The List of Rents for the given year. - """ - year = int(year) - df.year = df.year.astype(int) - if year in df.year.unique(): - df = df[df.year == year] - else: - df = df[df.year == df.year.max()] - start_instant = f"{df.year.max()}-01-01" - end_instant = f"{year}-01-01" - start_index = private_rent_index(start_instant) - end_index = private_rent_index(end_instant) - uprating_index = end_index / start_index - df.weekly_rent = np.round(df.weekly_rent * uprating_index, 2) - df.year = year - return df - - -def find_freeze_start(freeze_parameter: Parameter, period: str) -> str: - """Finds the first instant in which the LHA freeze was applied. Returns none if this is impossible. - - Args: - freeze_parameter (Parameter): The LHA freeze parameter. - period (str): The period to search up to. - - Returns: - str: The first instant in which the LHA freeze was applied. - """ - freeze_start = None - for i in range(len(freeze_parameter.values_list)): - param = freeze_parameter.values_list[i] - if param.instant_str > str(period): - continue - if ( - i < len(freeze_parameter.values_list) - 1 - and not freeze_parameter.values_list[i + 1].value - ): - return param.instant_str - return None - - -class BRMA_LHA_rate(Variable): - value_type = float - entity = BenUnit - label = "LHA rate" - documentation = "Local Housing Allowance rate" - definition_period = YEAR - unit = GBP - - def formula(benunit, period, parameters): - brma = benunit.value_from_first_person( - benunit.members.household("BRMA", period).decode_to_str() - ) - category = benunit("LHA_category", period).decode_to_str() - - from policyengine_uk.parameters.gov.dwp.LHA import lha_list_of_rents - - parameters = benunit.simulation.tax_benefit_system.parameters - lha = parameters.gov.dwp.LHA - - # We first need to know what time period to collect rents from. If LHA is frozen, we need to look earlier - # than the current time period. - - frozen = lha.freeze(period) - if frozen: - # Find the first year of the current freeze - freeze_start = find_freeze_start(lha.freeze, period.start) - lha_period = int(freeze_start[:4]) # Get year - else: - lha_period = int(period.start.year) - - private_rent_index = parameters.gov.indices.private_rent_index - lha_list_of_rents = time_shift_dataset( - lha_list_of_rents.copy(), lha_period, private_rent_index - ) - - percentile = lha.percentile(period) - - lha_rates = lha_list_of_rents.groupby( - ["brma", "lha_category"] - ).weekly_rent.quantile(percentile) - - lha_lookup_table = pd.DataFrame( - { - "brma": brma, - "lha_category": category, - } - ) - lha_lookup_table["weekly_rent"] = lha_lookup_table.apply( - lambda x: lha_rates.loc[x.brma, x.lha_category], axis=1 - ) - return lha_lookup_table.weekly_rent.values * 52 diff --git a/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py b/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py new file mode 100644 index 000000000..85ccc664d --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/LHA_allowed_bedrooms.py @@ -0,0 +1,57 @@ +from policyengine_uk.model_api import * +import pandas as pd +import warnings +from policyengine_core.model_api import * + +warnings.filterwarnings("ignore") + + +class LHA_allowed_bedrooms(Variable): + value_type = float + entity = BenUnit + label = "The number of bedrooms covered by LHA for the benefit unit" + definition_period = YEAR + reference = "https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/10/2021-04-06" + + def formula(benunit, period, parameters): + """ + LHA allows for one room for: + a) The benefit unit adult(s) + b) Each person over 16 outside the benefit unit + but within the household + Children must share rooms in pairs unless they are + opposite-sex and one is over 10. The number of bedrooms + allowed under LHA rules is the minimum number of bedrooms + required to allocate people satisfying these rules. + """ + person = benunit.members + age = person("age", period) + male = person("is_male", period) + under_16 = age < 16 + under_10 = age < 10 + child_over_10 = ~under_10 & under_16 + # One room each for over-16s outside the benefit unit + non_dependants = benunit.max( + person.household.sum(~under_16) + ) - benunit.sum(~under_16) + boys_under_10 = benunit.sum(under_10 & male) + boys_over_10 = benunit.sum(child_over_10 & male) + girls_under_10 = benunit.sum(under_10 & ~male) + girls_over_10 = benunit.sum(child_over_10 & ~male) + # First, have over-10s share where possible + over_10_rooms = (boys_over_10 + 1) // 2 + (girls_over_10 + 1) // 2 + # There may children over 10 still not sharing + space_for_boy_under_10 = boys_over_10 % 2 + space_for_girl_under_10 = girls_over_10 % 2 + # Have those spaces filled where possible by children under 10 + left_over_boys_under_10 = max_( + boys_under_10 - space_for_boy_under_10, 0 + ) + left_over_girls_under_10 = max_( + girls_under_10 - space_for_girl_under_10, 0 + ) + # The remaining children must share in pairs + under_10_rooms = ( + left_over_boys_under_10 + left_over_girls_under_10 + 1 + ) // 2 + return 1 + non_dependants + over_10_rooms + under_10_rooms diff --git a/policyengine_uk/variables/gov/dwp/LHA_cap.py b/policyengine_uk/variables/gov/dwp/LHA_cap.py new file mode 100644 index 000000000..d7139422c --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/LHA_cap.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * +import pandas as pd +import warnings +from policyengine_core.model_api import * + +warnings.filterwarnings("ignore") + + +class LHA_cap(Variable): + value_type = float + entity = BenUnit + label = "Applicable amount for LHA" + documentation = "Applicable amount for Local Housing Allowance" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + rent = benunit("benunit_rent", period) + cap = benunit("BRMA_LHA_rate", period) + return min_(rent, cap) diff --git a/policyengine_uk/variables/gov/dwp/LHA_category.py b/policyengine_uk/variables/gov/dwp/LHA_category.py new file mode 100644 index 000000000..473fb33ff --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/LHA_category.py @@ -0,0 +1,113 @@ +from policyengine_uk.model_api import * +import pandas as pd +import warnings +from policyengine_core.model_api import * + +warnings.filterwarnings("ignore") + + +class LHACategory(Enum): + A = "Shared accommodation" + B = "One bedroom" + C = "Two bedrooms" + D = "Three bedrooms" + E = "Four or more bedrooms" + + +class LHA_category(Variable): + value_type = Enum + entity = BenUnit + label = "LHA category for the benefit unit, taking into account LHA rules on the number of LHA-covered bedrooms" + definition_period = YEAR + possible_values = LHACategory + default_value = LHACategory.C + + def formula(benunit, period, parameters): + num_rooms = benunit("LHA_allowed_bedrooms", period.this_year) + person = benunit.members + household = person.household + is_shared = benunit.any( + household("is_shared_accommodation", period.this_year) + ) + num_adults_in_hh = benunit.max( + household.sum(person("is_adult", period)) + ) + eldest_adult_age_in_hh = benunit.max( + household.max(person("age", period)) + ) + has_children = benunit.any(person("is_child", period)) + # Households with only one adult, if under 35, can only claim shared if without children: + # https://www.legislation.gov.uk/uksi/2013/376/schedule/4/paragraph/28 + can_only_claim_shared = ( + (num_adults_in_hh == 1) + & (eldest_adult_age_in_hh < 35) + & ~has_children + ) + return select( + [ + is_shared | can_only_claim_shared, + num_rooms == 1, + num_rooms == 2, + num_rooms == 3, + num_rooms > 3, + ], + [ + LHACategory.A, + LHACategory.B, + LHACategory.C, + LHACategory.D, + LHACategory.E, + ], + ) + + +def time_shift_dataset( + df: pd.DataFrame, year: int, private_rent_index: Parameter +) -> pd.DataFrame: + """Check if we have rows of data for the given year. If so, remove all other years. If not, select the latest year rows and uprate using the private rent index. + + Args: + df (pd.DataFrame): The List of Rents. + year (int): The requests year. + private_rent_index (Parameter): The private rent index. + + Returns: + pd.DataFrame: The List of Rents for the given year. + """ + year = int(year) + df.year = df.year.astype(int) + if year in df.year.unique(): + df = df[df.year == year] + else: + df = df[df.year == df.year.max()] + start_instant = f"{df.year.max()}-01-01" + end_instant = f"{year}-01-01" + start_index = private_rent_index(start_instant) + end_index = private_rent_index(end_instant) + uprating_index = end_index / start_index + df.weekly_rent = np.round(df.weekly_rent * uprating_index, 2) + df.year = year + return df + + +def find_freeze_start(freeze_parameter: Parameter, period: str) -> str: + """Finds the first instant in which the LHA freeze was applied. Returns none if this is impossible. + + Args: + freeze_parameter (Parameter): The LHA freeze parameter. + period (str): The period to search up to. + + Returns: + str: The first instant in which the LHA freeze was applied. + """ + freeze_start = None + for i in range(len(freeze_parameter.values_list)): + param = freeze_parameter.values_list[i] + if param.instant_str > str(period): + continue + if ( + i < len(freeze_parameter.values_list) - 1 + and not freeze_parameter.values_list[i + 1].value + ): + return param.instant_str + return None diff --git a/policyengine_uk/variables/gov/dwp/LHA_eligible.py b/policyengine_uk/variables/gov/dwp/LHA_eligible.py new file mode 100644 index 000000000..2a7d31bdb --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/LHA_eligible.py @@ -0,0 +1,23 @@ +from policyengine_uk.model_api import * +import pandas as pd +import warnings +from policyengine_core.model_api import * + +warnings.filterwarnings("ignore") + + +class LHA_eligible(Variable): + value_type = bool + entity = BenUnit + label = "Eligibility for Local Housing Allowance" + documentation = ( + "Whether benefit unit is eligible for Local Housing Allowance" + ) + definition_period = YEAR + + def formula(benunit, period, parameters): + renting = benunit("benunit_is_renting", period) + anyone_in_social_housing = benunit.any( + benunit.members("in_social_housing", period) + ) + return renting & ~anyone_in_social_housing diff --git a/policyengine_uk/variables/gov/dwp/WTC_basic_element.py b/policyengine_uk/variables/gov/dwp/WTC_basic_element.py new file mode 100644 index 000000000..6d914b550 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/WTC_basic_element.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class WTC_basic_element(Variable): + value_type = float + entity = BenUnit + label = "Working Tax Credit basic element" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 11" + unit = GBP + defined_for = "is_WTC_eligible" + + def formula(benunit, period, parameters): + return parameters( + period + ).gov.dwp.tax_credits.working_tax_credit.elements.basic diff --git a/policyengine_uk/variables/gov/dwp/WTC_childcare_element.py b/policyengine_uk/variables/gov/dwp/WTC_childcare_element.py new file mode 100644 index 000000000..9bff28701 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/WTC_childcare_element.py @@ -0,0 +1,21 @@ +from policyengine_uk.model_api import * + + +class WTC_childcare_element(Variable): + value_type = float + entity = BenUnit + label = "Working Tax Credit childcare element" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 11" + unit = GBP + defined_for = "is_WTC_eligible" + + def formula(benunit, period, parameters): + WTC = parameters(period).gov.dwp.tax_credits.working_tax_credit + num_children = benunit("num_children", period) + childcare_1 = (num_children == 1) * WTC.elements.childcare_1 + childcare_2 = (num_children > 1) * WTC.elements.childcare_2 + max_childcare_amount = (childcare_1 + childcare_2) * WEEKS_IN_YEAR + expenses = add(benunit, period, ["childcare_expenses"]) + eligible_expenses = min_(max_childcare_amount, expenses) + return WTC.elements.childcare_coverage * eligible_expenses diff --git a/policyengine_uk/variables/gov/dwp/WTC_couple_element.py b/policyengine_uk/variables/gov/dwp/WTC_couple_element.py new file mode 100644 index 000000000..08350266d --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/WTC_couple_element.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class WTC_couple_element(Variable): + value_type = float + entity = BenUnit + label = "Working Tax Credit couple element" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 11" + unit = GBP + defined_for = "is_WTC_eligible" + + def formula(benunit, period, parameters): + WTC = parameters(period).gov.dwp.tax_credits.working_tax_credit + relation_type = benunit("relation_type", period) + relations = relation_type.possible_values + return (relation_type == relations.COUPLE) * WTC.elements.couple diff --git a/policyengine_uk/variables/gov/dwp/WTC_disabled_element.py b/policyengine_uk/variables/gov/dwp/WTC_disabled_element.py new file mode 100644 index 000000000..48ff30c18 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/WTC_disabled_element.py @@ -0,0 +1,25 @@ +from policyengine_uk.model_api import * + + +class WTC_disabled_element(Variable): + value_type = float + entity = BenUnit + label = "Working Tax Credit disabled element" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 11" + unit = GBP + defined_for = "is_WTC_eligible" + + def formula(benunit, period, parameters): + WTC = parameters(period).gov.dwp.tax_credits.working_tax_credit + person = benunit.members + person_meets_hours = ( + person("weekly_hours", period) >= WTC.min_hours.lower + ) + person_qualifies = ( + person_meets_hours + & person("is_disabled_for_benefits", period) + & person("is_adult", period) + ) + qualifies = benunit.any(person_qualifies) + return qualifies * WTC.elements.disabled diff --git a/policyengine_uk/variables/gov/dwp/WTC_lone_parent_element.py b/policyengine_uk/variables/gov/dwp/WTC_lone_parent_element.py new file mode 100644 index 000000000..8500415a7 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/WTC_lone_parent_element.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class WTC_lone_parent_element(Variable): + value_type = float + entity = BenUnit + label = "Working Tax Credit lone parent element" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 11" + unit = GBP + defined_for = "is_WTC_eligible" + + def formula(benunit, period, parameters): + WTC = parameters(period).gov.dwp.tax_credits.working_tax_credit + family_type = benunit("family_type", period) + families = family_type.possible_values + lone_parent = family_type == families.LONE_PARENT + return lone_parent * WTC.elements.lone_parent diff --git a/policyengine_uk/variables/gov/dwp/WTC_maximum_rate.py b/policyengine_uk/variables/gov/dwp/WTC_maximum_rate.py new file mode 100644 index 000000000..d89a15a57 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/WTC_maximum_rate.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * + + +class WTC_maximum_rate(Variable): + value_type = float + entity = BenUnit + label = "Working Tax Credit maximum rate" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 11" + unit = GBP + + adds = [ + "WTC_basic_element", + "WTC_couple_element", + "WTC_lone_parent_element", + "WTC_disabled_element", + "WTC_severely_disabled_element", + "WTC_worker_element", + "WTC_childcare_element", + ] diff --git a/policyengine_uk/variables/gov/dwp/WTC_severely_disabled_element.py b/policyengine_uk/variables/gov/dwp/WTC_severely_disabled_element.py new file mode 100644 index 000000000..d1bc2cfe3 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/WTC_severely_disabled_element.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class WTC_severely_disabled_element(Variable): + value_type = float + entity = BenUnit + label = "Working Tax Credit severely disabled element" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 11" + unit = GBP + + def formula(benunit, period, parameters): + WTC = parameters(period).gov.dwp.tax_credits.working_tax_credit + amount = ( + benunit("num_severely_disabled_adults", period) + * WTC.elements.severely_disabled + ) + return benunit("is_WTC_eligible", period) * amount diff --git a/policyengine_uk/variables/gov/dwp/WTC_worker_element.py b/policyengine_uk/variables/gov/dwp/WTC_worker_element.py new file mode 100644 index 000000000..65d231662 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/WTC_worker_element.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class WTC_worker_element(Variable): + value_type = float + entity = BenUnit + label = "Working Tax Credit worker element" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 11" + unit = GBP + defined_for = "is_WTC_eligible" + + def formula(benunit, period, parameters): + WTC = parameters(period).gov.dwp.tax_credits.working_tax_credit + hours = add(benunit, period, ["weekly_hours"]) + meets_hours_requirement = hours >= WTC.min_hours.default + return meets_hours_requirement * WTC.elements.worker diff --git a/policyengine_uk/variables/gov/dwp/aa_category.py b/policyengine_uk/variables/gov/dwp/aa_category.py new file mode 100644 index 000000000..65b4c82e5 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/aa_category.py @@ -0,0 +1,28 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.misc.categories.lower_or_higher import ( + LowerOrHigher, +) + + +class aa_category(Variable): + label = "Attendance Allowance category" + entity = Person + definition_period = YEAR + value_type = Enum + possible_values = LowerOrHigher + default_value = LowerOrHigher.NONE + + def formula(person, period, parameters): + aa = parameters(period).baseline.gov.dwp.attendance_allowance + SAFETY_MARGIN = 0.1 # Survey reported values could be slightly below eligible values when they should be above due to data manipulation + reported_weekly_aa = ( + person("attendance_allowance_reported", period) / WEEKS_IN_YEAR + ) + return select( + [ + reported_weekly_aa >= aa.higher * (1 - SAFETY_MARGIN), + reported_weekly_aa >= aa.lower * (1 - SAFETY_MARGIN), + ], + [LowerOrHigher.HIGHER, LowerOrHigher.LOWER], + default=LowerOrHigher.NONE, + ) diff --git a/policyengine_uk/variables/gov/dwp/access_fund.py b/policyengine_uk/variables/gov/dwp/access_fund.py new file mode 100644 index 000000000..f7e1b384e --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/access_fund.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class access_fund(Variable): + label = "Access Fund" + documentation = "Access Fund for educational assistance" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP diff --git a/policyengine_uk/variables/gov/dwp/additional_state_pension.py b/policyengine_uk/variables/gov/dwp/additional_state_pension.py new file mode 100644 index 000000000..067f964df --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/additional_state_pension.py @@ -0,0 +1,27 @@ +from policyengine_uk.model_api import * + + +class additional_state_pension(Variable): + label = "additional State Pension" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + simulation = person.simulation + if simulation.dataset is None: + return 0 + + data_year = simulation.dataset.time_period + reported = person("state_pension_reported", data_year) / WEEKS_IN_YEAR + type = person("state_pension_type", data_year) + maximum_basic_sp = parameters( + data_year + ).gov.dwp.state_pension.basic_state_pension.amount + amount_in_data_year = where( + type == StatePensionType.BASIC, + max_(reported - maximum_basic_sp, 0), + 0, + ) + return amount_in_data_year * WEEKS_IN_YEAR diff --git a/policyengine_uk/variables/gov/dwp/adult_ema.py b/policyengine_uk/variables/gov/dwp/adult_ema.py new file mode 100644 index 000000000..e669866ca --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/adult_ema.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class adult_ema(Variable): + label = "Adult EMA" + documentation = "Educational Maintenance Allowance for adults" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP diff --git a/policyengine_uk/variables/gov/dwp/AFCS.py b/policyengine_uk/variables/gov/dwp/afcs.py similarity index 55% rename from policyengine_uk/variables/gov/dwp/AFCS.py rename to policyengine_uk/variables/gov/dwp/afcs.py index e576310d9..bfe369746 100644 --- a/policyengine_uk/variables/gov/dwp/AFCS.py +++ b/policyengine_uk/variables/gov/dwp/afcs.py @@ -10,12 +10,3 @@ class afcs(Variable): uprating = "gov.obr.consumer_price_index" adds = ["afcs_reported"] - - -class afcs_reported(Variable): - value_type = float - entity = Person - label = "Armed Forces Compensation Scheme (reported)" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/afcs_reported.py b/policyengine_uk/variables/gov/dwp/afcs_reported.py new file mode 100644 index 000000000..9eceff58f --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/afcs_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class afcs_reported(Variable): + value_type = float + entity = Person + label = "Armed Forces Compensation Scheme (reported)" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/attendance_allowance.py b/policyengine_uk/variables/gov/dwp/attendance_allowance.py index 0aa1bf563..599811d7b 100644 --- a/policyengine_uk/variables/gov/dwp/attendance_allowance.py +++ b/policyengine_uk/variables/gov/dwp/attendance_allowance.py @@ -1,7 +1,4 @@ from policyengine_uk.model_api import * -from policyengine_uk.variables.misc.categories.lower_or_higher import ( - LowerOrHigher, -) class attendance_allowance(Variable): @@ -14,47 +11,15 @@ class attendance_allowance(Variable): def formula(person, period, parameters): aa = parameters(period).gov.dwp.attendance_allowance category = person("aa_category", period) + categories = category.possible_values return ( select( [ - category == LowerOrHigher.HIGHER, - category == LowerOrHigher.LOWER, + category == categories.HIGHER, + category == categories.LOWER, ], [aa.higher, aa.lower], default=0, ) * WEEKS_IN_YEAR ) - - -class attendance_allowance_reported(Variable): - value_type = float - entity = Person - label = "Attendance Allowance (reported)" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" - - -class aa_category(Variable): - label = "Attendance Allowance category" - entity = Person - definition_period = YEAR - value_type = Enum - possible_values = LowerOrHigher - default_value = LowerOrHigher.NONE - - def formula(person, period, parameters): - aa = parameters(period).baseline.gov.dwp.attendance_allowance - SAFETY_MARGIN = 0.1 # Survey reported values could be slightly below eligible values when they should be above due to data manipulation - reported_weekly_aa = ( - person("attendance_allowance_reported", period) / WEEKS_IN_YEAR - ) - return select( - [ - reported_weekly_aa >= aa.higher * (1 - SAFETY_MARGIN), - reported_weekly_aa >= aa.lower * (1 - SAFETY_MARGIN), - ], - [LowerOrHigher.HIGHER, LowerOrHigher.LOWER], - default=LowerOrHigher.NONE, - ) diff --git a/policyengine_uk/variables/gov/dwp/attendance_allowance_reported.py b/policyengine_uk/variables/gov/dwp/attendance_allowance_reported.py new file mode 100644 index 000000000..3abfd6361 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/attendance_allowance_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class attendance_allowance_reported(Variable): + value_type = float + entity = Person + label = "Attendance Allowance (reported)" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/baseline_ctc_entitlement.py b/policyengine_uk/variables/gov/dwp/baseline_ctc_entitlement.py new file mode 100644 index 000000000..f5601126f --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/baseline_ctc_entitlement.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + + +class baseline_ctc_entitlement(Variable): + label = "Receives Child Tax Credit (baseline)" + entity = BenUnit + definition_period = YEAR + value_type = float + + def formula(benunit, period, parameters): + if benunit.simulation.baseline is None: + return 1 + baseline = benunit.simulation.baseline.populations["benunit"] + return baseline("wtc_entitlement", period) diff --git a/policyengine_uk/variables/gov/dwp/baseline_income_support_entitlement.py b/policyengine_uk/variables/gov/dwp/baseline_income_support_entitlement.py new file mode 100644 index 000000000..dba73d6a9 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/baseline_income_support_entitlement.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class baseline_income_support_entitlement(Variable): + label = "Income Support eligible (baseline)" + entity = BenUnit + definition_period = YEAR + value_type = float + unit = GBP diff --git a/policyengine_uk/variables/gov/dwp/baseline_wtc_entitlement.py b/policyengine_uk/variables/gov/dwp/baseline_wtc_entitlement.py new file mode 100644 index 000000000..d811ce51e --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/baseline_wtc_entitlement.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + + +class baseline_wtc_entitlement(Variable): + label = "Baseline Working Tax Credit" + entity = BenUnit + definition_period = YEAR + value_type = float + + def formula(benunit, period, parameters): + if benunit.simulation.baseline is None: + return 1 + baseline = benunit.simulation.baseline.populations["benunit"] + return baseline("wtc_entitlement", period) diff --git a/policyengine_uk/variables/gov/dwp/basic_state_pension.py b/policyengine_uk/variables/gov/dwp/basic_state_pension.py new file mode 100644 index 000000000..e890a6de5 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/basic_state_pension.py @@ -0,0 +1,27 @@ +from policyengine_uk.model_api import * + + +class basic_state_pension(Variable): + label = "basic State Pension" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + simulation = person.simulation + if simulation.dataset is None: + return 0 + + data_year = simulation.dataset.time_period + reported = person("state_pension_reported", data_year) / WEEKS_IN_YEAR + type = person("state_pension_type", period) + maximum_basic_sp = parameters( + data_year + ).gov.dwp.state_pension.basic_state_pension.amount + amount_in_data_year = where( + type == StatePensionType.BASIC, min_(reported, maximum_basic_sp), 0 + ) + triple_lock = parameters.gov.dwp.state_pension.triple_lock.index + uprating_since_data_year = triple_lock(period) / triple_lock(data_year) + return amount_in_data_year * uprating_since_data_year * WEEKS_IN_YEAR diff --git a/policyengine_uk/variables/gov/dwp/benefit_cap.py b/policyengine_uk/variables/gov/dwp/benefit_cap.py index 1b210cc63..2f5375103 100644 --- a/policyengine_uk/variables/gov/dwp/benefit_cap.py +++ b/policyengine_uk/variables/gov/dwp/benefit_cap.py @@ -33,53 +33,3 @@ def formula(benunit, period, parameters): ) exempt = benunit("is_benefit_cap_exempt", period) return where(exempt, np.inf * np.ones_like(has_children), rate) - - -class is_benefit_cap_exempt(Variable): - value_type = bool - entity = BenUnit - label = "Whether exempt from the benefits cap" - definition_period = YEAR - - def formula(benunit, period, parameters): - QUAL_PERSONAL_BENEFITS = [ - "carers_allowance", - "dla", - "esa_contrib", - ] - QUAL_BENUNIT_BENEFITS = ["working_tax_credit", "esa_income"] - qualifying_benunit_benefits = add( - benunit, period, QUAL_BENUNIT_BENEFITS - ) - qualifying_personal_benefits = add( - benunit, period, QUAL_PERSONAL_BENEFITS - ) - return (qualifying_personal_benefits + qualifying_benunit_benefits) > 0 - - -class benefit_cap_reduction(Variable): - label = "benefit cap reduction" - entity = BenUnit - definition_period = YEAR - value_type = float - unit = GBP - - def formula(benunit, period, parameters): - CAPPED_BENEFITS = [ - "child_benefit", - "child_tax_credit", - "jsa_income", - "income_support", - "esa_income", - "universal_credit_pre_benefit_cap", - "housing_benefit_pre_benefit_cap", - "jsa_contrib", - "incapacity_benefit", - "esa_contrib", - "sda", - ] - return max_( - add(benunit, period, CAPPED_BENEFITS) - - benunit("benefit_cap", period), - 0, - ) diff --git a/policyengine_uk/variables/gov/dwp/benefit_cap_reduction.py b/policyengine_uk/variables/gov/dwp/benefit_cap_reduction.py new file mode 100644 index 000000000..da64022ac --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/benefit_cap_reduction.py @@ -0,0 +1,29 @@ +from policyengine_uk.model_api import * + + +class benefit_cap_reduction(Variable): + label = "benefit cap reduction" + entity = BenUnit + definition_period = YEAR + value_type = float + unit = GBP + + def formula(benunit, period, parameters): + CAPPED_BENEFITS = [ + "child_benefit", + "child_tax_credit", + "jsa_income", + "income_support", + "esa_income", + "universal_credit_pre_benefit_cap", + "housing_benefit_pre_benefit_cap", + "jsa_contrib", + "incapacity_benefit", + "esa_contrib", + "sda", + ] + return max_( + add(benunit, period, CAPPED_BENEFITS) + - benunit("benefit_cap", period), + 0, + ) diff --git a/policyengine_uk/variables/gov/dwp/bsp.py b/policyengine_uk/variables/gov/dwp/bsp.py new file mode 100644 index 000000000..f4ebdaaa7 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/bsp.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class bsp(Variable): + value_type = float + entity = Person + label = "Bereavement Support Payment" + definition_period = YEAR + unit = GBP + + adds = ["bsp_reported"] diff --git a/policyengine_uk/variables/gov/dwp/BSP.py b/policyengine_uk/variables/gov/dwp/bsp_reported.py similarity index 58% rename from policyengine_uk/variables/gov/dwp/BSP.py rename to policyengine_uk/variables/gov/dwp/bsp_reported.py index 0c7b04acf..b652755ed 100644 --- a/policyengine_uk/variables/gov/dwp/BSP.py +++ b/policyengine_uk/variables/gov/dwp/bsp_reported.py @@ -1,16 +1,6 @@ from policyengine_uk.model_api import * -class bsp(Variable): - value_type = float - entity = Person - label = "Bereavement Support Payment" - definition_period = YEAR - unit = GBP - - adds = ["bsp_reported"] - - class bsp_reported(Variable): value_type = float entity = Person diff --git a/policyengine_uk/variables/gov/dwp/care_hours.py b/policyengine_uk/variables/gov/dwp/care_hours.py new file mode 100644 index 000000000..c8714a642 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/care_hours.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class care_hours(Variable): + label = "hours providing care" + documentation = "Weekly hours providing care to others" + entity = Person + definition_period = YEAR + value_type = float + unit = "hour" diff --git a/policyengine_uk/variables/gov/dwp/carers_allowance.py b/policyengine_uk/variables/gov/dwp/carers_allowance.py index a5c0f3088..e14067709 100644 --- a/policyengine_uk/variables/gov/dwp/carers_allowance.py +++ b/policyengine_uk/variables/gov/dwp/carers_allowance.py @@ -14,21 +14,3 @@ def formula(person, period, parameters): weekly_care_hours = person("care_hours", period) meets_work_condition = weekly_care_hours >= ca.min_hours return (meets_work_condition | receives_ca) * ca.rate * WEEKS_IN_YEAR - - -class care_hours(Variable): - label = "hours providing care" - documentation = "Weekly hours providing care to others" - entity = Person - definition_period = YEAR - value_type = float - unit = "hour" - - -class carers_allowance_reported(Variable): - value_type = float - entity = Person - label = "Carer's Allowance (reported)" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/carers_allowance_reported.py b/policyengine_uk/variables/gov/dwp/carers_allowance_reported.py new file mode 100644 index 000000000..500e44bb7 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/carers_allowance_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class carers_allowance_reported(Variable): + value_type = float + entity = Person + label = "Carer's Allowance (reported)" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/child_ema.py b/policyengine_uk/variables/gov/dwp/child_ema.py new file mode 100644 index 000000000..cd7e5d08e --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/child_ema.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class child_ema(Variable): + label = "Child EMA" + documentation = "Educational Maintenance Allowance for children" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP diff --git a/policyengine_uk/variables/gov/dwp/child_tax_credit.py b/policyengine_uk/variables/gov/dwp/child_tax_credit.py new file mode 100644 index 000000000..988c6e886 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/child_tax_credit.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class child_tax_credit(Variable): + value_type = float + entity = BenUnit + label = "Child Tax Credit" + definition_period = YEAR + unit = GBP + defined_for = "would_claim_CTC" + adds = ["ctc_entitlement"] diff --git a/policyengine_uk/variables/gov/dwp/child_tax_credit_pre_minimum.py b/policyengine_uk/variables/gov/dwp/child_tax_credit_pre_minimum.py new file mode 100644 index 000000000..c85a8f9e7 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/child_tax_credit_pre_minimum.py @@ -0,0 +1,24 @@ +from policyengine_uk.model_api import * + + +class child_tax_credit_pre_minimum(Variable): + value_type = float + entity = BenUnit + label = "Child Tax Credit pre-minimum" + documentation = ( + "Child Tax Credit amount before the minimum tax credit is applied" + ) + defined_for = "would_claim_CTC" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + reduction_left = max_( + 0, + benunit("tax_credits_reduction", period) + - benunit("WTC_maximum_rate", period), + ) + return max_( + 0, + benunit("CTC_maximum_rate", period) - reduction_left, + ) diff --git a/policyengine_uk/variables/gov/dwp/child_tax_credit_reported.py b/policyengine_uk/variables/gov/dwp/child_tax_credit_reported.py new file mode 100644 index 000000000..19689de10 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/child_tax_credit_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class child_tax_credit_reported(Variable): + value_type = float + entity = Person + label = "Working Tax Credit" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/council_tax_benefit.py b/policyengine_uk/variables/gov/dwp/council_tax_benefit.py index 63bd9451c..630c0790e 100644 --- a/policyengine_uk/variables/gov/dwp/council_tax_benefit.py +++ b/policyengine_uk/variables/gov/dwp/council_tax_benefit.py @@ -1,15 +1,6 @@ from policyengine_uk.model_api import * -class council_tax_benefit_reported(Variable): - value_type = float - entity = Person - label = "Council Tax Benefit (reported)" - documentation = "Reported amount of Council Tax Benefit" - definition_period = YEAR - unit = GBP - - class council_tax_benefit(Variable): value_type = float entity = BenUnit diff --git a/policyengine_uk/variables/gov/dwp/council_tax_benefit_reported.py b/policyengine_uk/variables/gov/dwp/council_tax_benefit_reported.py new file mode 100644 index 000000000..63a6aaff7 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/council_tax_benefit_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class council_tax_benefit_reported(Variable): + value_type = float + entity = Person + label = "Council Tax Benefit (reported)" + documentation = "Reported amount of Council Tax Benefit" + definition_period = YEAR + unit = GBP diff --git a/policyengine_uk/variables/gov/dwp/ctc_child_limit_affected.py b/policyengine_uk/variables/gov/dwp/ctc_child_limit_affected.py new file mode 100644 index 000000000..4c1ff5961 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/ctc_child_limit_affected.py @@ -0,0 +1,25 @@ +from policyengine_uk.model_api import * + + +class ctc_child_limit_affected(Variable): + label = "affected by the CTC child limit" + entity = BenUnit + definition_period = YEAR + value_type = bool + + def formula(benunit, period, parameters): + person = benunit.members + CTC = parameters(period).gov.dwp.tax_credits.child_tax_credit + is_child_for_CTC = person("is_child_for_CTC", period) + is_CTC_child_limit_exempt = person("is_CTC_child_limit_exempt", period) + exempt_child = is_child_for_CTC & is_CTC_child_limit_exempt + exempt_children = benunit.sum(exempt_child) + child_limit = CTC.limit.child_count + spaces_left = max_(0, child_limit - exempt_children) + non_exempt_children = min_( + spaces_left, benunit.sum(is_child_for_CTC) - exempt_children + ) + return ( + exempt_children + non_exempt_children + < benunit.sum(is_child_for_CTC) + ) & (benunit("child_tax_credit", period) > 0) diff --git a/policyengine_uk/variables/gov/dwp/ctc_entitlement.py b/policyengine_uk/variables/gov/dwp/ctc_entitlement.py new file mode 100644 index 000000000..d2f375f2d --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/ctc_entitlement.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class ctc_entitlement(Variable): + label = "CTC entitlement" + entity = BenUnit + definition_period = YEAR + value_type = float + unit = GBP + + def formula(benunit, period, parameters): + return where( + benunit("tax_credits", period) > 0, + benunit("child_tax_credit_pre_minimum", period), + 0, + ) * (benunit("is_CTC_eligible", period)) diff --git a/policyengine_uk/variables/gov/dwp/dla/mobility.py b/policyengine_uk/variables/gov/dwp/dla/dla_m.py similarity index 56% rename from policyengine_uk/variables/gov/dwp/dla/mobility.py rename to policyengine_uk/variables/gov/dwp/dla/dla_m.py index 6a555bd19..742d4ea8b 100644 --- a/policyengine_uk/variables/gov/dwp/dla/mobility.py +++ b/policyengine_uk/variables/gov/dwp/dla/dla_m.py @@ -1,16 +1,4 @@ from policyengine_uk.model_api import * -from policyengine_uk.variables.misc.categories.lower_or_higher import ( - LowerOrHigher, -) - - -class dla_m_reported(Variable): - value_type = float - entity = Person - label = "DLA (mobility) (reported)" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" class dla_m(Variable): @@ -23,12 +11,13 @@ class dla_m(Variable): def formula(person, period, parameters): dla_m = parameters(period).gov.dwp.dla.mobility category = person("dla_m_category", period) + categories = category.possible_values return ( select( [ - category == LowerOrHigher.HIGHER, - category == LowerOrHigher.LOWER, - category == LowerOrHigher.NONE, + category == categories.HIGHER, + category == categories.LOWER, + category == categories.NONE, ], [ dla_m.higher, diff --git a/policyengine_uk/variables/gov/dwp/dla/dla_m_reported.py b/policyengine_uk/variables/gov/dwp/dla/dla_m_reported.py new file mode 100644 index 000000000..cdd0743d0 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/dla/dla_m_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class dla_m_reported(Variable): + value_type = float + entity = Person + label = "DLA (mobility) (reported)" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/dla/dla_sc.py b/policyengine_uk/variables/gov/dwp/dla/dla_sc.py new file mode 100644 index 000000000..f8d7be2e9 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/dla/dla_sc.py @@ -0,0 +1,31 @@ +from policyengine_uk.model_api import * + + +class dla_sc(Variable): + label = "DLA (self-care)" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + dla_sc = parameters(period).gov.dwp.dla.self_care + category = person("dla_sc_category", period) + categories = category.possible_values + return ( + select( + [ + category == categories.HIGHER, + category == categories.MIDDLE, + category == categories.LOWER, + category == categories.NONE, + ], + [ + dla_sc.higher, + dla_sc.middle, + dla_sc.lower, + 0, + ], + ) + * WEEKS_IN_YEAR + ) diff --git a/policyengine_uk/variables/gov/dwp/dla/dla_sc_middle_plus.py b/policyengine_uk/variables/gov/dwp/dla/dla_sc_middle_plus.py new file mode 100644 index 000000000..4a00bc6ea --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/dla/dla_sc_middle_plus.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class dla_sc_middle_plus(Variable): + value_type = bool + entity = Person + label = "Receives at least middle-rate DLA (self-care)" + definition_period = YEAR + + def formula(person, period, parameters): + category = person("dla_sc_category", period) + categories = category.possible_values + return is_in( + category, + [categories.MIDDLE, categories.HIGHER], + ) diff --git a/policyengine_uk/variables/gov/dwp/dla/dla_sc_reported.py b/policyengine_uk/variables/gov/dwp/dla/dla_sc_reported.py new file mode 100644 index 000000000..959584891 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/dla/dla_sc_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class dla_sc_reported(Variable): + value_type = float + entity = Person + label = "DLA (self-care) (reported)" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/dla/receives_highest_dla_sc.py b/policyengine_uk/variables/gov/dwp/dla/receives_highest_dla_sc.py new file mode 100644 index 000000000..e125377a3 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/dla/receives_highest_dla_sc.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class receives_highest_dla_sc(Variable): + label = "Receives the highest DLA (self-care) category" + entity = Person + definition_period = YEAR + value_type = bool + + def formula(person, period, parameters): + category = person("dla_sc_category", period) + return category == category.possible_values.HIGHER diff --git a/policyengine_uk/variables/gov/dwp/dla/self_care.py b/policyengine_uk/variables/gov/dwp/dla/self_care.py deleted file mode 100644 index 10feeb5bd..000000000 --- a/policyengine_uk/variables/gov/dwp/dla/self_care.py +++ /dev/null @@ -1,65 +0,0 @@ -from policyengine_uk.model_api import * -from policyengine_uk.variables.misc.categories.lower_middle_or_higher import ( - LowerMiddleOrHigher, -) - - -class dla_sc_reported(Variable): - value_type = float - entity = Person - label = "DLA (self-care) (reported)" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" - - -class dla_sc(Variable): - label = "DLA (self-care)" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - def formula(person, period, parameters): - dla_sc = parameters(period).gov.dwp.dla.self_care - category = person("dla_sc_category", period) - return ( - select( - [ - category == LowerMiddleOrHigher.HIGHER, - category == LowerMiddleOrHigher.MIDDLE, - category == LowerMiddleOrHigher.LOWER, - category == LowerMiddleOrHigher.NONE, - ], - [ - dla_sc.higher, - dla_sc.middle, - dla_sc.lower, - 0, - ], - ) - * WEEKS_IN_YEAR - ) - - -class dla_sc_middle_plus(Variable): - value_type = bool - entity = Person - label = "Receives at least middle-rate DLA (self-care)" - definition_period = YEAR - - def formula(person, period, parameters): - return is_in( - person("dla_sc_category", period), - [LowerMiddleOrHigher.MIDDLE, LowerMiddleOrHigher.HIGHER], - ) - - -class receives_highest_dla_sc(Variable): - label = "Receives the highest DLA (self-care) category" - entity = Person - definition_period = YEAR - value_type = bool - - def formula(person, period, parameters): - return person("dla_sc_category", period) == LowerMiddleOrHigher.HIGHER diff --git a/policyengine_uk/variables/gov/dwp/education_grants.py b/policyengine_uk/variables/gov/dwp/education_grants.py new file mode 100644 index 000000000..8af735b87 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/education_grants.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class education_grants(Variable): + label = "Education grants" + documentation = "Grants for education" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP diff --git a/policyengine_uk/variables/gov/dwp/esa.py b/policyengine_uk/variables/gov/dwp/esa.py new file mode 100644 index 000000000..7549bb33b --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/esa.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class esa(Variable): + label = "ESA" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + adds = ["esa_contrib", "esa_income"] diff --git a/policyengine_uk/variables/gov/dwp/esa_contrib.py b/policyengine_uk/variables/gov/dwp/esa_contrib.py new file mode 100644 index 000000000..328b9dbce --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/esa_contrib.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class esa_contrib(Variable): + value_type = float + entity = Person + label = "ESA (contribution-based)" + definition_period = YEAR + unit = GBP + + adds = ["esa_contrib_reported"] diff --git a/policyengine_uk/variables/gov/dwp/esa_contrib_reported.py b/policyengine_uk/variables/gov/dwp/esa_contrib_reported.py new file mode 100644 index 000000000..305ca6bbe --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/esa_contrib_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class esa_contrib_reported(Variable): + value_type = float + entity = Person + label = "Employment and Support Allowance (contribution-based) (reported)" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/ESA_income.py b/policyengine_uk/variables/gov/dwp/esa_income.py similarity index 55% rename from policyengine_uk/variables/gov/dwp/ESA_income.py rename to policyengine_uk/variables/gov/dwp/esa_income.py index 753a3a8f5..672525b1a 100644 --- a/policyengine_uk/variables/gov/dwp/ESA_income.py +++ b/policyengine_uk/variables/gov/dwp/esa_income.py @@ -1,15 +1,6 @@ from policyengine_uk.model_api import * -class esa_income_reported(Variable): - value_type = float - entity = Person - label = "ESA (income-based) (reported amount)" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" - - class esa_income(Variable): value_type = float entity = BenUnit diff --git a/policyengine_uk/variables/gov/dwp/esa_income_reported.py b/policyengine_uk/variables/gov/dwp/esa_income_reported.py new file mode 100644 index 000000000..36bd0500d --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/esa_income_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class esa_income_reported(Variable): + value_type = float + entity = Person + label = "ESA (income-based) (reported amount)" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/iidb.py b/policyengine_uk/variables/gov/dwp/iidb.py new file mode 100644 index 000000000..94410f424 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/iidb.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class iidb(Variable): + value_type = float + entity = Person + label = "Industrial Injuries Disablement Benefit" + definition_period = YEAR + unit = GBP + + adds = ["iidb_reported"] diff --git a/policyengine_uk/variables/gov/dwp/IIDB.py b/policyengine_uk/variables/gov/dwp/iidb_reported.py similarity index 58% rename from policyengine_uk/variables/gov/dwp/IIDB.py rename to policyengine_uk/variables/gov/dwp/iidb_reported.py index a92124ef3..ab910eba8 100644 --- a/policyengine_uk/variables/gov/dwp/IIDB.py +++ b/policyengine_uk/variables/gov/dwp/iidb_reported.py @@ -1,16 +1,6 @@ from policyengine_uk.model_api import * -class iidb(Variable): - value_type = float - entity = Person - label = "Industrial Injuries Disablement Benefit" - definition_period = YEAR - unit = GBP - - adds = ["iidb_reported"] - - class iidb_reported(Variable): value_type = float entity = Person diff --git a/policyengine_uk/variables/gov/dwp/incapacity_benefit.py b/policyengine_uk/variables/gov/dwp/incapacity_benefit.py index ba29edaf1..6883d5c29 100644 --- a/policyengine_uk/variables/gov/dwp/incapacity_benefit.py +++ b/policyengine_uk/variables/gov/dwp/incapacity_benefit.py @@ -2,19 +2,10 @@ class incapacity_benefit(Variable): - value_type = float - entity = Person label = "Incapacity Benefit" + entity = Person definition_period = YEAR + value_type = float unit = GBP adds = ["incapacity_benefit_reported"] - - -class incapacity_benefit_reported(Variable): - value_type = float - entity = Person - label = "Incapacity Benefit (reported)" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/incapacity_benefit_reported.py b/policyengine_uk/variables/gov/dwp/incapacity_benefit_reported.py new file mode 100644 index 000000000..6556ea2ba --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/incapacity_benefit_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class incapacity_benefit_reported(Variable): + value_type = float + entity = Person + label = "Incapacity Benefit (reported)" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/income_support.py b/policyengine_uk/variables/gov/dwp/income_support.py index 82c4fd1ad..6ec0effd4 100644 --- a/policyengine_uk/variables/gov/dwp/income_support.py +++ b/policyengine_uk/variables/gov/dwp/income_support.py @@ -1,168 +1,6 @@ from policyengine_uk.model_api import * -class income_support_reported(Variable): - value_type = float - entity = Person - label = "Income Support (reported amount)" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" - - -class would_claim_IS(Variable): - value_type = bool - entity = BenUnit - label = "Would claim Income Support" - documentation = ( - "Whether this family would claim Income Support if eligible" - ) - definition_period = YEAR - - def formula(benunit, period, parameters): - reported_is = add(benunit, period, ["income_support_reported"]) > 0 - claims_all_entitled_benefits = benunit( - "claims_all_entitled_benefits", period - ) - return reported_is | claims_all_entitled_benefits - - -class income_support_applicable_income(Variable): - value_type = float - entity = BenUnit - label = "Relevant income for Income Support means test" - definition_period = YEAR - unit = GBP - - def formula(benunit, period, parameters): - IS = parameters(period).gov.dwp.income_support - INCOME_COMPONENTS = [ - "employment_income", - "self_employment_income", - "property_income", - "private_pension_income", - ] - bi = parameters(period).gov.contrib.ubi_center.basic_income - if bi.interactions.include_in_means_tests: - INCOME_COMPONENTS.append("basic_income") - income = add(benunit, period, INCOME_COMPONENTS) - tax = add( - benunit, - period, - ["income_tax", "national_insurance"], - ) - income += add(benunit, period, ["social_security_income"]) - income -= tax - income -= add(benunit, period, ["pension_contributions"]) * 0.5 - family_type = benunit("family_type", period) - families = family_type.possible_values - # Calculate income disregards for each family type. - mt = IS.means_test - single = family_type == families.SINGLE - income_disregard_single = single * mt.income_disregard_single - single = family_type == families.SINGLE - income_disregard_couple = ( - benunit("is_couple", period) * mt.income_disregard_couple - ) - lone_parent = family_type == families.LONE_PARENT - income_disregard_lone_parent = ( - lone_parent * mt.income_disregard_lone_parent - ) - income_disregard = ( - income_disregard_single - + income_disregard_couple - + income_disregard_lone_parent - ) * WEEKS_IN_YEAR - return max_(0, income - income_disregard) - - -class income_support_eligible(Variable): - value_type = bool - entity = BenUnit - label = "Whether eligible for Income Support" - definition_period = YEAR - - def formula(benunit, period, parameters): - youngest_child_5_or_under = benunit("youngest_child_age", period) <= 5 - lone_parent = benunit("is_lone_parent", period) - lone_parent_with_young_child = lone_parent & youngest_child_5_or_under - has_carers = add(benunit, period, ["is_carer_for_benefits"]) > 0 - none_SP_age = ~benunit.any(benunit.members("is_SP_age", period)) - has_esa_income = benunit("esa_income", period) > 0 - already_claiming = ( - add(benunit, period, ["income_support_reported"]) > 0 - ) - return ( - (has_carers | lone_parent_with_young_child) - & none_SP_age - & ~has_esa_income - & already_claiming - ) - - -class income_support_applicable_amount(Variable): - value_type = float - entity = BenUnit - label = "Applicable amount of Income Support" - definition_period = YEAR - unit = GBP - - def formula(benunit, period, parameters): - IS = parameters(period).gov.dwp.income_support - amounts = IS.amounts - younger_age = benunit("youngest_adult_age", period) - older_age = benunit("eldest_adult_age", period) - younger_under_18 = younger_age < 18 - younger_under_25 = younger_age < 25 - older_under_18 = older_age < 18 - has_children = benunit.any(benunit.members("is_child", period)) - single = benunit("is_single", period) - single_under_25 = single & ~has_children & younger_under_25 - single_over_25 = single & ~has_children & ~younger_under_25 - lone_young = single & has_children & younger_under_18 - lone_old = single & has_children & ~younger_under_18 - couple_young = ~single & older_under_18 - couple_mixed = ~single & ~older_under_18 & younger_under_18 - couple_old = ~single & ~younger_under_18 - personal_allowance_weekly = select( - [ - single_under_25, - single_over_25, - lone_young, - lone_old, - couple_young, - couple_mixed, - couple_old, - ], - [ - amounts.amount_16_24, - amounts.amount_over_25, - amounts.amount_lone_16_17, - amounts.amount_lone_over_18, - amounts.amount_couples_16_17, - amounts.amount_couples_age_gap, - amounts.amount_couples_over_18, - ], - ) - personal_allowance = personal_allowance_weekly * WEEKS_IN_YEAR - premiums = benunit("benefits_premiums", period) - return personal_allowance + premiums - - -class income_support_entitlement(Variable): - label = "IS entitlement" - entity = BenUnit - definition_period = YEAR - value_type = float - unit = GBP - - def formula(benunit, period, parameters): - amount = benunit("income_support_applicable_amount", period) - income = benunit("income_support_applicable_income", period) - eligible = benunit("income_support_eligible", period) - return max_(0, amount - income) * eligible - - class income_support(Variable): value_type = float entity = BenUnit @@ -171,11 +9,3 @@ class income_support(Variable): unit = GBP defined_for = "would_claim_IS" adds = ["income_support_entitlement"] - - -class baseline_income_support_entitlement(Variable): - label = "Income Support eligible (baseline)" - entity = BenUnit - definition_period = YEAR - value_type = float - unit = GBP diff --git a/policyengine_uk/variables/gov/dwp/income_support_applicable_amount.py b/policyengine_uk/variables/gov/dwp/income_support_applicable_amount.py new file mode 100644 index 000000000..9c8733abf --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/income_support_applicable_amount.py @@ -0,0 +1,50 @@ +from policyengine_uk.model_api import * + + +class income_support_applicable_amount(Variable): + value_type = float + entity = BenUnit + label = "Applicable amount of Income Support" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + IS = parameters(period).gov.dwp.income_support + amounts = IS.amounts + younger_age = benunit("youngest_adult_age", period) + older_age = benunit("eldest_adult_age", period) + younger_under_18 = younger_age < 18 + younger_under_25 = younger_age < 25 + older_under_18 = older_age < 18 + has_children = benunit.any(benunit.members("is_child", period)) + single = benunit("is_single", period) + single_under_25 = single & ~has_children & younger_under_25 + single_over_25 = single & ~has_children & ~younger_under_25 + lone_young = single & has_children & younger_under_18 + lone_old = single & has_children & ~younger_under_18 + couple_young = ~single & older_under_18 + couple_mixed = ~single & ~older_under_18 & younger_under_18 + couple_old = ~single & ~younger_under_18 + personal_allowance_weekly = select( + [ + single_under_25, + single_over_25, + lone_young, + lone_old, + couple_young, + couple_mixed, + couple_old, + ], + [ + amounts.amount_16_24, + amounts.amount_over_25, + amounts.amount_lone_16_17, + amounts.amount_lone_over_18, + amounts.amount_couples_16_17, + amounts.amount_couples_age_gap, + amounts.amount_couples_over_18, + ], + ) + personal_allowance = personal_allowance_weekly * WEEKS_IN_YEAR + premiums = benunit("benefits_premiums", period) + return personal_allowance + premiums diff --git a/policyengine_uk/variables/gov/dwp/income_support_applicable_income.py b/policyengine_uk/variables/gov/dwp/income_support_applicable_income.py new file mode 100644 index 000000000..a3652c601 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/income_support_applicable_income.py @@ -0,0 +1,50 @@ +from policyengine_uk.model_api import * + + +class income_support_applicable_income(Variable): + value_type = float + entity = BenUnit + label = "Relevant income for Income Support means test" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + IS = parameters(period).gov.dwp.income_support + INCOME_COMPONENTS = [ + "employment_income", + "self_employment_income", + "property_income", + "private_pension_income", + ] + bi = parameters(period).gov.contrib.ubi_center.basic_income + if bi.interactions.include_in_means_tests: + INCOME_COMPONENTS.append("basic_income") + income = add(benunit, period, INCOME_COMPONENTS) + tax = add( + benunit, + period, + ["income_tax", "national_insurance"], + ) + income += add(benunit, period, ["social_security_income"]) + income -= tax + income -= add(benunit, period, ["pension_contributions"]) * 0.5 + family_type = benunit("family_type", period) + families = family_type.possible_values + # Calculate income disregards for each family type. + mt = IS.means_test + single = family_type == families.SINGLE + income_disregard_single = single * mt.income_disregard_single + single = family_type == families.SINGLE + income_disregard_couple = ( + benunit("is_couple", period) * mt.income_disregard_couple + ) + lone_parent = family_type == families.LONE_PARENT + income_disregard_lone_parent = ( + lone_parent * mt.income_disregard_lone_parent + ) + income_disregard = ( + income_disregard_single + + income_disregard_couple + + income_disregard_lone_parent + ) * WEEKS_IN_YEAR + return max_(0, income - income_disregard) diff --git a/policyengine_uk/variables/gov/dwp/income_support_eligible.py b/policyengine_uk/variables/gov/dwp/income_support_eligible.py new file mode 100644 index 000000000..53fa76932 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/income_support_eligible.py @@ -0,0 +1,25 @@ +from policyengine_uk.model_api import * + + +class income_support_eligible(Variable): + value_type = bool + entity = BenUnit + label = "Whether eligible for Income Support" + definition_period = YEAR + + def formula(benunit, period, parameters): + youngest_child_5_or_under = benunit("youngest_child_age", period) <= 5 + lone_parent = benunit("is_lone_parent", period) + lone_parent_with_young_child = lone_parent & youngest_child_5_or_under + has_carers = add(benunit, period, ["is_carer_for_benefits"]) > 0 + none_SP_age = ~benunit.any(benunit.members("is_SP_age", period)) + has_esa_income = benunit("esa_income", period) > 0 + already_claiming = ( + add(benunit, period, ["income_support_reported"]) > 0 + ) + return ( + (has_carers | lone_parent_with_young_child) + & none_SP_age + & ~has_esa_income + & already_claiming + ) diff --git a/policyengine_uk/variables/gov/dwp/income_support_entitlement.py b/policyengine_uk/variables/gov/dwp/income_support_entitlement.py new file mode 100644 index 000000000..3cfebc284 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/income_support_entitlement.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class income_support_entitlement(Variable): + label = "IS entitlement" + entity = BenUnit + definition_period = YEAR + value_type = float + unit = GBP + + def formula(benunit, period, parameters): + amount = benunit("income_support_applicable_amount", period) + income = benunit("income_support_applicable_income", period) + eligible = benunit("income_support_eligible", period) + return max_(0, amount - income) * eligible diff --git a/policyengine_uk/variables/gov/dwp/income_support_reported.py b/policyengine_uk/variables/gov/dwp/income_support_reported.py new file mode 100644 index 000000000..57ddb7856 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/income_support_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class income_support_reported(Variable): + value_type = float + entity = Person + label = "Income Support (reported amount)" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/is_CTC_child_limit_exempt.py b/policyengine_uk/variables/gov/dwp/is_CTC_child_limit_exempt.py new file mode 100644 index 000000000..d6b9e3dc8 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/is_CTC_child_limit_exempt.py @@ -0,0 +1,29 @@ +from policyengine_uk.model_api import * + + +class is_CTC_child_limit_exempt(Variable): + value_type = bool + entity = Person + label = "Exemption from Child Tax Credit child limit" + documentation = "Exemption from Child Tax Credit limit on number of children based on birth year" + definition_period = YEAR + + def formula(person, period, parameters): + limit_year = parameters( + period + ).gov.dwp.tax_credits.child_tax_credit.limit.start_year + # Children must be born before April 2017. + # We use < 2017 as the closer approximation than <= 2017. + born_before_limit = person("birth_year", period) < limit_year + + # Reform proposal + age_exemption = parameters.gov.contrib.two_child_limit.age_exemption.child_tax_credit( + period + ) + if age_exemption > 0: + is_exempt = person.benunit.any( + person("age", period) < age_exemption + ) + return born_before_limit | is_exempt + + return born_before_limit diff --git a/policyengine_uk/variables/gov/dwp/is_CTC_eligible.py b/policyengine_uk/variables/gov/dwp/is_CTC_eligible.py new file mode 100644 index 000000000..04d63a0b7 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/is_CTC_eligible.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class is_CTC_eligible(Variable): + value_type = bool + entity = BenUnit + label = "Child Tax Credit eligibility" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 8" + + def formula(benunit, period, parameters): + already_claiming = ( + add(benunit, period, ["child_tax_credit_reported"]) > 0 + ) + return ( + benunit.any(benunit.members("is_child_for_CTC", period)) + & already_claiming + ) diff --git a/policyengine_uk/variables/gov/dwp/is_SP_age.py b/policyengine_uk/variables/gov/dwp/is_SP_age.py new file mode 100644 index 000000000..e7226290d --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/is_SP_age.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class is_SP_age(Variable): + value_type = bool + entity = Person + label = "Whether the person is State Pension Age" + definition_period = YEAR + + def formula(person, period, parameters): + age = person("age", period) + threshold = person("state_pension_age", period) + return age >= threshold diff --git a/policyengine_uk/variables/gov/dwp/is_WTC_eligible.py b/policyengine_uk/variables/gov/dwp/is_WTC_eligible.py new file mode 100644 index 000000000..6ab0ed507 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/is_WTC_eligible.py @@ -0,0 +1,45 @@ +from policyengine_uk.model_api import * + + +class is_WTC_eligible(Variable): + value_type = bool + entity = BenUnit + label = "Working Tax Credit eligibility" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 10" + + def formula(benunit, period, parameters): + WTC = parameters(period).gov.dwp.tax_credits.working_tax_credit + person = benunit.members + person_hours = person("weekly_hours", period) + total_hours = benunit.sum(person_hours) + max_person_hours = benunit.max(person_hours) + has_disabled_adults = benunit("num_disabled_adults", period) > 0 + family_type = benunit("family_type", period) + families = family_type.possible_values + old = person("age", period.this_year) >= WTC.min_hours.old_age + has_old = benunit.any(old) + lone_parent = family_type == families.LONE_PARENT + couple_with_children = family_type == families.COUPLE_WITH_CHILDREN + eldest_25_plus = benunit("eldest_adult_age", period) >= 25 + youngest_under_60 = benunit("youngest_adult_age", period) < 60 + # Calculate WTC eligibility group. + lower_req = has_disabled_adults | has_old | lone_parent + medium_req = couple_with_children & ~lower_req + higher_req = eldest_25_plus & youngest_under_60 + # Calculate eligibility for each WTC group. + meets_lower = total_hours >= WTC.min_hours.lower + meets_medium_total_hours = ( + total_hours >= WTC.min_hours.couple_with_children + ) + meets_medium_person_hours = max_person_hours >= WTC.min_hours.lower + meets_medium = meets_medium_total_hours & meets_medium_person_hours + meets_higher = total_hours >= WTC.min_hours.default + already_claiming = ( + add(benunit, period, ["working_tax_credit_reported"]) > 0 + ) + return ( + (lower_req & meets_lower) + | (medium_req & meets_medium) + | (higher_req & meets_higher) + ) & already_claiming diff --git a/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt.py b/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt.py new file mode 100644 index 000000000..9f968edc0 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/is_benefit_cap_exempt.py @@ -0,0 +1,23 @@ +from policyengine_uk.model_api import * + + +class is_benefit_cap_exempt(Variable): + value_type = bool + entity = BenUnit + label = "Whether exempt from the benefits cap" + definition_period = YEAR + + def formula(benunit, period, parameters): + QUAL_PERSONAL_BENEFITS = [ + "carers_allowance", + "dla", + "esa_contrib", + ] + QUAL_BENUNIT_BENEFITS = ["working_tax_credit", "esa_income"] + qualifying_benunit_benefits = add( + benunit, period, QUAL_BENUNIT_BENEFITS + ) + qualifying_personal_benefits = add( + benunit, period, QUAL_PERSONAL_BENEFITS + ) + return (qualifying_personal_benefits + qualifying_benunit_benefits) > 0 diff --git a/policyengine_uk/variables/gov/dwp/is_child_for_CTC.py b/policyengine_uk/variables/gov/dwp/is_child_for_CTC.py new file mode 100644 index 000000000..9efe552ca --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/is_child_for_CTC.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class is_child_for_CTC(Variable): + value_type = bool + entity = Person + label = "Child eligible for Child Tax Credit" + definition_period = YEAR + reference = "Tax Credits Act 2002 s. 8" + + def formula(person, period, parameters): + return person("is_child_or_QYP", period) diff --git a/policyengine_uk/variables/gov/dwp/jsa.py b/policyengine_uk/variables/gov/dwp/jsa.py new file mode 100644 index 000000000..2d6cd849d --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/jsa.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class jsa(Variable): + value_type = float + entity = BenUnit + label = "Amount of Jobseeker's Allowance for this family" + definition_period = YEAR + unit = GBP + adds = ["jsa_income", "jsa_contrib"] diff --git a/policyengine_uk/variables/gov/dwp/jsa_contrib.py b/policyengine_uk/variables/gov/dwp/jsa_contrib.py new file mode 100644 index 000000000..f77a90ded --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/jsa_contrib.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class jsa_contrib(Variable): + value_type = float + entity = Person + label = "JSA (contribution-based)" + definition_period = YEAR + unit = GBP + + adds = ["jsa_contrib_reported"] diff --git a/policyengine_uk/variables/gov/dwp/JSA_contrib.py b/policyengine_uk/variables/gov/dwp/jsa_contrib_reported.py similarity index 59% rename from policyengine_uk/variables/gov/dwp/JSA_contrib.py rename to policyengine_uk/variables/gov/dwp/jsa_contrib_reported.py index c3983e4aa..24ecb0d70 100644 --- a/policyengine_uk/variables/gov/dwp/JSA_contrib.py +++ b/policyengine_uk/variables/gov/dwp/jsa_contrib_reported.py @@ -1,16 +1,6 @@ from policyengine_uk.model_api import * -class jsa_contrib(Variable): - value_type = float - entity = Person - label = "JSA (contribution-based)" - definition_period = YEAR - unit = GBP - - adds = ["jsa_contrib_reported"] - - class jsa_contrib_reported(Variable): value_type = float entity = Person diff --git a/policyengine_uk/variables/gov/dwp/jsa_income.py b/policyengine_uk/variables/gov/dwp/jsa_income.py new file mode 100644 index 000000000..80ba333fd --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/jsa_income.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class jsa_income(Variable): + value_type = float + entity = BenUnit + label = "JSA (income-based)" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + return benunit("jsa_income_reported", period) diff --git a/policyengine_uk/variables/gov/dwp/jsa_income_reported.py b/policyengine_uk/variables/gov/dwp/jsa_income_reported.py new file mode 100644 index 000000000..298017acc --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/jsa_income_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class jsa_income_reported(Variable): + value_type = float + entity = Person + label = "JSA (income-based) (reported amount)" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/maternity_allowance.py b/policyengine_uk/variables/gov/dwp/maternity_allowance.py index 43a6157f8..3f111aac2 100644 --- a/policyengine_uk/variables/gov/dwp/maternity_allowance.py +++ b/policyengine_uk/variables/gov/dwp/maternity_allowance.py @@ -1,14 +1,6 @@ from policyengine_uk.model_api import * -class maternity_allowance_reported(Variable): - value_type = float - entity = Person - label = "Maternity allowance" - definition_period = YEAR - unit = GBP - - class maternity_allowance(Variable): label = "Maternity Allowance" entity = Person @@ -17,22 +9,3 @@ class maternity_allowance(Variable): unit = GBP adds = ["maternity_allowance_reported"] - - -class ssmg_reported(Variable): - label = "Sure Start Maternity Grant (reported)" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - uprating = "gov.obr.consumer_price_index" - - -class ssmg(Variable): - label = "Sure Start Maternity Grant" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - adds = ["ssmg_reported"] diff --git a/policyengine_uk/variables/gov/dwp/maternity_allowance_reported.py b/policyengine_uk/variables/gov/dwp/maternity_allowance_reported.py new file mode 100644 index 000000000..a13cb4150 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/maternity_allowance_reported.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class maternity_allowance_reported(Variable): + value_type = float + entity = Person + label = "Maternity allowance" + definition_period = YEAR + unit = GBP diff --git a/policyengine_uk/variables/gov/dwp/new_state_pension.py b/policyengine_uk/variables/gov/dwp/new_state_pension.py new file mode 100644 index 000000000..c269b7bab --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/new_state_pension.py @@ -0,0 +1,21 @@ +from policyengine_uk.model_api import * + + +class new_state_pension(Variable): + label = "new State Pension" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + simulation = person.simulation + if simulation.dataset is None: + return 0 + + return where( + person("state_pension_type", period) == StatePensionType.NEW, + parameters(period).gov.dwp.state_pension.new_state_pension.amount + * WEEKS_IN_YEAR, + 0, + ) diff --git a/policyengine_uk/variables/gov/dwp/pension_credit/would_claim.py b/policyengine_uk/variables/gov/dwp/pension_credit/would_claim.py index 3cdfff959..48f9b020d 100644 --- a/policyengine_uk/variables/gov/dwp/pension_credit/would_claim.py +++ b/policyengine_uk/variables/gov/dwp/pension_credit/would_claim.py @@ -1,7 +1,4 @@ from policyengine_uk.model_api import * -from policyengine_uk.variables.gov.dwp.pension_credit.pension_credit import ( - pension_credit, -) class would_claim_pc(Variable): diff --git a/policyengine_uk/variables/gov/dwp/pip/daily_living.py b/policyengine_uk/variables/gov/dwp/pip/pip_dl.py similarity index 62% rename from policyengine_uk/variables/gov/dwp/pip/daily_living.py rename to policyengine_uk/variables/gov/dwp/pip/pip_dl.py index 25e58165b..3ed4371cf 100644 --- a/policyengine_uk/variables/gov/dwp/pip/daily_living.py +++ b/policyengine_uk/variables/gov/dwp/pip/pip_dl.py @@ -2,15 +2,6 @@ from policyengine_uk.variables.gov.dwp.pip.pip import PIPCategory -class pip_dl_reported(Variable): - value_type = float - entity = Person - label = "PIP (daily living) (reported)" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" - - class pip_dl(Variable): label = "PIP (daily living)" entity = Person @@ -36,13 +27,3 @@ def formula(person, period, parameters): ) * WEEKS_IN_YEAR ) - - -class receives_enhanced_pip_dl(Variable): - label = "Receives enhanced PIP (daily living)" - entity = Person - definition_period = YEAR - value_type = bool - - def formula(person, period, parameters): - return person("pip_dl_category", period) == PIPCategory.ENHANCED diff --git a/policyengine_uk/variables/gov/dwp/pip/pip_dl_reported.py b/policyengine_uk/variables/gov/dwp/pip/pip_dl_reported.py new file mode 100644 index 000000000..b73c8cf50 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/pip/pip_dl_reported.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.gov.dwp.pip.pip import PIPCategory + + +class pip_dl_reported(Variable): + value_type = float + entity = Person + label = "PIP (daily living) (reported)" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/pip/mobility.py b/policyengine_uk/variables/gov/dwp/pip/pip_m.py similarity index 79% rename from policyengine_uk/variables/gov/dwp/pip/mobility.py rename to policyengine_uk/variables/gov/dwp/pip/pip_m.py index cffa3435e..0e88b1efa 100644 --- a/policyengine_uk/variables/gov/dwp/pip/mobility.py +++ b/policyengine_uk/variables/gov/dwp/pip/pip_m.py @@ -2,15 +2,6 @@ from policyengine_uk.variables.gov.dwp.pip.pip import PIPCategory -class pip_m_reported(Variable): - value_type = float - entity = Person - label = "PIP (mobility) (reported)" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" - - class pip_m(Variable): label = "PIP (mobility)" entity = Person diff --git a/policyengine_uk/variables/gov/dwp/pip/pip_m_reported.py b/policyengine_uk/variables/gov/dwp/pip/pip_m_reported.py new file mode 100644 index 000000000..29348ee5f --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/pip/pip_m_reported.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.gov.dwp.pip.pip import PIPCategory + + +class pip_m_reported(Variable): + value_type = float + entity = Person + label = "PIP (mobility) (reported)" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/pip/receives_enhanced_pip_dl.py b/policyengine_uk/variables/gov/dwp/pip/receives_enhanced_pip_dl.py new file mode 100644 index 000000000..80102910a --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/pip/receives_enhanced_pip_dl.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.gov.dwp.pip.pip import PIPCategory + + +class receives_enhanced_pip_dl(Variable): + label = "Receives enhanced PIP (daily living)" + entity = Person + definition_period = YEAR + value_type = bool + + def formula(person, period, parameters): + return person("pip_dl_category", period) == PIPCategory.ENHANCED diff --git a/policyengine_uk/variables/gov/dwp/sda.py b/policyengine_uk/variables/gov/dwp/sda.py index 3641f0e11..011aeb176 100644 --- a/policyengine_uk/variables/gov/dwp/sda.py +++ b/policyengine_uk/variables/gov/dwp/sda.py @@ -15,12 +15,3 @@ def formula(person, period, parameters): # age-related addition. rate = parameters(period).gov.dwp.sda.maximum return reported * rate * WEEKS_IN_YEAR - - -class sda_reported(Variable): - value_type = float - entity = Person - label = "Severe Disablement Allowance (reported)" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/sda_reported.py b/policyengine_uk/variables/gov/dwp/sda_reported.py new file mode 100644 index 000000000..dba1e3a04 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/sda_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class sda_reported(Variable): + value_type = float + entity = Person + label = "Severe Disablement Allowance (reported)" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/ssmg.py b/policyengine_uk/variables/gov/dwp/ssmg.py new file mode 100644 index 000000000..6cef4b50d --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/ssmg.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class ssmg(Variable): + label = "Sure Start Maternity Grant" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + adds = ["ssmg_reported"] diff --git a/policyengine_uk/variables/gov/dwp/ssmg_reported.py b/policyengine_uk/variables/gov/dwp/ssmg_reported.py new file mode 100644 index 000000000..6f7a8ba8c --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/ssmg_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class ssmg_reported(Variable): + label = "Sure Start Maternity Grant (reported)" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/state_pension.py b/policyengine_uk/variables/gov/dwp/state_pension.py deleted file mode 100644 index b853ac95f..000000000 --- a/policyengine_uk/variables/gov/dwp/state_pension.py +++ /dev/null @@ -1,146 +0,0 @@ -from policyengine_uk.model_api import * - - -class state_pension_age(Variable): - value_type = float - entity = Person - label = "State Pension age for this person" - definition_period = YEAR - unit = "year" - - def formula(person, period, parameters): - SP = parameters(period).gov.dwp.state_pension - return where(person("is_male", period), SP.age.male, SP.age.female) - - -class is_SP_age(Variable): - value_type = bool - entity = Person - label = "Whether the person is State Pension Age" - definition_period = YEAR - - def formula(person, period, parameters): - age = person("age", period) - threshold = person("state_pension_age", period) - return age >= threshold - - -class StatePensionType(Enum): - BASIC = "basic" - NEW = "new" - NONE = "none" - - -class state_pension_type(Variable): - label = "State Pension type" - entity = Person - definition_period = YEAR - value_type = Enum - possible_values = StatePensionType - default_value = StatePensionType.BASIC - - def formula(person, period, parameters): - sp = parameters.gov.dwp.state_pension - male = person("is_male", period) - last_entry = sp.new_state_pension.active.values_list[0] - is_sp_age = person("is_SP_age", period) - if not last_entry: - values_if_sp_age = where( - is_sp_age, StatePensionType.BASIC, StatePensionType.NONE - ) - else: - instant = last_entry.instant_str - years_since_instant = period.start.year - int(instant[:4]) - male_age = sp.age.male(instant) + years_since_instant - female_age = sp.age.female(instant) + years_since_instant - age = person("age", period) - over_age = where(male, age >= male_age, age >= female_age) - values_if_sp_age = where( - over_age, StatePensionType.BASIC, StatePensionType.NEW - ) - - return where(is_sp_age, values_if_sp_age, StatePensionType.NONE) - - -class basic_state_pension(Variable): - label = "basic State Pension" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - def formula(person, period, parameters): - simulation = person.simulation - if simulation.dataset is None: - return 0 - - data_year = simulation.dataset.time_period - reported = person("state_pension_reported", data_year) / WEEKS_IN_YEAR - type = person("state_pension_type", period) - maximum_basic_sp = parameters( - data_year - ).gov.dwp.state_pension.basic_state_pension.amount - amount_in_data_year = where( - type == StatePensionType.BASIC, min_(reported, maximum_basic_sp), 0 - ) - triple_lock = parameters.gov.dwp.state_pension.triple_lock.index - uprating_since_data_year = triple_lock(period) / triple_lock(data_year) - return amount_in_data_year * uprating_since_data_year * WEEKS_IN_YEAR - - -class additional_state_pension(Variable): - label = "additional State Pension" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - def formula(person, period, parameters): - simulation = person.simulation - if simulation.dataset is None: - return 0 - - data_year = simulation.dataset.time_period - reported = person("state_pension_reported", data_year) / WEEKS_IN_YEAR - type = person("state_pension_type", data_year) - maximum_basic_sp = parameters( - data_year - ).gov.dwp.state_pension.basic_state_pension.amount - amount_in_data_year = where( - type == StatePensionType.BASIC, - max_(reported - maximum_basic_sp, 0), - 0, - ) - return amount_in_data_year * WEEKS_IN_YEAR - - -class new_state_pension(Variable): - label = "new State Pension" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - def formula(person, period, parameters): - simulation = person.simulation - if simulation.dataset is None: - return 0 - - return where( - person("state_pension_type", period) == StatePensionType.NEW, - parameters(period).gov.dwp.state_pension.new_state_pension.amount - * WEEKS_IN_YEAR, - 0, - ) - - -class state_pension_reported(Variable): - value_type = float - entity = Person - label = "Reported income from the State Pension" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" - - def formula_2022(person, period, parameters): - return person("state_pension_reported", period.last_year) diff --git a/policyengine_uk/variables/gov/dwp/state_pension_age.py b/policyengine_uk/variables/gov/dwp/state_pension_age.py new file mode 100644 index 000000000..641e07e80 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/state_pension_age.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class state_pension_age(Variable): + value_type = float + entity = Person + label = "State Pension age for this person" + definition_period = YEAR + unit = "year" + + def formula(person, period, parameters): + SP = parameters(period).gov.dwp.state_pension + return where(person("is_male", period), SP.age.male, SP.age.female) diff --git a/policyengine_uk/variables/gov/dwp/state_pension_reported.py b/policyengine_uk/variables/gov/dwp/state_pension_reported.py new file mode 100644 index 000000000..8870de5ba --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/state_pension_reported.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class state_pension_reported(Variable): + value_type = float + entity = Person + label = "Reported income from the State Pension" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" + + def formula_2022(person, period, parameters): + return person("state_pension_reported", period.last_year) diff --git a/policyengine_uk/variables/gov/dwp/state_pension_type.py b/policyengine_uk/variables/gov/dwp/state_pension_type.py new file mode 100644 index 000000000..1340ecfdf --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/state_pension_type.py @@ -0,0 +1,38 @@ +from policyengine_uk.model_api import * + + +class StatePensionType(Enum): + BASIC = "basic" + NEW = "new" + NONE = "none" + + +class state_pension_type(Variable): + label = "State Pension type" + entity = Person + definition_period = YEAR + value_type = Enum + possible_values = StatePensionType + default_value = StatePensionType.BASIC + + def formula(person, period, parameters): + sp = parameters.gov.dwp.state_pension + male = person("is_male", period) + last_entry = sp.new_state_pension.active.values_list[0] + is_sp_age = person("is_SP_age", period) + if not last_entry: + values_if_sp_age = where( + is_sp_age, StatePensionType.BASIC, StatePensionType.NONE + ) + else: + instant = last_entry.instant_str + years_since_instant = period.start.year - int(instant[:4]) + male_age = sp.age.male(instant) + years_since_instant + female_age = sp.age.female(instant) + years_since_instant + age = person("age", period) + over_age = where(male, age >= male_age, age >= female_age) + values_if_sp_age = where( + over_age, StatePensionType.BASIC, StatePensionType.NEW + ) + + return where(is_sp_age, values_if_sp_age, StatePensionType.NONE) diff --git a/policyengine_uk/variables/gov/dwp/student.py b/policyengine_uk/variables/gov/dwp/student.py deleted file mode 100644 index 05a3a65a9..000000000 --- a/policyengine_uk/variables/gov/dwp/student.py +++ /dev/null @@ -1,55 +0,0 @@ -from policyengine_uk.model_api import * - - -class student_loans(Variable): - value_type = float - entity = Person - label = "Student loans" - definition_period = YEAR - unit = GBP - - -class adult_ema(Variable): - label = "Adult EMA" - documentation = "Educational Maintenance Allowance for adults" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - -class child_ema(Variable): - label = "Child EMA" - documentation = "Educational Maintenance Allowance for children" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - -class access_fund(Variable): - label = "Access Fund" - documentation = "Access Fund for educational assistance" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - -class education_grants(Variable): - label = "Education grants" - documentation = "Grants for education" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - -class student_payments(Variable): - value_type = float - entity = Person - label = "Student payments" - definition_period = YEAR - unit = GBP - - adds = ["adult_ema", "child_ema", "access_fund", "education_grants"] diff --git a/policyengine_uk/variables/gov/dwp/student_loans.py b/policyengine_uk/variables/gov/dwp/student_loans.py new file mode 100644 index 000000000..11add09f0 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/student_loans.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class student_loans(Variable): + value_type = float + entity = Person + label = "Student loans" + definition_period = YEAR + unit = GBP diff --git a/policyengine_uk/variables/gov/dwp/student_payments.py b/policyengine_uk/variables/gov/dwp/student_payments.py new file mode 100644 index 000000000..f032a8dfd --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/student_payments.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class student_payments(Variable): + value_type = float + entity = Person + label = "Student payments" + definition_period = YEAR + unit = GBP + + adds = ["adult_ema", "child_ema", "access_fund", "education_grants"] diff --git a/policyengine_uk/variables/gov/dwp/tax_credits.py b/policyengine_uk/variables/gov/dwp/tax_credits.py index 42e10f5fe..d0918da21 100644 --- a/policyengine_uk/variables/gov/dwp/tax_credits.py +++ b/policyengine_uk/variables/gov/dwp/tax_credits.py @@ -1,524 +1,6 @@ from policyengine_uk.model_api import * -class working_tax_credit_reported(Variable): - value_type = float - entity = Person - label = "Working Tax Credit" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" - - -class child_tax_credit_reported(Variable): - value_type = float - entity = Person - label = "Working Tax Credit" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" - - -class tax_credits_applicable_income(Variable): - value_type = float - entity = BenUnit - label = "Applicable income for Tax Credits" - definition_period = YEAR - unit = GBP - reference = "The Tax Credits (Definition and Calculation of Income) Regulations 2002 s. 3" - - def formula(benunit, period, parameters): - TC = parameters(period).gov.dwp.tax_credits - STEP_1_COMPONENTS = [ - "private_pension_income", - "savings_interest_income", - "dividend_income", - "property_income", - ] - income = add(benunit, period, STEP_1_COMPONENTS) - income = max_(income - TC.means_test.non_earned_disregard, 0) - STEP_2_COMPONENTS = [ - "employment_income", - "self_employment_income", - "social_security_income", - "miscellaneous_income", - ] - bi = parameters(period).gov.contrib.ubi_center.basic_income - if bi.interactions.include_in_means_tests: - STEP_2_COMPONENTS.append("basic_income") - income += add(benunit, period, STEP_2_COMPONENTS) - EXEMPT_BENEFITS = ["income_support", "esa_income", "jsa_income"] - on_exempt_benefits = add(benunit, period, EXEMPT_BENEFITS) > 0 - return income * ~on_exempt_benefits - - -class is_CTC_child_limit_exempt(Variable): - value_type = bool - entity = Person - label = "Exemption from Child Tax Credit child limit" - documentation = "Exemption from Child Tax Credit limit on number of children based on birth year" - definition_period = YEAR - - def formula(person, period, parameters): - limit_year = parameters( - period - ).gov.dwp.tax_credits.child_tax_credit.limit.start_year - # Children must be born before April 2017. - # We use < 2017 as the closer approximation than <= 2017. - born_before_limit = person("birth_year", period) < limit_year - - # Reform proposal - age_exemption = parameters.gov.contrib.two_child_limit.age_exemption.child_tax_credit( - period - ) - if age_exemption > 0: - is_exempt = person.benunit.any( - person("age", period) < age_exemption - ) - return born_before_limit | is_exempt - - return born_before_limit - - -class is_child_for_CTC(Variable): - value_type = bool - entity = Person - label = "Child eligible for Child Tax Credit" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 8" - - def formula(person, period, parameters): - return person("is_child_or_QYP", period) - - -class is_CTC_eligible(Variable): - value_type = bool - entity = BenUnit - label = "Child Tax Credit eligibility" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 8" - - def formula(benunit, period, parameters): - already_claiming = ( - add(benunit, period, ["child_tax_credit_reported"]) > 0 - ) - return ( - benunit.any(benunit.members("is_child_for_CTC", period)) - & already_claiming - ) - - -class would_claim_CTC(Variable): - value_type = bool - entity = BenUnit - label = "Would claim Child Tax Credit" - documentation = ( - "Whether this family would claim Child Tax Credit if eligible" - ) - definition_period = YEAR - - def formula(benunit, period, parameters): - reported_ctc = add(benunit, period, ["child_tax_credit_reported"]) > 0 - claims_all_entitled_benefits = benunit( - "claims_all_entitled_benefits", period - ) - return reported_ctc | claims_all_entitled_benefits - - -class CTC_maximum_rate(Variable): - value_type = float - entity = BenUnit - label = "Maximum Child Tax Credit" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 9" - unit = GBP - - adds = [ - "CTC_family_element", - "CTC_child_element", - "CTC_disabled_child_element", - "CTC_severely_disabled_child_element", - ] - - -class CTC_family_element(Variable): - value_type = float - entity = BenUnit - label = "CTC entitlement in the Family Element" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 9" - unit = GBP - defined_for = "is_CTC_eligible" - - def formula(benunit, period, parameters): - return parameters( - period - ).gov.dwp.tax_credits.child_tax_credit.elements.family_element - - -class CTC_child_element(Variable): - value_type = float - entity = BenUnit - label = "Child Tax Credit child element" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 9" - unit = GBP - - def formula(benunit, period, parameters): - person = benunit.members - CTC = parameters(period).gov.dwp.tax_credits.child_tax_credit - is_child_for_CTC = person("is_child_for_CTC", period) - is_CTC_child_limit_exempt = person("is_CTC_child_limit_exempt", period) - exempt_child = is_child_for_CTC & is_CTC_child_limit_exempt - exempt_children = benunit.sum(exempt_child) - child_limit = CTC.limit.child_count - spaces_left = max_(0, child_limit - exempt_children) - non_exempt_children = min_( - spaces_left, benunit.sum(is_child_for_CTC) - exempt_children - ) - children = exempt_children + non_exempt_children - return CTC.elements.child_element * children - - -class ctc_child_limit_affected(Variable): - label = "affected by the CTC child limit" - entity = BenUnit - definition_period = YEAR - value_type = bool - - def formula(benunit, period, parameters): - person = benunit.members - CTC = parameters(period).gov.dwp.tax_credits.child_tax_credit - is_child_for_CTC = person("is_child_for_CTC", period) - is_CTC_child_limit_exempt = person("is_CTC_child_limit_exempt", period) - exempt_child = is_child_for_CTC & is_CTC_child_limit_exempt - exempt_children = benunit.sum(exempt_child) - child_limit = CTC.limit.child_count - spaces_left = max_(0, child_limit - exempt_children) - non_exempt_children = min_( - spaces_left, benunit.sum(is_child_for_CTC) - exempt_children - ) - return ( - exempt_children + non_exempt_children - < benunit.sum(is_child_for_CTC) - ) & (benunit("child_tax_credit", period) > 0) - - -class CTC_disabled_child_element(Variable): - value_type = float - entity = BenUnit - label = "CTC entitlement from disabled child elements" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 9" - unit = GBP - - def formula(benunit, period, parameters): - person = benunit.members - is_child_for_CTC = person("is_child_for_CTC", period) - is_disabled_for_benefits = person("is_disabled_for_benefits", period) - is_disabled_child = is_child_for_CTC & is_disabled_for_benefits - disabled_children = benunit.sum(is_disabled_child) - CTC = parameters(period).gov.dwp.tax_credits.child_tax_credit - amount = CTC.elements.dis_child_element * disabled_children - return benunit("is_CTC_eligible", period) * amount - - -class CTC_severely_disabled_child_element(Variable): - value_type = float - entity = BenUnit - label = "CTC entitlement from severely disabled child elements" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 9" - unit = GBP - defined_for = "is_CTC_eligible" - - def formula(benunit, period, parameters): - person = benunit.members - is_child_for_CTC = person("is_child_for_CTC", period) - is_severely_disabled_for_benefits = person( - "is_severely_disabled_for_benefits", period - ) - is_severely_disabled_child = ( - is_child_for_CTC & is_severely_disabled_for_benefits - ) - severely_disabled_children = benunit.sum(is_severely_disabled_child) - CTC = parameters(period).gov.dwp.tax_credits.child_tax_credit - return ( - CTC.elements.severe_dis_child_element * severely_disabled_children - ) - - -class is_WTC_eligible(Variable): - value_type = bool - entity = BenUnit - label = "Working Tax Credit eligibility" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 10" - - def formula(benunit, period, parameters): - WTC = parameters(period).gov.dwp.tax_credits.working_tax_credit - person = benunit.members - person_hours = person("weekly_hours", period) - total_hours = benunit.sum(person_hours) - max_person_hours = benunit.max(person_hours) - has_disabled_adults = benunit("num_disabled_adults", period) > 0 - family_type = benunit("family_type", period) - families = family_type.possible_values - old = person("age", period.this_year) >= WTC.min_hours.old_age - has_old = benunit.any(old) - lone_parent = family_type == families.LONE_PARENT - couple_with_children = family_type == families.COUPLE_WITH_CHILDREN - eldest_25_plus = benunit("eldest_adult_age", period) >= 25 - youngest_under_60 = benunit("youngest_adult_age", period) < 60 - # Calculate WTC eligibility group. - lower_req = has_disabled_adults | has_old | lone_parent - medium_req = couple_with_children & ~lower_req - higher_req = eldest_25_plus & youngest_under_60 - # Calculate eligibility for each WTC group. - meets_lower = total_hours >= WTC.min_hours.lower - meets_medium_total_hours = ( - total_hours >= WTC.min_hours.couple_with_children - ) - meets_medium_person_hours = max_person_hours >= WTC.min_hours.lower - meets_medium = meets_medium_total_hours & meets_medium_person_hours - meets_higher = total_hours >= WTC.min_hours.default - already_claiming = ( - add(benunit, period, ["working_tax_credit_reported"]) > 0 - ) - return ( - (lower_req & meets_lower) - | (medium_req & meets_medium) - | (higher_req & meets_higher) - ) & already_claiming - - -class would_claim_WTC(Variable): - value_type = bool - entity = BenUnit - label = "Would claim Working Tax Credit" - documentation = ( - "Whether this family would claim Working Tax Credit if eligible" - ) - definition_period = YEAR - - def formula(benunit, period, parameters): - reported_wtc = ( - add(benunit, period, ["working_tax_credit_reported"]) > 0 - ) - claims_all_entitled_benefits = benunit( - "claims_all_entitled_benefits", period - ) - return reported_wtc | claims_all_entitled_benefits - - -class WTC_maximum_rate(Variable): - value_type = float - entity = BenUnit - label = "Working Tax Credit maximum rate" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 11" - unit = GBP - - adds = [ - "WTC_basic_element", - "WTC_couple_element", - "WTC_lone_parent_element", - "WTC_disabled_element", - "WTC_severely_disabled_element", - "WTC_worker_element", - "WTC_childcare_element", - ] - - -class WTC_basic_element(Variable): - value_type = float - entity = BenUnit - label = "Working Tax Credit basic element" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 11" - unit = GBP - defined_for = "is_WTC_eligible" - - def formula(benunit, period, parameters): - return parameters( - period - ).gov.dwp.tax_credits.working_tax_credit.elements.basic - - -class WTC_couple_element(Variable): - value_type = float - entity = BenUnit - label = "Working Tax Credit couple element" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 11" - unit = GBP - defined_for = "is_WTC_eligible" - - def formula(benunit, period, parameters): - WTC = parameters(period).gov.dwp.tax_credits.working_tax_credit - relation_type = benunit("relation_type", period) - relations = relation_type.possible_values - return (relation_type == relations.COUPLE) * WTC.elements.couple - - -class WTC_lone_parent_element(Variable): - value_type = float - entity = BenUnit - label = "Working Tax Credit lone parent element" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 11" - unit = GBP - defined_for = "is_WTC_eligible" - - def formula(benunit, period, parameters): - WTC = parameters(period).gov.dwp.tax_credits.working_tax_credit - family_type = benunit("family_type", period) - families = family_type.possible_values - lone_parent = family_type == families.LONE_PARENT - return lone_parent * WTC.elements.lone_parent - - -class WTC_disabled_element(Variable): - value_type = float - entity = BenUnit - label = "Working Tax Credit disabled element" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 11" - unit = GBP - defined_for = "is_WTC_eligible" - - def formula(benunit, period, parameters): - WTC = parameters(period).gov.dwp.tax_credits.working_tax_credit - person = benunit.members - person_meets_hours = ( - person("weekly_hours", period) >= WTC.min_hours.lower - ) - person_qualifies = ( - person_meets_hours - & person("is_disabled_for_benefits", period) - & person("is_adult", period) - ) - qualifies = benunit.any(person_qualifies) - return qualifies * WTC.elements.disabled - - -class WTC_severely_disabled_element(Variable): - value_type = float - entity = BenUnit - label = "Working Tax Credit severely disabled element" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 11" - unit = GBP - - def formula(benunit, period, parameters): - WTC = parameters(period).gov.dwp.tax_credits.working_tax_credit - amount = ( - benunit("num_severely_disabled_adults", period) - * WTC.elements.severely_disabled - ) - return benunit("is_WTC_eligible", period) * amount - - -class WTC_worker_element(Variable): - value_type = float - entity = BenUnit - label = "Working Tax Credit worker element" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 11" - unit = GBP - defined_for = "is_WTC_eligible" - - def formula(benunit, period, parameters): - WTC = parameters(period).gov.dwp.tax_credits.working_tax_credit - hours = add(benunit, period, ["weekly_hours"]) - meets_hours_requirement = hours >= WTC.min_hours.default - return meets_hours_requirement * WTC.elements.worker - - -class WTC_childcare_element(Variable): - value_type = float - entity = BenUnit - label = "Working Tax Credit childcare element" - definition_period = YEAR - reference = "Tax Credits Act 2002 s. 11" - unit = GBP - defined_for = "is_WTC_eligible" - - def formula(benunit, period, parameters): - WTC = parameters(period).gov.dwp.tax_credits.working_tax_credit - num_children = benunit("num_children", period) - childcare_1 = (num_children == 1) * WTC.elements.childcare_1 - childcare_2 = (num_children > 1) * WTC.elements.childcare_2 - max_childcare_amount = (childcare_1 + childcare_2) * WEEKS_IN_YEAR - expenses = add(benunit, period, ["childcare_expenses"]) - eligible_expenses = min_(max_childcare_amount, expenses) - return WTC.elements.childcare_coverage * eligible_expenses - - -class tax_credits_reduction(Variable): - value_type = float - entity = BenUnit - label = "Reduction in Tax Credits from means-tested income" - definition_period = YEAR - unit = GBP - - def formula(benunit, period, parameters): - means_test = parameters(period).gov.dwp.tax_credits.means_test - CTC_amount = benunit("CTC_maximum_rate", period) - WTC_amount = benunit("WTC_maximum_rate", period) - CTC_only = (CTC_amount > 0) & (WTC_amount == 0) - threshold = where( - CTC_only, - means_test.income_threshold_CTC_only, - means_test.income_threshold, - ) - income = benunit("tax_credits_applicable_income", period) - overage = max_(0, income - threshold) - return overage * means_test.income_reduction_rate - - -class working_tax_credit_pre_minimum(Variable): - value_type = float - entity = BenUnit - label = "Working Tax Credit pre-minimum" - documentation = ( - "Working Tax Credit amount before the minimum tax credit is applied" - ) - defined_for = "would_claim_WTC" - definition_period = YEAR - unit = GBP - - def formula(benunit, period, parameters): - return max_( - 0, - benunit("WTC_maximum_rate", period) - - benunit("tax_credits_reduction", period), - ) - - -class child_tax_credit_pre_minimum(Variable): - value_type = float - entity = BenUnit - label = "Child Tax Credit pre-minimum" - documentation = ( - "Child Tax Credit amount before the minimum tax credit is applied" - ) - defined_for = "would_claim_CTC" - definition_period = YEAR - unit = GBP - - def formula(benunit, period, parameters): - reduction_left = max_( - 0, - benunit("tax_credits_reduction", period) - - benunit("WTC_maximum_rate", period), - ) - return max_( - 0, - benunit("CTC_maximum_rate", period) - reduction_left, - ) - - class tax_credits(Variable): value_type = float entity = BenUnit @@ -535,80 +17,3 @@ def formula(person, period, parameters): ) min_benefit = parameters(period).gov.dwp.tax_credits.min_benefit return where(amount < min_benefit, 0, amount) - - -class ctc_entitlement(Variable): - label = "CTC entitlement" - entity = BenUnit - definition_period = YEAR - value_type = float - unit = GBP - - def formula(benunit, period, parameters): - return where( - benunit("tax_credits", period) > 0, - benunit("child_tax_credit_pre_minimum", period), - 0, - ) * (benunit("is_CTC_eligible", period)) - - -class child_tax_credit(Variable): - value_type = float - entity = BenUnit - label = "Child Tax Credit" - definition_period = YEAR - unit = GBP - defined_for = "would_claim_CTC" - adds = ["ctc_entitlement"] - - -class wtc_entitlement(Variable): - label = "WTC entitlement" - entity = BenUnit - definition_period = YEAR - value_type = float - unit = GBP - defined_for = "is_WTC_eligible" - - def formula(benunit, period, parameters): - return where( - benunit("tax_credits", period) > 0, - benunit("working_tax_credit_pre_minimum", period), - 0, - ) - - -class working_tax_credit(Variable): - value_type = float - entity = BenUnit - label = "Working Tax Credit" - definition_period = YEAR - unit = GBP - defined_for = "would_claim_WTC" - adds = ["wtc_entitlement"] - - -class baseline_wtc_entitlement(Variable): - label = "Baseline Working Tax Credit" - entity = BenUnit - definition_period = YEAR - value_type = float - - def formula(benunit, period, parameters): - if benunit.simulation.baseline is None: - return 1 - baseline = benunit.simulation.baseline.populations["benunit"] - return baseline("wtc_entitlement", period) - - -class baseline_ctc_entitlement(Variable): - label = "Receives Child Tax Credit (baseline)" - entity = BenUnit - definition_period = YEAR - value_type = float - - def formula(benunit, period, parameters): - if benunit.simulation.baseline is None: - return 1 - baseline = benunit.simulation.baseline.populations["benunit"] - return baseline("wtc_entitlement", period) diff --git a/policyengine_uk/variables/gov/dwp/tax_credits_applicable_income.py b/policyengine_uk/variables/gov/dwp/tax_credits_applicable_income.py new file mode 100644 index 000000000..50b88a619 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/tax_credits_applicable_income.py @@ -0,0 +1,34 @@ +from policyengine_uk.model_api import * + + +class tax_credits_applicable_income(Variable): + value_type = float + entity = BenUnit + label = "Applicable income for Tax Credits" + definition_period = YEAR + unit = GBP + reference = "The Tax Credits (Definition and Calculation of Income) Regulations 2002 s. 3" + + def formula(benunit, period, parameters): + TC = parameters(period).gov.dwp.tax_credits + STEP_1_COMPONENTS = [ + "private_pension_income", + "savings_interest_income", + "dividend_income", + "property_income", + ] + income = add(benunit, period, STEP_1_COMPONENTS) + income = max_(income - TC.means_test.non_earned_disregard, 0) + STEP_2_COMPONENTS = [ + "employment_income", + "self_employment_income", + "social_security_income", + "miscellaneous_income", + ] + bi = parameters(period).gov.contrib.ubi_center.basic_income + if bi.interactions.include_in_means_tests: + STEP_2_COMPONENTS.append("basic_income") + income += add(benunit, period, STEP_2_COMPONENTS) + EXEMPT_BENEFITS = ["income_support", "esa_income", "jsa_income"] + on_exempt_benefits = add(benunit, period, EXEMPT_BENEFITS) > 0 + return income * ~on_exempt_benefits diff --git a/policyengine_uk/variables/gov/dwp/tax_credits_reduction.py b/policyengine_uk/variables/gov/dwp/tax_credits_reduction.py new file mode 100644 index 000000000..e503196b2 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/tax_credits_reduction.py @@ -0,0 +1,23 @@ +from policyengine_uk.model_api import * + + +class tax_credits_reduction(Variable): + value_type = float + entity = BenUnit + label = "Reduction in Tax Credits from means-tested income" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + means_test = parameters(period).gov.dwp.tax_credits.means_test + CTC_amount = benunit("CTC_maximum_rate", period) + WTC_amount = benunit("WTC_maximum_rate", period) + CTC_only = (CTC_amount > 0) & (WTC_amount == 0) + threshold = where( + CTC_only, + means_test.income_threshold_CTC_only, + means_test.income_threshold, + ) + income = benunit("tax_credits_applicable_income", period) + overage = max_(0, income - threshold) + return overage * means_test.income_reduction_rate diff --git a/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/uc_housing_costs_element.py b/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/uc_housing_costs_element.py index 7fabe37ae..1bba4ddef 100644 --- a/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/uc_housing_costs_element.py +++ b/policyengine_uk/variables/gov/dwp/universal_credit/housing_costs_element/uc_housing_costs_element.py @@ -1,7 +1,4 @@ from policyengine_uk.model_api import * -from policyengine_uk.variables.household.demographic.household import ( - TenureType, -) class uc_housing_costs_element(Variable): @@ -15,14 +12,15 @@ def formula(benunit, period, parameters): tenure_type = benunit.value_from_first_person( benunit.members.household("tenure_type", period) ) + tenure_types = tenure_type.possible_values rent = benunit("benunit_rent", period) rent_cap = benunit("LHA_cap", period) capped_rent_amount = min_(rent_cap, rent) max_housing_costs = select( [ - (tenure_type == TenureType.RENT_FROM_COUNCIL) - | (tenure_type == TenureType.RENT_FROM_HA), - tenure_type == TenureType.RENT_PRIVATELY, + (tenure_type == tenure_types.RENT_FROM_COUNCIL) + | (tenure_type == tenure_types.RENT_FROM_HA), + tenure_type == tenure_types.RENT_PRIVATELY, ], [rent, capped_rent_amount], default=0, diff --git a/policyengine_uk/variables/gov/dwp/WFA.py b/policyengine_uk/variables/gov/dwp/winter_fuel_allowance.py similarity index 88% rename from policyengine_uk/variables/gov/dwp/WFA.py rename to policyengine_uk/variables/gov/dwp/winter_fuel_allowance.py index 7e3b64fec..a980dfb89 100644 --- a/policyengine_uk/variables/gov/dwp/WFA.py +++ b/policyengine_uk/variables/gov/dwp/winter_fuel_allowance.py @@ -1,15 +1,6 @@ from policyengine_uk.model_api import * -class winter_fuel_allowance_reported(Variable): - value_type = float - entity = Person - label = "Winter fuel allowance" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" - - class winter_fuel_allowance(Variable): label = "Winter Fuel Allowance" entity = Household diff --git a/policyengine_uk/variables/gov/dwp/winter_fuel_allowance_reported.py b/policyengine_uk/variables/gov/dwp/winter_fuel_allowance_reported.py new file mode 100644 index 000000000..e89cd3419 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/winter_fuel_allowance_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class winter_fuel_allowance_reported(Variable): + value_type = float + entity = Person + label = "Winter fuel allowance" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/working_tax_credit.py b/policyengine_uk/variables/gov/dwp/working_tax_credit.py new file mode 100644 index 000000000..6611fcd8a --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/working_tax_credit.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class working_tax_credit(Variable): + value_type = float + entity = BenUnit + label = "Working Tax Credit" + definition_period = YEAR + unit = GBP + defined_for = "would_claim_WTC" + adds = ["wtc_entitlement"] diff --git a/policyengine_uk/variables/gov/dwp/working_tax_credit_pre_minimum.py b/policyengine_uk/variables/gov/dwp/working_tax_credit_pre_minimum.py new file mode 100644 index 000000000..fdea721f5 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/working_tax_credit_pre_minimum.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * + + +class working_tax_credit_pre_minimum(Variable): + value_type = float + entity = BenUnit + label = "Working Tax Credit pre-minimum" + documentation = ( + "Working Tax Credit amount before the minimum tax credit is applied" + ) + defined_for = "would_claim_WTC" + definition_period = YEAR + unit = GBP + + def formula(benunit, period, parameters): + return max_( + 0, + benunit("WTC_maximum_rate", period) + - benunit("tax_credits_reduction", period), + ) diff --git a/policyengine_uk/variables/gov/dwp/working_tax_credit_reported.py b/policyengine_uk/variables/gov/dwp/working_tax_credit_reported.py new file mode 100644 index 000000000..52709d2cb --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/working_tax_credit_reported.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class working_tax_credit_reported(Variable): + value_type = float + entity = Person + label = "Working Tax Credit" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/dwp/would_claim_CTC.py b/policyengine_uk/variables/gov/dwp/would_claim_CTC.py new file mode 100644 index 000000000..fea0e73e8 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/would_claim_CTC.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class would_claim_CTC(Variable): + value_type = bool + entity = BenUnit + label = "Would claim Child Tax Credit" + documentation = ( + "Whether this family would claim Child Tax Credit if eligible" + ) + definition_period = YEAR + + def formula(benunit, period, parameters): + reported_ctc = add(benunit, period, ["child_tax_credit_reported"]) > 0 + claims_all_entitled_benefits = benunit( + "claims_all_entitled_benefits", period + ) + return reported_ctc | claims_all_entitled_benefits diff --git a/policyengine_uk/variables/gov/dwp/would_claim_IS.py b/policyengine_uk/variables/gov/dwp/would_claim_IS.py new file mode 100644 index 000000000..50c04c601 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/would_claim_IS.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class would_claim_IS(Variable): + value_type = bool + entity = BenUnit + label = "Would claim Income Support" + documentation = ( + "Whether this family would claim Income Support if eligible" + ) + definition_period = YEAR + + def formula(benunit, period, parameters): + reported_is = add(benunit, period, ["income_support_reported"]) > 0 + claims_all_entitled_benefits = benunit( + "claims_all_entitled_benefits", period + ) + return reported_is | claims_all_entitled_benefits diff --git a/policyengine_uk/variables/gov/dwp/would_claim_WTC.py b/policyengine_uk/variables/gov/dwp/would_claim_WTC.py new file mode 100644 index 000000000..f86f58654 --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/would_claim_WTC.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * + + +class would_claim_WTC(Variable): + value_type = bool + entity = BenUnit + label = "Would claim Working Tax Credit" + documentation = ( + "Whether this family would claim Working Tax Credit if eligible" + ) + definition_period = YEAR + + def formula(benunit, period, parameters): + reported_wtc = ( + add(benunit, period, ["working_tax_credit_reported"]) > 0 + ) + claims_all_entitled_benefits = benunit( + "claims_all_entitled_benefits", period + ) + return reported_wtc | claims_all_entitled_benefits diff --git a/policyengine_uk/variables/gov/dwp/wtc_entitlement.py b/policyengine_uk/variables/gov/dwp/wtc_entitlement.py new file mode 100644 index 000000000..ff4eeabdc --- /dev/null +++ b/policyengine_uk/variables/gov/dwp/wtc_entitlement.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class wtc_entitlement(Variable): + label = "WTC entitlement" + entity = BenUnit + definition_period = YEAR + value_type = float + unit = GBP + defined_for = "is_WTC_eligible" + + def formula(benunit, period, parameters): + return where( + benunit("tax_credits", period) > 0, + benunit("working_tax_credit_pre_minimum", period), + 0, + ) diff --git a/policyengine_uk/variables/gov/hmrc/baseline_business_rates.py b/policyengine_uk/variables/gov/hmrc/baseline_business_rates.py new file mode 100644 index 000000000..b426300c2 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/baseline_business_rates.py @@ -0,0 +1,22 @@ +from policyengine_uk.model_api import * + + +class baseline_business_rates(Variable): + label = "Baseline business rates incidence" + documentation = ( + "Total incidence from business rates exposure in the baseline" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + br = parameters(period).gov.hmrc.business_rates.statistics.revenue + total_revenue = ( + br.ENGLAND # HMRC + + br.SCOTLAND # Revenue Scotland + + br.WALES # Welsh Revenue Authority + + br.NORTHERN_IRELAND # HMRC + ) + return household("shareholding", period) * total_revenue diff --git a/policyengine_uk/variables/gov/hmrc/baseline_child_benefit_entitlement.py b/policyengine_uk/variables/gov/hmrc/baseline_child_benefit_entitlement.py new file mode 100644 index 000000000..dd41fd1a9 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/baseline_child_benefit_entitlement.py @@ -0,0 +1,8 @@ +from policyengine_uk.model_api import * + + +class baseline_child_benefit_entitlement(Variable): + label = "Child Benefit (baseline)" + entity = BenUnit + definition_period = YEAR + value_type = float diff --git a/policyengine_uk/variables/gov/hmrc/baseline_vat.py b/policyengine_uk/variables/gov/hmrc/baseline_vat.py new file mode 100644 index 000000000..961ccfecf --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/baseline_vat.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * + + +class baseline_vat(Variable): + label = "baseline VAT" + entity = Household + definition_period = YEAR + value_type = float + unit = "currency-GBP" + + def formula(household, period, parameters): + full_rate_consumption = household("full_rate_vat_consumption", period) + reduced_rate_consumption = household( + "reduced_rate_vat_consumption", period + ) + vat = parameters(period).baseline.gov.hmrc.vat + return ( + full_rate_consumption * vat.standard_rate + + reduced_rate_consumption * vat.reduced_rate + ) / MICRODATA_VAT_COVERAGE diff --git a/policyengine_uk/variables/gov/hmrc/benunit_tax.py b/policyengine_uk/variables/gov/hmrc/benunit_tax.py new file mode 100644 index 000000000..0ad579ecd --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/benunit_tax.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class benunit_tax(Variable): + value_type = float + entity = BenUnit + label = "Benefit unit tax paid" + definition_period = YEAR + unit = GBP + + adds = ["tax"] diff --git a/policyengine_uk/variables/gov/hmrc/business_rates.py b/policyengine_uk/variables/gov/hmrc/business_rates.py index d2c64e475..883914522 100644 --- a/policyengine_uk/variables/gov/hmrc/business_rates.py +++ b/policyengine_uk/variables/gov/hmrc/business_rates.py @@ -1,25 +1,7 @@ from policyengine_uk.model_api import * - - -class baseline_business_rates(Variable): - label = "Baseline business rates incidence" - documentation = ( - "Total incidence from business rates exposure in the baseline" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - br = parameters(period).gov.hmrc.business_rates.statistics.revenue - total_revenue = ( - br.ENGLAND # HMRC - + br.SCOTLAND # Revenue Scotland - + br.WALES # Welsh Revenue Authority - + br.NORTHERN_IRELAND # HMRC - ) - return household("shareholding", period) * total_revenue +from policyengine_uk.variables.gov.hmrc.baseline_business_rates import ( + baseline_business_rates, +) class business_rates(Variable): diff --git a/policyengine_uk/variables/gov/hmrc/capital_gains_tax/adult_index_cg.py b/policyengine_uk/variables/gov/hmrc/capital_gains_tax/adult_index_cg.py new file mode 100644 index 000000000..3cd2f2d84 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/capital_gains_tax/adult_index_cg.py @@ -0,0 +1,19 @@ +from policyengine_uk.model_api import * +from policyengine_core.simulations import * + + +class adult_index_cg(Variable): + value_type = int + entity = Person + label = "index of adult in household, ranked by capital gains" + definition_period = YEAR + + def formula(person, period, parameters): + return ( + person.get_rank( + person.household, + -person("capital_gains_before_response", period), + condition=person("is_adult", period), + ) + + 1 + ) diff --git a/policyengine_uk/variables/gov/hmrc/capital_gains_tax/capital_gains_before_response.py b/policyengine_uk/variables/gov/hmrc/capital_gains_tax/capital_gains_before_response.py new file mode 100644 index 000000000..cc6f1fe6e --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/capital_gains_tax/capital_gains_before_response.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * +from policyengine_core.simulations import * + + +class capital_gains_before_response(Variable): + label = "capital gains before responses" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + uprating = "gov.obr.non_labour_income" diff --git a/policyengine_uk/variables/gov/hmrc/capital_gains_tax/capital_gains_behavioural_response.py b/policyengine_uk/variables/gov/hmrc/capital_gains_tax/capital_gains_behavioural_response.py new file mode 100644 index 000000000..b56a7cd14 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/capital_gains_tax/capital_gains_behavioural_response.py @@ -0,0 +1,33 @@ +from policyengine_uk.model_api import * +from policyengine_core.simulations import * + + +class capital_gains_behavioural_response(Variable): + value_type = float + entity = Person + label = "capital gains behavioral response" + unit = GBP + definition_period = YEAR + + def formula(person, period, parameters): + simulation = person.simulation + if simulation.baseline is None: + return 0 + + if ( + parameters( + period + ).gov.simulation.capital_gains_responses.elasticity + == 0 + ): + return 0 + + capital_gains = person("capital_gains_before_response", period) + tax_rate_change = person("relative_capital_gains_mtr_change", period) + elasticity = person("capital_gains_elasticity", period) + + # Calculate response using log differences + response_factor = np.exp(elasticity * tax_rate_change) - 1 + response = capital_gains * response_factor + + return response diff --git a/policyengine_uk/variables/gov/hmrc/capital_gains_tax/capital_gains_elasticity.py b/policyengine_uk/variables/gov/hmrc/capital_gains_tax/capital_gains_elasticity.py new file mode 100644 index 000000000..95b98b9d3 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/capital_gains_tax/capital_gains_elasticity.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * +from policyengine_core.simulations import * + + +class capital_gains_elasticity(Variable): + value_type = float + entity = Person + label = "elasticity of capital gains realizations" + unit = "/1" + definition_period = YEAR + + def formula(person, period, parameters): + gov = parameters(period).gov + return gov.simulation.capital_gains_responses.elasticity diff --git a/policyengine_uk/variables/gov/hmrc/capital_gains_tax/marginal_tax_rate_on_capital_gains.py b/policyengine_uk/variables/gov/hmrc/capital_gains_tax/marginal_tax_rate_on_capital_gains.py new file mode 100644 index 000000000..792d46dda --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/capital_gains_tax/marginal_tax_rate_on_capital_gains.py @@ -0,0 +1,50 @@ +from policyengine_uk.model_api import * +from policyengine_core.simulations import * + + +class marginal_tax_rate_on_capital_gains(Variable): + label = "capital gains marginal tax rate" + documentation = "Percent of marginal capital gains that do not increase household net income." + entity = Person + definition_period = YEAR + value_type = float + unit = "/1" + + def formula(person, period, parameters): + mtr_values = np.zeros(person.count, dtype=np.float32) + simulation = person.simulation + DELTA = 1_000 + adult_index_values = person("adult_index_cg", period) + for adult_index in [1, 2]: + alt_simulation = simulation.get_branch( + f"adult_{adult_index}_cg_rise" + ) + mask = adult_index_values == adult_index + for variable in simulation.tax_benefit_system.variables: + variable_data = simulation.tax_benefit_system.variables[ + variable + ] + if ( + variable not in simulation.input_variables + and not variable_data.is_input_variable() + ): + alt_simulation.delete_arrays(variable) + alt_simulation.set_input( + "capital_gains", + period, + person("capital_gains", period) + mask * DELTA, + ) + alt_person = alt_simulation.person + household_net_income = person.household( + "household_net_income", period + ) + household_net_income_higher_earnings = alt_person.household( + "household_net_income", period + ) + increase = ( + household_net_income_higher_earnings - household_net_income + ) + mtr_values += where(mask, 1 - increase / DELTA, 0) + + del simulation.branches[f"adult_{adult_index}_cg_rise"] + return mtr_values diff --git a/policyengine_uk/variables/gov/hmrc/capital_gains_tax/relative_capital_gains_mtr_change.py b/policyengine_uk/variables/gov/hmrc/capital_gains_tax/relative_capital_gains_mtr_change.py new file mode 100644 index 000000000..3028e3e70 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/capital_gains_tax/relative_capital_gains_mtr_change.py @@ -0,0 +1,59 @@ +from policyengine_uk.model_api import * +from policyengine_core.simulations import * + + +class relative_capital_gains_mtr_change(Variable): + value_type = float + entity = Person + label = "relative change in capital gains tax rate" + unit = "/1" + definition_period = YEAR + + def formula(person, period, parameters): + simulation: Simulation = person.simulation + baseline_branch = simulation.get_branch("baseline").get_branch( + "baseline_cgr_measurement" + ) + baseline_branch.set_input( + "capital_gains_before_response", + period, + person("capital_gains_before_response", period), + ) + baseline_person = baseline_branch.populations["person"] + baseline_branch.tax_benefit_system.neutralize_variable( + "capital_gains_behavioural_response" + ) + baseline_branch.set_input( + "capital_gains_before_response", + period, + person("capital_gains_before_response", period), + ) + baseline_mtr = baseline_person( + "marginal_tax_rate_on_capital_gains", period + ) + del simulation.branches["baseline"].branches[ + "baseline_cgr_measurement" + ] + + measurement_branch = simulation.get_branch("cgr_measurement") + measurement_branch.tax_benefit_system.neutralize_variable( + "capital_gains_behavioural_response" + ) + measurement_branch.set_input( + "capital_gains_before_response", + period, + person("capital_gains_before_response", period), + ) + measurement_person = measurement_branch.populations["person"] + reform_mtr = measurement_person( + "marginal_tax_rate_on_capital_gains", period + ) + del simulation.branches["cgr_measurement"] + + # Handle zeros in tax rates to prevent log(0) + min_rate = 0.001 + baseline_mtr_adj = np.maximum(baseline_mtr, min_rate) + reform_mtr_adj = np.maximum(reform_mtr, min_rate) + + # Calculate log difference + return np.log(reform_mtr_adj) - np.log(baseline_mtr_adj) diff --git a/policyengine_uk/variables/gov/hmrc/capital_gains_tax/responses.py b/policyengine_uk/variables/gov/hmrc/capital_gains_tax/responses.py deleted file mode 100644 index f0a6a53c2..000000000 --- a/policyengine_uk/variables/gov/hmrc/capital_gains_tax/responses.py +++ /dev/null @@ -1,176 +0,0 @@ -from policyengine_uk.model_api import * -from policyengine_core.simulations import * - - -class relative_capital_gains_mtr_change(Variable): - value_type = float - entity = Person - label = "relative change in capital gains tax rate" - unit = "/1" - definition_period = YEAR - - def formula(person, period, parameters): - simulation: Simulation = person.simulation - baseline_branch = simulation.get_branch("baseline").get_branch( - "baseline_cgr_measurement" - ) - baseline_branch.set_input( - "capital_gains_before_response", - period, - person("capital_gains_before_response", period), - ) - baseline_person = baseline_branch.populations["person"] - baseline_branch.tax_benefit_system.neutralize_variable( - "capital_gains_behavioural_response" - ) - baseline_branch.set_input( - "capital_gains_before_response", - period, - person("capital_gains_before_response", period), - ) - baseline_mtr = baseline_person( - "marginal_tax_rate_on_capital_gains", period - ) - del simulation.branches["baseline"].branches[ - "baseline_cgr_measurement" - ] - - measurement_branch = simulation.get_branch("cgr_measurement") - measurement_branch.tax_benefit_system.neutralize_variable( - "capital_gains_behavioural_response" - ) - measurement_branch.set_input( - "capital_gains_before_response", - period, - person("capital_gains_before_response", period), - ) - measurement_person = measurement_branch.populations["person"] - reform_mtr = measurement_person( - "marginal_tax_rate_on_capital_gains", period - ) - del simulation.branches["cgr_measurement"] - - # Handle zeros in tax rates to prevent log(0) - min_rate = 0.001 - baseline_mtr_adj = np.maximum(baseline_mtr, min_rate) - reform_mtr_adj = np.maximum(reform_mtr, min_rate) - - # Calculate log difference - return np.log(reform_mtr_adj) - np.log(baseline_mtr_adj) - - -class capital_gains_elasticity(Variable): - value_type = float - entity = Person - label = "elasticity of capital gains realizations" - unit = "/1" - definition_period = YEAR - - def formula(person, period, parameters): - gov = parameters(period).gov - return gov.simulation.capital_gains_responses.elasticity - - -class capital_gains_behavioural_response(Variable): - value_type = float - entity = Person - label = "capital gains behavioral response" - unit = GBP - definition_period = YEAR - - def formula(person, period, parameters): - simulation = person.simulation - if simulation.baseline is None: - return 0 - - if ( - parameters( - period - ).gov.simulation.capital_gains_responses.elasticity - == 0 - ): - return 0 - - capital_gains = person("capital_gains_before_response", period) - tax_rate_change = person("relative_capital_gains_mtr_change", period) - elasticity = person("capital_gains_elasticity", period) - - # Calculate response using log differences - response_factor = np.exp(elasticity * tax_rate_change) - 1 - response = capital_gains * response_factor - - return response - - -class capital_gains_before_response(Variable): - label = "capital gains before responses" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - uprating = "gov.obr.non_labour_income" - - -class adult_index_cg(Variable): - value_type = int - entity = Person - label = "index of adult in household, ranked by capital gains" - definition_period = YEAR - - def formula(person, period, parameters): - return ( - person.get_rank( - person.household, - -person("capital_gains_before_response", period), - condition=person("is_adult", period), - ) - + 1 - ) - - -class marginal_tax_rate_on_capital_gains(Variable): - label = "capital gains marginal tax rate" - documentation = "Percent of marginal capital gains that do not increase household net income." - entity = Person - definition_period = YEAR - value_type = float - unit = "/1" - - def formula(person, period, parameters): - mtr_values = np.zeros(person.count, dtype=np.float32) - simulation = person.simulation - DELTA = 1_000 - adult_index_values = person("adult_index_cg", period) - for adult_index in [1, 2]: - alt_simulation = simulation.get_branch( - f"adult_{adult_index}_cg_rise" - ) - mask = adult_index_values == adult_index - for variable in simulation.tax_benefit_system.variables: - variable_data = simulation.tax_benefit_system.variables[ - variable - ] - if ( - variable not in simulation.input_variables - and not variable_data.is_input_variable() - ): - alt_simulation.delete_arrays(variable) - alt_simulation.set_input( - "capital_gains", - period, - person("capital_gains", period) + mask * DELTA, - ) - alt_person = alt_simulation.person - household_net_income = person.household( - "household_net_income", period - ) - household_net_income_higher_earnings = alt_person.household( - "household_net_income", period - ) - increase = ( - household_net_income_higher_earnings - household_net_income - ) - mtr_values += where(mask, 1 - increase / DELTA, 0) - - del simulation.branches[f"adult_{adult_index}_cg_rise"] - return mtr_values diff --git a/policyengine_uk/variables/gov/hmrc/child_benefit.py b/policyengine_uk/variables/gov/hmrc/child_benefit.py index 5b3a7dd12..4b4c471d6 100644 --- a/policyengine_uk/variables/gov/hmrc/child_benefit.py +++ b/policyengine_uk/variables/gov/hmrc/child_benefit.py @@ -1,99 +1,6 @@ from policyengine_uk.model_api import * -class child_benefit_reported(Variable): - label = "Child Benefit (reported amount)" - documentation = "Reported amount received for Child Benefit" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - uprating = "gov.obr.consumer_price_index" - - -class would_claim_child_benefit(Variable): - label = "Would claim Child Benefit" - documentation = ( - "Whether this benefit unit would claim Child Benefit if eligible" - ) - entity = BenUnit - definition_period = YEAR - value_type = bool - - def formula(benunit, period, parameters): - takeup_rate = parameters(period).gov.hmrc.child_benefit.takeup - overall_p = takeup_rate.overall - return (random(benunit) < overall_p) * ~benunit( - "child_benefit_opts_out", period - ) - - -class child_benefit_opts_out(Variable): - label = "opts out of Child Benefit" - documentation = ( - "Whether this family would opt out of receiving Child Benefit payments" - ) - entity = BenUnit - definition_period = YEAR - value_type = bool - - def formula(benunit, period, parameters): - if benunit.simulation.dataset is not None: - ani = benunit.members("adjusted_net_income", period) - hmrc = parameters(period).gov.hmrc - cb_hitc = hmrc.income_tax.charges.CB_HITC - cb = hmrc.child_benefit - in_phase_out = ani > cb_hitc.phase_out_end - return where( - benunit.any(in_phase_out), - random(benunit) < cb.opt_out_rate, - False, - ) - else: - # If we're not in a microsimulation, assume the family would not opt out - return False - - -class child_benefit_respective_amount(Variable): - label = "Child Benefit (respective amount)" - documentation = "The amount of this benefit unit's Child Benefit which is in respect of this person" - entity = Person - definition_period = MONTH - value_type = float - unit = GBP - reference = ( - "https://www.legislation.gov.uk/ukpga/1992/4/part/IX", - "https://www.legislation.gov.uk/uksi/2006/965/regulation/2", - ) - defined_for = "is_child_or_QYP" - - def formula(person, period, parameters): - eligible = True - if parameters( - period - ).gov.contrib.ubi_center.basic_income.interactions.withdraw_cb: - eligible &= ( - person.benunit.sum(person("basic_income", period.this_year)) - == 0 - ) - is_eldest = person("is_eldest_child", period.this_year) - child_benefit = parameters(period).gov.hmrc.child_benefit.amount - amount = where( - is_eldest, child_benefit.eldest, child_benefit.additional - ) - return eligible * amount * WEEKS_IN_YEAR / MONTHS_IN_YEAR - - -class child_benefit_entitlement(Variable): - label = "CB entitlement" - entity = BenUnit - definition_period = YEAR - value_type = float - unit = GBP - - adds = ["child_benefit_respective_amount"] - - class child_benefit(Variable): label = "Child Benefit" documentation = "Total Child Benefit for the benefit unit" @@ -104,24 +11,3 @@ class child_benefit(Variable): category = BENEFIT defined_for = "would_claim_child_benefit" adds = ["child_benefit_entitlement"] - - -class child_benefit_less_tax_charge(Variable): - label = "Child Benefit (less tax charge)" - documentation = ( - "Child Benefit, minus the Child Benefit High-Income Tax Charge" - ) - entity = BenUnit - definition_period = YEAR - value_type = float - unit = GBP - - adds = ["child_benefit"] - subtracts = ["CB_HITC"] - - -class baseline_child_benefit_entitlement(Variable): - label = "Child Benefit (baseline)" - entity = BenUnit - definition_period = YEAR - value_type = float diff --git a/policyengine_uk/variables/gov/hmrc/child_benefit_entitlement.py b/policyengine_uk/variables/gov/hmrc/child_benefit_entitlement.py new file mode 100644 index 000000000..f2619b8a1 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/child_benefit_entitlement.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class child_benefit_entitlement(Variable): + label = "CB entitlement" + entity = BenUnit + definition_period = YEAR + value_type = float + unit = GBP + + adds = ["child_benefit_respective_amount"] diff --git a/policyengine_uk/variables/gov/hmrc/child_benefit_less_tax_charge.py b/policyengine_uk/variables/gov/hmrc/child_benefit_less_tax_charge.py new file mode 100644 index 000000000..491f0adfd --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/child_benefit_less_tax_charge.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class child_benefit_less_tax_charge(Variable): + label = "Child Benefit (less tax charge)" + documentation = ( + "Child Benefit, minus the Child Benefit High-Income Tax Charge" + ) + entity = BenUnit + definition_period = YEAR + value_type = float + unit = GBP + + adds = ["child_benefit"] + subtracts = ["CB_HITC"] diff --git a/policyengine_uk/variables/gov/hmrc/child_benefit_opts_out.py b/policyengine_uk/variables/gov/hmrc/child_benefit_opts_out.py new file mode 100644 index 000000000..a7526c47b --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/child_benefit_opts_out.py @@ -0,0 +1,27 @@ +from policyengine_uk.model_api import * + + +class child_benefit_opts_out(Variable): + label = "opts out of Child Benefit" + documentation = ( + "Whether this family would opt out of receiving Child Benefit payments" + ) + entity = BenUnit + definition_period = YEAR + value_type = bool + + def formula(benunit, period, parameters): + if benunit.simulation.dataset is not None: + ani = benunit.members("adjusted_net_income", period) + hmrc = parameters(period).gov.hmrc + cb_hitc = hmrc.income_tax.charges.CB_HITC + cb = hmrc.child_benefit + in_phase_out = ani > cb_hitc.phase_out_end + return where( + benunit.any(in_phase_out), + random(benunit) < cb.opt_out_rate, + False, + ) + else: + # If we're not in a microsimulation, assume the family would not opt out + return False diff --git a/policyengine_uk/variables/gov/hmrc/child_benefit_reported.py b/policyengine_uk/variables/gov/hmrc/child_benefit_reported.py new file mode 100644 index 000000000..e80bff485 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/child_benefit_reported.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class child_benefit_reported(Variable): + label = "Child Benefit (reported amount)" + documentation = "Reported amount received for Child Benefit" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/gov/hmrc/child_benefit_respective_amount.py b/policyengine_uk/variables/gov/hmrc/child_benefit_respective_amount.py new file mode 100644 index 000000000..810cd3b0e --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/child_benefit_respective_amount.py @@ -0,0 +1,31 @@ +from policyengine_uk.model_api import * + + +class child_benefit_respective_amount(Variable): + label = "Child Benefit (respective amount)" + documentation = "The amount of this benefit unit's Child Benefit which is in respect of this person" + entity = Person + definition_period = MONTH + value_type = float + unit = GBP + reference = ( + "https://www.legislation.gov.uk/ukpga/1992/4/part/IX", + "https://www.legislation.gov.uk/uksi/2006/965/regulation/2", + ) + defined_for = "is_child_or_QYP" + + def formula(person, period, parameters): + eligible = True + if parameters( + period + ).gov.contrib.ubi_center.basic_income.interactions.withdraw_cb: + eligible &= ( + person.benunit.sum(person("basic_income", period.this_year)) + == 0 + ) + is_eldest = person("is_eldest_child", period.this_year) + child_benefit = parameters(period).gov.hmrc.child_benefit.amount + amount = where( + is_eldest, child_benefit.eldest, child_benefit.additional + ) + return eligible * amount * WEEKS_IN_YEAR / MONTHS_IN_YEAR diff --git a/policyengine_uk/variables/gov/hmrc/corporate_sdlt.py b/policyengine_uk/variables/gov/hmrc/corporate_sdlt.py new file mode 100644 index 000000000..93e3a121f --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/corporate_sdlt.py @@ -0,0 +1,19 @@ +from policyengine_uk.model_api import * + + +class corporate_sdlt(Variable): + label = "Stamp Duty (corporations)" + documentation = ( + "Stamp Duty paid by corporations, incident on this household" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + sd = parameters(period).gov.hmrc.stamp_duty.statistics + return household("shareholding", period) * ( + sd.residential.corporate.revenue + + sd.non_residential.corporate.revenue + ) diff --git a/policyengine_uk/variables/gov/hmrc/expected_sdlt.py b/policyengine_uk/variables/gov/hmrc/expected_sdlt.py new file mode 100644 index 000000000..afaf80876 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/expected_sdlt.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class expected_sdlt(Variable): + label = "Stamp Duty (expected)" + documentation = "Expected value of Stamp Duty Land Tax" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + if parameters(period).gov.hmrc.stamp_duty.abolish: + return 0 + return ( + household.state("property_sale_rate", period) + * household("stamp_duty_land_tax", period) + ) + household("corporate_sdlt", period) diff --git a/policyengine_uk/variables/gov/hmrc/household_tax.py b/policyengine_uk/variables/gov/hmrc/household_tax.py new file mode 100644 index 000000000..d1c4a598e --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/household_tax.py @@ -0,0 +1,49 @@ +from policyengine_uk.model_api import * + + +class household_tax(Variable): + value_type = float + entity = Household + label = "household taxes" + documentation = "Total taxes owed by the household" + definition_period = YEAR + unit = GBP + adds = [ + "expected_sdlt", + "expected_ltt", + "expected_lbtt", + "corporate_sdlt", + "business_rates", + "council_tax", + "domestic_rates", + "fuel_duty", + "tv_licence", + "wealth_tax", + "non_primary_residence_wealth_tax", + "income_tax", + "national_insurance", + "LVT", + "carbon_tax", + "vat_change", + "capital_gains_tax", + "private_school_vat", + "corporate_incident_tax_revenue_change", + "consumer_incident_tax_revenue_change", + "high_income_incident_tax_change", + "employer_ni_response_capital_incidence", + "employer_ni_response_consumer_incidence", + ] + + def formula(household, period, parameters): + if parameters(period).gov.contrib.abolish_council_tax: + return add( + household, + period, + [ + tax + for tax in household_tax.adds + if tax not in ["council_tax"] + ], + ) + else: + return add(household, period, household_tax.adds) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/allowances.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/allowances.py index 159468262..1225fdc7f 100644 --- a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/allowances.py +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/allowances.py @@ -1,224 +1,5 @@ from policyengine_uk.model_api import * -""" -This file calculates the allowances to which taxpayers are entitled. This follows step 3 of the Income Tax Act 2007 s. 23. -""" - - -class personal_allowance(Variable): - value_type = float - entity = Person - label = "Personal Allowance for the year" - unit = GBP - definition_period = YEAR - reference = "Income Tax Act 2007 s. 35" - - def formula(person, period, parameters): - params = parameters(period) - PA = params.gov.hmrc.income_tax.allowances.personal_allowance - personal_allowance = PA.amount - ANI = person("adjusted_net_income", period) - excess = max_(0, ANI - PA.maximum_ANI) - reduction = excess * PA.reduction_rate - return max_(0, personal_allowance - reduction) - - -class blind_persons_allowance(Variable): - value_type = float - entity = Person - label = "Blind Person's Allowance for the year (not simulated)" - definition_period = YEAR - reference = "Income Tax Act 2007 s. 38" - unit = GBP - - -class married_couples_allowance(Variable): - value_type = float - entity = Person - label = "Married Couples' allowance for the year" - definition_period = YEAR - unit = GBP - - -class married_couples_allowance_deduction(Variable): - value_type = float - entity = Person - label = "Deduction from Married Couples' allowance for the year" - definition_period = YEAR - unit = GBP - - def formula(person, period, parameters): - rate = parameters( - period - ).gov.hmrc.income_tax.allowances.married_couples_allowance.deduction_rate - return person("married_couples_allowance", period) * rate - - -class capped_mcad(Variable): - label = "capped Married Couples' Allowance deduction" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - def formula(person, period, parameters): - capping_value = add( - person, period, ["income_tax_pre_charges", "CB_HITC"] - ) - return min_( - person("married_couples_allowance_deduction", period), - capping_value, - ) - - -class pension_annual_allowance(Variable): - value_type = float - entity = Person - label = "Annual Allowance for pension contributions" - definition_period = YEAR - unit = GBP - - def formula(person, period, parameters): - allowance = parameters( - period - ).gov.hmrc.income_tax.allowances.annual_allowance - ANI = person("adjusted_net_income", period) - reduction = max_(0, ANI - allowance.taper) * allowance.reduction_rate - return max_(allowance.minimum, allowance.default - reduction) - - -class trading_allowance(Variable): - value_type = float - entity = Person - label = "Trading Allowance for the year" - definition_period = YEAR - reference = "Income Tax (Trading and Other Income) Act 2005 s. 783AF" - unit = GBP - - def formula(person, period, parameters): - return parameters( - period - ).gov.hmrc.income_tax.allowances.trading_allowance - - -class trading_allowance_deduction(Variable): - value_type = float - entity = Person - label = "Deduction applied by the trading allowance" - definition_period = YEAR - reference = "Income Tax (Trading and Other Income) Act 2005 s. 783AF" - unit = GBP - - def formula(person, period, parameters): - return min_( - person("trading_allowance", period), - person("self_employment_income", period), - ) - - -class property_allowance(Variable): - value_type = float - entity = Person - label = "Property Allowance for the year" - definition_period = YEAR - reference = "Income Tax (Trading and Other Income) Act 2005 s. 783BF" - unit = GBP - - def formula(person, period, parameters): - return parameters( - period - ).gov.hmrc.income_tax.allowances.property_allowance - - -class property_allowance_deduction(Variable): - value_type = float - entity = Person - label = "Deduction applied by the property allowance" - definition_period = YEAR - reference = "Income Tax (Trading and Other Income) Act 2005 s. 783AF" - unit = GBP - - def formula(person, period, parameters): - return min_( - person("property_income", period), - person("property_allowance", period), - ) - - -class savings_allowance(Variable): - value_type = float - entity = Person - label = "Savings Allowance for the year" - definition_period = YEAR - reference = "Income Tax Act 2007 s. 12B" - unit = GBP - - def formula(person, period, parameters): - tax_band = person("tax_band", period) - tax_bands = tax_band.possible_values - amounts = parameters( - period - ).gov.hmrc.income_tax.allowances.personal_savings_allowance - return select( - [ - tax_band == tax_bands.ADDITIONAL, - tax_band == tax_bands.HIGHER, - ( - (tax_band == tax_bands.STARTER) - | (tax_band == tax_bands.BASIC) - | (tax_band == tax_bands.INTERMEDIATE) - ), - tax_band == tax_bands.NONE, - ], - [amounts.additional, amounts.higher, amounts.basic, amounts.basic], - ) - - -class dividend_allowance(Variable): - value_type = float - entity = Person - label = "Dividend allowance for the person" - definition_period = YEAR - reference = "Income Tax Act 2007 s. 13A" - unit = GBP - - def formula(person, period, parameters): - return parameters( - period - ).gov.hmrc.income_tax.allowances.dividend_allowance - - -class gift_aid(Variable): - value_type = float - entity = Person - label = "Expenditure under Gift Aid" - definition_period = YEAR - unit = GBP - - -class covenanted_payments(Variable): - value_type = float - entity = Person - label = "Covenanted payments to charities" - definition_period = YEAR - unit = GBP - - -class charitable_investment_gifts(Variable): - value_type = float - entity = Person - label = "Gifts of qualifying investment or property to charities" - definition_period = YEAR - unit = GBP - - -class other_deductions(Variable): - value_type = float - entity = Person - label = "All other tax deductions" - definition_period = YEAR - unit = GBP - class allowances(Variable): value_type = float diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/blind_persons_allowance.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/blind_persons_allowance.py new file mode 100644 index 000000000..464b62690 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/blind_persons_allowance.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class blind_persons_allowance(Variable): + value_type = float + entity = Person + label = "Blind Person's Allowance for the year (not simulated)" + definition_period = YEAR + reference = "Income Tax Act 2007 s. 38" + unit = GBP diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/capped_mcad.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/capped_mcad.py new file mode 100644 index 000000000..6df277172 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/capped_mcad.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class capped_mcad(Variable): + label = "capped Married Couples' Allowance deduction" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + capping_value = add( + person, period, ["income_tax_pre_charges", "CB_HITC"] + ) + return min_( + person("married_couples_allowance_deduction", period), + capping_value, + ) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/charitable_investment_gifts.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/charitable_investment_gifts.py new file mode 100644 index 000000000..8f48fb8ba --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/charitable_investment_gifts.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class charitable_investment_gifts(Variable): + value_type = float + entity = Person + label = "Gifts of qualifying investment or property to charities" + definition_period = YEAR + unit = GBP diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/covenanted_payments.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/covenanted_payments.py new file mode 100644 index 000000000..9d7c7c89a --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/covenanted_payments.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class covenanted_payments(Variable): + value_type = float + entity = Person + label = "Covenanted payments to charities" + definition_period = YEAR + unit = GBP diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/dividend_allowance.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/dividend_allowance.py new file mode 100644 index 000000000..adb6b5c1b --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/dividend_allowance.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class dividend_allowance(Variable): + value_type = float + entity = Person + label = "Dividend allowance for the person" + definition_period = YEAR + reference = "Income Tax Act 2007 s. 13A" + unit = GBP + + def formula(person, period, parameters): + return parameters( + period + ).gov.hmrc.income_tax.allowances.dividend_allowance diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/gift_aid.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/gift_aid.py new file mode 100644 index 000000000..bf6fc4708 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/gift_aid.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class gift_aid(Variable): + value_type = float + entity = Person + label = "Expenditure under Gift Aid" + definition_period = YEAR + unit = GBP diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/marriage_allowance.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/marriage_allowance.py index 93dedb733..642bc7fa6 100644 --- a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/marriage_allowance.py +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/marriage_allowance.py @@ -1,52 +1,4 @@ from policyengine_uk.model_api import * -from numpy import ceil - - -class unused_personal_allowance(Variable): - value_type = float - entity = Person - label = "Unused personal allowance" - definition_period = YEAR - unit = GBP - - def formula(person, period, parameters): - return max_( - person("personal_allowance", period) - - person("adjusted_net_income", period), - 0, - ) - - -class meets_marriage_allowance_income_conditions(Variable): - label = "Meets Marriage Allowance income conditions" - documentation = "Whether this person (and their partner) meets the conditions for this person to be eligible for the Marriage Allowance, as set out in the Income Tax Act 2007 sections 55B and 55C" - entity = Person - definition_period = YEAR - value_type = bool - reference = "https://www.legislation.gov.uk/ukpga/2007/3/section/55B" - - def formula(person, period): - band = person("tax_band", period) - bands = band.possible_values - return ( - (band == bands.BASIC) - | (band == bands.STARTER) - | (band == bands.INTERMEDIATE) - ) - - -class partners_unused_personal_allowance(Variable): - label = "Partner's unused personal allowance" - documentation = "The personal tax allowance not used by this person's partner, if they exist" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - def formula(person, period, parameters): - is_adult = person("is_adult", period) - pa = person("unused_personal_allowance", period) - return person.benunit.sum(is_adult * pa) - pa class marriage_allowance(Variable): @@ -74,7 +26,7 @@ def formula(person, period, parameters): # Round up. rounding_increment = allowances.marriage_allowance.rounding_increment amount_if_eligible = ( - ceil(amount_if_eligible_pre_rounding / rounding_increment) + np.ceil(amount_if_eligible_pre_rounding / rounding_increment) * rounding_increment ) return eligible * amount_if_eligible * (random(person) < takeup_rate) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/married_couples_allowance.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/married_couples_allowance.py new file mode 100644 index 000000000..2ab95a9a8 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/married_couples_allowance.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class married_couples_allowance(Variable): + value_type = float + entity = Person + label = "Married Couples' allowance for the year" + definition_period = YEAR + unit = GBP diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/married_couples_allowance_deduction.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/married_couples_allowance_deduction.py new file mode 100644 index 000000000..9b2014d47 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/married_couples_allowance_deduction.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class married_couples_allowance_deduction(Variable): + value_type = float + entity = Person + label = "Deduction from Married Couples' allowance for the year" + definition_period = YEAR + unit = GBP + + def formula(person, period, parameters): + rate = parameters( + period + ).gov.hmrc.income_tax.allowances.married_couples_allowance.deduction_rate + return person("married_couples_allowance", period) * rate diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/meets_marriage_allowance_income_conditions.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/meets_marriage_allowance_income_conditions.py new file mode 100644 index 000000000..45c9204c1 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/meets_marriage_allowance_income_conditions.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * +from numpy import ceil + + +class meets_marriage_allowance_income_conditions(Variable): + label = "Meets Marriage Allowance income conditions" + documentation = "Whether this person (and their partner) meets the conditions for this person to be eligible for the Marriage Allowance, as set out in the Income Tax Act 2007 sections 55B and 55C" + entity = Person + definition_period = YEAR + value_type = bool + reference = "https://www.legislation.gov.uk/ukpga/2007/3/section/55B" + + def formula(person, period): + band = person("tax_band", period) + bands = band.possible_values + return ( + (band == bands.BASIC) + | (band == bands.STARTER) + | (band == bands.INTERMEDIATE) + ) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/other_deductions.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/other_deductions.py new file mode 100644 index 000000000..886a61007 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/other_deductions.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class other_deductions(Variable): + value_type = float + entity = Person + label = "All other tax deductions" + definition_period = YEAR + unit = GBP diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/partners_unused_personal_allowance.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/partners_unused_personal_allowance.py new file mode 100644 index 000000000..b0272068a --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/partners_unused_personal_allowance.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * +from numpy import ceil + + +class partners_unused_personal_allowance(Variable): + label = "Partner's unused personal allowance" + documentation = "The personal tax allowance not used by this person's partner, if they exist" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + def formula(person, period, parameters): + is_adult = person("is_adult", period) + pa = person("unused_personal_allowance", period) + return person.benunit.sum(is_adult * pa) - pa diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/pension_annual_allowance.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/pension_annual_allowance.py new file mode 100644 index 000000000..0c877005d --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/pension_annual_allowance.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class pension_annual_allowance(Variable): + value_type = float + entity = Person + label = "Annual Allowance for pension contributions" + definition_period = YEAR + unit = GBP + + def formula(person, period, parameters): + allowance = parameters( + period + ).gov.hmrc.income_tax.allowances.annual_allowance + ANI = person("adjusted_net_income", period) + reduction = max_(0, ANI - allowance.taper) * allowance.reduction_rate + return max_(allowance.minimum, allowance.default - reduction) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/personal_allowance.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/personal_allowance.py new file mode 100644 index 000000000..02d60e568 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/personal_allowance.py @@ -0,0 +1,19 @@ +from policyengine_uk.model_api import * + + +class personal_allowance(Variable): + value_type = float + entity = Person + label = "Personal Allowance for the year" + unit = GBP + definition_period = YEAR + reference = "Income Tax Act 2007 s. 35" + + def formula(person, period, parameters): + params = parameters(period) + PA = params.gov.hmrc.income_tax.allowances.personal_allowance + personal_allowance = PA.amount + ANI = person("adjusted_net_income", period) + excess = max_(0, ANI - PA.maximum_ANI) + reduction = excess * PA.reduction_rate + return max_(0, personal_allowance - reduction) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/property_allowance.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/property_allowance.py new file mode 100644 index 000000000..6d8d3aeaa --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/property_allowance.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class property_allowance(Variable): + value_type = float + entity = Person + label = "Property Allowance for the year" + definition_period = YEAR + reference = "Income Tax (Trading and Other Income) Act 2005 s. 783BF" + unit = GBP + + def formula(person, period, parameters): + return parameters( + period + ).gov.hmrc.income_tax.allowances.property_allowance diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/property_allowance_deduction.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/property_allowance_deduction.py new file mode 100644 index 000000000..9dcf43f48 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/property_allowance_deduction.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class property_allowance_deduction(Variable): + value_type = float + entity = Person + label = "Deduction applied by the property allowance" + definition_period = YEAR + reference = "Income Tax (Trading and Other Income) Act 2005 s. 783AF" + unit = GBP + + def formula(person, period, parameters): + return min_( + person("property_income", period), + person("property_allowance", period), + ) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/savings_allowance.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/savings_allowance.py new file mode 100644 index 000000000..6c046ff0c --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/savings_allowance.py @@ -0,0 +1,30 @@ +from policyengine_uk.model_api import * + + +class savings_allowance(Variable): + value_type = float + entity = Person + label = "Savings Allowance for the year" + definition_period = YEAR + reference = "Income Tax Act 2007 s. 12B" + unit = GBP + + def formula(person, period, parameters): + tax_band = person("tax_band", period) + tax_bands = tax_band.possible_values + amounts = parameters( + period + ).gov.hmrc.income_tax.allowances.personal_savings_allowance + return select( + [ + tax_band == tax_bands.ADDITIONAL, + tax_band == tax_bands.HIGHER, + ( + (tax_band == tax_bands.STARTER) + | (tax_band == tax_bands.BASIC) + | (tax_band == tax_bands.INTERMEDIATE) + ), + tax_band == tax_bands.NONE, + ], + [amounts.additional, amounts.higher, amounts.basic, amounts.basic], + ) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/trading_allowance.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/trading_allowance.py new file mode 100644 index 000000000..96027b288 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/trading_allowance.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class trading_allowance(Variable): + value_type = float + entity = Person + label = "Trading Allowance for the year" + definition_period = YEAR + reference = "Income Tax (Trading and Other Income) Act 2005 s. 783AF" + unit = GBP + + def formula(person, period, parameters): + return parameters( + period + ).gov.hmrc.income_tax.allowances.trading_allowance diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/trading_allowance_deduction.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/trading_allowance_deduction.py new file mode 100644 index 000000000..aa4dee6e8 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/trading_allowance_deduction.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class trading_allowance_deduction(Variable): + value_type = float + entity = Person + label = "Deduction applied by the trading allowance" + definition_period = YEAR + reference = "Income Tax (Trading and Other Income) Act 2005 s. 783AF" + unit = GBP + + def formula(person, period, parameters): + return min_( + person("trading_allowance", period), + person("self_employment_income", period), + ) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/allowances/unused_personal_allowance.py b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/unused_personal_allowance.py new file mode 100644 index 000000000..4257638e6 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/allowances/unused_personal_allowance.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * +from numpy import ceil + + +class unused_personal_allowance(Variable): + value_type = float + entity = Person + label = "Unused personal allowance" + definition_period = YEAR + unit = GBP + + def formula(person, period, parameters): + return max_( + person("personal_allowance", period) + - person("adjusted_net_income", period), + 0, + ) diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/base.py b/policyengine_uk/variables/gov/hmrc/income_tax/base.py deleted file mode 100644 index 513139493..000000000 --- a/policyengine_uk/variables/gov/hmrc/income_tax/base.py +++ /dev/null @@ -1,40 +0,0 @@ -from policyengine_uk.model_api import * - -""" -The calculation of income tax is specified by the Income Tax Act 2007 s. 23 -which outlines the main steps. Step 1 indicates that the components of 'total income' -should be added together, but the Act itself does not specify what those components are. -Instead, other Acts (the Income Tax (Earnings and Pensions) Act 2003 and the Income Tax -(Trading and Other Income) Act 2005) impose the charges to income tax on each component of -income. The relevant sections of each act imposing charges to income tax are given -for each component of income below. The components are guided by the article at -https://www.pruadviser.co.uk/knowledge-literature/knowledge-library/the-seven-steps-required-to-calculate-an-individuals-income-tax-liability/ -""" - - -class total_pension_income(Variable): - label = "Total pension income" - documentation = "Private, personal and State Pension income" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - adds = ["private_pension_income", "state_pension"] - - -class social_security_income(Variable): - value_type = float - entity = Person - label = "Income from social security for tax purposes" - definition_period = YEAR - reference = "Income Tax (Earnings and Pensions) Act 2003 s. 1(1)(b)" - unit = GBP - - adds = [ - "state_pension", - "incapacity_benefit", - "jsa_contrib", - "esa_contrib", - "carers_allowance", - ] diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/social_security_income.py b/policyengine_uk/variables/gov/hmrc/income_tax/social_security_income.py new file mode 100644 index 000000000..71a04a141 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/social_security_income.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class social_security_income(Variable): + value_type = float + entity = Person + label = "Income from social security for tax purposes" + definition_period = YEAR + reference = "Income Tax (Earnings and Pensions) Act 2003 s. 1(1)(b)" + unit = GBP + + adds = [ + "state_pension", + "incapacity_benefit", + "jsa_contrib_reported", + "esa_contrib_reported", + "carers_allowance", + ] diff --git a/policyengine_uk/variables/gov/hmrc/income_tax/total_pension_income.py b/policyengine_uk/variables/gov/hmrc/income_tax/total_pension_income.py new file mode 100644 index 000000000..9a6697121 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/income_tax/total_pension_income.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class total_pension_income(Variable): + label = "Total pension income" + documentation = "Private, personal and State Pension income" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + + adds = ["private_pension_income", "state_pension"] diff --git a/policyengine_uk/variables/gov/hmrc/sdlt_liable.py b/policyengine_uk/variables/gov/hmrc/sdlt_liable.py new file mode 100644 index 000000000..e609d0266 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/sdlt_liable.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class sdlt_liable(Variable): + label = "Liable for Stamp Duty" + documentation = "Whether the household is liable for Stamp Duty Land Tax" + entity = Household + definition_period = YEAR + value_type = bool + unit = GBP + + def formula(household, period): + country = household("country", period) + countries = country.possible_values + return np.isin( + country.decode_to_str(), + [countries.ENGLAND.name, countries.NORTHERN_IRELAND.name], + ) diff --git a/policyengine_uk/variables/gov/hmrc/sdlt_on_non_residential_property_rent.py b/policyengine_uk/variables/gov/hmrc/sdlt_on_non_residential_property_rent.py new file mode 100644 index 000000000..bd1dc3ee2 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/sdlt_on_non_residential_property_rent.py @@ -0,0 +1,19 @@ +from policyengine_uk.model_api import * + + +class sdlt_on_non_residential_property_rent(Variable): + label = "Stamp Duty on non-residential property" + documentation = "Tax charge from rental of non-residential property" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + reference = "https://www.legislation.gov.uk/ukpga/2003/14/schedule/5" + + def formula(household, period, parameters): + stamp_duty = parameters(period).gov.hmrc.stamp_duty + cumulative_rent = household("cumulative_non_residential_rent", period) + rent = household("non_residential_rent", period) + return stamp_duty.non_residential.rent.calc( + cumulative_rent + rent + ) - stamp_duty.non_residential.rent.calc(cumulative_rent) diff --git a/policyengine_uk/variables/gov/hmrc/sdlt_on_non_residential_property_transactions.py b/policyengine_uk/variables/gov/hmrc/sdlt_on_non_residential_property_transactions.py new file mode 100644 index 000000000..6f2fd883d --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/sdlt_on_non_residential_property_transactions.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class sdlt_on_non_residential_property_transactions(Variable): + label = "Stamp Duty on non-residential property" + documentation = "Tax charge from purchase of non-residential property" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + reference = "https://www.legislation.gov.uk/ukpga/2003/14/section/55" + + def formula(household, period, parameters): + stamp_duty = parameters(period).gov.hmrc.stamp_duty + price = household("non_residential_property_purchased", period) + return stamp_duty.non_residential.purchase.calc(price) diff --git a/policyengine_uk/variables/gov/hmrc/sdlt_on_rent.py b/policyengine_uk/variables/gov/hmrc/sdlt_on_rent.py new file mode 100644 index 000000000..06f153211 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/sdlt_on_rent.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class sdlt_on_rent(Variable): + label = "SDLT on property rental" + documentation = "Stamp Duty Land Tax on property rental agreements" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + reference = "https://www.legislation.gov.uk/ukpga/2003/14/section/55" + + adds = [ + "sdlt_on_residential_property_rent", + "sdlt_on_non_residential_property_rent", + ] diff --git a/policyengine_uk/variables/gov/hmrc/sdlt_on_residential_property_rent.py b/policyengine_uk/variables/gov/hmrc/sdlt_on_residential_property_rent.py new file mode 100644 index 000000000..80101151f --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/sdlt_on_residential_property_rent.py @@ -0,0 +1,19 @@ +from policyengine_uk.model_api import * + + +class sdlt_on_residential_property_rent(Variable): + label = "Stamp Duty on residential property" + documentation = "Tax charge from rental of residential property" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + reference = "https://www.legislation.gov.uk/ukpga/2003/14/schedule/5" + + def formula(household, period, parameters): + stamp_duty = parameters(period).gov.hmrc.stamp_duty + cumulative_rent = household("cumulative_residential_rent", period) + rent = household("rent", period) + return stamp_duty.residential.rent.calc( + cumulative_rent + rent + ) - stamp_duty.residential.rent.calc(cumulative_rent) diff --git a/policyengine_uk/variables/gov/hmrc/sdlt_on_residential_property_transactions.py b/policyengine_uk/variables/gov/hmrc/sdlt_on_residential_property_transactions.py new file mode 100644 index 000000000..8ebe7f560 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/sdlt_on_residential_property_transactions.py @@ -0,0 +1,40 @@ +from policyengine_uk.model_api import * + + +class sdlt_on_residential_property_transactions(Variable): + label = "Stamp Duty on residential property" + documentation = "Tax charge from purchase of residential property" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + reference = "https://www.legislation.gov.uk/ukpga/2003/14/section/55" + + def formula(household, period, parameters): + stamp_duty = parameters(period).gov.hmrc.stamp_duty + # Tax on main-home purchases + price_limit = stamp_duty.residential.purchase.main.first.max + price = household("main_residential_property_purchased", period) + residential_purchase_qualifies_as_first_buy = household( + "main_residential_property_purchased_is_first_home", period + ) & (price < price_limit) + main_residential_purchase_tax = where( + residential_purchase_qualifies_as_first_buy, + stamp_duty.residential.purchase.main.first.rate.calc(price), + stamp_duty.residential.purchase.main.subsequent.calc(price), + ) + # Tax on second-home purchases + second_home_price = household( + "additional_residential_property_purchased", period + ) + price = where( + second_home_price < stamp_duty.residential.purchase.additional.min, + 0, + second_home_price, + ) + additional_residential_purchase_tax = ( + stamp_duty.residential.purchase.additional.rate.calc(price) + ) + return ( + main_residential_purchase_tax + additional_residential_purchase_tax + ) diff --git a/policyengine_uk/variables/gov/hmrc/sdlt_on_transactions.py b/policyengine_uk/variables/gov/hmrc/sdlt_on_transactions.py new file mode 100644 index 000000000..6746175fa --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/sdlt_on_transactions.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class sdlt_on_transactions(Variable): + label = "SDLT on property transactions" + documentation = "Stamp Duty Land Tax on property transfers" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + reference = "https://www.legislation.gov.uk/ukpga/2003/14/section/55" + + adds = [ + "sdlt_on_residential_property_transactions", + "sdlt_on_non_residential_property_transactions", + ] diff --git a/policyengine_uk/variables/gov/hmrc/stamp_duty_land_tax.py b/policyengine_uk/variables/gov/hmrc/stamp_duty_land_tax.py index 34c47785c..3421f0ea5 100644 --- a/policyengine_uk/variables/gov/hmrc/stamp_duty_land_tax.py +++ b/policyengine_uk/variables/gov/hmrc/stamp_duty_land_tax.py @@ -1,161 +1,6 @@ from policyengine_uk.model_api import * -class sdlt_on_residential_property_transactions(Variable): - label = "Stamp Duty on residential property" - documentation = "Tax charge from purchase of residential property" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - reference = "https://www.legislation.gov.uk/ukpga/2003/14/section/55" - - def formula(household, period, parameters): - stamp_duty = parameters(period).gov.hmrc.stamp_duty - # Tax on main-home purchases - price_limit = stamp_duty.residential.purchase.main.first.max - price = household("main_residential_property_purchased", period) - residential_purchase_qualifies_as_first_buy = household( - "main_residential_property_purchased_is_first_home", period - ) & (price < price_limit) - main_residential_purchase_tax = where( - residential_purchase_qualifies_as_first_buy, - stamp_duty.residential.purchase.main.first.rate.calc(price), - stamp_duty.residential.purchase.main.subsequent.calc(price), - ) - # Tax on second-home purchases - second_home_price = household( - "additional_residential_property_purchased", period - ) - price = where( - second_home_price < stamp_duty.residential.purchase.additional.min, - 0, - second_home_price, - ) - additional_residential_purchase_tax = ( - stamp_duty.residential.purchase.additional.rate.calc(price) - ) - return ( - main_residential_purchase_tax + additional_residential_purchase_tax - ) - - -class sdlt_on_residential_property_rent(Variable): - label = "Stamp Duty on residential property" - documentation = "Tax charge from rental of residential property" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - reference = "https://www.legislation.gov.uk/ukpga/2003/14/schedule/5" - - def formula(household, period, parameters): - stamp_duty = parameters(period).gov.hmrc.stamp_duty - cumulative_rent = household("cumulative_residential_rent", period) - rent = household("rent", period) - return stamp_duty.residential.rent.calc( - cumulative_rent + rent - ) - stamp_duty.residential.rent.calc(cumulative_rent) - - -class sdlt_on_non_residential_property_transactions(Variable): - label = "Stamp Duty on non-residential property" - documentation = "Tax charge from purchase of non-residential property" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - reference = "https://www.legislation.gov.uk/ukpga/2003/14/section/55" - - def formula(household, period, parameters): - stamp_duty = parameters(period).gov.hmrc.stamp_duty - price = household("non_residential_property_purchased", period) - return stamp_duty.non_residential.purchase.calc(price) - - -class sdlt_on_non_residential_property_rent(Variable): - label = "Stamp Duty on non-residential property" - documentation = "Tax charge from rental of non-residential property" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - reference = "https://www.legislation.gov.uk/ukpga/2003/14/schedule/5" - - def formula(household, period, parameters): - stamp_duty = parameters(period).gov.hmrc.stamp_duty - cumulative_rent = household("cumulative_non_residential_rent", period) - rent = household("non_residential_rent", period) - return stamp_duty.non_residential.rent.calc( - cumulative_rent + rent - ) - stamp_duty.non_residential.rent.calc(cumulative_rent) - - -class sdlt_on_transactions(Variable): - label = "SDLT on property transactions" - documentation = "Stamp Duty Land Tax on property transfers" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - reference = "https://www.legislation.gov.uk/ukpga/2003/14/section/55" - - adds = [ - "sdlt_on_residential_property_transactions", - "sdlt_on_non_residential_property_transactions", - ] - - -class sdlt_on_rent(Variable): - label = "SDLT on property rental" - documentation = "Stamp Duty Land Tax on property rental agreements" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - reference = "https://www.legislation.gov.uk/ukpga/2003/14/section/55" - - adds = [ - "sdlt_on_residential_property_rent", - "sdlt_on_non_residential_property_rent", - ] - - -class sdlt_liable(Variable): - label = "Liable for Stamp Duty" - documentation = "Whether the household is liable for Stamp Duty Land Tax" - entity = Household - definition_period = YEAR - value_type = bool - unit = GBP - - def formula(household, period): - country = household("country", period) - countries = country.possible_values - return np.isin( - country.decode_to_str(), - [countries.ENGLAND.name, countries.NORTHERN_IRELAND.name], - ) - - -class corporate_sdlt(Variable): - label = "Stamp Duty (corporations)" - documentation = ( - "Stamp Duty paid by corporations, incident on this household" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - sd = parameters(period).gov.hmrc.stamp_duty.statistics - return household("shareholding", period) * ( - sd.residential.corporate.revenue - + sd.non_residential.corporate.revenue - ) - - class stamp_duty_land_tax(Variable): label = "Stamp Duty Land Tax" documentation = "Total tax liability for Stamp Duty Land Tax" @@ -169,20 +14,3 @@ class stamp_duty_land_tax(Variable): "sdlt_on_transactions", "sdlt_on_rent", ] - - -class expected_sdlt(Variable): - label = "Stamp Duty (expected)" - documentation = "Expected value of Stamp Duty Land Tax" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - if parameters(period).gov.hmrc.stamp_duty.abolish: - return 0 - return ( - household.state("property_sale_rate", period) - * household("stamp_duty_land_tax", period) - ) + household("corporate_sdlt", period) diff --git a/policyengine_uk/variables/gov/hmrc/tax.py b/policyengine_uk/variables/gov/hmrc/tax.py index 3df50c968..95d862eb5 100644 --- a/policyengine_uk/variables/gov/hmrc/tax.py +++ b/policyengine_uk/variables/gov/hmrc/tax.py @@ -9,80 +9,3 @@ class tax(Variable): unit = GBP adds = ["income_tax", "national_insurance"] - - -class household_tax(Variable): - value_type = float - entity = Household - label = "household taxes" - documentation = "Total taxes owed by the household" - definition_period = YEAR - unit = GBP - adds = [ - "expected_sdlt", - "expected_ltt", - "expected_lbtt", - "corporate_sdlt", - "business_rates", - "council_tax", - "domestic_rates", - "fuel_duty", - "tv_licence", - "wealth_tax", - "non_primary_residence_wealth_tax", - "income_tax", - "national_insurance", - "LVT", - "carbon_tax", - "vat_change", - "capital_gains_tax", - "private_school_vat", - "corporate_incident_tax_revenue_change", - "consumer_incident_tax_revenue_change", - "high_income_incident_tax_change", - "employer_ni_response_capital_incidence", - "employer_ni_response_consumer_incidence", - ] - - def formula(household, period, parameters): - if parameters(period).gov.contrib.abolish_council_tax: - return add( - household, - period, - [ - tax - for tax in household_tax.adds - if tax not in ["council_tax"] - ], - ) - else: - return add(household, period, household_tax.adds) - - -class benunit_tax(Variable): - value_type = float - entity = BenUnit - label = "Benefit unit tax paid" - definition_period = YEAR - unit = GBP - - adds = ["tax"] - - -class tax_reported(Variable): - value_type = float - entity = Person - label = "Reported tax paid" - definition_period = YEAR - unit = GBP - - -class tax_modelling(Variable): - value_type = float - entity = Person - label = "Difference between reported and imputed tax liabilities" - definition_period = YEAR - unit = GBP - - adds = ["tax"] - subtracts = ["tax_reported"] diff --git a/policyengine_uk/variables/gov/hmrc/tax_modelling.py b/policyengine_uk/variables/gov/hmrc/tax_modelling.py new file mode 100644 index 000000000..c8141f4b8 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/tax_modelling.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class tax_modelling(Variable): + value_type = float + entity = Person + label = "Difference between reported and imputed tax liabilities" + definition_period = YEAR + unit = GBP + + adds = ["tax"] + subtracts = ["tax_reported"] diff --git a/policyengine_uk/variables/gov/hmrc/tax_reported.py b/policyengine_uk/variables/gov/hmrc/tax_reported.py new file mode 100644 index 000000000..07dda8179 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/tax_reported.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class tax_reported(Variable): + value_type = float + entity = Person + label = "Reported tax paid" + definition_period = YEAR + unit = GBP diff --git a/policyengine_uk/variables/gov/hmrc/vat.py b/policyengine_uk/variables/gov/hmrc/vat.py index 59c0df030..e0994c86f 100644 --- a/policyengine_uk/variables/gov/hmrc/vat.py +++ b/policyengine_uk/variables/gov/hmrc/vat.py @@ -1,12 +1,5 @@ from policyengine_uk.model_api import * -MICRODATA_VAT_COVERAGE = 0.38 -""" -LCFS (from which ETB data is derived) microdata is known to under-report household consumption. We scale up the VAT liability to hit HMRC-reported VAT receipts (following the approach of IFS' TAXBEN, though they might have better coverage anyway from access to the non-EUL dataset). - -For HMRC statistics see: https://www.gov.uk/government/statistics/value-added-tax-vat-annual-statistics -""" - class vat(Variable): label = "VAT" @@ -25,32 +18,3 @@ def formula(household, period, parameters): full_rate_consumption * vat.standard_rate + reduced_rate_consumption * vat.reduced_rate ) / MICRODATA_VAT_COVERAGE - - -class baseline_vat(Variable): - label = "baseline VAT" - entity = Household - definition_period = YEAR - value_type = float - unit = "currency-GBP" - - def formula(household, period, parameters): - full_rate_consumption = household("full_rate_vat_consumption", period) - reduced_rate_consumption = household( - "reduced_rate_vat_consumption", period - ) - vat = parameters(period).baseline.gov.hmrc.vat - return ( - full_rate_consumption * vat.standard_rate - + reduced_rate_consumption * vat.reduced_rate - ) / MICRODATA_VAT_COVERAGE - - -class vat_change(Variable): - label = "change in VAT liability" - entity = Household - definition_period = YEAR - value_type = float - unit = "currency-GBP" - adds = ["vat"] - subtracts = ["baseline_vat"] diff --git a/policyengine_uk/variables/gov/hmrc/vat_change.py b/policyengine_uk/variables/gov/hmrc/vat_change.py new file mode 100644 index 000000000..e72ef52e7 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/vat_change.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class vat_change(Variable): + label = "change in VAT liability" + entity = Household + definition_period = YEAR + value_type = float + unit = "currency-GBP" + adds = ["vat"] + subtracts = ["baseline_vat"] diff --git a/policyengine_uk/variables/gov/hmrc/would_claim_child_benefit.py b/policyengine_uk/variables/gov/hmrc/would_claim_child_benefit.py new file mode 100644 index 000000000..7d609dd41 --- /dev/null +++ b/policyengine_uk/variables/gov/hmrc/would_claim_child_benefit.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class would_claim_child_benefit(Variable): + label = "Would claim Child Benefit" + documentation = ( + "Whether this benefit unit would claim Child Benefit if eligible" + ) + entity = BenUnit + definition_period = YEAR + value_type = bool + + def formula(benunit, period, parameters): + takeup_rate = parameters(period).gov.hmrc.child_benefit.takeup + overall_p = takeup_rate.overall + return (random(benunit) < overall_p) * ~benunit( + "child_benefit_opts_out", period + ) diff --git a/policyengine_uk/variables/gov/revenue_scotland/expected_lbtt.py b/policyengine_uk/variables/gov/revenue_scotland/expected_lbtt.py new file mode 100644 index 000000000..bdb9d43f3 --- /dev/null +++ b/policyengine_uk/variables/gov/revenue_scotland/expected_lbtt.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class expected_lbtt(Variable): + label = "Land and Buildings Transaction Tax (expected)" + documentation = "Expected value of LBTT" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period): + property_sale_rate = household.state("property_sale_rate", period) + lbtt = household("land_and_buildings_transaction_tax", period) + return property_sale_rate * lbtt diff --git a/policyengine_uk/variables/gov/revenue_scotland/land_and_buildings_transaction_tax.py b/policyengine_uk/variables/gov/revenue_scotland/land_and_buildings_transaction_tax.py new file mode 100644 index 000000000..283caa7ae --- /dev/null +++ b/policyengine_uk/variables/gov/revenue_scotland/land_and_buildings_transaction_tax.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class land_and_buildings_transaction_tax(Variable): + label = "Land and Buildings Transaction Tax" + documentation = "Total tax liability for Scotland's LBTT" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period): + LBTTS = [ + "lbtt_on_transactions", + "lbtt_on_rent", + ] + lbtt_if_liable = add(household, period, LBTTS) + return household("lbtt_liable", period) * lbtt_if_liable diff --git a/policyengine_uk/variables/gov/revenue_scotland/lbtt.py b/policyengine_uk/variables/gov/revenue_scotland/lbtt.py deleted file mode 100644 index ec141198f..000000000 --- a/policyengine_uk/variables/gov/revenue_scotland/lbtt.py +++ /dev/null @@ -1,160 +0,0 @@ -from policyengine_uk.model_api import * - - -class lbtt_on_residential_property_transactions(Variable): - label = "LBTT on residential property" - documentation = "LBTT charge on purchase of residential property" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - lbtt = parameters(period).gov.revenue_scotland.lbtt - # Tax on main-home purchases - price = household("main_residential_property_purchased", period) - residential_purchase_qualifies_as_first_buy = household( - "main_residential_property_purchased_is_first_home", period - ) - main_residential_purchase_tax = where( - residential_purchase_qualifies_as_first_buy, - lbtt.residential.first_time_buyer_rate.calc(price), - lbtt.residential.rate.calc(price), - ) - # Tax on second-home purchases - second_home_price = household( - "additional_residential_property_purchased", period - ) - lbtt2 = lbtt.residential.rate.calc(second_home_price) - surcharge = ( - lbtt.residential.additional_residence_surcharge * second_home_price - ) - additional_residential_purchase_tax = lbtt2 + surcharge - return ( - main_residential_purchase_tax + additional_residential_purchase_tax - ) - - -class lbtt_on_residential_property_rent(Variable): - label = "LBTT on residential property rent" - documentation = "LBTT charge on rental of residential property" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - lbtt = parameters(period).gov.revenue_scotland.lbtt - cumulative_rent = household("cumulative_residential_rent", period) - rent = household("rent", period) - lbtt_cumulative_rent = lbtt.rent.calc(cumulative_rent) - lbtt_total_rent = lbtt.rent.calc(cumulative_rent + rent) - return lbtt_total_rent - lbtt_cumulative_rent - - -class lbtt_on_non_residential_property_transactions(Variable): - label = "LBTT on non-residential property transactions" - documentation = "LBTT charge from purchase of non-residential property" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - lbtt = parameters(period).gov.revenue_scotland.lbtt - price = household("non_residential_property_purchased", period) - return lbtt.non_residential.calc(price) - - -class lbtt_on_non_residential_property_rent(Variable): - label = "LBTT on non-residential property" - documentation = ( - "LBTT charge from purchase or rental of non-residential property" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - lbtt = parameters(period).gov.revenue_scotland.lbtt - cumulative_rent = household("cumulative_non_residential_rent", period) - rent = household("non_residential_rent", period) - lbtt_cumulative_rent = lbtt.rent.calc(cumulative_rent) - lbtt_total_rent = lbtt.rent.calc(cumulative_rent + rent) - return lbtt_total_rent - lbtt_cumulative_rent - - -class lbtt_liable(Variable): - label = "Liable for Land and Buildings Transaction Tax" - documentation = "Whether the household is liable for Land and Buildings Transaction Tax" - entity = Household - definition_period = YEAR - value_type = bool - unit = GBP - - def formula(household, period): - country = household("country", period) - countries = country.possible_values - return country == countries.SCOTLAND - - -class lbtt_on_transactions(Variable): - label = "LBTT on property transactions" - documentation = "Land and Buildings Transaction Tax on property transfers" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - adds = [ - "lbtt_on_residential_property_transactions", - "lbtt_on_non_residential_property_transactions", - ] - - -class lbtt_on_rent(Variable): - label = "LBTT on property rental" - documentation = ( - "Land and Buildings Transaction Tax on property rental agreements" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - adds = [ - "lbtt_on_residential_property_rent", - "lbtt_on_non_residential_property_rent", - ] - - -class land_and_buildings_transaction_tax(Variable): - label = "Land and Buildings Transaction Tax" - documentation = "Total tax liability for Scotland's LBTT" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period): - LBTTS = [ - "lbtt_on_transactions", - "lbtt_on_rent", - ] - lbtt_if_liable = add(household, period, LBTTS) - return household("lbtt_liable", period) * lbtt_if_liable - - -class expected_lbtt(Variable): - label = "Land and Buildings Transaction Tax (expected)" - documentation = "Expected value of LBTT" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period): - property_sale_rate = household.state("property_sale_rate", period) - lbtt = household("land_and_buildings_transaction_tax", period) - return property_sale_rate * lbtt diff --git a/policyengine_uk/variables/gov/revenue_scotland/lbtt_liable.py b/policyengine_uk/variables/gov/revenue_scotland/lbtt_liable.py new file mode 100644 index 000000000..7db980ae9 --- /dev/null +++ b/policyengine_uk/variables/gov/revenue_scotland/lbtt_liable.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class lbtt_liable(Variable): + label = "Liable for Land and Buildings Transaction Tax" + documentation = "Whether the household is liable for Land and Buildings Transaction Tax" + entity = Household + definition_period = YEAR + value_type = bool + unit = GBP + + def formula(household, period): + country = household("country", period) + countries = country.possible_values + return country == countries.SCOTLAND diff --git a/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_non_residential_property_rent.py b/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_non_residential_property_rent.py new file mode 100644 index 000000000..985ceeaa7 --- /dev/null +++ b/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_non_residential_property_rent.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * + + +class lbtt_on_non_residential_property_rent(Variable): + label = "LBTT on non-residential property" + documentation = ( + "LBTT charge from purchase or rental of non-residential property" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + lbtt = parameters(period).gov.revenue_scotland.lbtt + cumulative_rent = household("cumulative_non_residential_rent", period) + rent = household("non_residential_rent", period) + lbtt_cumulative_rent = lbtt.rent.calc(cumulative_rent) + lbtt_total_rent = lbtt.rent.calc(cumulative_rent + rent) + return lbtt_total_rent - lbtt_cumulative_rent diff --git a/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_non_residential_property_transactions.py b/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_non_residential_property_transactions.py new file mode 100644 index 000000000..f140413f5 --- /dev/null +++ b/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_non_residential_property_transactions.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class lbtt_on_non_residential_property_transactions(Variable): + label = "LBTT on non-residential property transactions" + documentation = "LBTT charge from purchase of non-residential property" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + lbtt = parameters(period).gov.revenue_scotland.lbtt + price = household("non_residential_property_purchased", period) + return lbtt.non_residential.calc(price) diff --git a/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_rent.py b/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_rent.py new file mode 100644 index 000000000..94a6c58dc --- /dev/null +++ b/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_rent.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class lbtt_on_rent(Variable): + label = "LBTT on property rental" + documentation = ( + "Land and Buildings Transaction Tax on property rental agreements" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + adds = [ + "lbtt_on_residential_property_rent", + "lbtt_on_non_residential_property_rent", + ] diff --git a/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_residential_property_rent.py b/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_residential_property_rent.py new file mode 100644 index 000000000..8c6720f0e --- /dev/null +++ b/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_residential_property_rent.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class lbtt_on_residential_property_rent(Variable): + label = "LBTT on residential property rent" + documentation = "LBTT charge on rental of residential property" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + lbtt = parameters(period).gov.revenue_scotland.lbtt + cumulative_rent = household("cumulative_residential_rent", period) + rent = household("rent", period) + lbtt_cumulative_rent = lbtt.rent.calc(cumulative_rent) + lbtt_total_rent = lbtt.rent.calc(cumulative_rent + rent) + return lbtt_total_rent - lbtt_cumulative_rent diff --git a/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_residential_property_transactions.py b/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_residential_property_transactions.py new file mode 100644 index 000000000..2948eb9d3 --- /dev/null +++ b/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_residential_property_transactions.py @@ -0,0 +1,35 @@ +from policyengine_uk.model_api import * + + +class lbtt_on_residential_property_transactions(Variable): + label = "LBTT on residential property" + documentation = "LBTT charge on purchase of residential property" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + lbtt = parameters(period).gov.revenue_scotland.lbtt + # Tax on main-home purchases + price = household("main_residential_property_purchased", period) + residential_purchase_qualifies_as_first_buy = household( + "main_residential_property_purchased_is_first_home", period + ) + main_residential_purchase_tax = where( + residential_purchase_qualifies_as_first_buy, + lbtt.residential.first_time_buyer_rate.calc(price), + lbtt.residential.rate.calc(price), + ) + # Tax on second-home purchases + second_home_price = household( + "additional_residential_property_purchased", period + ) + lbtt2 = lbtt.residential.rate.calc(second_home_price) + surcharge = ( + lbtt.residential.additional_residence_surcharge * second_home_price + ) + additional_residential_purchase_tax = lbtt2 + surcharge + return ( + main_residential_purchase_tax + additional_residential_purchase_tax + ) diff --git a/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_transactions.py b/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_transactions.py new file mode 100644 index 000000000..605476e43 --- /dev/null +++ b/policyengine_uk/variables/gov/revenue_scotland/lbtt_on_transactions.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class lbtt_on_transactions(Variable): + label = "LBTT on property transactions" + documentation = "Land and Buildings Transaction Tax on property transfers" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + adds = [ + "lbtt_on_residential_property_transactions", + "lbtt_on_non_residential_property_transactions", + ] diff --git a/policyengine_uk/variables/gov/simulation/labor_supply_response/employment_income_behavioral_response.py b/policyengine_uk/variables/gov/simulation/labor_supply_response/employment_income_behavioral_response.py new file mode 100644 index 000000000..bc6d03939 --- /dev/null +++ b/policyengine_uk/variables/gov/simulation/labor_supply_response/employment_income_behavioral_response.py @@ -0,0 +1,47 @@ +from policyengine_uk.model_api import * + + +class employment_income_behavioral_response(Variable): + value_type = float + entity = Person + label = "income-related labor supply change" + unit = GBP + definition_period = YEAR + + def formula(person, period, parameters): + lsr = parameters(period).gov.simulation.labor_supply_responses + simulation = person.simulation + if simulation.baseline is None: + return 0 # No reform, no impact + if lsr.income_elasticity == 0 and lsr.substitution_elasticity == 0: + return 0 + + measurement_branch = simulation.get_branch( + "lsr_measurement", clone_system=True + ) # A branch without LSRs + baseline_branch = simulation.get_branch("baseline").get_branch( + "baseline_lsr_measurement", clone_system=True + ) # Already created by default + + # (system with LSRs) <- (system without LSRs used to calculate LSRs) + # | + # * -(baseline system without LSRs used to calculate LSRs) + + for branch in [measurement_branch, baseline_branch]: + branch.tax_benefit_system.neutralize_variable( + "employment_income_behavioral_response" + ) + branch.set_input( + "employment_income_before_lsr", + period, + person("employment_income_before_lsr", period), + ) + + return add( + person, + period, + [ + "income_elasticity_lsr", + "substitution_elasticity_lsr", + ], + ) diff --git a/policyengine_uk/variables/gov/simulation/labor_supply_response/income_elasticity_lsr.py b/policyengine_uk/variables/gov/simulation/labor_supply_response/income_elasticity_lsr.py new file mode 100644 index 000000000..f7d985bf5 --- /dev/null +++ b/policyengine_uk/variables/gov/simulation/labor_supply_response/income_elasticity_lsr.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class income_elasticity_lsr(Variable): + value_type = float + entity = Person + label = "income elasticity of labor supply response" + unit = GBP + definition_period = YEAR + requires_computation_after = "employment_income_behavioral_response" + + def formula(person, period, parameters): + lsr = parameters(period).gov.simulation.labor_supply_responses + employment_income = person("employment_income_before_lsr", period) + income_change = person("relative_income_change", period) + + return employment_income * income_change * lsr.income_elasticity diff --git a/policyengine_uk/variables/gov/simulation/labor_supply_response/labor_supply_response.py b/policyengine_uk/variables/gov/simulation/labor_supply_response/labor_supply_response.py deleted file mode 100644 index eefa3de13..000000000 --- a/policyengine_uk/variables/gov/simulation/labor_supply_response/labor_supply_response.py +++ /dev/null @@ -1,155 +0,0 @@ -from policyengine_uk.model_api import * - - -class relative_income_change(Variable): - value_type = float - entity = Person - label = "relative income change" - unit = "/1" - definition_period = YEAR - requires_computation_after = "employment_income_behavioral_response" - - def formula(person, period, parameters): - simulation = person.simulation - measurement_branch = simulation.get_branch("lsr_measurement") - baseline_branch = simulation.get_branch("baseline").get_branch( - "baseline_lsr_measurement" - ) - baseline_branch.set_input( - "capital_gains_before_response", - period, - person("capital_gains_before_response", period), - ) - measurement_person = measurement_branch.populations["person"] - baseline_person = baseline_branch.populations["person"] - baseline_net_income = baseline_person.household( - "household_net_income", period - ) - net_income = measurement_person.household( - "household_net_income", period - ) - income_change_bound = parameters( - period - ).gov.simulation.labor_supply_responses.bounds.income_change - # _c suffix for "clipped" - baseline_net_income_c = np.clip(baseline_net_income, 1, None) - net_income_c = np.clip(net_income, 1, None) - relative_change = ( - net_income_c - baseline_net_income_c - ) / baseline_net_income_c - return np.clip( - relative_change, -income_change_bound, income_change_bound - ) - - -class relative_wage_change(Variable): - value_type = float - entity = Person - label = "relative wage change" - unit = "/1" - definition_period = YEAR - requires_computation_after = "employment_income_behavioral_response" - - def formula(person, period, parameters): - simulation = person.simulation - measurement_branch = simulation.get_branch("lsr_measurement") - baseline_branch = simulation.get_branch("baseline").get_branch( - "baseline_lsr_measurement" - ) - baseline_branch.set_input( - "capital_gains_before_response", - period, - person("capital_gains_before_response", period), - ) - measurement_person = measurement_branch.populations["person"] - baseline_person = baseline_branch.populations["person"] - baseline_mtr = baseline_person("marginal_tax_rate", period) - baseline_wage = 1 - baseline_mtr - mtr = measurement_person("marginal_tax_rate", period) - wage_rate = 1 - mtr - # _c suffix for "clipped" - baseline_wage_c = np.where(baseline_wage == 0, 0.01, baseline_wage) - wage_rate_c = np.where(wage_rate == 0, 0.01, wage_rate) - relative_change = (wage_rate_c - baseline_wage_c) / baseline_wage_c - wage_change_bound = parameters( - period - ).gov.simulation.labor_supply_responses.bounds.effective_wage_rate_change - return np.clip(relative_change, -wage_change_bound, wage_change_bound) - - -class income_elasticity_lsr(Variable): - value_type = float - entity = Person - label = "income elasticity of labor supply response" - unit = GBP - definition_period = YEAR - requires_computation_after = "employment_income_behavioral_response" - - def formula(person, period, parameters): - lsr = parameters(period).gov.simulation.labor_supply_responses - employment_income = person("employment_income_before_lsr", period) - income_change = person("relative_income_change", period) - - return employment_income * income_change * lsr.income_elasticity - - -class substitution_elasticity_lsr(Variable): - value_type = float - entity = Person - label = "substitution elasticity of labor supply response" - unit = GBP - definition_period = YEAR - requires_computation_after = "employment_income_behavioral_response" - - def formula(person, period, parameters): - lsr = parameters(period).gov.simulation.labor_supply_responses - employment_income = person("employment_income_before_lsr", period) - wage_change = person("relative_wage_change", period) - - return employment_income * wage_change * lsr.substitution_elasticity - - -class employment_income_behavioral_response(Variable): - value_type = float - entity = Person - label = "income-related labor supply change" - unit = GBP - definition_period = YEAR - - def formula(person, period, parameters): - lsr = parameters(period).gov.simulation.labor_supply_responses - simulation = person.simulation - if simulation.baseline is None: - return 0 # No reform, no impact - if lsr.income_elasticity == 0 and lsr.substitution_elasticity == 0: - return 0 - - measurement_branch = simulation.get_branch( - "lsr_measurement", clone_system=True - ) # A branch without LSRs - baseline_branch = simulation.get_branch("baseline").get_branch( - "baseline_lsr_measurement", clone_system=True - ) # Already created by default - - # (system with LSRs) <- (system without LSRs used to calculate LSRs) - # | - # * -(baseline system without LSRs used to calculate LSRs) - - for branch in [measurement_branch, baseline_branch]: - branch.tax_benefit_system.neutralize_variable( - "employment_income_behavioral_response" - ) - branch.set_input( - "employment_income_before_lsr", - period, - person("employment_income_before_lsr", period), - ) - - return add( - person, - period, - [ - "income_elasticity_lsr", - "substitution_elasticity_lsr", - ], - ) diff --git a/policyengine_uk/variables/gov/simulation/labor_supply_response/relative_income_change.py b/policyengine_uk/variables/gov/simulation/labor_supply_response/relative_income_change.py new file mode 100644 index 000000000..42eb4a66e --- /dev/null +++ b/policyengine_uk/variables/gov/simulation/labor_supply_response/relative_income_change.py @@ -0,0 +1,42 @@ +from policyengine_uk.model_api import * + + +class relative_income_change(Variable): + value_type = float + entity = Person + label = "relative income change" + unit = "/1" + definition_period = YEAR + requires_computation_after = "employment_income_behavioral_response" + + def formula(person, period, parameters): + simulation = person.simulation + measurement_branch = simulation.get_branch("lsr_measurement") + baseline_branch = simulation.get_branch("baseline").get_branch( + "baseline_lsr_measurement" + ) + baseline_branch.set_input( + "capital_gains_before_response", + period, + person("capital_gains_before_response", period), + ) + measurement_person = measurement_branch.populations["person"] + baseline_person = baseline_branch.populations["person"] + baseline_net_income = baseline_person.household( + "household_net_income", period + ) + net_income = measurement_person.household( + "household_net_income", period + ) + income_change_bound = parameters( + period + ).gov.simulation.labor_supply_responses.bounds.income_change + # _c suffix for "clipped" + baseline_net_income_c = np.clip(baseline_net_income, 1, None) + net_income_c = np.clip(net_income, 1, None) + relative_change = ( + net_income_c - baseline_net_income_c + ) / baseline_net_income_c + return np.clip( + relative_change, -income_change_bound, income_change_bound + ) diff --git a/policyengine_uk/variables/gov/simulation/labor_supply_response/relative_wage_change.py b/policyengine_uk/variables/gov/simulation/labor_supply_response/relative_wage_change.py new file mode 100644 index 000000000..62b9d79ef --- /dev/null +++ b/policyengine_uk/variables/gov/simulation/labor_supply_response/relative_wage_change.py @@ -0,0 +1,36 @@ +from policyengine_uk.model_api import * + + +class relative_wage_change(Variable): + value_type = float + entity = Person + label = "relative wage change" + unit = "/1" + definition_period = YEAR + requires_computation_after = "employment_income_behavioral_response" + + def formula(person, period, parameters): + simulation = person.simulation + measurement_branch = simulation.get_branch("lsr_measurement") + baseline_branch = simulation.get_branch("baseline").get_branch( + "baseline_lsr_measurement" + ) + baseline_branch.set_input( + "capital_gains_before_response", + period, + person("capital_gains_before_response", period), + ) + measurement_person = measurement_branch.populations["person"] + baseline_person = baseline_branch.populations["person"] + baseline_mtr = baseline_person("marginal_tax_rate", period) + baseline_wage = 1 - baseline_mtr + mtr = measurement_person("marginal_tax_rate", period) + wage_rate = 1 - mtr + # _c suffix for "clipped" + baseline_wage_c = np.where(baseline_wage == 0, 0.01, baseline_wage) + wage_rate_c = np.where(wage_rate == 0, 0.01, wage_rate) + relative_change = (wage_rate_c - baseline_wage_c) / baseline_wage_c + wage_change_bound = parameters( + period + ).gov.simulation.labor_supply_responses.bounds.effective_wage_rate_change + return np.clip(relative_change, -wage_change_bound, wage_change_bound) diff --git a/policyengine_uk/variables/gov/simulation/labor_supply_response/substitution_elasticity_lsr.py b/policyengine_uk/variables/gov/simulation/labor_supply_response/substitution_elasticity_lsr.py new file mode 100644 index 000000000..c95ed925d --- /dev/null +++ b/policyengine_uk/variables/gov/simulation/labor_supply_response/substitution_elasticity_lsr.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class substitution_elasticity_lsr(Variable): + value_type = float + entity = Person + label = "substitution elasticity of labor supply response" + unit = GBP + definition_period = YEAR + requires_computation_after = "employment_income_behavioral_response" + + def formula(person, period, parameters): + lsr = parameters(period).gov.simulation.labor_supply_responses + employment_income = person("employment_income_before_lsr", period) + wage_change = person("relative_wage_change", period) + + return employment_income * wage_change * lsr.substitution_elasticity diff --git a/policyengine_uk/variables/gov/treasury/energy_bills_rebate/council_tax_rebate.py b/policyengine_uk/variables/gov/treasury/energy_bills_rebate/council_tax_rebate.py index 0680ddbdd..f80c4c1c3 100644 --- a/policyengine_uk/variables/gov/treasury/energy_bills_rebate/council_tax_rebate.py +++ b/policyengine_uk/variables/gov/treasury/energy_bills_rebate/council_tax_rebate.py @@ -1,7 +1,4 @@ from policyengine_uk.model_api import * -from policyengine_uk.variables.input.housing import ( - CouncilTaxBand, -) class ebr_council_tax_rebate(Variable): @@ -17,10 +14,11 @@ def formula(household, period, parameters): period ).gov.treasury.energy_bills_rebate.council_tax_rebate ct_band = household("council_tax_band", period) + ct_band_values = ct_band.possible_values eligible = np.any( np.array( [ - ct_band == getattr(CouncilTaxBand, band) + ct_band == getattr(ct_band_values, band) for band in ctr.bands ] ), diff --git a/policyengine_uk/variables/gov/treasury/price_cap_subsidy/energy_price_cap_subsidy.py b/policyengine_uk/variables/gov/treasury/price_cap_subsidy/energy_price_cap_subsidy.py deleted file mode 100644 index 641d1c738..000000000 --- a/policyengine_uk/variables/gov/treasury/price_cap_subsidy/energy_price_cap_subsidy.py +++ /dev/null @@ -1,86 +0,0 @@ -from policyengine_uk.model_api import * - - -class monthly_domestic_energy_consumption(Variable): - label = "Monthly domestic energy consumption" - entity = Household - definition_period = MONTH - value_type = float - unit = "currency-GBP" - - def formula(household, period, parameters): - # From RF "A chilling crisis", p11. Available at https://www.resolutionfoundation.org/app/uploads/2022/08/A-chilling-crisis.pdf - # This is very approximate but gives us some distribution of seasonal energy consumption at least. - ENERGY_CONSUMPTION_RATIOS = [ - 350, - 350, - 350, - 180, - 180, - 180, - 180, - 180, - 180, - 300, - 300, - 300, - ] - consumption_distribution = np.array(ENERGY_CONSUMPTION_RATIOS) / sum( - ENERGY_CONSUMPTION_RATIOS - ) - month = period.start.month - return ( - household("domestic_energy_consumption", period.this_year) - * consumption_distribution[month - 1] - ) - - -class monthly_epg_consumption_level(Variable): - label = "Monthly EPG subsidy level" - entity = Household - definition_period = MONTH - value_type = float - unit = "currency-GBP" - - def formula(household, period, parameters): - energy_consumption = household( - "monthly_domestic_energy_consumption", period - ) - ofgem = parameters.gov.ofgem - price_cap = ofgem.energy_price_cap(period) - price_guarantee = ofgem.energy_price_guarantee(period) - return energy_consumption * price_guarantee / price_cap - - -class monthly_epg_subsidy(Variable): - label = "Monthly EPG subsidy" - entity = Household - definition_period = MONTH - value_type = float - unit = "currency-GBP" - - def formula(household, period, parameters): - energy_consumption = household( - "monthly_domestic_energy_consumption", period - ) - epg_consumption_level = household( - "monthly_epg_consumption_level", period - ) - return max_(0, energy_consumption - epg_consumption_level) - - -class epg_subsidy(Variable): - label = "Energy price guarantee subsidy" - documentation = "Reduction in energy bills due to offsetting the price cap and compensating energy firms." - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - total_subsidy = 0 - for month in range(1, MONTHS_IN_YEAR + 1): - total_subsidy = total_subsidy + household( - "monthly_epg_subsidy", f"{period.this_year}-{month:02d}" - ) - return total_subsidy diff --git a/policyengine_uk/variables/gov/treasury/price_cap_subsidy/epg_subsidy.py b/policyengine_uk/variables/gov/treasury/price_cap_subsidy/epg_subsidy.py new file mode 100644 index 000000000..1ca0fb2bf --- /dev/null +++ b/policyengine_uk/variables/gov/treasury/price_cap_subsidy/epg_subsidy.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class epg_subsidy(Variable): + label = "Energy price guarantee subsidy" + documentation = "Reduction in energy bills due to offsetting the price cap and compensating energy firms." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + total_subsidy = 0 + for month in range(1, MONTHS_IN_YEAR + 1): + total_subsidy = total_subsidy + household( + "monthly_epg_subsidy", f"{period.this_year}-{month:02d}" + ) + return total_subsidy diff --git a/policyengine_uk/variables/gov/treasury/price_cap_subsidy/monthly_domestic_energy_consumption.py b/policyengine_uk/variables/gov/treasury/price_cap_subsidy/monthly_domestic_energy_consumption.py new file mode 100644 index 000000000..758f94c1e --- /dev/null +++ b/policyengine_uk/variables/gov/treasury/price_cap_subsidy/monthly_domestic_energy_consumption.py @@ -0,0 +1,35 @@ +from policyengine_uk.model_api import * + + +class monthly_domestic_energy_consumption(Variable): + label = "Monthly domestic energy consumption" + entity = Household + definition_period = MONTH + value_type = float + unit = "currency-GBP" + + def formula(household, period, parameters): + # From RF "A chilling crisis", p11. Available at https://www.resolutionfoundation.org/app/uploads/2022/08/A-chilling-crisis.pdf + # This is very approximate but gives us some distribution of seasonal energy consumption at least. + ENERGY_CONSUMPTION_RATIOS = [ + 350, + 350, + 350, + 180, + 180, + 180, + 180, + 180, + 180, + 300, + 300, + 300, + ] + consumption_distribution = np.array(ENERGY_CONSUMPTION_RATIOS) / sum( + ENERGY_CONSUMPTION_RATIOS + ) + month = period.start.month + return ( + household("domestic_energy_consumption", period.this_year) + * consumption_distribution[month - 1] + ) diff --git a/policyengine_uk/variables/gov/treasury/price_cap_subsidy/monthly_epg_consumption_level.py b/policyengine_uk/variables/gov/treasury/price_cap_subsidy/monthly_epg_consumption_level.py new file mode 100644 index 000000000..e196a1536 --- /dev/null +++ b/policyengine_uk/variables/gov/treasury/price_cap_subsidy/monthly_epg_consumption_level.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class monthly_epg_consumption_level(Variable): + label = "Monthly EPG subsidy level" + entity = Household + definition_period = MONTH + value_type = float + unit = "currency-GBP" + + def formula(household, period, parameters): + energy_consumption = household( + "monthly_domestic_energy_consumption", period + ) + ofgem = parameters.gov.ofgem + price_cap = ofgem.energy_price_cap(period) + price_guarantee = ofgem.energy_price_guarantee(period) + return energy_consumption * price_guarantee / price_cap diff --git a/policyengine_uk/variables/gov/treasury/price_cap_subsidy/monthly_epg_subsidy.py b/policyengine_uk/variables/gov/treasury/price_cap_subsidy/monthly_epg_subsidy.py new file mode 100644 index 000000000..88f99855c --- /dev/null +++ b/policyengine_uk/variables/gov/treasury/price_cap_subsidy/monthly_epg_subsidy.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class monthly_epg_subsidy(Variable): + label = "Monthly EPG subsidy" + entity = Household + definition_period = MONTH + value_type = float + unit = "currency-GBP" + + def formula(household, period, parameters): + energy_consumption = household( + "monthly_domestic_energy_consumption", period + ) + epg_consumption_level = household( + "monthly_epg_consumption_level", period + ) + return max_(0, energy_consumption - epg_consumption_level) diff --git a/policyengine_uk/variables/gov/wra/expected_ltt.py b/policyengine_uk/variables/gov/wra/expected_ltt.py new file mode 100644 index 000000000..68d192594 --- /dev/null +++ b/policyengine_uk/variables/gov/wra/expected_ltt.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class expected_ltt(Variable): + label = "Land Transaction Tax (expected)" + documentation = "Expected value of Land Transaction Tax" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period): + property_sale_rate = household.state("property_sale_rate", period) + land_transaction_tax = household("land_transaction_tax", period) + return property_sale_rate * land_transaction_tax diff --git a/policyengine_uk/variables/gov/wra/land_transaction_tax.py b/policyengine_uk/variables/gov/wra/land_transaction_tax.py index 722b97bf5..fbef94f7b 100644 --- a/policyengine_uk/variables/gov/wra/land_transaction_tax.py +++ b/policyengine_uk/variables/gov/wra/land_transaction_tax.py @@ -1,135 +1,6 @@ from policyengine_uk.model_api import * -class ltt_liable(Variable): - label = "Liable for Land Transaction Tax" - documentation = ( - "Whether the household is liable to pay the Wales Land Transaction Tax" - ) - entity = Household - definition_period = YEAR - value_type = bool - - def formula(household, period): - country = household("country", period) - return country == country.possible_values.WALES - - -class ltt_on_residential_property_transactions(Variable): - label = "LTT on residential property" - documentation = ( - "Land Transaction Tax charge on residential property transactions" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - ltt = parameters(period).gov.wra.land_transaction_tax - main_home_price = household( - "main_residential_property_purchased", period - ) - primary_residential_purchase_tax = ltt.residential.primary.calc( - main_home_price - ) - second_home_price = household( - "additional_residential_property_purchased", period - ) - additional_residential_purchase_tax = ltt.residential.higher_rate.calc( - second_home_price - ) - return ( - primary_residential_purchase_tax - + additional_residential_purchase_tax - ) - - -class ltt_on_residential_property_rent(Variable): - label = "LTT on residential property rent" - documentation = ( - "Land Transaction Tax charge on residential property rental agreements" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - ltt = parameters(period).gov.wra.land_transaction_tax - cumulative_rent = household("cumulative_residential_rent", period) - rent = household("rent", period) - ltt_on_cumulative_rent = ltt.rent.calc(cumulative_rent) - ltt_on_total_rent = ltt.rent.calc(cumulative_rent + rent) - return ltt_on_total_rent - ltt_on_cumulative_rent - - -class ltt_on_non_residential_property_transactions(Variable): - label = "LTT on non-residential property transactions" - documentation = ( - "Land Transaction Tax charge on non-residential property transactions" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - ltt = parameters(period).gov.wra.land_transaction_tax - price = household("non_residential_property_purchased", period) - non_residential_purchase_tax = ltt.non_residential.calc(price) - return household("ltt_liable", period) * non_residential_purchase_tax - - -class ltt_on_non_residential_property_rent(Variable): - label = "LTT on non-residential property rent" - documentation = "Land Transaction Tax charge on non-residential property rental agreements" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - ltt = parameters(period).gov.wra.land_transaction_tax - cumulative_rent = household("cumulative_non_residential_rent", period) - rent = household("non_residential_rent", period) - ltt_on_cumulative_rent = ltt.rent.calc(cumulative_rent) - ltt_on_total_rent = ltt.rent.calc(cumulative_rent + rent) - return ltt_on_total_rent - ltt_on_cumulative_rent - - -class ltt_on_transactions(Variable): - label = "LTT on property transactions" - documentation = "Land Transaction Tax on property transfers" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period): - LTT_TRANSACTION_VARIABLES = [ - "ltt_on_residential_property_transactions", - "ltt_on_non_residential_property_transactions", - ] - return add(household, period, LTT_TRANSACTION_VARIABLES) - - -class ltt_on_rent(Variable): - label = "LTT on property rental" - documentation = "Land Transaction Tax on property rental agreements" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period): - LTT_RENT_VARIABLES = [ - "ltt_on_residential_property_rent", - "ltt_on_non_residential_property_rent", - ] - return add(household, period, LTT_RENT_VARIABLES) - - class land_transaction_tax(Variable): label = "Land Transaction Tax" documentation = "Total tax liability for Land Transaction Tax" @@ -143,17 +14,3 @@ def formula(household, period): household, period, ["ltt_on_transactions", "ltt_on_rent"] ) return household("ltt_liable", period) * ltt_if_liable - - -class expected_ltt(Variable): - label = "Land Transaction Tax (expected)" - documentation = "Expected value of Land Transaction Tax" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period): - property_sale_rate = household.state("property_sale_rate", period) - land_transaction_tax = household("land_transaction_tax", period) - return property_sale_rate * land_transaction_tax diff --git a/policyengine_uk/variables/gov/wra/ltt_liable.py b/policyengine_uk/variables/gov/wra/ltt_liable.py new file mode 100644 index 000000000..94b548761 --- /dev/null +++ b/policyengine_uk/variables/gov/wra/ltt_liable.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class ltt_liable(Variable): + label = "Liable for Land Transaction Tax" + documentation = ( + "Whether the household is liable to pay the Wales Land Transaction Tax" + ) + entity = Household + definition_period = YEAR + value_type = bool + + def formula(household, period): + country = household("country", period) + return country == country.possible_values.WALES diff --git a/policyengine_uk/variables/gov/wra/ltt_on_non_residential_property_rent.py b/policyengine_uk/variables/gov/wra/ltt_on_non_residential_property_rent.py new file mode 100644 index 000000000..0ca0c0e16 --- /dev/null +++ b/policyengine_uk/variables/gov/wra/ltt_on_non_residential_property_rent.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class ltt_on_non_residential_property_rent(Variable): + label = "LTT on non-residential property rent" + documentation = "Land Transaction Tax charge on non-residential property rental agreements" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + ltt = parameters(period).gov.wra.land_transaction_tax + cumulative_rent = household("cumulative_non_residential_rent", period) + rent = household("non_residential_rent", period) + ltt_on_cumulative_rent = ltt.rent.calc(cumulative_rent) + ltt_on_total_rent = ltt.rent.calc(cumulative_rent + rent) + return ltt_on_total_rent - ltt_on_cumulative_rent diff --git a/policyengine_uk/variables/gov/wra/ltt_on_non_residential_property_transactions.py b/policyengine_uk/variables/gov/wra/ltt_on_non_residential_property_transactions.py new file mode 100644 index 000000000..d6e476a39 --- /dev/null +++ b/policyengine_uk/variables/gov/wra/ltt_on_non_residential_property_transactions.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class ltt_on_non_residential_property_transactions(Variable): + label = "LTT on non-residential property transactions" + documentation = ( + "Land Transaction Tax charge on non-residential property transactions" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + ltt = parameters(period).gov.wra.land_transaction_tax + price = household("non_residential_property_purchased", period) + non_residential_purchase_tax = ltt.non_residential.calc(price) + return household("ltt_liable", period) * non_residential_purchase_tax diff --git a/policyengine_uk/variables/gov/wra/ltt_on_rent.py b/policyengine_uk/variables/gov/wra/ltt_on_rent.py new file mode 100644 index 000000000..8accfe8ed --- /dev/null +++ b/policyengine_uk/variables/gov/wra/ltt_on_rent.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class ltt_on_rent(Variable): + label = "LTT on property rental" + documentation = "Land Transaction Tax on property rental agreements" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period): + LTT_RENT_VARIABLES = [ + "ltt_on_residential_property_rent", + "ltt_on_non_residential_property_rent", + ] + return add(household, period, LTT_RENT_VARIABLES) diff --git a/policyengine_uk/variables/gov/wra/ltt_on_residential_property_rent.py b/policyengine_uk/variables/gov/wra/ltt_on_residential_property_rent.py new file mode 100644 index 000000000..aa1a52981 --- /dev/null +++ b/policyengine_uk/variables/gov/wra/ltt_on_residential_property_rent.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * + + +class ltt_on_residential_property_rent(Variable): + label = "LTT on residential property rent" + documentation = ( + "Land Transaction Tax charge on residential property rental agreements" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + ltt = parameters(period).gov.wra.land_transaction_tax + cumulative_rent = household("cumulative_residential_rent", period) + rent = household("rent", period) + ltt_on_cumulative_rent = ltt.rent.calc(cumulative_rent) + ltt_on_total_rent = ltt.rent.calc(cumulative_rent + rent) + return ltt_on_total_rent - ltt_on_cumulative_rent diff --git a/policyengine_uk/variables/gov/wra/ltt_on_residential_property_transactions.py b/policyengine_uk/variables/gov/wra/ltt_on_residential_property_transactions.py new file mode 100644 index 000000000..a4bd16bff --- /dev/null +++ b/policyengine_uk/variables/gov/wra/ltt_on_residential_property_transactions.py @@ -0,0 +1,31 @@ +from policyengine_uk.model_api import * + + +class ltt_on_residential_property_transactions(Variable): + label = "LTT on residential property" + documentation = ( + "Land Transaction Tax charge on residential property transactions" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + ltt = parameters(period).gov.wra.land_transaction_tax + main_home_price = household( + "main_residential_property_purchased", period + ) + primary_residential_purchase_tax = ltt.residential.primary.calc( + main_home_price + ) + second_home_price = household( + "additional_residential_property_purchased", period + ) + additional_residential_purchase_tax = ltt.residential.higher_rate.calc( + second_home_price + ) + return ( + primary_residential_purchase_tax + + additional_residential_purchase_tax + ) diff --git a/policyengine_uk/variables/gov/wra/ltt_on_transactions.py b/policyengine_uk/variables/gov/wra/ltt_on_transactions.py new file mode 100644 index 000000000..8be8b442f --- /dev/null +++ b/policyengine_uk/variables/gov/wra/ltt_on_transactions.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class ltt_on_transactions(Variable): + label = "LTT on property transactions" + documentation = "Land Transaction Tax on property transfers" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period): + LTT_TRANSACTION_VARIABLES = [ + "ltt_on_residential_property_transactions", + "ltt_on_non_residential_property_transactions", + ] + return add(household, period, LTT_TRANSACTION_VARIABLES) diff --git a/policyengine_uk/variables/household/BRMA.py b/policyengine_uk/variables/household/BRMA.py new file mode 100644 index 000000000..da5ac0adb --- /dev/null +++ b/policyengine_uk/variables/household/BRMA.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.locations import BRMAName +from policyengine_uk.variables.household.demographic.geography import Region +import pandas as pd +import numpy as np + + +class BRMA(Variable): + value_type = Enum + possible_values = BRMAName + default_value = BRMAName.MAIDSTONE + entity = Household + label = "Broad Rental Market Area" + definition_period = YEAR diff --git a/policyengine_uk/variables/household/cliff.py b/policyengine_uk/variables/household/cliff.py deleted file mode 100644 index 19060c1e8..000000000 --- a/policyengine_uk/variables/household/cliff.py +++ /dev/null @@ -1,42 +0,0 @@ -from policyengine_uk.model_api import * - - -class cliff_evaluated(Variable): - value_type = bool - entity = Person - label = "cliff evaluated" - unit = GBP - documentation = "Whether this person's cliff has been simulated. If not, then the cliff gap is assumed to be zero." - definition_period = YEAR - - def formula(person, period, parameters): - adult_index_values = person("adult_index", period) - cliff_adult_count = parameters( - period - ).gov.simulation.marginal_tax_rate_adults - return adult_index_values <= cliff_adult_count - - -class cliff_gap(Variable): - value_type = float - entity = Person - label = "cliff gap" - unit = GBP - documentation = "Amount of income lost if this person's employment income increased by delta amount." - definition_period = YEAR - - def formula(person, period, parameters): - delta = parameters(period).gov.simulation.marginal_tax_rate_delta - mtr = person("marginal_tax_rate", period) - return max_(0, (mtr - 1) * delta) - - -class is_on_cliff(Variable): - value_type = bool - entity = Person - label = "is on a tax-benefit cliff" - documentation = "Whether this person would be worse off if their employment income were higher by delta amount." - definition_period = YEAR - - def formula(person, period, parameters): - return person("cliff_gap", period) > 0 diff --git a/policyengine_uk/variables/household/cliff_evaluated.py b/policyengine_uk/variables/household/cliff_evaluated.py new file mode 100644 index 000000000..e89385f5a --- /dev/null +++ b/policyengine_uk/variables/household/cliff_evaluated.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class cliff_evaluated(Variable): + value_type = bool + entity = Person + label = "cliff evaluated" + unit = GBP + documentation = "Whether this person's cliff has been simulated. If not, then the cliff gap is assumed to be zero." + definition_period = YEAR + + def formula(person, period, parameters): + adult_index_values = person("adult_index", period) + cliff_adult_count = parameters( + period + ).gov.simulation.marginal_tax_rate_adults + return adult_index_values <= cliff_adult_count diff --git a/policyengine_uk/variables/household/cliff_gap.py b/policyengine_uk/variables/household/cliff_gap.py new file mode 100644 index 000000000..d5e4de51a --- /dev/null +++ b/policyengine_uk/variables/household/cliff_gap.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class cliff_gap(Variable): + value_type = float + entity = Person + label = "cliff gap" + unit = GBP + documentation = "Amount of income lost if this person's employment income increased by delta amount." + definition_period = YEAR + + def formula(person, period, parameters): + delta = parameters(period).gov.simulation.marginal_tax_rate_delta + mtr = person("marginal_tax_rate", period) + return max_(0, (mtr - 1) * delta) diff --git a/policyengine_uk/variables/household/consumption/additional_residential_property_purchased.py b/policyengine_uk/variables/household/consumption/additional_residential_property_purchased.py new file mode 100644 index 000000000..5e5487757 --- /dev/null +++ b/policyengine_uk/variables/household/consumption/additional_residential_property_purchased.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class additional_residential_property_purchased(Variable): + label = "Residential property bought (additional)" + documentation = "The price paid for the purchase of a residential property in the year, for use as a second home or another non-main-residence purpose. Only include the value of a single purchase." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period): + property_purchased = household("property_purchased", period) + other_residential_property_value = household( + "other_residential_property_value", period + ) + return other_residential_property_value * property_purchased diff --git a/policyengine_uk/variables/household/consumption/benunit_rent.py b/policyengine_uk/variables/household/consumption/benunit_rent.py new file mode 100644 index 000000000..efa0485bc --- /dev/null +++ b/policyengine_uk/variables/household/consumption/benunit_rent.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class benunit_rent(Variable): + value_type = float + entity = BenUnit + label = "Rent" + documentation = "Gross rent that members of this family are liable for" + definition_period = YEAR + unit = GBP + + adds = ["personal_rent"] diff --git a/policyengine_uk/variables/household/consumption/council_tax_less_benefit.py b/policyengine_uk/variables/household/consumption/council_tax_less_benefit.py new file mode 100644 index 000000000..b1a407394 --- /dev/null +++ b/policyengine_uk/variables/household/consumption/council_tax_less_benefit.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class council_tax_less_benefit(Variable): + label = "Council Tax (less CTB)" + documentation = "Council Tax minus the Council Tax Benefit" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + person = household.members + council_tax_benefit = household.sum( + person.benunit("council_tax_benefit", period) + * person("is_benunit_head", period) + ) + return household("council_tax", period) - council_tax_benefit diff --git a/policyengine_uk/variables/household/consumption/diesel_litres.py b/policyengine_uk/variables/household/consumption/diesel_litres.py new file mode 100644 index 000000000..54c150178 --- /dev/null +++ b/policyengine_uk/variables/household/consumption/diesel_litres.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class diesel_litres(Variable): + label = "Diesel volume" + documentation = "Total litres of diesel bought" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + return household("diesel_spending", period) / household( + "diesel_price", period + ) diff --git a/policyengine_uk/variables/household/consumption/diesel_price.py b/policyengine_uk/variables/household/consumption/diesel_price.py new file mode 100644 index 000000000..803a9cd4f --- /dev/null +++ b/policyengine_uk/variables/household/consumption/diesel_price.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class diesel_price(Variable): + label = "Price of diesel per litre" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + return parameters(period).household.consumption.fuel.prices.diesel diff --git a/policyengine_uk/variables/household/consumption/expense.py b/policyengine_uk/variables/household/consumption/expense.py deleted file mode 100644 index 144652366..000000000 --- a/policyengine_uk/variables/household/consumption/expense.py +++ /dev/null @@ -1,71 +0,0 @@ -from policyengine_uk.model_api import * - - -class benunit_rent(Variable): - value_type = float - entity = BenUnit - label = "Rent" - documentation = "Gross rent that members of this family are liable for" - definition_period = YEAR - unit = GBP - - adds = ["personal_rent"] - - -class personal_rent(Variable): - value_type = float - entity = Person - label = "Rent liable" - documentation = "The gross rent this person is liable for" - definition_period = YEAR - unit = GBP - defined_for = "is_household_head" - adds = ["rent"] - - -class family_rent(Variable): - value_type = float - entity = BenUnit - label = "Gross rent for the family" - definition_period = YEAR - unit = GBP - - adds = ["personal_rent"] - - -class weekly_childcare_expenses(Variable): - value_type = float - entity = Person - label = "Average cost of childcare" - definition_period = YEAR - unit = GBP - - def formula(person, period, parameters): - return person("childcare_expenses", period) / WEEKS_IN_YEAR - - -class housing_costs(Variable): - value_type = float - entity = Household - label = "Total housing costs" - definition_period = YEAR - unit = GBP - - adds = ["rent", "mortgage"] - - -class council_tax_less_benefit(Variable): - label = "Council Tax (less CTB)" - documentation = "Council Tax minus the Council Tax Benefit" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - person = household.members - council_tax_benefit = household.sum( - person.benunit("council_tax_benefit", period) - * person("is_benunit_head", period) - ) - return household("council_tax", period) - council_tax_benefit diff --git a/policyengine_uk/variables/household/consumption/family_rent.py b/policyengine_uk/variables/household/consumption/family_rent.py new file mode 100644 index 000000000..508f42d75 --- /dev/null +++ b/policyengine_uk/variables/household/consumption/family_rent.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class family_rent(Variable): + value_type = float + entity = BenUnit + label = "Gross rent for the family" + definition_period = YEAR + unit = GBP + + adds = ["personal_rent"] diff --git a/policyengine_uk/variables/household/consumption/fuel.py b/policyengine_uk/variables/household/consumption/fuel.py deleted file mode 100644 index ffb943c59..000000000 --- a/policyengine_uk/variables/household/consumption/fuel.py +++ /dev/null @@ -1,51 +0,0 @@ -from policyengine_uk.model_api import * - - -class petrol_litres(Variable): - label = "Petrol volume" - documentation = "Total litres of petrol bought" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - return household("petrol_spending", period) / household( - "petrol_price", period - ) - - -class diesel_litres(Variable): - label = "Diesel volume" - documentation = "Total litres of diesel bought" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - return household("diesel_spending", period) / household( - "diesel_price", period - ) - - -class petrol_price(Variable): - label = "Price of petrol per litre" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - return parameters(period).household.consumption.fuel.prices.petrol - - -class diesel_price(Variable): - label = "Price of diesel per litre" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - return parameters(period).household.consumption.fuel.prices.diesel diff --git a/policyengine_uk/variables/household/consumption/full_rate_vat_consumption.py b/policyengine_uk/variables/household/consumption/full_rate_vat_consumption.py new file mode 100644 index 000000000..3c686678f --- /dev/null +++ b/policyengine_uk/variables/household/consumption/full_rate_vat_consumption.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class full_rate_vat_consumption(Variable): + label = "consumption of VAT full-rated goods and services" + entity = Household + definition_period = YEAR + value_type = float + unit = "currency-GBP" + + def formula(household, period, parameters): + # If unknown, assume half of consumption is VAT full-rated. + return household("consumption", period) * household( + "full_rate_vat_expenditure_rate", period + ) diff --git a/policyengine_uk/variables/household/consumption/full_rate_vat_expenditure_rate.py b/policyengine_uk/variables/household/consumption/full_rate_vat_expenditure_rate.py new file mode 100644 index 000000000..db100a4bf --- /dev/null +++ b/policyengine_uk/variables/household/consumption/full_rate_vat_expenditure_rate.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class full_rate_vat_expenditure_rate(Variable): + label = "VAT full-rated expenditure rate" + entity = Household + definition_period = YEAR + value_type = float + unit = "/1" + default_value = 0.5 diff --git a/policyengine_uk/variables/household/consumption/housing_costs.py b/policyengine_uk/variables/household/consumption/housing_costs.py new file mode 100644 index 000000000..8cbc9391b --- /dev/null +++ b/policyengine_uk/variables/household/consumption/housing_costs.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class housing_costs(Variable): + value_type = float + entity = Household + label = "Total housing costs" + definition_period = YEAR + unit = GBP + + adds = ["rent", "mortgage"] diff --git a/policyengine_uk/variables/household/consumption/main_residential_property_purchased.py b/policyengine_uk/variables/household/consumption/main_residential_property_purchased.py new file mode 100644 index 000000000..6bfda02e9 --- /dev/null +++ b/policyengine_uk/variables/household/consumption/main_residential_property_purchased.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class main_residential_property_purchased(Variable): + label = "Residential property bought (main)" + documentation = "The price paid for the purchase of a residential property in the year, for use as a main residence. Only include the value of a single purchase." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period): + property_purchased = household("property_purchased", period) + main_residence_value = household("main_residence_value", period) + return main_residence_value * property_purchased diff --git a/policyengine_uk/variables/household/consumption/main_residential_property_purchased_is_first_home.py b/policyengine_uk/variables/household/consumption/main_residential_property_purchased_is_first_home.py new file mode 100644 index 000000000..b49c5b4fe --- /dev/null +++ b/policyengine_uk/variables/household/consumption/main_residential_property_purchased_is_first_home.py @@ -0,0 +1,24 @@ +from policyengine_uk.model_api import * + + +class main_residential_property_purchased_is_first_home(Variable): + label = "Residential property bought is first home" + documentation = "Whether the residential property bought this year as a main residence was as a first-time buyer." + entity = Household + definition_period = YEAR + value_type = bool + unit = GBP + + def formula(household, period, parameters): + residential_sd = parameters( + period + ).gov.hmrc.stamp_duty.statistics.residential.household + age = household.sum( + household.members("is_household_head", period) + * household.members("age", period) + ) + percentage_claiming_ftbr = ( + residential_sd.first_time_buyers_relief.calc(age) + / residential_sd.transactions_by_age.calc(age) + ) + return random(household) < percentage_claiming_ftbr diff --git a/policyengine_uk/variables/household/consumption/mortgage.py b/policyengine_uk/variables/household/consumption/mortgage.py new file mode 100644 index 000000000..cbecd751a --- /dev/null +++ b/policyengine_uk/variables/household/consumption/mortgage.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + + +class mortgage(Variable): + value_type = float + entity = Household + label = "total mortgage payments" + documentation: str = "Total amount spent on mortgage payments" + definition_period = YEAR + unit = GBP + adds = [ + "mortgage_interest_repayment", + "mortgage_capital_repayment", + ] diff --git a/policyengine_uk/variables/household/consumption/non_residential_property_purchased.py b/policyengine_uk/variables/household/consumption/non_residential_property_purchased.py new file mode 100644 index 000000000..39cc2edfb --- /dev/null +++ b/policyengine_uk/variables/household/consumption/non_residential_property_purchased.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class non_residential_property_purchased(Variable): + label = "Non-residential property bought" + documentation = "The price paid for the purchase of a non-residential property in the year. Only include the value of a single purchase." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + defined_for = "property_purchased" + adds = ["non_residential_property_value"] diff --git a/policyengine_uk/variables/household/consumption/personal_rent.py b/policyengine_uk/variables/household/consumption/personal_rent.py new file mode 100644 index 000000000..8dbd3dd76 --- /dev/null +++ b/policyengine_uk/variables/household/consumption/personal_rent.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class personal_rent(Variable): + value_type = float + entity = Person + label = "Rent liable" + documentation = "The gross rent this person is liable for" + definition_period = YEAR + unit = GBP + defined_for = "is_household_head" + adds = ["rent"] diff --git a/policyengine_uk/variables/household/consumption/petrol_litres.py b/policyengine_uk/variables/household/consumption/petrol_litres.py new file mode 100644 index 000000000..c24caf325 --- /dev/null +++ b/policyengine_uk/variables/household/consumption/petrol_litres.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class petrol_litres(Variable): + label = "Petrol volume" + documentation = "Total litres of petrol bought" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + return household("petrol_spending", period) / household( + "petrol_price", period + ) diff --git a/policyengine_uk/variables/household/consumption/petrol_price.py b/policyengine_uk/variables/household/consumption/petrol_price.py new file mode 100644 index 000000000..3004d723c --- /dev/null +++ b/policyengine_uk/variables/household/consumption/petrol_price.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class petrol_price(Variable): + label = "Price of petrol per litre" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + return parameters(period).household.consumption.fuel.prices.petrol diff --git a/policyengine_uk/variables/household/consumption/property.py b/policyengine_uk/variables/household/consumption/property.py deleted file mode 100644 index 44bc9b34d..000000000 --- a/policyengine_uk/variables/household/consumption/property.py +++ /dev/null @@ -1,90 +0,0 @@ -from policyengine_uk.model_api import * - - -class property_sale_rate(Variable): - label = "Residential property sale rate" - documentation = "The percentage of residential property value owned by households sold in the year" - entity = State - definition_period = YEAR - value_type = float - unit = "/1" - - def formula(household, period, parameters): - return parameters(period).gov.hmrc.stamp_duty.property_sale_rate - - -class main_residential_property_purchased(Variable): - label = "Residential property bought (main)" - documentation = "The price paid for the purchase of a residential property in the year, for use as a main residence. Only include the value of a single purchase." - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period): - property_purchased = household("property_purchased", period) - main_residence_value = household("main_residence_value", period) - return main_residence_value * property_purchased - - -class additional_residential_property_purchased(Variable): - label = "Residential property bought (additional)" - documentation = "The price paid for the purchase of a residential property in the year, for use as a second home or another non-main-residence purpose. Only include the value of a single purchase." - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period): - property_purchased = household("property_purchased", period) - other_residential_property_value = household( - "other_residential_property_value", period - ) - return other_residential_property_value * property_purchased - - -class main_residential_property_purchased_is_first_home(Variable): - label = "Residential property bought is first home" - documentation = "Whether the residential property bought this year as a main residence was as a first-time buyer." - entity = Household - definition_period = YEAR - value_type = bool - unit = GBP - - def formula(household, period, parameters): - residential_sd = parameters( - period - ).gov.hmrc.stamp_duty.statistics.residential.household - age = household.sum( - household.members("is_household_head", period) - * household.members("age", period) - ) - percentage_claiming_ftbr = ( - residential_sd.first_time_buyers_relief.calc(age) - / residential_sd.transactions_by_age.calc(age) - ) - return random(household) < percentage_claiming_ftbr - - -class non_residential_property_purchased(Variable): - label = "Non-residential property bought" - documentation = "The price paid for the purchase of a non-residential property in the year. Only include the value of a single purchase." - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - defined_for = "property_purchased" - adds = ["non_residential_property_value"] - - -class mortgage(Variable): - value_type = float - entity = Household - label = "total mortgage payments" - documentation: str = "Total amount spent on mortgage payments" - definition_period = YEAR - unit = GBP - adds = [ - "mortgage_interest_repayment", - "mortgage_capital_repayment", - ] diff --git a/policyengine_uk/variables/household/consumption/property_sale_rate.py b/policyengine_uk/variables/household/consumption/property_sale_rate.py new file mode 100644 index 000000000..c54c068e0 --- /dev/null +++ b/policyengine_uk/variables/household/consumption/property_sale_rate.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class property_sale_rate(Variable): + label = "Residential property sale rate" + documentation = "The percentage of residential property value owned by households sold in the year" + entity = State + definition_period = YEAR + value_type = float + unit = "/1" + + def formula(household, period, parameters): + return parameters(period).gov.hmrc.stamp_duty.property_sale_rate diff --git a/policyengine_uk/variables/household/consumption/reduced_rate_vat_consumption.py b/policyengine_uk/variables/household/consumption/reduced_rate_vat_consumption.py new file mode 100644 index 000000000..a4e7b6b95 --- /dev/null +++ b/policyengine_uk/variables/household/consumption/reduced_rate_vat_consumption.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + + +class reduced_rate_vat_consumption(Variable): + label = "consumption of VAT reduced-rated goods and services" + entity = Household + definition_period = YEAR + value_type = float + unit = "currency-GBP" + + def formula(household, period, parameters): + vat = parameters(period).gov.hmrc.vat + consumption = household("consumption", period) + return consumption * vat.reduced_rate_share diff --git a/policyengine_uk/variables/household/consumption/vat.py b/policyengine_uk/variables/household/consumption/vat.py deleted file mode 100644 index dec5d3dc8..000000000 --- a/policyengine_uk/variables/household/consumption/vat.py +++ /dev/null @@ -1,37 +0,0 @@ -from policyengine_uk.model_api import * - - -class full_rate_vat_consumption(Variable): - label = "consumption of VAT full-rated goods and services" - entity = Household - definition_period = YEAR - value_type = float - unit = "currency-GBP" - - def formula(household, period, parameters): - # If unknown, assume half of consumption is VAT full-rated. - return household("consumption", period) * household( - "full_rate_vat_expenditure_rate", period - ) - - -class full_rate_vat_expenditure_rate(Variable): - label = "VAT full-rated expenditure rate" - entity = Household - definition_period = YEAR - value_type = float - unit = "/1" - default_value = 0.5 - - -class reduced_rate_vat_consumption(Variable): - label = "consumption of VAT reduced-rated goods and services" - entity = Household - definition_period = YEAR - value_type = float - unit = "currency-GBP" - - def formula(household, period, parameters): - vat = parameters(period).gov.hmrc.vat - consumption = household("consumption", period) - return consumption * vat.reduced_rate_share diff --git a/policyengine_uk/variables/household/consumption/weekly_childcare_expenses.py b/policyengine_uk/variables/household/consumption/weekly_childcare_expenses.py new file mode 100644 index 000000000..a77acbd17 --- /dev/null +++ b/policyengine_uk/variables/household/consumption/weekly_childcare_expenses.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class weekly_childcare_expenses(Variable): + value_type = float + entity = Person + label = "Average cost of childcare" + definition_period = YEAR + unit = GBP + + def formula(person, period, parameters): + return person("childcare_expenses", period) / WEEKS_IN_YEAR diff --git a/policyengine_uk/variables/household/demographic/accommodation_type.py b/policyengine_uk/variables/household/demographic/accommodation_type.py new file mode 100644 index 000000000..9beb07cd1 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/accommodation_type.py @@ -0,0 +1,22 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.geography import Region + + +class AccommodationType(Enum): + HOUSE_DETACHED = "Detached house" + HOUSE_SEMI_DETACHED = "Semi-detached house" + HOUSE_TERRACED = "Terraced house" + FLAT = "Purpose-built flat or maisonette" + CONVERTED_HOUSE = "Converted house or building" + MOBILE = "Caravan/Mobile home or houseboat" + OTHER = "Other" + UNKNOWN = "Unknown" + + +class accommodation_type(Variable): + value_type = Enum + possible_values = AccommodationType + default_value = AccommodationType.UNKNOWN + entity = Household + label = "Type of accommodation" + definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/adult_index.py b/policyengine_uk/variables/household/demographic/adult_index.py new file mode 100644 index 000000000..c2ae19384 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/adult_index.py @@ -0,0 +1,19 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class adult_index(Variable): + value_type = int + entity = Person + label = "Index of adult in household" + definition_period = YEAR + + def formula(person, period, parameters): + return ( + person.get_rank( + person.household, + -person("age", period), + condition=person("is_adult", period), + ) + + 1 + ) diff --git a/policyengine_uk/variables/household/demographic/age_18_64.py b/policyengine_uk/variables/household/demographic/age_18_64.py new file mode 100644 index 000000000..26942dc67 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/age_18_64.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class age_18_64(Variable): + value_type = bool + entity = Person + label = "Whether the person is age 18 to 64" + definition_period = YEAR + + def formula(person, period, parameters): + age = person("age", period) + return (age >= 18) & (age <= 64) diff --git a/policyengine_uk/variables/household/demographic/age_over_64.py b/policyengine_uk/variables/household/demographic/age_over_64.py new file mode 100644 index 000000000..fa341df5f --- /dev/null +++ b/policyengine_uk/variables/household/demographic/age_over_64.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class age_over_64(Variable): + value_type = bool + entity = Person + label = "Whether the person is over age 64" + definition_period = YEAR + + def formula(person, period, parameters): + return person("age", period) > 64 diff --git a/policyengine_uk/variables/household/demographic/age_under_18.py b/policyengine_uk/variables/household/demographic/age_under_18.py new file mode 100644 index 000000000..8d8ee1acd --- /dev/null +++ b/policyengine_uk/variables/household/demographic/age_under_18.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class age_under_18(Variable): + value_type = bool + entity = Person + label = "Whether the person is under age 18" + definition_period = YEAR + + def formula(person, period, parameters): + return person("age", period) < 18 diff --git a/policyengine_uk/variables/household/demographic/benunit.py b/policyengine_uk/variables/household/demographic/benunit.py deleted file mode 100644 index 7464f74af..000000000 --- a/policyengine_uk/variables/household/demographic/benunit.py +++ /dev/null @@ -1,219 +0,0 @@ -from policyengine_uk.variables.household.demographic.household import ( - TenureType, -) -from policyengine_uk.variables.household.demographic.geography import Region -from policyengine_uk.model_api import * - - -class benunit_id(Variable): - value_type = int - entity = BenUnit - label = "ID for the family" - definition_period = YEAR - - -class families(Variable): - value_type = float - entity = BenUnit - label = "Variable holding families" - definition_period = YEAR - default_value = 1 - - -class benunit_weight(Variable): - value_type = float - entity = BenUnit - label = "Weight factor for the benefit unit" - definition_period = YEAR - - def formula(benunit, period, parameters): - return benunit.max(benunit.members("person_weight", period)) - - -class is_married(Variable): - value_type = bool - entity = BenUnit - label = "Married" - documentation = "Whether the benefit unit adults are married to each other or in a civil partnership" - definition_period = YEAR - - def formula(benunit, period, parameters): - return add(benunit, period, ["is_adult"]) == 2 - - -class FamilyType(Enum): - SINGLE = "Single, with no children" - COUPLE_NO_CHILDREN = "Couple, with no children" - LONE_PARENT = "Lone parent, with children" - COUPLE_WITH_CHILDREN = "Couple, with children" - - -class RelationType(Enum): - SINGLE = "Single" - COUPLE = "Couple" - - -class family_type(Variable): - value_type = Enum - entity = BenUnit - default_value = FamilyType.SINGLE - possible_values = FamilyType - label = "Family composition" - definition_period = YEAR - - def formula(benunit, period, parameters): - two_adults = add(benunit, period, ["is_adult"]) == 2 - has_children = benunit.any(benunit.members("is_child", period)) - single = ~two_adults & ~has_children - couple_no_children = two_adults & ~has_children - lone_parent = ~two_adults & has_children - couple_with_children = two_adults & has_children - return select( - [single, couple_no_children, lone_parent, couple_with_children], - FamilyType, - ) - - -class relation_type(Variable): - value_type = Enum - entity = BenUnit - default_value = RelationType.SINGLE - possible_values = RelationType - label = "Whether single or a couple" - definition_period = YEAR - - def formula(benunit, period, parameters): - return where( - benunit.sum(benunit.members("is_adult", period)) == 1, - RelationType.SINGLE, - RelationType.COUPLE, - ) - - -class eldest_adult_age(Variable): - value_type = float - entity = BenUnit - label = "Eldest adult age" - definition_period = YEAR - - def formula(benunit, period, parameters): - return benunit.max( - where( - benunit.members("is_adult", period), - benunit.members("age", period), - -np.inf, - ) - ) - - -class youngest_adult_age(Variable): - value_type = float - entity = BenUnit - label = "Eldest adult age" - definition_period = YEAR - - def formula(benunit, period, parameters): - return benunit.min( - where( - benunit.members("is_adult", period), - benunit.members("age", period), - np.inf, - ) - ) - - -class eldest_child_age(Variable): - value_type = float - entity = BenUnit - label = "Eldest adult age" - definition_period = YEAR - - def formula(benunit, period, parameters): - return benunit.max( - where( - benunit.members("is_child", period), - benunit.members("age", period), - -np.inf, - ) - ) - - -class youngest_child_age(Variable): - value_type = float - entity = BenUnit - label = "Eldest adult age" - definition_period = YEAR - - def formula(benunit, period, parameters): - return benunit.min( - where( - benunit.members("is_child", period), - benunit.members("age", period), - np.inf, - ) - ) - - -class num_children(Variable): - value_type = int - entity = BenUnit - label = "The number of children in the family" - definition_period = YEAR - - def formula(benunit, period, parameters): - return benunit.sum(benunit.members("is_child", period)) - - -class num_adults(Variable): - value_type = int - entity = BenUnit - label = "The number of adults in the family" - definition_period = YEAR - - def formula(benunit, period, parameters): - return benunit.sum(benunit.members("is_adult", period)) - - -class benunit_tenure_type(Variable): - value_type = Enum - possible_values = TenureType - default_value = TenureType.RENT_PRIVATELY - entity = BenUnit - label = "Tenure type of the family's household" - definition_period = YEAR - - def formula(benunit, period, parameters): - return benunit.value_from_first_person( - benunit.members.household("tenure_type", period) - ) - - -class benunit_is_renting(Variable): - value_type = bool - entity = BenUnit - label = "Whether this family is renting" - definition_period = YEAR - - def formula(benunit, period, parameters): - tenure = benunit("benunit_tenure_type", period) - tenures = tenure.possible_values - RENT_TENURES = [ - tenures.RENT_PRIVATELY, - tenures.RENT_FROM_COUNCIL, - tenures.RENT_FROM_HA, - ] - return sum([tenure == tenure_type for tenure_type in RENT_TENURES]) > 0 - - -class benunit_region(Variable): - label = "benefit unit region" - entity = BenUnit - definition_period = YEAR - value_type = Enum - possible_values = Region - default_value = Region.LONDON - - def formula(benunit, period, parameters): - return benunit.value_from_first_person( - benunit.members.household("region", period) - ) diff --git a/policyengine_uk/variables/household/demographic/benunit_has_carer.py b/policyengine_uk/variables/household/demographic/benunit_has_carer.py new file mode 100644 index 000000000..3a4d55268 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/benunit_has_carer.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class benunit_has_carer(Variable): + value_type = bool + entity = BenUnit + label = "Benefit unit has a carer" + definition_period = YEAR + + def formula(benunit, period, parameters): + return benunit("num_carers", period) > 0 diff --git a/policyengine_uk/variables/household/demographic/benunit_id.py b/policyengine_uk/variables/household/demographic/benunit_id.py new file mode 100644 index 000000000..c837cbabe --- /dev/null +++ b/policyengine_uk/variables/household/demographic/benunit_id.py @@ -0,0 +1,8 @@ +from policyengine_uk.model_api import * + + +class benunit_id(Variable): + value_type = int + entity = BenUnit + label = "ID for the family" + definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/benunit_is_renting.py b/policyengine_uk/variables/household/demographic/benunit_is_renting.py new file mode 100644 index 000000000..5d29ac284 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/benunit_is_renting.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class benunit_is_renting(Variable): + value_type = bool + entity = BenUnit + label = "Whether this family is renting" + definition_period = YEAR + + def formula(benunit, period, parameters): + tenure = benunit("benunit_tenure_type", period) + tenures = tenure.possible_values + RENT_TENURES = [ + tenures.RENT_PRIVATELY, + tenures.RENT_FROM_COUNCIL, + tenures.RENT_FROM_HA, + ] + return sum([tenure == tenure_type for tenure_type in RENT_TENURES]) > 0 diff --git a/policyengine_uk/variables/household/demographic/benunit_region.py b/policyengine_uk/variables/household/demographic/benunit_region.py new file mode 100644 index 000000000..f37ddafc3 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/benunit_region.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.geography import Region + + +class benunit_region(Variable): + label = "benefit unit region" + entity = BenUnit + definition_period = YEAR + value_type = Enum + possible_values = Region + default_value = Region.LONDON + + def formula(benunit, period, parameters): + return benunit.value_from_first_person( + benunit.members.household("region", period) + ) diff --git a/policyengine_uk/variables/household/demographic/benunit_tenure_type.py b/policyengine_uk/variables/household/demographic/benunit_tenure_type.py new file mode 100644 index 000000000..8496db50c --- /dev/null +++ b/policyengine_uk/variables/household/demographic/benunit_tenure_type.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.tenure_type import ( + TenureType, +) + + +class benunit_tenure_type(Variable): + value_type = Enum + possible_values = TenureType + default_value = TenureType.RENT_PRIVATELY + entity = BenUnit + label = "Tenure type of the family's household" + definition_period = YEAR + + def formula(benunit, period, parameters): + return benunit.value_from_first_person( + benunit.members.household("tenure_type", period) + ) diff --git a/policyengine_uk/variables/household/demographic/benunit_weight.py b/policyengine_uk/variables/household/demographic/benunit_weight.py new file mode 100644 index 000000000..57a10ee7f --- /dev/null +++ b/policyengine_uk/variables/household/demographic/benunit_weight.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class benunit_weight(Variable): + value_type = float + entity = BenUnit + label = "Weight factor for the benefit unit" + definition_period = YEAR + + def formula(benunit, period, parameters): + return benunit.max(benunit.members("person_weight", period)) diff --git a/policyengine_uk/variables/household/demographic/birth_year.py b/policyengine_uk/variables/household/demographic/birth_year.py new file mode 100644 index 000000000..f555e29c1 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/birth_year.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class birth_year(Variable): + value_type = int + entity = Person + label = "The birth year of the person" + definition_period = YEAR + + def formula(person, period, parameters): + return period.start.year - person("age", period) diff --git a/policyengine_uk/variables/household/demographic/care.py b/policyengine_uk/variables/household/demographic/care.py deleted file mode 100644 index 54f89fb51..000000000 --- a/policyengine_uk/variables/household/demographic/care.py +++ /dev/null @@ -1,51 +0,0 @@ -from policyengine_uk.model_api import * - - -class is_carer_for_benefits(Variable): - value_type = bool - entity = Person - label = "Whether this person is a carer for benefits purposes" - definition_period = YEAR - - def formula(person, period, parameters): - return person("receives_carers_allowance", period) - - -class benunit_has_carer(Variable): - value_type = bool - entity = BenUnit - label = "Benefit unit has a carer" - definition_period = YEAR - - def formula(benunit, period, parameters): - return benunit("num_carers", period) > 0 - - -class num_carers(Variable): - value_type = int - entity = BenUnit - label = "Number of carers in the family" - definition_period = YEAR - - def formula(benunit, period, parameters): - return add(benunit, period, ["is_carer_for_benefits"]) - - -class carer_premium(Variable): - value_type = float - entity = BenUnit - label = "Carer premium" - definition_period = YEAR - reference = ( - "The Social Security Amendment (Carer Premium) Regulations 2002" - ) - unit = GBP - - def formula(benunit, period, parameters): - carers = benunit("num_carers", period.this_year) - CP = parameters(period).gov.dwp.carer_premium - weekly_premium = select( - [carers == 0, carers == 1, carers == 2], - [0, CP.single, CP.couple], - ) - return weekly_premium * WEEKS_IN_YEAR diff --git a/policyengine_uk/variables/household/demographic/carer_premium.py b/policyengine_uk/variables/household/demographic/carer_premium.py new file mode 100644 index 000000000..81191362b --- /dev/null +++ b/policyengine_uk/variables/household/demographic/carer_premium.py @@ -0,0 +1,21 @@ +from policyengine_uk.model_api import * + + +class carer_premium(Variable): + value_type = float + entity = BenUnit + label = "Carer premium" + definition_period = YEAR + reference = ( + "The Social Security Amendment (Carer Premium) Regulations 2002" + ) + unit = GBP + + def formula(benunit, period, parameters): + carers = benunit("num_carers", period.this_year) + CP = parameters(period).gov.dwp.carer_premium + weekly_premium = select( + [carers == 0, carers == 1, carers == 2], + [0, CP.single, CP.couple], + ) + return weekly_premium * WEEKS_IN_YEAR diff --git a/policyengine_uk/variables/household/demographic/child_index.py b/policyengine_uk/variables/household/demographic/child_index.py new file mode 100644 index 000000000..a3b038330 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/child_index.py @@ -0,0 +1,24 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class child_index(Variable): + value_type = int + entity = Person + label = "Child reference number" + definition_period = YEAR + + def formula(person, period, parameters): + # The child index, by age, descending (e.g. "first child" = 1) + child_ranking = ( + person.get_rank( + person.benunit, + -person("age", period), + ) + + 1 + ) + # Fill in adult values + values = where(person("is_child", period), child_ranking, 100) + # Base to 0 + values = values - person.benunit.min(values) + 1 + return where(person("is_child", period), values, -1) diff --git a/policyengine_uk/variables/household/demographic/country.py b/policyengine_uk/variables/household/demographic/country.py index 75bdda92e..6fb28ddb2 100644 --- a/policyengine_uk/variables/household/demographic/country.py +++ b/policyengine_uk/variables/household/demographic/country.py @@ -1,32 +1,37 @@ from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.geography import Region -class state_id(Variable): - label = "State ID" - documentation = "Identity of the state" - entity = State - definition_period = YEAR - value_type = int - - -class person_state_id(Variable): - label = "State ID" - documentation = "Identity of the state" - entity = Person - definition_period = YEAR - value_type = int +class Country(Enum): + ENGLAND = "England" + NORTHERN_IRELAND = "Northern Ireland" + SCOTLAND = "Scotland" + WALES = "Wales" + UNKNOWN = "Unknown" -class person_state_role(Variable): - label = "State role" - entity = Person +class country(Variable): + value_type = Enum + possible_values = Country + default_value = Country.ENGLAND + entity = Household + label = "Country of the UK" definition_period = YEAR - value_type = str - -class state_weight(Variable): - label = "State weight" - documentation = "Weight value" - entity = State - definition_period = YEAR - value_type = float + def formula(household, period, parameters): + region = household("region", period) + return select( + [ + region == Region.UNKNOWN, + region == Region.SCOTLAND, + region == Region.WALES, + region == Region.NORTHERN_IRELAND, + ], + [ + Country.UNKNOWN, + Country.SCOTLAND, + Country.WALES, + Country.NORTHERN_IRELAND, + ], + default=Country.ENGLAND, + ) diff --git a/policyengine_uk/variables/household/demographic/current_education.py b/policyengine_uk/variables/household/demographic/current_education.py new file mode 100644 index 000000000..e11d2846b --- /dev/null +++ b/policyengine_uk/variables/household/demographic/current_education.py @@ -0,0 +1,44 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.highest_education import ( + EducationType, +) +import pandas as pd + + +class current_education(Variable): + value_type = Enum + possible_values = EducationType + default_value = EducationType.NOT_IN_EDUCATION + entity = Person + label = "Current education" + definition_period = YEAR + + def formula(person, period, parameters): + if ( + person.get_holder("current_education").get_array(period.last_year) + is not None + ): + return person("current_education", period.last_year) + age = person("age", period) + return np.select( + [ + age < 4, + (age >= 4) & (age < 7), + (age >= 7) & (age < 11), + (age >= 11) & (age < 14), + (age >= 14) & (age < 16), + (age >= 16) & (age < 18), + (age >= 18) & (age < 20), + age >= 20, + ], + [ + EducationType.PRE_PRIMARY, + EducationType.NOT_COMPLETED_PRIMARY, + EducationType.PRIMARY, + EducationType.LOWER_SECONDARY, + EducationType.UPPER_SECONDARY, + EducationType.POST_SECONDARY, + EducationType.TERTIARY, + EducationType.NOT_IN_EDUCATION, + ], + ) diff --git a/policyengine_uk/variables/household/demographic/disability.py b/policyengine_uk/variables/household/demographic/disability.py deleted file mode 100644 index c12ac7441..000000000 --- a/policyengine_uk/variables/household/demographic/disability.py +++ /dev/null @@ -1,200 +0,0 @@ -from policyengine_uk.model_api import * - - -class is_disabled_for_benefits(Variable): - value_type = bool - entity = Person - label = "Has a disability" - documentation = "Whether this person is disabled for benefits purposes" - definition_period = YEAR - reference = "Child Tax Credit Regulations 2002 s. 8" - - def formula(person, period, parameters): - QUALIFYING_BENEFITS = [ - "dla", - "pip", - ] - return add(person, period, QUALIFYING_BENEFITS) > 0 - - -class is_enhanced_disabled_for_benefits(Variable): - value_type = bool - entity = Person - label = "Whether meets the middle disability benefit entitlement" - definition_period = YEAR - - def formula(person, period, parameters): - DLA_requirement = ( - parameters(period).gov.dwp.dla.self_care.higher * WEEKS_IN_YEAR - ) - return person("dla_sc", period) >= DLA_requirement - - -class is_severely_disabled_for_benefits(Variable): - value_type = bool - entity = Person - label = "Has a severe disability" - documentation = ( - "Whether this person is severely disabled for benefits purposes" - ) - definition_period = YEAR - reference = "Child Tax Credit Regulations 2002 s. 8" - - def formula(person, period, parameters): - benefit = parameters(period).gov.dwp - THRESHOLD_SAFETY_GAP = 10 * WEEKS_IN_YEAR - paragraph_3 = ( - person("dla_sc", period) - >= benefit.dla.self_care.higher * WEEKS_IN_YEAR - - THRESHOLD_SAFETY_GAP - ) - paragraph_4 = ( - person("pip_dl", period) - >= benefit.pip.daily_living.enhanced * WEEKS_IN_YEAR - - THRESHOLD_SAFETY_GAP - ) - paragraph_5 = person("afcs", period) > 0 - return sum([paragraph_3, paragraph_4, paragraph_5]) > 0 - - -class num_disabled_children(Variable): - value_type = int - entity = BenUnit - label = "Number of disabled children" - definition_period = YEAR - - def formula(benunit, period, parameters): - child = benunit.members("is_child_or_QYP", period) - disabled = benunit.members("is_disabled_for_benefits", period) - return benunit.sum(child & disabled) - - -class num_enhanced_disabled_children(Variable): - value_type = int - entity = BenUnit - label = "Number of enhanced disabled children" - definition_period = YEAR - - def formula(benunit, period, parameters): - child = benunit.members("is_child_or_QYP", period) - enhanced_disabled = benunit.members( - "is_enhanced_disabled_for_benefits", period - ) - return benunit.sum(child & enhanced_disabled) - - -class num_severely_disabled_children(Variable): - value_type = int - entity = BenUnit - label = "Number of severely disabled children" - definition_period = YEAR - - def formula(benunit, period, parameters): - child = benunit.members("is_child_or_QYP", period) - severely_disabled = benunit.members( - "is_severely_disabled_for_benefits", period - ) - return benunit.sum(child & severely_disabled) - - -class num_disabled_adults(Variable): - value_type = int - entity = BenUnit - label = "Number of disabled adults" - definition_period = YEAR - - def formula(benunit, period, parameters): - adult = benunit.members("is_adult", period) - disabled = benunit.members("is_disabled_for_benefits", period) - return benunit.sum(adult & disabled) - - -class num_enhanced_disabled_adults(Variable): - value_type = int - entity = BenUnit - label = "Number of enhanced disabled adults" - definition_period = YEAR - - def formula(benunit, period, parameters): - adult = benunit.members("is_adult", period) - enhanced_disabled = benunit.members( - "is_enhanced_disabled_for_benefits", period - ) - return benunit.sum(adult & enhanced_disabled) - - -class num_severely_disabled_adults(Variable): - value_type = int - entity = BenUnit - label = "Number of severely disabled adults" - definition_period = YEAR - - def formula(benunit, period, parameters): - adult = benunit.members("is_adult", period) - severely_disabled = benunit.members( - "is_severely_disabled_for_benefits", period - ) - return benunit.sum(adult & severely_disabled) - - -class disability_premium(Variable): - value_type = float - entity = BenUnit - label = "Disability premium" - definition_period = YEAR - reference = "The Social Security Amendment (Enhanced Disability Premium) Regulations 2000" - unit = GBP - - def formula(benunit, period, parameters): - dis = parameters(period).gov.dwp.disability_premia - single = benunit("is_single", period.this_year) - couple = benunit("is_couple", period.this_year) - single_premium = single * dis.disability_single - couple_premium = couple * dis.disability_couple - has_disabled_adults = ( - benunit("num_disabled_adults", period.this_year) > 0 - ) - weekly_amount = single_premium + couple_premium - return weekly_amount * WEEKS_IN_YEAR * has_disabled_adults - - -class severe_disability_premium(Variable): - value_type = float - entity = BenUnit - label = "Severe disability premium" - definition_period = YEAR - reference = "The Social Security Amendment (Enhanced Disability Premium) Regulations 2000" - unit = GBP - - def formula(benunit, period, parameters): - dis = parameters(period).gov.dwp.disability_premia - single = benunit("is_single", period.this_year) - couple = benunit("is_couple", period.this_year) - single_premium = single * dis.severe_single - couple_premium = couple * dis.severe_couple - has_severely_disabled_adults = ( - benunit("num_severely_disabled_adults", period.this_year) > 0 - ) - weekly_amount = single_premium + couple_premium - return weekly_amount * WEEKS_IN_YEAR * has_severely_disabled_adults - - -class enhanced_disability_premium(Variable): - value_type = float - entity = BenUnit - label = "Enhanced disability premium" - definition_period = YEAR - reference = "The Social Security Amendment (Enhanced Disability Premium) Regulations 2000" - unit = GBP - - def formula(benunit, period, parameters): - dis = parameters(period).gov.dwp.disability_premia - single = benunit("is_single", period.this_year) - couple = benunit("is_couple", period.this_year) - single_premium = single * dis.enhanced_single - couple_premium = couple * dis.enhanced_couple - has_enhanced_disabled_adults = ( - benunit("num_enhanced_disabled_adults", period.this_year) > 0 - ) - weekly_amount = single_premium + couple_premium - return weekly_amount * WEEKS_IN_YEAR * has_enhanced_disabled_adults diff --git a/policyengine_uk/variables/household/demographic/disability_premium.py b/policyengine_uk/variables/household/demographic/disability_premium.py new file mode 100644 index 000000000..c819bf1ea --- /dev/null +++ b/policyengine_uk/variables/household/demographic/disability_premium.py @@ -0,0 +1,22 @@ +from policyengine_uk.model_api import * + + +class disability_premium(Variable): + value_type = float + entity = BenUnit + label = "Disability premium" + definition_period = YEAR + reference = "The Social Security Amendment (Enhanced Disability Premium) Regulations 2000" + unit = GBP + + def formula(benunit, period, parameters): + dis = parameters(period).gov.dwp.disability_premia + single = benunit("is_single", period.this_year) + couple = benunit("is_couple", period.this_year) + single_premium = single * dis.disability_single + couple_premium = couple * dis.disability_couple + has_disabled_adults = ( + benunit("num_disabled_adults", period.this_year) > 0 + ) + weekly_amount = single_premium + couple_premium + return weekly_amount * WEEKS_IN_YEAR * has_disabled_adults diff --git a/policyengine_uk/variables/household/demographic/eldest_adult_age.py b/policyengine_uk/variables/household/demographic/eldest_adult_age.py new file mode 100644 index 000000000..de775e51e --- /dev/null +++ b/policyengine_uk/variables/household/demographic/eldest_adult_age.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class eldest_adult_age(Variable): + value_type = float + entity = BenUnit + label = "Eldest adult age" + definition_period = YEAR + + def formula(benunit, period, parameters): + return benunit.max( + where( + benunit.members("is_adult", period), + benunit.members("age", period), + -np.inf, + ) + ) diff --git a/policyengine_uk/variables/household/demographic/eldest_child_age.py b/policyengine_uk/variables/household/demographic/eldest_child_age.py new file mode 100644 index 000000000..d471c19e6 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/eldest_child_age.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class eldest_child_age(Variable): + value_type = float + entity = BenUnit + label = "Eldest adult age" + definition_period = YEAR + + def formula(benunit, period, parameters): + return benunit.max( + where( + benunit.members("is_child", period), + benunit.members("age", period), + -np.inf, + ) + ) diff --git a/policyengine_uk/variables/household/demographic/enhanced_disability_premium.py b/policyengine_uk/variables/household/demographic/enhanced_disability_premium.py new file mode 100644 index 000000000..184a75188 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/enhanced_disability_premium.py @@ -0,0 +1,22 @@ +from policyengine_uk.model_api import * + + +class enhanced_disability_premium(Variable): + value_type = float + entity = BenUnit + label = "Enhanced disability premium" + definition_period = YEAR + reference = "The Social Security Amendment (Enhanced Disability Premium) Regulations 2000" + unit = GBP + + def formula(benunit, period, parameters): + dis = parameters(period).gov.dwp.disability_premia + single = benunit("is_single", period.this_year) + couple = benunit("is_couple", period.this_year) + single_premium = single * dis.enhanced_single + couple_premium = couple * dis.enhanced_couple + has_enhanced_disabled_adults = ( + benunit("num_enhanced_disabled_adults", period.this_year) > 0 + ) + weekly_amount = single_premium + couple_premium + return weekly_amount * WEEKS_IN_YEAR * has_enhanced_disabled_adults diff --git a/policyengine_uk/variables/household/demographic/families.py b/policyengine_uk/variables/household/demographic/families.py new file mode 100644 index 000000000..3c5a86933 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/families.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class families(Variable): + value_type = float + entity = BenUnit + label = "Variable holding families" + definition_period = YEAR + default_value = 1 diff --git a/policyengine_uk/variables/household/demographic/family_type.py b/policyengine_uk/variables/household/demographic/family_type.py new file mode 100644 index 000000000..26fb8e913 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/family_type.py @@ -0,0 +1,29 @@ +from policyengine_uk.model_api import * + + +class FamilyType(Enum): + SINGLE = "Single, with no children" + COUPLE_NO_CHILDREN = "Couple, with no children" + LONE_PARENT = "Lone parent, with children" + COUPLE_WITH_CHILDREN = "Couple, with children" + + +class family_type(Variable): + value_type = Enum + entity = BenUnit + default_value = FamilyType.SINGLE + possible_values = FamilyType + label = "Family composition" + definition_period = YEAR + + def formula(benunit, period, parameters): + two_adults = add(benunit, period, ["is_adult"]) == 2 + has_children = benunit.any(benunit.members("is_child", period)) + single = ~two_adults & ~has_children + couple_no_children = two_adults & ~has_children + lone_parent = ~two_adults & has_children + couple_with_children = two_adults & has_children + return select( + [single, couple_no_children, lone_parent, couple_with_children], + FamilyType, + ) diff --git a/policyengine_uk/variables/household/demographic/gender.py b/policyengine_uk/variables/household/demographic/gender.py new file mode 100644 index 000000000..6cda88702 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/gender.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class Gender(Enum): + MALE = "Male" + FEMALE = "Female" + + +class gender(Variable): + value_type = Enum + possible_values = Gender + default_value = Gender.MALE + entity = Person + label = "Gender of the person" + definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/highest_education.py b/policyengine_uk/variables/household/demographic/highest_education.py new file mode 100644 index 000000000..4bd7efd05 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/highest_education.py @@ -0,0 +1,22 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class EducationType(Enum): + NOT_IN_EDUCATION = "Not in education" + PRE_PRIMARY = "Pre-primary" + NOT_COMPLETED_PRIMARY = "Not completed primary" + PRIMARY = "Primary" + LOWER_SECONDARY = "Lower Secondary" + UPPER_SECONDARY = "Upper Secondary" + POST_SECONDARY = "Post Secondary" + TERTIARY = "Tertiary" + + +class highest_education(Variable): + value_type = Enum + possible_values = EducationType + default_value = EducationType.UPPER_SECONDARY + entity = Person + label = "Highest status education completed" + definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/household.py b/policyengine_uk/variables/household/demographic/household.py deleted file mode 100644 index ffbfe4b7a..000000000 --- a/policyengine_uk/variables/household/demographic/household.py +++ /dev/null @@ -1,235 +0,0 @@ -from policyengine_uk.model_api import * -from policyengine_uk.variables.household.demographic.geography import Region - - -class household_id(Variable): - value_type = int - entity = Household - label = "ID for the household" - definition_period = YEAR - - -class households(Variable): - value_type = float - entity = Household - label = "Variable holding households" - definition_period = YEAR - default_value = 1 - - -class num_bedrooms(Variable): - value_type = int - entity = Household - label = "The number of bedrooms in the house" - definition_period = YEAR - - -class is_shared_accommodation(Variable): - value_type = bool - entity = Household - label = "Whether the household is shared accommodation" - definition_period = YEAR - - -class household_weight(Variable): - value_type = float - entity = Household - label = "Weight factor for the household" - definition_period = YEAR - uprating = "gov.ons.population" - default_value = 1 - - -class Country(Enum): - UNKNOWN = "Unknown" - SCOTLAND = "Scotland" - ENGLAND = "England" - WALES = "Wales" - NORTHERN_IRELAND = "Northern Ireland" - - -class country(Variable): - value_type = Enum - possible_values = Country - default_value = Country.ENGLAND - entity = Household - label = "Country of the UK" - definition_period = YEAR - - def formula(household, period, parameters): - region = household("region", period) - return select( - [ - region == Region.UNKNOWN, - region == Region.SCOTLAND, - region == Region.WALES, - region == Region.NORTHERN_IRELAND, - ], - [ - Country.UNKNOWN, - Country.SCOTLAND, - Country.WALES, - Country.NORTHERN_IRELAND, - ], - default=Country.ENGLAND, - ) - - -class TenureType(Enum): - RENT_FROM_COUNCIL = "Rented from Council" - RENT_FROM_HA = "Rented from a Housing Association" - RENT_PRIVATELY = "Rented privately" - OWNED_OUTRIGHT = "Owned outright" - OWNED_WITH_MORTGAGE = "Owned with a mortgage" - - -class ONSTenureType(Enum): - RENT_FROM_COUNCIL = "Rented from Council" - RENT_FROM_HA = "Rented from a Housing Association" - RENT_PRIVATELY = "Rented privately" - OWNER_OCCUPIED = "Owner occupied" - - -class tenure_type(Variable): - value_type = Enum - possible_values = TenureType - default_value = TenureType.RENT_PRIVATELY - entity = Household - label = "Tenure type of the household" - definition_period = YEAR - - -class ons_tenure_type(Variable): - label = "ONS tenure type" - documentation = "Tenure type matching ONS_produced statistical breakdowns." - entity = Household - definition_period = YEAR - value_type = Enum - possible_values = ONSTenureType - default_value = ONSTenureType.OWNER_OCCUPIED - - def formula(household, period, parameters): - tenure = household("tenure_type", period) - return select( - [ - tenure == TenureType.RENT_FROM_HA, - tenure == TenureType.RENT_FROM_COUNCIL, - tenure == TenureType.RENT_PRIVATELY, - tenure == TenureType.OWNED_OUTRIGHT, - tenure == TenureType.OWNED_WITH_MORTGAGE, - ], - [ - ONSTenureType.RENT_FROM_HA, - ONSTenureType.RENT_FROM_COUNCIL, - ONSTenureType.RENT_PRIVATELY, - ONSTenureType.OWNER_OCCUPIED, - ONSTenureType.OWNER_OCCUPIED, - ], - ) - - -class is_renting(Variable): - value_type = bool - entity = Household - label = "Is renting" - definition_period = YEAR - - def formula(household, period, parameters): - tenure = household("tenure_type", period) - tenures = tenure.possible_values - RENT_TENURES = [ - tenures.RENT_PRIVATELY, - tenures.RENT_FROM_COUNCIL, - tenures.RENT_PRIVATELY, - ] - return is_in(tenure, RENT_TENURES) - - -class AccommodationType(Enum): - HOUSE_DETACHED = "Detached house" - HOUSE_SEMI_DETACHED = "Semi-detached house" - HOUSE_TERRACED = "Terraced house" - FLAT = "Purpose-built flat or maisonette" - CONVERTED_HOUSE = "Converted house or building" - MOBILE = "Caravan/Mobile home or houseboat" - OTHER = "Other" - UNKNOWN = "Unknown" - - -class accommodation_type(Variable): - value_type = Enum - possible_values = AccommodationType - default_value = AccommodationType.UNKNOWN - entity = Household - label = "Type of accommodation" - definition_period = YEAR - - -class household_equivalisation_bhc(Variable): - value_type = float - entity = Household - label = "Equivalisation factor to account for household composition, before housing costs" - definition_period = YEAR - - def formula(household, period, parameters): - count_other_adults = max_( - household.sum(household.members("is_adult", period)) - 1, 0 - ) - count_young_children = household.sum( - household.members("is_young_child", period) - ) - count_older_children = household.sum( - household.members("is_older_child", period) - ) - return ( - 0.67 - + 0.33 * count_other_adults - + 0.33 * count_older_children - + 0.2 * count_young_children - ) - - -class household_equivalisation_ahc(Variable): - value_type = float - entity = Household - label = "Equivalisation factor to account for household composition, after housing costs" - definition_period = YEAR - - def formula(household, period, parameters): - count_other_adults = max_( - household.sum(household.members("is_adult", period)) - 1, 0 - ) - count_young_children = household.sum( - household.members("is_young_child", period) - ) - count_older_children = household.sum( - household.members("is_older_child", period) - ) - return ( - 0.58 - + 0.42 * count_other_adults - + 0.42 * count_older_children - + 0.2 * count_young_children - ) - - -class household_count_people(Variable): - value_type = int - entity = Household - label = "Number of people" - definition_period = YEAR - unit = "person" - - def formula(household, period, parameters): - return household.nb_persons() - - -class household_num_benunits(Variable): - value_type = int - entity = Household - label = "Number of benefit units" - definition_period = YEAR - unit = "benefit unit" - - def formula(household, period, parameters): - return household.sum(household.members("is_benunit_head", period)) diff --git a/policyengine_uk/variables/household/demographic/household_count_people.py b/policyengine_uk/variables/household/demographic/household_count_people.py new file mode 100644 index 000000000..b2bb970a8 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/household_count_people.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.geography import Region + + +class household_count_people(Variable): + value_type = int + entity = Household + label = "Number of people" + definition_period = YEAR + unit = "person" + + def formula(household, period, parameters): + return household.nb_persons() diff --git a/policyengine_uk/variables/household/demographic/household_equivalisation_ahc.py b/policyengine_uk/variables/household/demographic/household_equivalisation_ahc.py new file mode 100644 index 000000000..4fbfb5991 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/household_equivalisation_ahc.py @@ -0,0 +1,26 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.geography import Region + + +class household_equivalisation_ahc(Variable): + value_type = float + entity = Household + label = "Equivalisation factor to account for household composition, after housing costs" + definition_period = YEAR + + def formula(household, period, parameters): + count_other_adults = max_( + household.sum(household.members("is_adult", period)) - 1, 0 + ) + count_young_children = household.sum( + household.members("is_young_child", period) + ) + count_older_children = household.sum( + household.members("is_older_child", period) + ) + return ( + 0.58 + + 0.42 * count_other_adults + + 0.42 * count_older_children + + 0.2 * count_young_children + ) diff --git a/policyengine_uk/variables/household/demographic/household_equivalisation_bhc.py b/policyengine_uk/variables/household/demographic/household_equivalisation_bhc.py new file mode 100644 index 000000000..48a1bbf51 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/household_equivalisation_bhc.py @@ -0,0 +1,26 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.geography import Region + + +class household_equivalisation_bhc(Variable): + value_type = float + entity = Household + label = "Equivalisation factor to account for household composition, before housing costs" + definition_period = YEAR + + def formula(household, period, parameters): + count_other_adults = max_( + household.sum(household.members("is_adult", period)) - 1, 0 + ) + count_young_children = household.sum( + household.members("is_young_child", period) + ) + count_older_children = household.sum( + household.members("is_older_child", period) + ) + return ( + 0.67 + + 0.33 * count_other_adults + + 0.33 * count_older_children + + 0.2 * count_young_children + ) diff --git a/policyengine_uk/variables/household/demographic/household_id.py b/policyengine_uk/variables/household/demographic/household_id.py new file mode 100644 index 000000000..398a34b8c --- /dev/null +++ b/policyengine_uk/variables/household/demographic/household_id.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.geography import Region + + +class household_id(Variable): + value_type = int + entity = Household + label = "ID for the household" + definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/household_num_benunits.py b/policyengine_uk/variables/household/demographic/household_num_benunits.py new file mode 100644 index 000000000..5c5a0b174 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/household_num_benunits.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.geography import Region + + +class household_num_benunits(Variable): + value_type = int + entity = Household + label = "Number of benefit units" + definition_period = YEAR + unit = "benefit unit" + + def formula(household, period, parameters): + return household.sum(household.members("is_benunit_head", period)) diff --git a/policyengine_uk/variables/household/demographic/household_weight.py b/policyengine_uk/variables/household/demographic/household_weight.py new file mode 100644 index 000000000..34062b635 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/household_weight.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.geography import Region + + +class household_weight(Variable): + value_type = float + entity = Household + label = "Weight factor for the household" + definition_period = YEAR + uprating = "gov.ons.population" + default_value = 1 diff --git a/policyengine_uk/variables/household/demographic/households.py b/policyengine_uk/variables/household/demographic/households.py new file mode 100644 index 000000000..ba0197b04 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/households.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.geography import Region + + +class households(Variable): + value_type = float + entity = Household + label = "Variable holding households" + definition_period = YEAR + default_value = 1 diff --git a/policyengine_uk/variables/household/demographic/in_FE.py b/policyengine_uk/variables/household/demographic/in_FE.py new file mode 100644 index 000000000..54bae4671 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/in_FE.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class in_FE(Variable): + value_type = bool + entity = Person + label = "Whether this person is in Further Education" + definition_period = YEAR + set_input = set_input_dispatch_by_period diff --git a/policyengine_uk/variables/household/demographic/in_HE.py b/policyengine_uk/variables/household/demographic/in_HE.py new file mode 100644 index 000000000..47240bb29 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/in_HE.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class in_HE(Variable): + value_type = bool + entity = Person + label = "In higher education" + definition_period = YEAR + reference = "Whether this person is in Higher Education" + set_input = set_input_dispatch_by_period diff --git a/policyengine_uk/variables/household/demographic/in_social_housing.py b/policyengine_uk/variables/household/demographic/in_social_housing.py new file mode 100644 index 000000000..f33c2e768 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/in_social_housing.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class in_social_housing(Variable): + value_type = bool + entity = Person + label = "Whether this person lives in social housing" + definition_period = YEAR + + def formula(person, period, parameters): + tenure = person.household("tenure_type", period.this_year) + tenures = tenure.possible_values + return is_in(tenure, tenures.RENT_FROM_COUNCIL, tenures.RENT_FROM_HA) diff --git a/policyengine_uk/variables/household/demographic/is_WA_adult.py b/policyengine_uk/variables/household/demographic/is_WA_adult.py new file mode 100644 index 000000000..8db33a6db --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_WA_adult.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class is_WA_adult(Variable): + value_type = bool + entity = Person + label = "Whether is a working-age adult" + definition_period = YEAR + + def formula(person, period, parameters): + return person("is_adult", period) & ~person("is_SP_age", period) diff --git a/policyengine_uk/variables/household/demographic/is_adult.py b/policyengine_uk/variables/household/demographic/is_adult.py new file mode 100644 index 000000000..29bb92d53 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_adult.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class is_adult(Variable): + value_type = bool + entity = Person + label = "Whether this person is an adult" + definition_period = YEAR + + def formula(person, period, parameters): + return person("age", period) >= 18 diff --git a/policyengine_uk/variables/household/demographic/is_benunit_eldest_child.py b/policyengine_uk/variables/household/demographic/is_benunit_eldest_child.py new file mode 100644 index 000000000..58e883aab --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_benunit_eldest_child.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class is_benunit_eldest_child(Variable): + value_type = bool + entity = Person + label = "Eldest child in the benefit unit" + definition_period = YEAR + + def formula(person, period, parameters): + age = person("age", period) + is_child = person("is_child", period) + eldest_age = person.benunit("eldest_child_age", period) + age_tie = person.benunit.sum((age == eldest_age) & is_child) > 1 + is_eldest_age = person("age", period) == eldest_age + child_id = person("person_id", period) * is_child + max_child_id = person.benunit.max(child_id) + has_max_child_id = child_id == max_child_id + return where(is_eldest_age & age_tie, has_max_child_id, is_eldest_age) diff --git a/policyengine_uk/variables/household/demographic/is_benunit_head.py b/policyengine_uk/variables/household/demographic/is_benunit_head.py new file mode 100644 index 000000000..4ed35a0dc --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_benunit_head.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class is_benunit_head(Variable): + value_type = bool + entity = Person + label = "Whether this person is the head-of-family" + definition_period = YEAR + + def formula(person, period, parameters): + return person.get_rank(person.benunit, person("age", period)) == 0 diff --git a/policyengine_uk/variables/household/demographic/is_carer_for_benefits.py b/policyengine_uk/variables/household/demographic/is_carer_for_benefits.py new file mode 100644 index 000000000..2db914673 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_carer_for_benefits.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class is_carer_for_benefits(Variable): + value_type = bool + entity = Person + label = "Whether this person is a carer for benefits purposes" + definition_period = YEAR + + def formula(person, period, parameters): + return person("receives_carers_allowance", period) diff --git a/policyengine_uk/variables/household/demographic/is_child.py b/policyengine_uk/variables/household/demographic/is_child.py new file mode 100644 index 000000000..675c320d2 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_child.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class is_child(Variable): + value_type = bool + entity = Person + label = "Is a child" + definition_period = YEAR + + def formula(person, period, parameters): + return person("age", period) < 18 diff --git a/policyengine_uk/variables/household/demographic/is_disabled_for_benefits.py b/policyengine_uk/variables/household/demographic/is_disabled_for_benefits.py new file mode 100644 index 000000000..199b8ed9a --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_disabled_for_benefits.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class is_disabled_for_benefits(Variable): + value_type = bool + entity = Person + label = "Has a disability" + documentation = "Whether this person is disabled for benefits purposes" + definition_period = YEAR + reference = "Child Tax Credit Regulations 2002 s. 8" + + def formula(person, period, parameters): + QUALIFYING_BENEFITS = [ + "dla", + "pip", + ] + return add(person, period, QUALIFYING_BENEFITS) > 0 diff --git a/policyengine_uk/variables/household/demographic/is_eldest_child.py b/policyengine_uk/variables/household/demographic/is_eldest_child.py new file mode 100644 index 000000000..cce261193 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_eldest_child.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class is_eldest_child(Variable): + label = "Is the eldest child" + documentation = ( + "Whether this person is the eldest child in the benefit unit" + ) + entity = Person + definition_period = YEAR + value_type = bool + + def formula(person, period, parameters): + index = person("child_index", period) + index = where(index < 0, 100, index) + lowest_index = person.benunit.min(index) + return index == lowest_index diff --git a/policyengine_uk/variables/household/demographic/is_enhanced_disabled_for_benefits.py b/policyengine_uk/variables/household/demographic/is_enhanced_disabled_for_benefits.py new file mode 100644 index 000000000..7782b4ca9 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_enhanced_disabled_for_benefits.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + + +class is_enhanced_disabled_for_benefits(Variable): + value_type = bool + entity = Person + label = "Whether meets the middle disability benefit entitlement" + definition_period = YEAR + + def formula(person, period, parameters): + DLA_requirement = ( + parameters(period).gov.dwp.dla.self_care.higher * WEEKS_IN_YEAR + ) + return person("dla_sc", period) >= DLA_requirement diff --git a/policyengine_uk/variables/household/demographic/is_female.py b/policyengine_uk/variables/household/demographic/is_female.py new file mode 100644 index 000000000..23abd522c --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_female.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class is_female(Variable): + value_type = bool + entity = Person + label = "Whether the person is female" + definition_period = YEAR + + def formula(person, period, parameters): + gender = person("gender", period) + return gender == gender.possible_values.FEMALE diff --git a/policyengine_uk/variables/household/demographic/is_household_head.py b/policyengine_uk/variables/household/demographic/is_household_head.py new file mode 100644 index 000000000..44cc52da9 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_household_head.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class is_household_head(Variable): + value_type = bool + entity = Person + label = "Whether this person is the head-of-household" + definition_period = YEAR + + def formula(person, period, parameters): + return person.get_rank(person.household, person("age", period)) == 0 diff --git a/policyengine_uk/variables/household/demographic/is_male.py b/policyengine_uk/variables/household/demographic/is_male.py new file mode 100644 index 000000000..364ba86de --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_male.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class is_male(Variable): + value_type = bool + entity = Person + label = "Whether the person is male" + definition_period = YEAR + + def formula(person, period, parameters): + gender = person("gender", period) + return gender == gender.possible_values.MALE diff --git a/policyengine_uk/variables/household/demographic/is_married.py b/policyengine_uk/variables/household/demographic/is_married.py new file mode 100644 index 000000000..6f234b4ae --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_married.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class is_married(Variable): + value_type = bool + entity = BenUnit + label = "Married" + documentation = "Whether the benefit unit adults are married to each other or in a civil partnership" + definition_period = YEAR + + def formula(benunit, period, parameters): + return add(benunit, period, ["is_adult"]) == 2 diff --git a/policyengine_uk/variables/household/demographic/is_older_child.py b/policyengine_uk/variables/household/demographic/is_older_child.py new file mode 100644 index 000000000..a7555133f --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_older_child.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class is_older_child(Variable): + value_type = bool + entity = Person + label = "Whether the person is over 14 but under 18" + definition_period = YEAR + + def formula(person, period, parameters): + age = person("age", period) + return (age >= 14) & (age < 18) diff --git a/policyengine_uk/variables/household/demographic/is_renting.py b/policyengine_uk/variables/household/demographic/is_renting.py new file mode 100644 index 000000000..150f134c6 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_renting.py @@ -0,0 +1,19 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.geography import Region + + +class is_renting(Variable): + value_type = bool + entity = Household + label = "Is renting" + definition_period = YEAR + + def formula(household, period, parameters): + tenure = household("tenure_type", period) + tenures = tenure.possible_values + RENT_TENURES = [ + tenures.RENT_PRIVATELY, + tenures.RENT_FROM_COUNCIL, + tenures.RENT_PRIVATELY, + ] + return is_in(tenure, RENT_TENURES) diff --git a/policyengine_uk/variables/household/demographic/is_severely_disabled_for_benefits.py b/policyengine_uk/variables/household/demographic/is_severely_disabled_for_benefits.py new file mode 100644 index 000000000..16dc1b6aa --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_severely_disabled_for_benefits.py @@ -0,0 +1,28 @@ +from policyengine_uk.model_api import * + + +class is_severely_disabled_for_benefits(Variable): + value_type = bool + entity = Person + label = "Has a severe disability" + documentation = ( + "Whether this person is severely disabled for benefits purposes" + ) + definition_period = YEAR + reference = "Child Tax Credit Regulations 2002 s. 8" + + def formula(person, period, parameters): + benefit = parameters(period).gov.dwp + THRESHOLD_SAFETY_GAP = 10 * WEEKS_IN_YEAR + paragraph_3 = ( + person("dla_sc", period) + >= benefit.dla.self_care.higher * WEEKS_IN_YEAR + - THRESHOLD_SAFETY_GAP + ) + paragraph_4 = ( + person("pip_dl", period) + >= benefit.pip.daily_living.enhanced * WEEKS_IN_YEAR + - THRESHOLD_SAFETY_GAP + ) + paragraph_5 = person("afcs", period) > 0 + return sum([paragraph_3, paragraph_4, paragraph_5]) > 0 diff --git a/policyengine_uk/variables/household/demographic/is_shared_accommodation.py b/policyengine_uk/variables/household/demographic/is_shared_accommodation.py new file mode 100644 index 000000000..69f8bd4dd --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_shared_accommodation.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.geography import Region + + +class is_shared_accommodation(Variable): + value_type = bool + entity = Household + label = "Whether the household is shared accommodation" + definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/is_young_child.py b/policyengine_uk/variables/household/demographic/is_young_child.py new file mode 100644 index 000000000..a4708d441 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/is_young_child.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class is_young_child(Variable): + value_type = bool + entity = Person + label = "Whether the person is under 14" + definition_period = YEAR + + def formula(person, period, parameters): + return person("age", period.this_year) < 14 diff --git a/policyengine_uk/variables/household/demographic/marital_status.py b/policyengine_uk/variables/household/demographic/marital_status.py new file mode 100644 index 000000000..e70c06faf --- /dev/null +++ b/policyengine_uk/variables/household/demographic/marital_status.py @@ -0,0 +1,26 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class MaritalStatus(Enum): + SINGLE = "Single" + MARRIED = "Married" + SEPARATED = "Separated" + DIVORCED = "Divorced" + WIDOWED = "Widowed" + + +class marital_status(Variable): + value_type = Enum + possible_values = MaritalStatus + default_value = MaritalStatus.SINGLE + entity = Person + label = "Marital status" + definition_period = YEAR + + def formula(person, period, parameters): + return where( + person.benunit("is_married", period), + MaritalStatus.MARRIED, + MaritalStatus.SINGLE, + ) diff --git a/policyengine_uk/variables/household/demographic/num_adults.py b/policyengine_uk/variables/household/demographic/num_adults.py new file mode 100644 index 000000000..5697db5cb --- /dev/null +++ b/policyengine_uk/variables/household/demographic/num_adults.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class num_adults(Variable): + value_type = int + entity = BenUnit + label = "The number of adults in the family" + definition_period = YEAR + + def formula(benunit, period, parameters): + return benunit.sum(benunit.members("is_adult", period)) diff --git a/policyengine_uk/variables/household/demographic/num_bedrooms.py b/policyengine_uk/variables/household/demographic/num_bedrooms.py new file mode 100644 index 000000000..d30aff8b2 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/num_bedrooms.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.household.demographic.geography import Region + + +class num_bedrooms(Variable): + value_type = int + entity = Household + label = "The number of bedrooms in the house" + definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/num_carers.py b/policyengine_uk/variables/household/demographic/num_carers.py new file mode 100644 index 000000000..464b83119 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/num_carers.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class num_carers(Variable): + value_type = int + entity = BenUnit + label = "Number of carers in the family" + definition_period = YEAR + + def formula(benunit, period, parameters): + return add(benunit, period, ["is_carer_for_benefits"]) diff --git a/policyengine_uk/variables/household/demographic/num_children.py b/policyengine_uk/variables/household/demographic/num_children.py new file mode 100644 index 000000000..b9e33ef21 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/num_children.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class num_children(Variable): + value_type = int + entity = BenUnit + label = "The number of children in the family" + definition_period = YEAR + + def formula(benunit, period, parameters): + return benunit.sum(benunit.members("is_child", period)) diff --git a/policyengine_uk/variables/household/demographic/num_disabled_adults.py b/policyengine_uk/variables/household/demographic/num_disabled_adults.py new file mode 100644 index 000000000..0bf0731b0 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/num_disabled_adults.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class num_disabled_adults(Variable): + value_type = int + entity = BenUnit + label = "Number of disabled adults" + definition_period = YEAR + + def formula(benunit, period, parameters): + adult = benunit.members("is_adult", period) + disabled = benunit.members("is_disabled_for_benefits", period) + return benunit.sum(adult & disabled) diff --git a/policyengine_uk/variables/household/demographic/num_disabled_children.py b/policyengine_uk/variables/household/demographic/num_disabled_children.py new file mode 100644 index 000000000..8626c12fd --- /dev/null +++ b/policyengine_uk/variables/household/demographic/num_disabled_children.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class num_disabled_children(Variable): + value_type = int + entity = BenUnit + label = "Number of disabled children" + definition_period = YEAR + + def formula(benunit, period, parameters): + child = benunit.members("is_child_or_QYP", period) + disabled = benunit.members("is_disabled_for_benefits", period) + return benunit.sum(child & disabled) diff --git a/policyengine_uk/variables/household/demographic/num_enhanced_disabled_adults.py b/policyengine_uk/variables/household/demographic/num_enhanced_disabled_adults.py new file mode 100644 index 000000000..8c4d77afb --- /dev/null +++ b/policyengine_uk/variables/household/demographic/num_enhanced_disabled_adults.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class num_enhanced_disabled_adults(Variable): + value_type = int + entity = BenUnit + label = "Number of enhanced disabled adults" + definition_period = YEAR + + def formula(benunit, period, parameters): + adult = benunit.members("is_adult", period) + enhanced_disabled = benunit.members( + "is_enhanced_disabled_for_benefits", period + ) + return benunit.sum(adult & enhanced_disabled) diff --git a/policyengine_uk/variables/household/demographic/num_enhanced_disabled_children.py b/policyengine_uk/variables/household/demographic/num_enhanced_disabled_children.py new file mode 100644 index 000000000..04bd60b77 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/num_enhanced_disabled_children.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class num_enhanced_disabled_children(Variable): + value_type = int + entity = BenUnit + label = "Number of enhanced disabled children" + definition_period = YEAR + + def formula(benunit, period, parameters): + child = benunit.members("is_child_or_QYP", period) + enhanced_disabled = benunit.members( + "is_enhanced_disabled_for_benefits", period + ) + return benunit.sum(child & enhanced_disabled) diff --git a/policyengine_uk/variables/household/demographic/num_severely_disabled_adults.py b/policyengine_uk/variables/household/demographic/num_severely_disabled_adults.py new file mode 100644 index 000000000..7fd4c2289 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/num_severely_disabled_adults.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class num_severely_disabled_adults(Variable): + value_type = int + entity = BenUnit + label = "Number of severely disabled adults" + definition_period = YEAR + + def formula(benunit, period, parameters): + adult = benunit.members("is_adult", period) + severely_disabled = benunit.members( + "is_severely_disabled_for_benefits", period + ) + return benunit.sum(adult & severely_disabled) diff --git a/policyengine_uk/variables/household/demographic/num_severely_disabled_children.py b/policyengine_uk/variables/household/demographic/num_severely_disabled_children.py new file mode 100644 index 000000000..2b2c8d594 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/num_severely_disabled_children.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class num_severely_disabled_children(Variable): + value_type = int + entity = BenUnit + label = "Number of severely disabled children" + definition_period = YEAR + + def formula(benunit, period, parameters): + child = benunit.members("is_child_or_QYP", period) + severely_disabled = benunit.members( + "is_severely_disabled_for_benefits", period + ) + return benunit.sum(child & severely_disabled) diff --git a/policyengine_uk/variables/household/demographic/ons_tenure_type.py b/policyengine_uk/variables/household/demographic/ons_tenure_type.py new file mode 100644 index 000000000..a51ed7371 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/ons_tenure_type.py @@ -0,0 +1,37 @@ +from policyengine_uk.model_api import * + + +class ONSTenureType(Enum): + OWNER_OCCUPIED = "Owner occupied" + RENT_PRIVATELY = "Rent privately" + RENT_FROM_COUNCIL = "Rent from council" + RENT_FROM_HA = "Rent from housing association" + + +class ons_tenure_type(Variable): + label = "ONS tenure type" + documentation = "Tenure type matching ONS_produced statistical breakdowns." + entity = Household + definition_period = YEAR + value_type = Enum + possible_values = ONSTenureType + default_value = ONSTenureType.OWNER_OCCUPIED + + def formula(household, period, parameters): + tenure = household("tenure_type", period) + tenure_types = tenure.possible_values + return select( + [ + tenure == tenure_types.RENT_FROM_HA, + tenure == tenure_types.RENT_FROM_COUNCIL, + tenure == tenure_types.RENT_PRIVATELY, + tenure == tenure_types.OWNER_OCCUPIED, + ], + [ + ONSTenureType.RENT_FROM_HA, + ONSTenureType.RENT_FROM_COUNCIL, + ONSTenureType.RENT_PRIVATELY, + ONSTenureType.OWNER_OCCUPIED, + ], + default=ONSTenureType.OWNER_OCCUPIED, + ) diff --git a/policyengine_uk/variables/household/demographic/over_16.py b/policyengine_uk/variables/household/demographic/over_16.py new file mode 100644 index 000000000..7890afbeb --- /dev/null +++ b/policyengine_uk/variables/household/demographic/over_16.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class over_16(Variable): + value_type = bool + entity = Person + label = "Whether the person is over 16" + definition_period = YEAR + + def formula(person, period, parameters): + return person("age", period) >= 16 diff --git a/policyengine_uk/variables/household/demographic/people.py b/policyengine_uk/variables/household/demographic/people.py new file mode 100644 index 000000000..875ab9394 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/people.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class people(Variable): + value_type = float + entity = Person + label = "Variable holding people" + definition_period = YEAR + default_value = 1 diff --git a/policyengine_uk/variables/household/demographic/person.py b/policyengine_uk/variables/household/demographic/person.py deleted file mode 100644 index b1c8dbcd6..000000000 --- a/policyengine_uk/variables/household/demographic/person.py +++ /dev/null @@ -1,377 +0,0 @@ -from policyengine_uk.model_api import * -import pandas as pd - - -class person_id(Variable): - value_type = int - entity = Person - label = "ID for the person" - definition_period = YEAR - - -class people(Variable): - value_type = float - entity = Person - label = "Variable holding people" - definition_period = YEAR - default_value = 1 - - -class raw_person_weight(Variable): - value_type = float - entity = Person - label = "Weight factor" - documentation = "Used to translate from survey respondents to UK residents" - definition_period = YEAR - default_value = 1 - - -class person_weight(Variable): - value_type = float - entity = Person - label = "Weight" - definition_period = YEAR - - def formula(person, period, parameters): - return person.household("household_weight", period) - - -class adult_index(Variable): - value_type = int - entity = Person - label = "Index of adult in household" - definition_period = YEAR - - def formula(person, period, parameters): - return ( - person.get_rank( - person.household, - -person("age", period), - condition=person("is_adult", period), - ) - + 1 - ) - - -class birth_year(Variable): - value_type = int - entity = Person - label = "The birth year of the person" - definition_period = YEAR - - def formula(person, period, parameters): - return period.start.year - person("age", period) - - -class over_16(Variable): - value_type = bool - entity = Person - label = "Whether the person is over 16" - definition_period = YEAR - - def formula(person, period, parameters): - return person("age", period) >= 16 - - -class is_adult(Variable): - value_type = bool - entity = Person - label = "Whether this person is an adult" - definition_period = YEAR - - def formula(person, period, parameters): - return person("age", period) >= 18 - - -class is_child(Variable): - value_type = bool - entity = Person - label = "Is a child" - definition_period = YEAR - - def formula(person, period, parameters): - return person("age", period) < 18 - - -class child_index(Variable): - value_type = int - entity = Person - label = "Child reference number" - definition_period = YEAR - - def formula(person, period, parameters): - # The child index, by age, descending (e.g. "first child" = 1) - child_ranking = ( - person.get_rank( - person.benunit, - -person("age", period), - ) - + 1 - ) - # Fill in adult values - values = where(person("is_child", period), child_ranking, 100) - # Base to 0 - values = values - person.benunit.min(values) + 1 - return where(person("is_child", period), values, -1) - - -class is_eldest_child(Variable): - label = "Is the eldest child" - documentation = ( - "Whether this person is the eldest child in the benefit unit" - ) - entity = Person - definition_period = YEAR - value_type = bool - - def formula(person, period, parameters): - index = person("child_index", period) - index = where(index < 0, 100, index) - lowest_index = person.benunit.min(index) - return index == lowest_index - - -class is_benunit_eldest_child(Variable): - value_type = bool - entity = Person - label = "Eldest child in the benefit unit" - definition_period = YEAR - - def formula(person, period, parameters): - age = person("age", period) - is_child = person("is_child", period) - eldest_age = person.benunit("eldest_child_age", period) - age_tie = person.benunit.sum((age == eldest_age) & is_child) > 1 - is_eldest_age = person("age", period) == eldest_age - child_id = person("person_id", period) * is_child - max_child_id = person.benunit.max(child_id) - has_max_child_id = child_id == max_child_id - return where(is_eldest_age & age_tie, has_max_child_id, is_eldest_age) - - -class MaritalStatus(Enum): - SINGLE = "Single" - MARRIED = "Married" - SEPARATED = "Separated" - DIVORCED = "Divorced" - WIDOWED = "Widowed" - - -class marital_status(Variable): - value_type = Enum - possible_values = MaritalStatus - default_value = MaritalStatus.SINGLE - entity = Person - label = "Marital status" - definition_period = YEAR - - def formula(person, period, parameters): - return where( - person.benunit("is_married", period), - MaritalStatus.MARRIED, - MaritalStatus.SINGLE, - ) - - -class EducationType(Enum): - NOT_IN_EDUCATION = "Not in education" - PRE_PRIMARY = "Pre-primary" - NOT_COMPLETED_PRIMARY = "Not completed primary" - PRIMARY = "Primary" - LOWER_SECONDARY = "Lower Secondary" - UPPER_SECONDARY = "Upper Secondary" - POST_SECONDARY = "Post Secondary" - TERTIARY = "Tertiary" - - -class current_education(Variable): - value_type = Enum - possible_values = EducationType - default_value = EducationType.NOT_IN_EDUCATION - entity = Person - label = "Current education" - definition_period = YEAR - - def formula(person, period, parameters): - if ( - person.get_holder("current_education").get_array(period.last_year) - is not None - ): - return person("current_education", period.last_year) - age = person("age", period) - return np.select( - [ - age < 4, - (age >= 4) & (age < 7), - (age >= 7) & (age < 11), - (age >= 11) & (age < 14), - (age >= 14) & (age < 16), - (age >= 16) & (age < 18), - (age >= 18) & (age < 20), - age >= 20, - ], - [ - EducationType.PRE_PRIMARY, - EducationType.NOT_COMPLETED_PRIMARY, - EducationType.PRIMARY, - EducationType.LOWER_SECONDARY, - EducationType.UPPER_SECONDARY, - EducationType.POST_SECONDARY, - EducationType.TERTIARY, - EducationType.NOT_IN_EDUCATION, - ], - ) - - -class highest_education(Variable): - value_type = Enum - possible_values = EducationType - default_value = EducationType.UPPER_SECONDARY - entity = Person - label = "Highest status education completed" - definition_period = YEAR - - -class in_FE(Variable): - value_type = bool - entity = Person - label = "Whether this person is in Further Education" - definition_period = YEAR - set_input = set_input_dispatch_by_period - - -class in_HE(Variable): - value_type = bool - entity = Person - label = "In higher education" - definition_period = YEAR - reference = "Whether this person is in Higher Education" - set_input = set_input_dispatch_by_period - - -class Gender(Enum): - MALE = "Male" - FEMALE = "Female" - - -class gender(Variable): - value_type = Enum - possible_values = Gender - default_value = Gender.MALE - entity = Person - label = "Gender of the person" - definition_period = YEAR - - -class is_male(Variable): - value_type = bool - entity = Person - label = "Whether the person is male" - definition_period = YEAR - - def formula(person, period, parameters): - return person("gender", period) == Gender.MALE - - -class is_female(Variable): - value_type = bool - entity = Person - label = "Whether the person is female" - definition_period = YEAR - - def formula(person, period, parameters): - return person("gender", period) == Gender.FEMALE - - -class is_household_head(Variable): - value_type = bool - entity = Person - label = "Whether this person is the head-of-household" - definition_period = YEAR - - def formula(person, period, parameters): - return person.get_rank(person.household, person("age", period)) == 0 - - -class is_benunit_head(Variable): - value_type = bool - entity = Person - label = "Whether this person is the head-of-family" - definition_period = YEAR - - def formula(person, period, parameters): - return person.get_rank(person.benunit, person("age", period)) == 0 - - -class in_social_housing(Variable): - value_type = bool - entity = Person - label = "Whether this person lives in social housing" - definition_period = YEAR - - def formula(person, period, parameters): - tenure = person.household("tenure_type", period.this_year) - tenures = tenure.possible_values - return is_in(tenure, tenures.RENT_FROM_COUNCIL, tenures.RENT_FROM_HA) - - -class is_WA_adult(Variable): - value_type = bool - entity = Person - label = "Whether is a working-age adult" - definition_period = YEAR - - def formula(person, period, parameters): - return person("is_adult", period) & ~person("is_SP_age", period) - - -class is_young_child(Variable): - value_type = bool - entity = Person - label = "Whether the person is under 14" - definition_period = YEAR - - def formula(person, period, parameters): - return person("age", period.this_year) < 14 - - -class age_under_18(Variable): - value_type = bool - entity = Person - label = "Whether the person is under age 18" - definition_period = YEAR - - def formula(person, period, parameters): - return person("age", period) < 18 - - -class age_18_64(Variable): - value_type = bool - entity = Person - label = "Whether the person is age 18 to 64" - definition_period = YEAR - - def formula(person, period, parameters): - age = person("age", period) - return (age >= 18) & (age <= 64) - - -class age_over_64(Variable): - value_type = bool - entity = Person - label = "Whether the person is over age 64" - definition_period = YEAR - - def formula(person, period, parameters): - return person("age", period) > 64 - - -class is_older_child(Variable): - value_type = bool - entity = Person - label = "Whether the person is over 14 but under 18" - definition_period = YEAR - - def formula(person, period, parameters): - age = person("age", period) - return (age >= 14) & (age < 18) diff --git a/policyengine_uk/variables/household/demographic/person_benunit_id.py b/policyengine_uk/variables/household/demographic/person_benunit_id.py new file mode 100644 index 000000000..2714bdb01 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/person_benunit_id.py @@ -0,0 +1,8 @@ +from policyengine_uk.model_api import * + + +class person_benunit_id(Variable): + value_type = float + label = "Person's benefit unit ID" + entity = Person + definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/person_benunit_role.py b/policyengine_uk/variables/household/demographic/person_benunit_role.py new file mode 100644 index 000000000..43dad6897 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/person_benunit_role.py @@ -0,0 +1,8 @@ +from policyengine_uk.model_api import * + + +class person_benunit_role(Variable): + value_type = str + label = "Role (adult/child)" + entity = Person + definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/person_household_id.py b/policyengine_uk/variables/household/demographic/person_household_id.py new file mode 100644 index 000000000..b82a0a58b --- /dev/null +++ b/policyengine_uk/variables/household/demographic/person_household_id.py @@ -0,0 +1,8 @@ +from policyengine_uk.model_api import * + + +class person_household_id(Variable): + value_type = float + label = "Person's household ID" + entity = Person + definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/person_household_role.py b/policyengine_uk/variables/household/demographic/person_household_role.py new file mode 100644 index 000000000..3ff28d58d --- /dev/null +++ b/policyengine_uk/variables/household/demographic/person_household_role.py @@ -0,0 +1,8 @@ +from policyengine_uk.model_api import * + + +class person_household_role(Variable): + value_type = str + label = "Role (adult/child)" + entity = Person + definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/person_id.py b/policyengine_uk/variables/household/demographic/person_id.py new file mode 100644 index 000000000..b10442bb3 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/person_id.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class person_id(Variable): + value_type = int + entity = Person + label = "ID for the person" + definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/person_state_id.py b/policyengine_uk/variables/household/demographic/person_state_id.py new file mode 100644 index 000000000..d1c5720bd --- /dev/null +++ b/policyengine_uk/variables/household/demographic/person_state_id.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class person_state_id(Variable): + label = "State ID" + documentation = "Identity of the state" + entity = Person + definition_period = YEAR + value_type = int diff --git a/policyengine_uk/variables/household/demographic/person_state_role.py b/policyengine_uk/variables/household/demographic/person_state_role.py new file mode 100644 index 000000000..8b87c798a --- /dev/null +++ b/policyengine_uk/variables/household/demographic/person_state_role.py @@ -0,0 +1,8 @@ +from policyengine_uk.model_api import * + + +class person_state_role(Variable): + label = "State role" + entity = Person + definition_period = YEAR + value_type = str diff --git a/policyengine_uk/variables/household/demographic/person_weight.py b/policyengine_uk/variables/household/demographic/person_weight.py new file mode 100644 index 000000000..05d159156 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/person_weight.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class person_weight(Variable): + value_type = float + entity = Person + label = "Weight" + definition_period = YEAR + + def formula(person, period, parameters): + return person.household("household_weight", period) diff --git a/policyengine_uk/variables/household/demographic/raw_person_weight.py b/policyengine_uk/variables/household/demographic/raw_person_weight.py new file mode 100644 index 000000000..dde045360 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/raw_person_weight.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * +import pandas as pd + + +class raw_person_weight(Variable): + value_type = float + entity = Person + label = "Weight factor" + documentation = "Used to translate from survey respondents to UK residents" + definition_period = YEAR + default_value = 1 diff --git a/policyengine_uk/variables/household/demographic/relation_type.py b/policyengine_uk/variables/household/demographic/relation_type.py new file mode 100644 index 000000000..ae32d6c62 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/relation_type.py @@ -0,0 +1,22 @@ +from policyengine_uk.model_api import * + + +class RelationType(Enum): + SINGLE = "Single" + COUPLE = "Couple" + + +class relation_type(Variable): + value_type = Enum + entity = BenUnit + default_value = RelationType.SINGLE + possible_values = RelationType + label = "Whether single or a couple" + definition_period = YEAR + + def formula(benunit, period, parameters): + return where( + benunit.sum(benunit.members("is_adult", period)) == 1, + RelationType.SINGLE, + RelationType.COUPLE, + ) diff --git a/policyengine_uk/variables/household/demographic/relations.py b/policyengine_uk/variables/household/demographic/relations.py deleted file mode 100644 index a9f637435..000000000 --- a/policyengine_uk/variables/household/demographic/relations.py +++ /dev/null @@ -1,36 +0,0 @@ -from policyengine_uk.model_api import * - - -class person_benunit_id(Variable): - value_type = float - label = "Person's benefit unit ID" - entity = Person - definition_period = YEAR - - -class person_household_id(Variable): - value_type = float - label = "Person's household ID" - entity = Person - definition_period = YEAR - - -class role(Variable): - value_type = str - label = "Role (adult/child)" - entity = Person - definition_period = YEAR - - -class person_benunit_role(Variable): - value_type = str - label = "Role (adult/child)" - entity = Person - definition_period = YEAR - - -class person_household_role(Variable): - value_type = str - label = "Role (adult/child)" - entity = Person - definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/role.py b/policyengine_uk/variables/household/demographic/role.py new file mode 100644 index 000000000..8a523e247 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/role.py @@ -0,0 +1,8 @@ +from policyengine_uk.model_api import * + + +class role(Variable): + value_type = str + label = "Role (adult/child)" + entity = Person + definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/severe_disability_premium.py b/policyengine_uk/variables/household/demographic/severe_disability_premium.py new file mode 100644 index 000000000..727c57c76 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/severe_disability_premium.py @@ -0,0 +1,22 @@ +from policyengine_uk.model_api import * + + +class severe_disability_premium(Variable): + value_type = float + entity = BenUnit + label = "Severe disability premium" + definition_period = YEAR + reference = "The Social Security Amendment (Enhanced Disability Premium) Regulations 2000" + unit = GBP + + def formula(benunit, period, parameters): + dis = parameters(period).gov.dwp.disability_premia + single = benunit("is_single", period.this_year) + couple = benunit("is_couple", period.this_year) + single_premium = single * dis.severe_single + couple_premium = couple * dis.severe_couple + has_severely_disabled_adults = ( + benunit("num_severely_disabled_adults", period.this_year) > 0 + ) + weekly_amount = single_premium + couple_premium + return weekly_amount * WEEKS_IN_YEAR * has_severely_disabled_adults diff --git a/policyengine_uk/variables/household/demographic/state_id.py b/policyengine_uk/variables/household/demographic/state_id.py new file mode 100644 index 000000000..6871ab3f7 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/state_id.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class state_id(Variable): + label = "State ID" + documentation = "Identity of the state" + entity = State + definition_period = YEAR + value_type = int diff --git a/policyengine_uk/variables/household/demographic/state_weight.py b/policyengine_uk/variables/household/demographic/state_weight.py new file mode 100644 index 000000000..35fd65341 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/state_weight.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class state_weight(Variable): + label = "State weight" + documentation = "Weight value" + entity = State + definition_period = YEAR + value_type = float diff --git a/policyengine_uk/variables/household/demographic/tenure_type.py b/policyengine_uk/variables/household/demographic/tenure_type.py new file mode 100644 index 000000000..1eaca8134 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/tenure_type.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class TenureType(Enum): + RENT_FROM_COUNCIL = "Rented from Council" + RENT_FROM_HA = "Rented from a Housing Association" + RENT_PRIVATELY = "Rented privately" + OWNED_OUTRIGHT = "Owned outright" + OWNED_WITH_MORTGAGE = "Owned with a mortgage" + + +class tenure_type(Variable): + value_type = Enum + possible_values = TenureType + default_value = TenureType.RENT_PRIVATELY + entity = Household + label = "Tenure type of the household" + definition_period = YEAR diff --git a/policyengine_uk/variables/household/demographic/youngest_adult_age.py b/policyengine_uk/variables/household/demographic/youngest_adult_age.py new file mode 100644 index 000000000..0f51550fd --- /dev/null +++ b/policyengine_uk/variables/household/demographic/youngest_adult_age.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class youngest_adult_age(Variable): + value_type = float + entity = BenUnit + label = "Eldest adult age" + definition_period = YEAR + + def formula(benunit, period, parameters): + return benunit.min( + where( + benunit.members("is_adult", period), + benunit.members("age", period), + np.inf, + ) + ) diff --git a/policyengine_uk/variables/household/demographic/youngest_child_age.py b/policyengine_uk/variables/household/demographic/youngest_child_age.py new file mode 100644 index 000000000..32406e7b5 --- /dev/null +++ b/policyengine_uk/variables/household/demographic/youngest_child_age.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class youngest_child_age(Variable): + value_type = float + entity = BenUnit + label = "Eldest adult age" + definition_period = YEAR + + def formula(benunit, period, parameters): + return benunit.min( + where( + benunit.members("is_child", period), + benunit.members("age", period), + np.inf, + ) + ) diff --git a/policyengine_uk/variables/household/income/base_net_income.py b/policyengine_uk/variables/household/income/base_net_income.py new file mode 100644 index 000000000..51ee47226 --- /dev/null +++ b/policyengine_uk/variables/household/income/base_net_income.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class base_net_income(Variable): + value_type = float + entity = Person + label = "Existing net income for the person to use as a base in microsimulation" + definition_period = YEAR + unit = GBP diff --git a/policyengine_uk/variables/household/income/baseline_hbai_excluded_income.py b/policyengine_uk/variables/household/income/baseline_hbai_excluded_income.py new file mode 100644 index 000000000..428212a1d --- /dev/null +++ b/policyengine_uk/variables/household/income/baseline_hbai_excluded_income.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class baseline_hbai_excluded_income(Variable): + label = "HBAI-excluded income (baseline)" + documentation = "Total value of income not included in HBAI household net income in the baseline" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + if not parameters(period).household.poverty.exclude_non_hbai_income: + return 0 + # If baseline policy not viable from the above method, + # no change in HBAI excluded income + return household("hbai_excluded_income", period) diff --git a/policyengine_uk/variables/household/income/benefit.py b/policyengine_uk/variables/household/income/benefit.py deleted file mode 100644 index ceb2b2a0a..000000000 --- a/policyengine_uk/variables/household/income/benefit.py +++ /dev/null @@ -1,236 +0,0 @@ -from policyengine_uk.model_api import * - -""" -This file contains variables that are commonly used in benefit eligibility calculations. -""" - - -class household_benefits(Variable): - value_type = float - entity = Household - label = "household benefits" - documentation = "Total value of benefits received by household" - definition_period = YEAR - unit = GBP - adds = [ - "child_benefit", - "esa_income", - "esa_contrib", - "housing_benefit", - "income_support", - "jsa_income", - "jsa_contrib", - "pension_credit", - "universal_credit", - "working_tax_credit", - "child_tax_credit", - "attendance_allowance", - "afcs", - "bsp", - "carers_allowance", - "dla", - "iidb", - "incapacity_benefit", - "jsa_contrib", - "pip", - "sda", - "state_pension", - "maternity_allowance", - "statutory_sick_pay", - "statutory_maternity_pay", - "ssmg", - "basic_income", - "epg_subsidy", - "cost_of_living_support_payment", - "energy_bills_rebate", - "winter_fuel_allowance", - "tax_free_childcare", - "extended_childcare_entitlement", - "universal_childcare_entitlement", - "targeted_childcare_entitlement", - "care_to_learn", - "nhs_spending", - "dfe_education_spending", - "dft_subsidy_spending", - ] - - def formula(household, period, parameters): - contrib = parameters(period).gov.contrib - uprating = contrib.benefit_uprating - benefits = household_benefits.adds - if contrib.abolish_council_tax: - benefits = [ - benefit - for benefit in benefits - if benefit != "council_tax_benefit" - ] - general_benefits = add( - household, - period, - [ - benefit - for benefit in benefits - if benefit not in ["basic_income"] - ], - ) - non_sp_benefits = add( - household, - period, - [ - benefit - for benefit in benefits - if benefit not in ["state_pension", "basic_income"] - ], - ) - return ( - add(household, period, benefits) - + general_benefits * uprating.all - + non_sp_benefits * uprating.non_sp - ) - - -class is_QYP(Variable): - value_type = bool - entity = Person - label = "Whether this person is a qualifying young person for benefits purposes" - definition_period = YEAR - - def formula(person, period, parameters): - education = person("current_education", period) - under_20 = person("age", period) < 20 - in_education = ~( - education == education.possible_values.NOT_IN_EDUCATION - ) - return under_20 & in_education - - -class is_child_or_QYP(Variable): - value_type = bool - entity = Person - label = "Whether this person is a child or qualifying young person for most benefits" - definition_period = YEAR - - def formula(person, period, parameters): - return (person("age", period) < 16) | person("is_QYP", period) - - -class benefits_premiums(Variable): - value_type = float - entity = BenUnit - label = "Value of premiums for disability and carer status" - definition_period = YEAR - unit = GBP - - adds = [ - "disability_premium", - "enhanced_disability_premium", - "severe_disability_premium", - "carer_premium", - ] - - -class benunit_weekly_hours(Variable): - value_type = float - entity = BenUnit - label = "Average weekly hours worked by adults in the benefit unit" - definition_period = YEAR - unit = "hour" - - adds = ["weekly_hours"] - - -class is_single(Variable): - value_type = bool - entity = BenUnit - label = "Whether this benefit unit contains a single claimant for benefits" - definition_period = YEAR - - def formula(benunit, period, parameters): - relation_type = benunit("relation_type", period) - relations = relation_type.possible_values - return relation_type == relations.SINGLE - - -class is_couple(Variable): - value_type = bool - entity = BenUnit - label = "Whether this benefit unit contains a joint couple claimant for benefits" - definition_period = YEAR - - def formula(benunit, period, parameters): - relation_type = benunit("relation_type", period) - relations = relation_type.possible_values - return relation_type == relations.COUPLE - - -class is_lone_parent(Variable): - value_type = bool - entity = BenUnit - label = "Whether the family is a lone parent family" - definition_period = YEAR - - def formula(benunit, period, parameters): - family_type = benunit("family_type", period) - families = family_type.possible_values - return family_type == families.LONE_PARENT - - -class is_single_person(Variable): - value_type = bool - entity = BenUnit - label = "Whether the family is a single person" - definition_period = YEAR - - def formula(benunit, period, parameters): - family_type = benunit("family_type", period) - families = family_type.possible_values - return family_type == families.SINGLE - - -class claims_all_entitled_benefits(Variable): - value_type = bool - entity = BenUnit - label = "Claims all eligible benefits" - definition_period = YEAR - documentation = ( - "Whether this family would claim any benefit they are entitled to" - ) - - def formula(benunit, period, parameters): - # Return false if we have any reported values in the simulation for benefits. - return ( - add( - benunit, - period, - [ - "child_tax_credit_reported", - "working_tax_credit_reported", - "universal_credit_reported", - "housing_benefit_reported", - "jsa_income_reported", - "income_support_reported", - "esa_income_reported", - ], - ).sum() - < 1 - ) - - -class claims_legacy_benefits(Variable): - value_type = bool - entity = BenUnit - label = "Claims legacy benefits" - documentation = "Whether this family is currently receiving legacy benefits (overrides UC claimant status)" - definition_period = YEAR - - def formula(benunit, period, parameters): - BENEFITS = [ - "child_tax_credit_reported", - "working_tax_credit_reported", - "housing_benefit_reported", - "esa_income_reported", - "income_support_reported", - "jsa_income_reported", - ] - - return add(benunit, period, BENEFITS) > 0 diff --git a/policyengine_uk/variables/household/income/benefits_premiums.py b/policyengine_uk/variables/household/income/benefits_premiums.py new file mode 100644 index 000000000..071f8c4fb --- /dev/null +++ b/policyengine_uk/variables/household/income/benefits_premiums.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class benefits_premiums(Variable): + value_type = float + entity = BenUnit + label = "Value of premiums for disability and carer status" + definition_period = YEAR + unit = GBP + + adds = [ + "disability_premium", + "enhanced_disability_premium", + "severe_disability_premium", + "carer_premium", + ] diff --git a/policyengine_uk/variables/household/income/benunit_weekly_hours.py b/policyengine_uk/variables/household/income/benunit_weekly_hours.py new file mode 100644 index 000000000..140803bd0 --- /dev/null +++ b/policyengine_uk/variables/household/income/benunit_weekly_hours.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class benunit_weekly_hours(Variable): + value_type = float + entity = BenUnit + label = "Average weekly hours worked by adults in the benefit unit" + definition_period = YEAR + unit = "hour" + + adds = ["weekly_hours"] diff --git a/policyengine_uk/variables/household/income/capital_gains.py b/policyengine_uk/variables/household/income/capital_gains.py new file mode 100644 index 000000000..537ae4048 --- /dev/null +++ b/policyengine_uk/variables/household/income/capital_gains.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class capital_gains(Variable): + label = "capital gains" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + uprating = "gov.obr.non_labour_income" + adds = [ + "capital_gains_before_response", + "capital_gains_behavioural_response", + ] diff --git a/policyengine_uk/variables/household/income/capital_income.py b/policyengine_uk/variables/household/income/capital_income.py new file mode 100644 index 000000000..91aa83efb --- /dev/null +++ b/policyengine_uk/variables/household/income/capital_income.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class capital_income(Variable): + value_type = float + entity = Person + label = "Income from savings or dividends" + definition_period = YEAR + unit = GBP + + adds = ["savings_interest_income", "dividend_income"] diff --git a/policyengine_uk/variables/household/income/claims_all_entitled_benefits.py b/policyengine_uk/variables/household/income/claims_all_entitled_benefits.py new file mode 100644 index 000000000..9cc15032e --- /dev/null +++ b/policyengine_uk/variables/household/income/claims_all_entitled_benefits.py @@ -0,0 +1,30 @@ +from policyengine_uk.model_api import * + + +class claims_all_entitled_benefits(Variable): + value_type = bool + entity = BenUnit + label = "Claims all eligible benefits" + definition_period = YEAR + documentation = ( + "Whether this family would claim any benefit they are entitled to" + ) + + def formula(benunit, period, parameters): + # Return false if we have any reported values in the simulation for benefits. + return ( + add( + benunit, + period, + [ + "child_tax_credit_reported", + "working_tax_credit_reported", + "universal_credit_reported", + "housing_benefit_reported", + "jsa_income_reported", + "income_support_reported", + "esa_income_reported", + ], + ).sum() + < 1 + ) diff --git a/policyengine_uk/variables/household/income/claims_legacy_benefits.py b/policyengine_uk/variables/household/income/claims_legacy_benefits.py new file mode 100644 index 000000000..cfb05e360 --- /dev/null +++ b/policyengine_uk/variables/household/income/claims_legacy_benefits.py @@ -0,0 +1,21 @@ +from policyengine_uk.model_api import * + + +class claims_legacy_benefits(Variable): + value_type = bool + entity = BenUnit + label = "Claims legacy benefits" + documentation = "Whether this family is currently receiving legacy benefits (overrides UC claimant status)" + definition_period = YEAR + + def formula(benunit, period, parameters): + BENEFITS = [ + "child_tax_credit_reported", + "working_tax_credit_reported", + "housing_benefit_reported", + "esa_income_reported", + "income_support_reported", + "jsa_income_reported", + ] + + return add(benunit, period, BENEFITS) > 0 diff --git a/policyengine_uk/variables/household/income/deep_poverty_gap.py b/policyengine_uk/variables/household/income/deep_poverty_gap.py new file mode 100644 index 000000000..610bf7793 --- /dev/null +++ b/policyengine_uk/variables/household/income/deep_poverty_gap.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class deep_poverty_gap(Variable): + label = "deep poverty gap" + documentation = "The financial gap between net household income and the deep poverty line (before housing costs)." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + income = household("hbai_household_net_income", period) + line = household("deep_poverty_line", period) + return max_(0, line - income) diff --git a/policyengine_uk/variables/household/income/deep_poverty_line.py b/policyengine_uk/variables/household/income/deep_poverty_line.py new file mode 100644 index 000000000..34960373a --- /dev/null +++ b/policyengine_uk/variables/household/income/deep_poverty_line.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class deep_poverty_line(Variable): + label = "deep poverty line" + documentation = "The line below which a household is in deep poverty." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + return household("poverty_line", period) / 2 diff --git a/policyengine_uk/variables/household/income/earned_income.py b/policyengine_uk/variables/household/income/earned_income.py new file mode 100644 index 000000000..72a066f8f --- /dev/null +++ b/policyengine_uk/variables/household/income/earned_income.py @@ -0,0 +1,19 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class earned_income(Variable): + value_type = float + entity = Person + label = "Total earned income" + definition_period = YEAR + unit = GBP + + def formula(person, period, parameters): + COMPONENTS = [ + "employment_income", + "self_employment_income", + "private_pension_income", + ] + return add(person, period, COMPONENTS) diff --git a/policyengine_uk/variables/household/income/employment_status.py b/policyengine_uk/variables/household/income/employment_status.py new file mode 100644 index 000000000..2b3d06941 --- /dev/null +++ b/policyengine_uk/variables/household/income/employment_status.py @@ -0,0 +1,27 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class EmploymentStatus(Enum): + FT_EMPLOYED = "Full-time employed" + PT_EMPLOYED = "Part-time employed" + FT_SELF_EMPLOYED = "Full-time self-employed" + PT_SELF_EMPLOYED = "Part-time self-employed" + UNEMPLOYED = "Unemployed" + RETIRED = "Retired" + STUDENT = "Student" + CARER = "Carer" + LONG_TERM_DISABLED = "Long-term sick/disabled" + SHORT_TERM_DISABLED = "Short-term sick/disabled" + OTHER_INACTIVE = "Other inactive" + CHILD = "Child" + + +class employment_status(Variable): + value_type = Enum + entity = Person + possible_values = EmploymentStatus + default_value = EmploymentStatus.UNEMPLOYED + label = "Employment status of the person" + definition_period = YEAR diff --git a/policyengine_uk/variables/household/income/equiv_hbai_household_net_income.py b/policyengine_uk/variables/household/income/equiv_hbai_household_net_income.py new file mode 100644 index 000000000..e3f6253bd --- /dev/null +++ b/policyengine_uk/variables/household/income/equiv_hbai_household_net_income.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class equiv_hbai_household_net_income(Variable): + value_type = float + entity = Household + label = "Equivalised household net income (HBAI)" + definition_period = YEAR + unit = GBP + + def formula(household, period, parameters): + income = household("hbai_household_net_income", period) + equivalisation = household("household_equivalisation_bhc", period) + return income / equivalisation diff --git a/policyengine_uk/variables/household/income/equiv_hbai_household_net_income_ahc.py b/policyengine_uk/variables/household/income/equiv_hbai_household_net_income_ahc.py new file mode 100644 index 000000000..ddb760ebe --- /dev/null +++ b/policyengine_uk/variables/household/income/equiv_hbai_household_net_income_ahc.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class equiv_hbai_household_net_income_ahc(Variable): + value_type = float + entity = Household + label = "Equivalised household net income, after housing costs (HBAI)" + definition_period = YEAR + unit = GBP + + def formula(household, period, parameters): + income = household("hbai_household_net_income_ahc", period) + equivalisation = household("household_equivalisation_ahc", period) + return income / equivalisation diff --git a/policyengine_uk/variables/household/income/equiv_household_net_income.py b/policyengine_uk/variables/household/income/equiv_household_net_income.py new file mode 100644 index 000000000..35c75b016 --- /dev/null +++ b/policyengine_uk/variables/household/income/equiv_household_net_income.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class equiv_household_net_income(Variable): + value_type = float + entity = Household + label = "Equivalised household net income" + definition_period = YEAR + unit = GBP + + def formula(household, period, parameters): + income = household("household_net_income", period) + equivalisation = household("household_equivalisation_bhc", period) + return income / equivalisation diff --git a/policyengine_uk/variables/household/income/hbai_excluded_income.py b/policyengine_uk/variables/household/income/hbai_excluded_income.py new file mode 100644 index 000000000..5fb23d87c --- /dev/null +++ b/policyengine_uk/variables/household/income/hbai_excluded_income.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class hbai_excluded_income(Variable): + label = "HBAI-excluded income" + documentation = ( + "Total value of income not included in HBAI household net income" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + VARIABLES = [ + "corporate_tax_incidence", + ] + return -add(household, period, VARIABLES) diff --git a/policyengine_uk/variables/household/income/hbai_excluded_income_change.py b/policyengine_uk/variables/household/income/hbai_excluded_income_change.py new file mode 100644 index 000000000..d241d967a --- /dev/null +++ b/policyengine_uk/variables/household/income/hbai_excluded_income_change.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class hbai_excluded_income_change(Variable): + label = "Change in HBAI-excluded income" + documentation = "Effect of policy reforms on HBAI-excluded income" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + hbai_excluded_income = household("hbai_excluded_income", period) + baseline_hbai_excluded_income = household( + "baseline_hbai_excluded_income", period + ) + return hbai_excluded_income - baseline_hbai_excluded_income diff --git a/policyengine_uk/variables/household/income/hbai_household_net_income.py b/policyengine_uk/variables/household/income/hbai_household_net_income.py new file mode 100644 index 000000000..5ddb728b5 --- /dev/null +++ b/policyengine_uk/variables/household/income/hbai_household_net_income.py @@ -0,0 +1,54 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class hbai_household_net_income(Variable): + value_type = float + entity = Household + label = "Household net income (HBAI definition)" + documentation = "Disposable income for the household, following the definition used for official poverty statistics" + unit = GBP + definition_period = YEAR + + adds = [ + "household_market_income", + "child_benefit", + "esa_income", + "esa_contrib", + "housing_benefit", + "income_support", + "jsa_income", + "jsa_contrib", + "pension_credit", + "universal_credit", + "working_tax_credit", + "child_tax_credit", + "attendance_allowance", + "afcs", + "bsp", + "carers_allowance", + "dla", + "iidb", + "incapacity_benefit", + "jsa_contrib", + "pip", + "sda", + "state_pension", + "maternity_allowance", + "statutory_sick_pay", + "statutory_maternity_pay", + "ssmg", + "basic_income", + "cost_of_living_support_payment", + "winter_fuel_allowance", + "tax_free_childcare", + # Reference for tax-free-childcare: https://assets.publishing.service.gov.uk/media/5e7b191886650c744175d08b/households-below-average-income-1994-1995-2018-2019.pdf + ] + subtracts = [ + "council_tax", + "domestic_rates", + "wealth_tax", + "income_tax", + "national_insurance", + ] diff --git a/policyengine_uk/variables/household/income/hbai_household_net_income_ahc.py b/policyengine_uk/variables/household/income/hbai_household_net_income_ahc.py new file mode 100644 index 000000000..0e59918d4 --- /dev/null +++ b/policyengine_uk/variables/household/income/hbai_household_net_income_ahc.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class hbai_household_net_income_ahc(Variable): + value_type = float + entity = Household + label = "Household net income, after housing costs" + definition_period = YEAR + unit = GBP + + adds = ["hbai_household_net_income"] + subtracts = ["housing_costs"] diff --git a/policyengine_uk/variables/household/income/hours_worked.py b/policyengine_uk/variables/household/income/hours_worked.py new file mode 100644 index 000000000..403447d8f --- /dev/null +++ b/policyengine_uk/variables/household/income/hours_worked.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class hours_worked(Variable): + value_type = float + entity = Person + label = "Total amount of hours worked by this person" + definition_period = YEAR + unit = "hour" diff --git a/policyengine_uk/variables/household/income/household_benefits.py b/policyengine_uk/variables/household/income/household_benefits.py new file mode 100644 index 000000000..5240eda15 --- /dev/null +++ b/policyengine_uk/variables/household/income/household_benefits.py @@ -0,0 +1,85 @@ +from policyengine_uk.model_api import * + + +class household_benefits(Variable): + value_type = float + entity = Household + label = "household benefits" + documentation = "Total value of benefits received by household" + definition_period = YEAR + unit = GBP + adds = [ + "child_benefit", + "esa_income", + "esa_contrib", + "housing_benefit", + "income_support", + "jsa_income", + "jsa_contrib", + "pension_credit", + "universal_credit", + "working_tax_credit", + "child_tax_credit", + "attendance_allowance", + "afcs", + "bsp", + "carers_allowance", + "dla", + "iidb", + "incapacity_benefit", + "jsa_contrib", + "pip", + "sda", + "state_pension", + "maternity_allowance", + "statutory_sick_pay", + "statutory_maternity_pay", + "ssmg", + "basic_income", + "epg_subsidy", + "cost_of_living_support_payment", + "energy_bills_rebate", + "winter_fuel_allowance", + "tax_free_childcare", + "extended_childcare_entitlement", + "universal_childcare_entitlement", + "targeted_childcare_entitlement", + "care_to_learn", + "nhs_spending", + "dfe_education_spending", + "dft_subsidy_spending", + ] + + def formula(household, period, parameters): + contrib = parameters(period).gov.contrib + uprating = contrib.benefit_uprating + benefits = household_benefits.adds + if contrib.abolish_council_tax: + benefits = [ + benefit + for benefit in benefits + if benefit != "council_tax_benefit" + ] + general_benefits = add( + household, + period, + [ + benefit + for benefit in benefits + if benefit not in ["basic_income"] + ], + ) + non_sp_benefits = add( + household, + period, + [ + benefit + for benefit in benefits + if benefit not in ["state_pension", "basic_income"] + ], + ) + return ( + add(household, period, benefits) + + general_benefits * uprating.all + + non_sp_benefits * uprating.non_sp + ) diff --git a/policyengine_uk/variables/household/income/household_gross_income.py b/policyengine_uk/variables/household/income/household_gross_income.py new file mode 100644 index 000000000..ce42907a7 --- /dev/null +++ b/policyengine_uk/variables/household/income/household_gross_income.py @@ -0,0 +1,19 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class household_gross_income(Variable): + value_type = float + entity = Household + unit = GBP + label = "Household gross income" + definition_period = YEAR + unit = GBP + + def formula(household, period, parameters): + return add( + household, + period, + ["household_market_income", "household_benefits"], + ) diff --git a/policyengine_uk/variables/household/income/household_income_decile.py b/policyengine_uk/variables/household/income/household_income_decile.py new file mode 100644 index 000000000..785b1068a --- /dev/null +++ b/policyengine_uk/variables/household/income/household_income_decile.py @@ -0,0 +1,24 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class household_income_decile(Variable): + label = "household income decile" + documentation = "Decile of household income (person-weighted)" + entity = Household + definition_period = YEAR + value_type = int + + def formula(household, period, parameters): + income = household("equiv_hbai_household_net_income", period) + count_people = household("household_count_people", period) + household_weight = household("household_weight", period) + weighted_income = MicroSeries( + income, weights=household_weight * count_people + ) + decile = weighted_income.decile_rank().values + # Set negatives to -1. + # This avoids the bottom decile summing to a negative number, + # which would flip the % change in the interface. + return where(income < 0, -1, decile) diff --git a/policyengine_uk/variables/household/income/household_market_income.py b/policyengine_uk/variables/household/income/household_market_income.py new file mode 100644 index 000000000..9c5ad7a52 --- /dev/null +++ b/policyengine_uk/variables/household/income/household_market_income.py @@ -0,0 +1,31 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class household_market_income(Variable): + value_type = float + entity = Household + label = "household market income" + documentation = "Market income for the household" + definition_period = YEAR + unit = GBP + adds = [ + "employment_income", + "self_employment_income", + "savings_interest_income", + "dividend_income", + "miscellaneous_income", + "property_income", + "private_pension_income", + "private_transfer_income", + "maintenance_income", + "capital_gains", + ] + + def formula(person, period, parameters): + total = add(person, period, household_market_income.adds) + contrib = parameters( + period + ).gov.contrib.policyengine.economy.gdp_per_capita + return total * (contrib + 1) diff --git a/policyengine_uk/variables/household/income/household_net_income.py b/policyengine_uk/variables/household/income/household_net_income.py new file mode 100644 index 000000000..6b9923709 --- /dev/null +++ b/policyengine_uk/variables/household/income/household_net_income.py @@ -0,0 +1,25 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class household_net_income(Variable): + label = "household net income" + documentation = "household net income" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + adds = [ + "household_market_income", + "household_benefits", + ] + subtracts = [ + "household_tax", + ] + + def formula(household, period, parameters): + market_income = household("household_market_income", period) + benefits = household("household_benefits", period) + tax = household("household_tax", period) + return np.round(market_income + benefits - tax) diff --git a/policyengine_uk/variables/household/income/household_net_income_ahc.py b/policyengine_uk/variables/household/income/household_net_income_ahc.py new file mode 100644 index 000000000..6a9ee0cc2 --- /dev/null +++ b/policyengine_uk/variables/household/income/household_net_income_ahc.py @@ -0,0 +1,27 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class household_net_income_ahc(Variable): + label = "household net income" + documentation = "household net income" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + adds = [ + "household_market_income", + "household_benefits", + ] + subtracts = [ + "household_tax", + "housing_costs", + ] + + def formula(household, period, parameters): + market_income = household("household_market_income", period) + benefits = household("household_benefits", period) + tax = household("household_tax", period) + housing_costs = household("housing_costs", period) + return np.round(market_income + benefits - tax - housing_costs) diff --git a/policyengine_uk/variables/household/income/household_statutory_maternity_pay.py b/policyengine_uk/variables/household/income/household_statutory_maternity_pay.py new file mode 100644 index 000000000..8d921423d --- /dev/null +++ b/policyengine_uk/variables/household/income/household_statutory_maternity_pay.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class household_statutory_maternity_pay(Variable): + label = "Statutory maternity pay" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP diff --git a/policyengine_uk/variables/household/income/household_statutory_paternity_pay.py b/policyengine_uk/variables/household/income/household_statutory_paternity_pay.py new file mode 100644 index 000000000..bb26e6d87 --- /dev/null +++ b/policyengine_uk/variables/household/income/household_statutory_paternity_pay.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class household_statutory_paternity_pay(Variable): + label = "Statutory paternity pay" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP diff --git a/policyengine_uk/variables/household/income/household_statutory_sick_pay.py b/policyengine_uk/variables/household/income/household_statutory_sick_pay.py new file mode 100644 index 000000000..b822ccd8e --- /dev/null +++ b/policyengine_uk/variables/household/income/household_statutory_sick_pay.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class household_statutory_sick_pay(Variable): + label = "Statutory sick pay" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP diff --git a/policyengine_uk/variables/household/income/in_deep_poverty.py b/policyengine_uk/variables/household/income/in_deep_poverty.py new file mode 100644 index 000000000..5ec879173 --- /dev/null +++ b/policyengine_uk/variables/household/income/in_deep_poverty.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class in_deep_poverty(Variable): + label = "in deep poverty" + documentation = "Whether the household is in deep absolute poverty (below half the poverty line, before housing costs)." + entity = Household + definition_period = YEAR + value_type = bool + + def formula(household, period, parameters): + return household("deep_poverty_gap", period) > 0 diff --git a/policyengine_uk/variables/household/income/in_deep_poverty_ahc.py b/policyengine_uk/variables/household/income/in_deep_poverty_ahc.py new file mode 100644 index 000000000..feb370179 --- /dev/null +++ b/policyengine_uk/variables/household/income/in_deep_poverty_ahc.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class in_deep_poverty_ahc(Variable): + value_type = bool + entity = Household + label = "Whether the household is in deep absolute poverty (below half the poverty line), after housing costs" + definition_period = YEAR + + def formula(household, period, parameters): + income = household("equiv_hbai_household_net_income_ahc", period) + threshold = parameters( + period + ).household.poverty.absolute_poverty_threshold_ahc + return income < (threshold * WEEKS_IN_YEAR / 2) diff --git a/policyengine_uk/variables/household/income/in_deep_poverty_bhc.py b/policyengine_uk/variables/household/income/in_deep_poverty_bhc.py new file mode 100644 index 000000000..abab678f6 --- /dev/null +++ b/policyengine_uk/variables/household/income/in_deep_poverty_bhc.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class in_deep_poverty_bhc(Variable): + value_type = bool + entity = Household + label = "Whether the household is in deep absolute poverty (below half the poverty line), before housing costs" + definition_period = YEAR + + def formula(household, period, parameters): + income = household("equiv_hbai_household_net_income", period) + threshold = parameters( + period + ).household.poverty.absolute_poverty_threshold_bhc + return income < (threshold * WEEKS_IN_YEAR / 2) diff --git a/policyengine_uk/variables/household/income/in_poverty.py b/policyengine_uk/variables/household/income/in_poverty.py new file mode 100644 index 000000000..51796b7af --- /dev/null +++ b/policyengine_uk/variables/household/income/in_poverty.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + + +class in_poverty(Variable): + label = "in poverty" + documentation = ( + "Whether the household is in absolute poverty (before housing costs)." + ) + entity = Household + definition_period = YEAR + value_type = bool + + def formula(household, period, parameters): + return household("poverty_gap", period) > 0 diff --git a/policyengine_uk/variables/household/income/in_poverty_ahc.py b/policyengine_uk/variables/household/income/in_poverty_ahc.py new file mode 100644 index 000000000..ac71c6d7c --- /dev/null +++ b/policyengine_uk/variables/household/income/in_poverty_ahc.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class in_poverty_ahc(Variable): + value_type = bool + entity = Household + label = "Whether the household is in absolute poverty, after housing costs" + definition_period = YEAR + + def formula(household, period, parameters): + income = household("equiv_hbai_household_net_income_ahc", period) + threshold = parameters( + period + ).household.poverty.absolute_poverty_threshold_ahc + return income < (threshold * WEEKS_IN_YEAR) diff --git a/policyengine_uk/variables/household/income/in_poverty_bhc.py b/policyengine_uk/variables/household/income/in_poverty_bhc.py new file mode 100644 index 000000000..68c9a4146 --- /dev/null +++ b/policyengine_uk/variables/household/income/in_poverty_bhc.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + + +class in_poverty_bhc(Variable): + value_type = bool + entity = Household + label = ( + "Whether the household is in absolute poverty, before housing costs" + ) + definition_period = YEAR + + def formula(household, period, parameters): + income = household("equiv_hbai_household_net_income", period) + return income < household("poverty_threshold_bhc", period) diff --git a/policyengine_uk/variables/household/income/in_relative_poverty_ahc.py b/policyengine_uk/variables/household/income/in_relative_poverty_ahc.py new file mode 100644 index 000000000..31b436254 --- /dev/null +++ b/policyengine_uk/variables/household/income/in_relative_poverty_ahc.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class in_relative_poverty_ahc(Variable): + label = "in relative poverty (AHC)" + entity = Household + definition_period = YEAR + value_type = bool + + def formula(household, period, parameters): + income = household("equiv_hbai_household_net_income_ahc", period) + # Less than 60% of median income + median_income = MicroSeries( + income, weights=household("household_weight", period) + ).median() + return income < (median_income * 0.6) diff --git a/policyengine_uk/variables/household/income/in_relative_poverty_bhc.py b/policyengine_uk/variables/household/income/in_relative_poverty_bhc.py new file mode 100644 index 000000000..063703bd2 --- /dev/null +++ b/policyengine_uk/variables/household/income/in_relative_poverty_bhc.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class in_relative_poverty_bhc(Variable): + label = "in relative poverty (AHC)" + entity = Household + definition_period = YEAR + value_type = bool + + def formula(household, period, parameters): + income = household("equiv_hbai_household_net_income", period) + # Less than 60% of median income + median_income = MicroSeries( + income, weights=household("household_weight", period) + ).median() + return income < (median_income * 0.6) diff --git a/policyengine_uk/variables/household/income/in_work.py b/policyengine_uk/variables/household/income/in_work.py new file mode 100644 index 000000000..d6abb86bc --- /dev/null +++ b/policyengine_uk/variables/household/income/in_work.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class in_work(Variable): + value_type = bool + entity = Person + label = "Worked some hours" + definition_period = YEAR + + def formula(person, period, parameters): + has_hours_worked = person("hours_worked", period) > 0 + earnings = add( + person, period, ["employment_income", "self_employment_income"] + ) + has_earnings = earnings > 0 + return has_hours_worked | has_earnings diff --git a/policyengine_uk/variables/household/income/income.py b/policyengine_uk/variables/household/income/income.py deleted file mode 100644 index c1b496d07..000000000 --- a/policyengine_uk/variables/household/income/income.py +++ /dev/null @@ -1,483 +0,0 @@ -from policyengine_uk.model_api import * -import datetime -import numpy as np - - -class earned_income(Variable): - value_type = float - entity = Person - label = "Total earned income" - definition_period = YEAR - unit = GBP - - def formula(person, period, parameters): - COMPONENTS = [ - "employment_income", - "self_employment_income", - "private_pension_income", - ] - return add(person, period, COMPONENTS) - - -class market_income(Variable): - value_type = float - entity = Person - label = "Market income" - documentation = "Income from market sources" - definition_period = YEAR - unit = GBP - - def formula(person, period, parameters): - INCOME_VARIABLES = [ - "employment_income", - "self_employment_income", - "savings_interest_income", - "dividend_income", - "miscellaneous_income", - "property_income", - "private_pension_income", - "private_transfer_income", - "maintenance_income", - ] - income = add(person, period, INCOME_VARIABLES) - return income - person("maintenance_expenses", period) - - -class household_gross_income(Variable): - value_type = float - entity = Household - unit = GBP - label = "Household gross income" - definition_period = YEAR - unit = GBP - - def formula(household, period, parameters): - return add( - household, - period, - ["household_market_income", "household_benefits"], - ) - - -class hours_worked(Variable): - value_type = float - entity = Person - label = "Total amount of hours worked by this person" - definition_period = YEAR - unit = "hour" - - -class in_work(Variable): - value_type = bool - entity = Person - label = "Worked some hours" - definition_period = YEAR - - def formula(person, period, parameters): - has_hours_worked = person("hours_worked", period) > 0 - earnings = add( - person, period, ["employment_income", "self_employment_income"] - ) - has_earnings = earnings > 0 - return has_hours_worked | has_earnings - - -class weekly_hours(Variable): - value_type = float - entity = Person - label = "Weekly hours" - documentation = "Average weekly hours worked" - definition_period = YEAR - unit = "hour" - quantity_type = FLOW - - def formula(person, period, parameters): - return person("hours_worked", period) / WEEKS_IN_YEAR - - -class EmploymentStatus(Enum): - FT_EMPLOYED = "Full-time employed" - PT_EMPLOYED = "Part-time employed" - FT_SELF_EMPLOYED = "Full-time self-employed" - PT_SELF_EMPLOYED = "Part-time self-employed" - UNEMPLOYED = "Unemployed" - RETIRED = "Retired" - STUDENT = "Student" - CARER = "Carer" - LONG_TERM_DISABLED = "Long-term sick/disabled" - SHORT_TERM_DISABLED = "Short-term sick/disabled" - OTHER_INACTIVE = "Inactive for another reason" - CHILD = "Child" - - -class employment_status(Variable): - value_type = Enum - entity = Person - possible_values = EmploymentStatus - default_value = EmploymentStatus.UNEMPLOYED - label = "Employment status of the person" - definition_period = YEAR - - -class capital_income(Variable): - value_type = float - entity = Person - label = "Income from savings or dividends" - definition_period = YEAR - unit = GBP - - adds = ["savings_interest_income", "dividend_income"] - - -class hbai_household_net_income(Variable): - value_type = float - entity = Household - label = "Household net income (HBAI definition)" - documentation = "Disposable income for the household, following the definition used for official poverty statistics" - unit = GBP - definition_period = YEAR - - adds = [ - "household_market_income", - "child_benefit", - "esa_income", - "esa_contrib", - "housing_benefit", - "income_support", - "jsa_income", - "jsa_contrib", - "pension_credit", - "universal_credit", - "working_tax_credit", - "child_tax_credit", - "attendance_allowance", - "afcs", - "bsp", - "carers_allowance", - "dla", - "iidb", - "incapacity_benefit", - "jsa_contrib", - "pip", - "sda", - "state_pension", - "maternity_allowance", - "statutory_sick_pay", - "statutory_maternity_pay", - "ssmg", - "basic_income", - "cost_of_living_support_payment", - "winter_fuel_allowance", - "tax_free_childcare", - # Reference for tax-free-childcare: https://assets.publishing.service.gov.uk/media/5e7b191886650c744175d08b/households-below-average-income-1994-1995-2018-2019.pdf - ] - subtracts = [ - "council_tax", - "domestic_rates", - "wealth_tax", - "income_tax", - "national_insurance", - ] - - -class household_net_income(Variable): - label = "household net income" - documentation = "household net income" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - adds = [ - "household_market_income", - "household_benefits", - ] - subtracts = [ - "household_tax", - ] - - def formula(household, period, parameters): - market_income = household("household_market_income", period) - benefits = household("household_benefits", period) - tax = household("household_tax", period) - return np.round(market_income + benefits - tax) - - -class household_net_income_ahc(Variable): - label = "household net income" - documentation = "household net income" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - adds = [ - "household_market_income", - "household_benefits", - ] - subtracts = [ - "household_tax", - "housing_costs", - ] - - def formula(household, period, parameters): - market_income = household("household_market_income", period) - benefits = household("household_benefits", period) - tax = household("household_tax", period) - housing_costs = household("housing_costs", period) - return np.round(market_income + benefits - tax - housing_costs) - - -class inflation_adjustment(Variable): - label = ( - f"inflation multiplier to get {datetime.datetime.now().year} prices" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = "/1" - - def formula(household, period, parameters): - cpi = parameters.gov.obr.consumer_price_index - current_period_cpi = cpi(period) - now_cpi = cpi(datetime.datetime.now().strftime("%Y-01-01")) - return now_cpi / current_period_cpi - - -class real_household_net_income(Variable): - label = ( - f"real household net income ({datetime.datetime.now().year} prices)" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - net_income = household("household_net_income", period) - return net_income * household("inflation_adjustment", period) - - -class real_household_net_income_ahc(Variable): - label = ( - f"real household net income ({datetime.datetime.now().year} prices)" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - net_income = household("household_net_income_ahc", period) - return net_income * household("inflation_adjustment", period) - - -class hbai_household_net_income_ahc(Variable): - value_type = float - entity = Household - label = "Household net income, after housing costs" - definition_period = YEAR - unit = GBP - - adds = ["hbai_household_net_income"] - subtracts = ["housing_costs"] - - -class equiv_household_net_income(Variable): - value_type = float - entity = Household - label = "Equivalised household net income" - definition_period = YEAR - unit = GBP - - def formula(household, period, parameters): - income = household("household_net_income", period) - equivalisation = household("household_equivalisation_bhc", period) - return income / equivalisation - - -class equiv_hbai_household_net_income(Variable): - value_type = float - entity = Household - label = "Equivalised household net income (HBAI)" - definition_period = YEAR - unit = GBP - - def formula(household, period, parameters): - income = household("hbai_household_net_income", period) - equivalisation = household("household_equivalisation_bhc", period) - return income / equivalisation - - -class equiv_hbai_household_net_income_ahc(Variable): - value_type = float - entity = Household - label = "Equivalised household net income, after housing costs (HBAI)" - definition_period = YEAR - unit = GBP - - def formula(household, period, parameters): - income = household("hbai_household_net_income_ahc", period) - equivalisation = household("household_equivalisation_ahc", period) - return income / equivalisation - - -class base_net_income(Variable): - value_type = float - entity = Person - label = "Existing net income for the person to use as a base in microsimulation" - definition_period = YEAR - unit = GBP - - -class is_apprentice(Variable): - value_type = bool - entity = Person - label = "In an apprenticeship programme" - definition_period = YEAR - default_value = False - - -class MinimumWageCategory(Enum): - APPRENTICE = "Apprentice" - UNDER_18 = "Under 18" - BETWEEN_18_20 = "18 to 20" - BETWEEN_21_22 = "21 to 22" - BETWEEN_23_24 = "23 to 24" - OVER_24 = "25 or over" - - -class minimum_wage_category(Variable): - value_type = Enum - possible_values = MinimumWageCategory - default_value = MinimumWageCategory.OVER_24 - entity = Person - label = "Minimum wage category" - definition_period = YEAR - - def formula(person, period, parameters): - age = person("age", period) - return select( - [ - person("is_apprentice", period), - age < 18, - (age >= 18) & (age <= 20), - (age >= 21) & (age <= 22), - (age >= 23) & (age <= 24), - ], - [ - MinimumWageCategory.APPRENTICE, - MinimumWageCategory.UNDER_18, - MinimumWageCategory.BETWEEN_18_20, - MinimumWageCategory.BETWEEN_21_22, - MinimumWageCategory.BETWEEN_23_24, - ], - default=MinimumWageCategory.OVER_24, - ) - - -class minimum_wage(Variable): - value_type = float - entity = Person - label = "Minimum wage" - definition_period = YEAR - unit = GBP - - def formula(person, period, parameters): - MW = parameters(period).gov.hmrc.minimum_wage - return MW[person("minimum_wage_category", period)] - - -class household_market_income(Variable): - value_type = float - entity = Household - label = "household market income" - documentation = "Market income for the household" - definition_period = YEAR - unit = GBP - adds = [ - "employment_income", - "self_employment_income", - "savings_interest_income", - "dividend_income", - "miscellaneous_income", - "property_income", - "private_pension_income", - "private_transfer_income", - "maintenance_income", - "capital_gains", - ] - - def formula(person, period, parameters): - total = add(person, period, household_market_income.adds) - contrib = parameters( - period - ).gov.contrib.policyengine.economy.gdp_per_capita - return total * (contrib + 1) - - -class household_income_decile(Variable): - label = "household income decile" - documentation = "Decile of household income (person-weighted)" - entity = Household - definition_period = YEAR - value_type = int - - def formula(household, period, parameters): - income = household("equiv_hbai_household_net_income", period) - count_people = household("household_count_people", period) - household_weight = household("household_weight", period) - weighted_income = MicroSeries( - income, weights=household_weight * count_people - ) - decile = weighted_income.decile_rank().values - # Set negatives to -1. - # This avoids the bottom decile summing to a negative number, - # which would flip the % change in the interface. - return where(income < 0, -1, decile) - - -class income_decile(Variable): - label = "income decile" - documentation = "Decile of household net income. Households are sorted by disposable income, and then divided into 10 equally-populated groups." - entity = Person - definition_period = YEAR - value_type = int - - def formula(person, period, parameters): - return person.household("household_income_decile", period) - - -class household_statutory_maternity_pay(Variable): - label = "Statutory maternity pay" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - -class household_statutory_paternity_pay(Variable): - label = "Statutory paternity pay" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - -class household_statutory_sick_pay(Variable): - label = "Statutory sick pay" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - - -class capital_gains(Variable): - label = "capital gains" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - uprating = "gov.obr.non_labour_income" - adds = [ - "capital_gains_before_response", - "capital_gains_behavioural_response", - ] diff --git a/policyengine_uk/variables/household/income/income_decile.py b/policyengine_uk/variables/household/income/income_decile.py new file mode 100644 index 000000000..87f3f6194 --- /dev/null +++ b/policyengine_uk/variables/household/income/income_decile.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class income_decile(Variable): + label = "income decile" + documentation = "Decile of household net income. Households are sorted by disposable income, and then divided into 10 equally-populated groups." + entity = Person + definition_period = YEAR + value_type = int + + def formula(person, period, parameters): + return person.household("household_income_decile", period) diff --git a/policyengine_uk/variables/household/income/inflation_adjustment.py b/policyengine_uk/variables/household/income/inflation_adjustment.py new file mode 100644 index 000000000..c387e807e --- /dev/null +++ b/policyengine_uk/variables/household/income/inflation_adjustment.py @@ -0,0 +1,19 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class inflation_adjustment(Variable): + label = ( + f"inflation multiplier to get {datetime.datetime.now().year} prices" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = "/1" + + def formula(household, period, parameters): + cpi = parameters.gov.obr.consumer_price_index + current_period_cpi = cpi(period) + now_cpi = cpi(datetime.datetime.now().strftime("%Y-01-01")) + return now_cpi / current_period_cpi diff --git a/policyengine_uk/variables/household/income/is_QYP.py b/policyengine_uk/variables/household/income/is_QYP.py new file mode 100644 index 000000000..90cbe847b --- /dev/null +++ b/policyengine_uk/variables/household/income/is_QYP.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + + +class is_QYP(Variable): + value_type = bool + entity = Person + label = "Whether this person is a qualifying young person for benefits purposes" + definition_period = YEAR + + def formula(person, period, parameters): + education = person("current_education", period) + under_20 = person("age", period) < 20 + in_education = ~( + education == education.possible_values.NOT_IN_EDUCATION + ) + return under_20 & in_education diff --git a/policyengine_uk/variables/household/income/is_apprentice.py b/policyengine_uk/variables/household/income/is_apprentice.py new file mode 100644 index 000000000..abf53d985 --- /dev/null +++ b/policyengine_uk/variables/household/income/is_apprentice.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class is_apprentice(Variable): + value_type = bool + entity = Person + label = "In an apprenticeship programme" + definition_period = YEAR + default_value = False diff --git a/policyengine_uk/variables/household/income/is_child_or_QYP.py b/policyengine_uk/variables/household/income/is_child_or_QYP.py new file mode 100644 index 000000000..ef43be15a --- /dev/null +++ b/policyengine_uk/variables/household/income/is_child_or_QYP.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class is_child_or_QYP(Variable): + value_type = bool + entity = Person + label = "Whether this person is a child or qualifying young person for most benefits" + definition_period = YEAR + + def formula(person, period, parameters): + return (person("age", period) < 16) | person("is_QYP", period) diff --git a/policyengine_uk/variables/household/income/is_couple.py b/policyengine_uk/variables/household/income/is_couple.py new file mode 100644 index 000000000..236a1da10 --- /dev/null +++ b/policyengine_uk/variables/household/income/is_couple.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class is_couple(Variable): + value_type = bool + entity = BenUnit + label = "Whether this benefit unit contains a joint couple claimant for benefits" + definition_period = YEAR + + def formula(benunit, period, parameters): + relation_type = benunit("relation_type", period) + relations = relation_type.possible_values + return relation_type == relations.COUPLE diff --git a/policyengine_uk/variables/household/income/is_lone_parent.py b/policyengine_uk/variables/household/income/is_lone_parent.py new file mode 100644 index 000000000..07ffdcb0b --- /dev/null +++ b/policyengine_uk/variables/household/income/is_lone_parent.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class is_lone_parent(Variable): + value_type = bool + entity = BenUnit + label = "Whether the family is a lone parent family" + definition_period = YEAR + + def formula(benunit, period, parameters): + family_type = benunit("family_type", period) + families = family_type.possible_values + return family_type == families.LONE_PARENT diff --git a/policyengine_uk/variables/household/income/is_single.py b/policyengine_uk/variables/household/income/is_single.py new file mode 100644 index 000000000..b226945fe --- /dev/null +++ b/policyengine_uk/variables/household/income/is_single.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class is_single(Variable): + value_type = bool + entity = BenUnit + label = "Whether this benefit unit contains a single claimant for benefits" + definition_period = YEAR + + def formula(benunit, period, parameters): + relation_type = benunit("relation_type", period) + relations = relation_type.possible_values + return relation_type == relations.SINGLE diff --git a/policyengine_uk/variables/household/income/is_single_person.py b/policyengine_uk/variables/household/income/is_single_person.py new file mode 100644 index 000000000..b8da33b4e --- /dev/null +++ b/policyengine_uk/variables/household/income/is_single_person.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class is_single_person(Variable): + value_type = bool + entity = BenUnit + label = "Whether the family is a single person" + definition_period = YEAR + + def formula(benunit, period, parameters): + family_type = benunit("family_type", period) + families = family_type.possible_values + return family_type == families.SINGLE diff --git a/policyengine_uk/variables/household/income/market_income.py b/policyengine_uk/variables/household/income/market_income.py new file mode 100644 index 000000000..9df8de09c --- /dev/null +++ b/policyengine_uk/variables/household/income/market_income.py @@ -0,0 +1,27 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class market_income(Variable): + value_type = float + entity = Person + label = "Market income" + documentation = "Income from market sources" + definition_period = YEAR + unit = GBP + + def formula(person, period, parameters): + INCOME_VARIABLES = [ + "employment_income", + "self_employment_income", + "savings_interest_income", + "dividend_income", + "miscellaneous_income", + "property_income", + "private_pension_income", + "private_transfer_income", + "maintenance_income", + ] + income = add(person, period, INCOME_VARIABLES) + return income - person("maintenance_expenses", period) diff --git a/policyengine_uk/variables/household/income/minimum_wage.py b/policyengine_uk/variables/household/income/minimum_wage.py new file mode 100644 index 000000000..bf54d87bc --- /dev/null +++ b/policyengine_uk/variables/household/income/minimum_wage.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class minimum_wage(Variable): + value_type = float + entity = Person + label = "Minimum wage" + definition_period = YEAR + unit = GBP + + def formula(person, period, parameters): + MW = parameters(period).gov.hmrc.minimum_wage + return MW[person("minimum_wage_category", period)] diff --git a/policyengine_uk/variables/household/income/minimum_wage_category.py b/policyengine_uk/variables/household/income/minimum_wage_category.py new file mode 100644 index 000000000..0051e53d6 --- /dev/null +++ b/policyengine_uk/variables/household/income/minimum_wage_category.py @@ -0,0 +1,41 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class MinimumWageCategory(Enum): + APPRENTICE = "Apprentice" + UNDER_18 = "Under 18" + BETWEEN_18_20 = "18 to 20" + BETWEEN_21_22 = "21 to 22" + BETWEEN_23_24 = "23 to 24" + OVER_24 = "25 or over" + + +class minimum_wage_category(Variable): + value_type = Enum + possible_values = MinimumWageCategory + default_value = MinimumWageCategory.OVER_24 + entity = Person + label = "Minimum wage category" + definition_period = YEAR + + def formula(person, period, parameters): + age = person("age", period) + return select( + [ + person("is_apprentice", period), + age < 18, + (age >= 18) & (age <= 20), + (age >= 21) & (age <= 22), + (age >= 23) & (age <= 24), + ], + [ + MinimumWageCategory.APPRENTICE, + MinimumWageCategory.UNDER_18, + MinimumWageCategory.BETWEEN_18_20, + MinimumWageCategory.BETWEEN_21_22, + MinimumWageCategory.BETWEEN_23_24, + ], + default=MinimumWageCategory.OVER_24, + ) diff --git a/policyengine_uk/variables/household/income/poverty.py b/policyengine_uk/variables/household/income/poverty.py deleted file mode 100644 index ce7dee48d..000000000 --- a/policyengine_uk/variables/household/income/poverty.py +++ /dev/null @@ -1,280 +0,0 @@ -from policyengine_uk.model_api import * - - -class poverty_line(Variable): - label = "poverty line" - documentation = "The line below which a household is in poverty." - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - equivalisation = household("household_equivalisation_bhc", period) - return ( - parameters(period).household.poverty.absolute_poverty_threshold_bhc - * WEEKS_IN_YEAR - * equivalisation - ) - - -class deep_poverty_line(Variable): - label = "deep poverty line" - documentation = "The line below which a household is in deep poverty." - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - return household("poverty_line", period) / 2 - - -class poverty_gap(Variable): - label = "poverty gap" - documentation = "The financial gap between net household income and the poverty line (before housing costs)." - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - income = household("hbai_household_net_income", period) - line = household("poverty_line", period) - return max_(0, line - income) - - -class deep_poverty_gap(Variable): - label = "deep poverty gap" - documentation = "The financial gap between net household income and the deep poverty line (before housing costs)." - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - income = household("hbai_household_net_income", period) - line = household("deep_poverty_line", period) - return max_(0, line - income) - - -class in_poverty(Variable): - label = "in poverty" - documentation = ( - "Whether the household is in absolute poverty (before housing costs)." - ) - entity = Household - definition_period = YEAR - value_type = bool - - def formula(household, period, parameters): - return household("poverty_gap", period) > 0 - - -class in_deep_poverty(Variable): - label = "in deep poverty" - documentation = "Whether the household is in deep absolute poverty (below half the poverty line, before housing costs)." - entity = Household - definition_period = YEAR - value_type = bool - - def formula(household, period, parameters): - return household("deep_poverty_gap", period) > 0 - - -class in_poverty_bhc(Variable): - value_type = bool - entity = Household - label = ( - "Whether the household is in absolute poverty, before housing costs" - ) - definition_period = YEAR - - def formula(household, period, parameters): - income = household("equiv_hbai_household_net_income", period) - return income < household("poverty_threshold_bhc", period) - - -class poverty_threshold_bhc(Variable): - label = "Poverty threshold (BHC)" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - return ( - parameters(period).household.poverty.absolute_poverty_threshold_bhc - * WEEKS_IN_YEAR - ) - - -class in_poverty_ahc(Variable): - value_type = bool - entity = Household - label = "Whether the household is in absolute poverty, after housing costs" - definition_period = YEAR - - def formula(household, period, parameters): - income = household("equiv_hbai_household_net_income_ahc", period) - threshold = parameters( - period - ).household.poverty.absolute_poverty_threshold_ahc - return income < (threshold * WEEKS_IN_YEAR) - - -class in_relative_poverty_ahc(Variable): - label = "in relative poverty (AHC)" - entity = Household - definition_period = YEAR - value_type = bool - - def formula(household, period, parameters): - income = household("equiv_hbai_household_net_income_ahc", period) - # Less than 60% of median income - median_income = MicroSeries( - income, weights=household("household_weight", period) - ).median() - return income < (median_income * 0.6) - - -class in_relative_poverty_bhc(Variable): - label = "in relative poverty (AHC)" - entity = Household - definition_period = YEAR - value_type = bool - - def formula(household, period, parameters): - income = household("equiv_hbai_household_net_income", period) - # Less than 60% of median income - median_income = MicroSeries( - income, weights=household("household_weight", period) - ).median() - return income < (median_income * 0.6) - - -class in_deep_poverty_bhc(Variable): - value_type = bool - entity = Household - label = "Whether the household is in deep absolute poverty (below half the poverty line), before housing costs" - definition_period = YEAR - - def formula(household, period, parameters): - income = household("equiv_hbai_household_net_income", period) - threshold = parameters( - period - ).household.poverty.absolute_poverty_threshold_bhc - return income < (threshold * WEEKS_IN_YEAR / 2) - - -class in_deep_poverty_ahc(Variable): - value_type = bool - entity = Household - label = "Whether the household is in deep absolute poverty (below half the poverty line), after housing costs" - definition_period = YEAR - - def formula(household, period, parameters): - income = household("equiv_hbai_household_net_income_ahc", period) - threshold = parameters( - period - ).household.poverty.absolute_poverty_threshold_ahc - return income < (threshold * WEEKS_IN_YEAR / 2) - - -class poverty_line_bhc(Variable): - value_type = float - entity = Household - label = "The poverty line for the household, before housing costs" - definition_period = YEAR - - def formula(household, period, parameters): - threshold = parameters( - period - ).household.poverty.absolute_poverty_threshold_bhc - equivalisation = household("household_equivalisation_bhc", period) - return threshold * equivalisation * WEEKS_IN_YEAR - - -class poverty_line_ahc(Variable): - value_type = float - entity = Household - label = "The poverty line for the household, after housing costs" - definition_period = YEAR - - def formula(household, period, parameters): - threshold = parameters( - period - ).household.poverty.absolute_poverty_threshold_ahc - equivalisation = household("household_equivalisation_ahc", period) - return threshold * equivalisation * WEEKS_IN_YEAR - - -class poverty_gap_bhc(Variable): - value_type = float - entity = Household - label = "Positive financial gap between net household income and the poverty line" - definition_period = YEAR - - def formula(household, period, parameters): - net_income = household("hbai_household_net_income", period) - return max_(0, household("poverty_line_bhc", period) - net_income) - - -class poverty_gap_ahc(Variable): - value_type = float - entity = Household - label = "Positive financial gap between net household income and the poverty line, after housing costs" - definition_period = YEAR - - def formula(household, period, parameters): - net_income = household("hbai_household_net_income_ahc", period) - return max_(0, household("poverty_line_ahc", period) - net_income) - - -class baseline_hbai_excluded_income(Variable): - label = "HBAI-excluded income (baseline)" - documentation = "Total value of income not included in HBAI household net income in the baseline" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - if not parameters(period).household.poverty.exclude_non_hbai_income: - return 0 - # If baseline policy not viable from the above method, - # no change in HBAI excluded income - return household("hbai_excluded_income", period) - - -class hbai_excluded_income(Variable): - label = "HBAI-excluded income" - documentation = ( - "Total value of income not included in HBAI household net income" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - VARIABLES = [ - "corporate_tax_incidence", - ] - return -add(household, period, VARIABLES) - - -class hbai_excluded_income_change(Variable): - label = "Change in HBAI-excluded income" - documentation = "Effect of policy reforms on HBAI-excluded income" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - def formula(household, period, parameters): - hbai_excluded_income = household("hbai_excluded_income", period) - baseline_hbai_excluded_income = household( - "baseline_hbai_excluded_income", period - ) - return hbai_excluded_income - baseline_hbai_excluded_income diff --git a/policyengine_uk/variables/household/income/poverty_gap.py b/policyengine_uk/variables/household/income/poverty_gap.py new file mode 100644 index 000000000..b08a2169e --- /dev/null +++ b/policyengine_uk/variables/household/income/poverty_gap.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class poverty_gap(Variable): + label = "poverty gap" + documentation = "The financial gap between net household income and the poverty line (before housing costs)." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + income = household("hbai_household_net_income", period) + line = household("poverty_line", period) + return max_(0, line - income) diff --git a/policyengine_uk/variables/household/income/poverty_gap_ahc.py b/policyengine_uk/variables/household/income/poverty_gap_ahc.py new file mode 100644 index 000000000..bd21d62a0 --- /dev/null +++ b/policyengine_uk/variables/household/income/poverty_gap_ahc.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class poverty_gap_ahc(Variable): + value_type = float + entity = Household + label = "Positive financial gap between net household income and the poverty line, after housing costs" + definition_period = YEAR + + def formula(household, period, parameters): + net_income = household("hbai_household_net_income_ahc", period) + return max_(0, household("poverty_line_ahc", period) - net_income) diff --git a/policyengine_uk/variables/household/income/poverty_gap_bhc.py b/policyengine_uk/variables/household/income/poverty_gap_bhc.py new file mode 100644 index 000000000..6bc516cf2 --- /dev/null +++ b/policyengine_uk/variables/household/income/poverty_gap_bhc.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class poverty_gap_bhc(Variable): + value_type = float + entity = Household + label = "Positive financial gap between net household income and the poverty line" + definition_period = YEAR + + def formula(household, period, parameters): + net_income = household("hbai_household_net_income", period) + return max_(0, household("poverty_line_bhc", period) - net_income) diff --git a/policyengine_uk/variables/household/income/poverty_line.py b/policyengine_uk/variables/household/income/poverty_line.py new file mode 100644 index 000000000..86b4d60f8 --- /dev/null +++ b/policyengine_uk/variables/household/income/poverty_line.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class poverty_line(Variable): + label = "poverty line" + documentation = "The line below which a household is in poverty." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + equivalisation = household("household_equivalisation_bhc", period) + return ( + parameters(period).household.poverty.absolute_poverty_threshold_bhc + * WEEKS_IN_YEAR + * equivalisation + ) diff --git a/policyengine_uk/variables/household/income/poverty_line_ahc.py b/policyengine_uk/variables/household/income/poverty_line_ahc.py new file mode 100644 index 000000000..0235d0d75 --- /dev/null +++ b/policyengine_uk/variables/household/income/poverty_line_ahc.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class poverty_line_ahc(Variable): + value_type = float + entity = Household + label = "The poverty line for the household, after housing costs" + definition_period = YEAR + + def formula(household, period, parameters): + threshold = parameters( + period + ).household.poverty.absolute_poverty_threshold_ahc + equivalisation = household("household_equivalisation_ahc", period) + return threshold * equivalisation * WEEKS_IN_YEAR diff --git a/policyengine_uk/variables/household/income/poverty_line_bhc.py b/policyengine_uk/variables/household/income/poverty_line_bhc.py new file mode 100644 index 000000000..b79bfb3bc --- /dev/null +++ b/policyengine_uk/variables/household/income/poverty_line_bhc.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class poverty_line_bhc(Variable): + value_type = float + entity = Household + label = "The poverty line for the household, before housing costs" + definition_period = YEAR + + def formula(household, period, parameters): + threshold = parameters( + period + ).household.poverty.absolute_poverty_threshold_bhc + equivalisation = household("household_equivalisation_bhc", period) + return threshold * equivalisation * WEEKS_IN_YEAR diff --git a/policyengine_uk/variables/household/income/poverty_threshold_bhc.py b/policyengine_uk/variables/household/income/poverty_threshold_bhc.py new file mode 100644 index 000000000..b99d45329 --- /dev/null +++ b/policyengine_uk/variables/household/income/poverty_threshold_bhc.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class poverty_threshold_bhc(Variable): + label = "Poverty threshold (BHC)" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + return ( + parameters(period).household.poverty.absolute_poverty_threshold_bhc + * WEEKS_IN_YEAR + ) diff --git a/policyengine_uk/variables/household/income/real_household_net_income.py b/policyengine_uk/variables/household/income/real_household_net_income.py new file mode 100644 index 000000000..16fe9a35d --- /dev/null +++ b/policyengine_uk/variables/household/income/real_household_net_income.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class real_household_net_income(Variable): + label = ( + f"real household net income ({datetime.datetime.now().year} prices)" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + net_income = household("household_net_income", period) + return net_income * household("inflation_adjustment", period) diff --git a/policyengine_uk/variables/household/income/real_household_net_income_ahc.py b/policyengine_uk/variables/household/income/real_household_net_income_ahc.py new file mode 100644 index 000000000..2df75c855 --- /dev/null +++ b/policyengine_uk/variables/household/income/real_household_net_income_ahc.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class real_household_net_income_ahc(Variable): + label = ( + f"real household net income ({datetime.datetime.now().year} prices)" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + def formula(household, period, parameters): + net_income = household("household_net_income_ahc", period) + return net_income * household("inflation_adjustment", period) diff --git a/policyengine_uk/variables/household/income/weekly_hours.py b/policyengine_uk/variables/household/income/weekly_hours.py new file mode 100644 index 000000000..e9a16ac54 --- /dev/null +++ b/policyengine_uk/variables/household/income/weekly_hours.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * +import datetime +import numpy as np + + +class weekly_hours(Variable): + value_type = float + entity = Person + label = "Weekly hours" + documentation = "Average weekly hours worked" + definition_period = YEAR + unit = "hour" + quantity_type = FLOW + + def formula(person, period, parameters): + return person("hours_worked", period) / WEEKS_IN_YEAR diff --git a/policyengine_uk/variables/household/is_on_cliff.py b/policyengine_uk/variables/household/is_on_cliff.py new file mode 100644 index 000000000..adc1febe0 --- /dev/null +++ b/policyengine_uk/variables/household/is_on_cliff.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class is_on_cliff(Variable): + value_type = bool + entity = Person + label = "is on a tax-benefit cliff" + documentation = "Whether this person would be worse off if their employment income were higher by delta amount." + definition_period = YEAR + + def formula(person, period, parameters): + return person("cliff_gap", period) > 0 diff --git a/policyengine_uk/variables/household/geography.py b/policyengine_uk/variables/household/region.py similarity index 65% rename from policyengine_uk/variables/household/geography.py rename to policyengine_uk/variables/household/region.py index c87856747..e59932d35 100644 --- a/policyengine_uk/variables/household/geography.py +++ b/policyengine_uk/variables/household/region.py @@ -4,18 +4,6 @@ import pandas as pd import numpy as np -label = "Geography" -index = -1 - - -class BRMA(Variable): - value_type = Enum - possible_values = BRMAName - default_value = BRMAName.MAIDSTONE - entity = Household - label = "Broad Rental Market Area" - definition_period = YEAR - class region(Variable): value_type = Enum diff --git a/policyengine_uk/variables/household/wealth/corporate_land_value.py b/policyengine_uk/variables/household/wealth/corporate_land_value.py new file mode 100644 index 000000000..3dfcd9cb3 --- /dev/null +++ b/policyengine_uk/variables/household/wealth/corporate_land_value.py @@ -0,0 +1,20 @@ +from policyengine_uk.model_api import * + + +class corporate_land_value(Variable): + entity = Household + label = "Land value" + documentation = "Estimated total land value indirectly owned by the household from corporate holdings" + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = STOCK + + def formula(household, period, parameters): + wealth = parameters(period).household.wealth + corporate_wealth = household("corporate_wealth", period) + corporate_wealth_intensity = ( + wealth.land.value.aggregate_corporate_land_value + / wealth.corporate_wealth + ) + return corporate_wealth * corporate_wealth_intensity diff --git a/policyengine_uk/variables/household/wealth/corporate_tax_incidence.py b/policyengine_uk/variables/household/wealth/corporate_tax_incidence.py new file mode 100644 index 000000000..56e2b9c5b --- /dev/null +++ b/policyengine_uk/variables/household/wealth/corporate_tax_incidence.py @@ -0,0 +1,17 @@ +from policyengine_uk.model_api import * + + +class corporate_tax_incidence(Variable): + label = "Corporate tax incidence" + documentation = ( + "Reduction in value of corporate wealth due to taxes on corporations" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + + adds = [ + "corporate_sdlt", + "business_rates", + ] diff --git a/policyengine_uk/variables/household/wealth/financial.py b/policyengine_uk/variables/household/wealth/gross_financial_wealth.py similarity index 54% rename from policyengine_uk/variables/household/wealth/financial.py rename to policyengine_uk/variables/household/wealth/gross_financial_wealth.py index c66753be5..60e6736a9 100644 --- a/policyengine_uk/variables/household/wealth/financial.py +++ b/policyengine_uk/variables/household/wealth/gross_financial_wealth.py @@ -8,12 +8,3 @@ class gross_financial_wealth(Variable): value_type = float unit = GBP uprating = "household.wealth.financial_assets" - - -class net_financial_wealth(Variable): - label = "Net financial wealth" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - uprating = "household.wealth.financial_assets" diff --git a/policyengine_uk/variables/household/wealth/household_land_value.py b/policyengine_uk/variables/household/wealth/household_land_value.py new file mode 100644 index 000000000..b57874561 --- /dev/null +++ b/policyengine_uk/variables/household/wealth/household_land_value.py @@ -0,0 +1,23 @@ +from policyengine_uk.model_api import * + + +class household_land_value(Variable): + entity = Household + label = "Land value" + documentation = ( + "Estimated total land value directly owned by the household" + ) + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = STOCK + + def formula(household, period, parameters): + wealth = parameters(period).household.wealth + property_wealth_intensity = ( + wealth.land.value.aggregate_household_land_value + / wealth.property_wealth + ) + property_wealth = household("property_wealth", period) + owned_land = household("owned_land", period) + return property_wealth * property_wealth_intensity + owned_land diff --git a/policyengine_uk/variables/household/wealth/household_wealth_decile.py b/policyengine_uk/variables/household/wealth/household_wealth_decile.py new file mode 100644 index 000000000..efaa62dc2 --- /dev/null +++ b/policyengine_uk/variables/household/wealth/household_wealth_decile.py @@ -0,0 +1,22 @@ +from policyengine_uk.model_api import * + + +class household_wealth_decile(Variable): + label = "household wealth decile" + documentation = "Decile of wealth income (person-weighted)" + entity = Household + definition_period = YEAR + value_type = int + + def formula(household, period, parameters): + wealth = household("total_wealth", period) + count_people = household("household_count_people", period) + household_weight = household("household_weight", period) + weighted_wealth = MicroSeries( + wealth, weights=household_weight * count_people + ) + decile = weighted_wealth.decile_rank().values + # Set negatives to -1. + # This avoids the bottom decile summing to a negative number, + # which would flip the % change in the interface. + return where(wealth < 0, -1, decile) diff --git a/policyengine_uk/variables/household/wealth/land.py b/policyengine_uk/variables/household/wealth/land.py deleted file mode 100644 index 581cda0b1..000000000 --- a/policyengine_uk/variables/household/wealth/land.py +++ /dev/null @@ -1,56 +0,0 @@ -from policyengine_uk.model_api import * - - -class land_value(Variable): - label = "Land value" - documentation = ( - "Estimated total land value (directly and indirectly owned)" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - quantity_type = STOCK - - adds = ["household_land_value", "corporate_land_value"] - - -class household_land_value(Variable): - entity = Household - label = "Land value" - documentation = ( - "Estimated total land value directly owned by the household" - ) - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = STOCK - - def formula(household, period, parameters): - wealth = parameters(period).household.wealth - property_wealth_intensity = ( - wealth.land.value.aggregate_household_land_value - / wealth.property_wealth - ) - property_wealth = household("property_wealth", period) - owned_land = household("owned_land", period) - return property_wealth * property_wealth_intensity + owned_land - - -class corporate_land_value(Variable): - entity = Household - label = "Land value" - documentation = "Estimated total land value indirectly owned by the household from corporate holdings" - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = STOCK - - def formula(household, period, parameters): - wealth = parameters(period).household.wealth - corporate_wealth = household("corporate_wealth", period) - corporate_wealth_intensity = ( - wealth.land.value.aggregate_corporate_land_value - / wealth.corporate_wealth - ) - return corporate_wealth * corporate_wealth_intensity diff --git a/policyengine_uk/variables/household/wealth/land_value.py b/policyengine_uk/variables/household/wealth/land_value.py new file mode 100644 index 000000000..085eca259 --- /dev/null +++ b/policyengine_uk/variables/household/wealth/land_value.py @@ -0,0 +1,15 @@ +from policyengine_uk.model_api import * + + +class land_value(Variable): + label = "Land value" + documentation = ( + "Estimated total land value (directly and indirectly owned)" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + quantity_type = STOCK + + adds = ["household_land_value", "corporate_land_value"] diff --git a/policyengine_uk/variables/household/wealth/net_financial_wealth.py b/policyengine_uk/variables/household/wealth/net_financial_wealth.py new file mode 100644 index 000000000..3ae66a674 --- /dev/null +++ b/policyengine_uk/variables/household/wealth/net_financial_wealth.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class net_financial_wealth(Variable): + label = "Net financial wealth" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + uprating = "household.wealth.financial_assets" diff --git a/policyengine_uk/variables/household/wealth/property_wealth.py b/policyengine_uk/variables/household/wealth/property_wealth.py new file mode 100644 index 000000000..2ea53aa28 --- /dev/null +++ b/policyengine_uk/variables/household/wealth/property_wealth.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class property_wealth(Variable): + label = "Property wealth" + documentation = "Total property wealth across all owned properties" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + quantity_type = STOCK + adds = ["residential_property_value", "non_residential_property_value"] diff --git a/policyengine_uk/variables/household/wealth/property.py b/policyengine_uk/variables/household/wealth/residential_property_value.py similarity index 53% rename from policyengine_uk/variables/household/wealth/property.py rename to policyengine_uk/variables/household/wealth/residential_property_value.py index d7abc44f7..b97fa30ac 100644 --- a/policyengine_uk/variables/household/wealth/property.py +++ b/policyengine_uk/variables/household/wealth/residential_property_value.py @@ -11,14 +11,3 @@ class residential_property_value(Variable): quantity_type = STOCK adds = ["main_residence_value", "other_residential_property_value"] - - -class property_wealth(Variable): - label = "Property wealth" - documentation = "Total property wealth across all owned properties" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - quantity_type = STOCK - adds = ["residential_property_value", "non_residential_property_value"] diff --git a/policyengine_uk/variables/household/wealth/corporate.py b/policyengine_uk/variables/household/wealth/shareholding.py similarity index 71% rename from policyengine_uk/variables/household/wealth/corporate.py rename to policyengine_uk/variables/household/wealth/shareholding.py index 21a99c081..68a283eea 100644 --- a/policyengine_uk/variables/household/wealth/corporate.py +++ b/policyengine_uk/variables/household/wealth/shareholding.py @@ -22,19 +22,3 @@ def formula(household, period, parameters): wealth = household("corporate_wealth", period) total_wealth = nbs.household.financial_net_worth return wealth / total_wealth - - -class corporate_tax_incidence(Variable): - label = "Corporate tax incidence" - documentation = ( - "Reduction in value of corporate wealth due to taxes on corporations" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - adds = [ - "corporate_sdlt", - "business_rates", - ] diff --git a/policyengine_uk/variables/household/wealth/total_wealth.py b/policyengine_uk/variables/household/wealth/total_wealth.py index c3ba3e531..641e344ca 100644 --- a/policyengine_uk/variables/household/wealth/total_wealth.py +++ b/policyengine_uk/variables/household/wealth/total_wealth.py @@ -9,24 +9,3 @@ class total_wealth(Variable): unit = GBP adds = ["property_wealth", "corporate_wealth"] - - -class household_wealth_decile(Variable): - label = "household wealth decile" - documentation = "Decile of wealth income (person-weighted)" - entity = Household - definition_period = YEAR - value_type = int - - def formula(household, period, parameters): - wealth = household("total_wealth", period) - count_people = household("household_count_people", period) - household_weight = household("household_weight", period) - weighted_wealth = MicroSeries( - wealth, weights=household_weight * count_people - ) - decile = weighted_wealth.decile_rank().values - # Set negatives to -1. - # This avoids the bottom decile summing to a negative number, - # which would flip the % change in the interface. - return where(wealth < 0, -1, decile) diff --git a/policyengine_uk/variables/input/consumption/alcohol_and_tobacco_consumption.py b/policyengine_uk/variables/input/consumption/alcohol_and_tobacco_consumption.py new file mode 100644 index 000000000..3d81f4a8d --- /dev/null +++ b/policyengine_uk/variables/input/consumption/alcohol_and_tobacco_consumption.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class alcohol_and_tobacco_consumption(Variable): + entity = Household + label = "alcohol and tobacco consumption" + documentation = "Total yearly expenditure on alcohol and tobacco" + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/childcare_expenses.py b/policyengine_uk/variables/input/consumption/childcare_expenses.py new file mode 100644 index 000000000..5e9b0ebd5 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/childcare_expenses.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class childcare_expenses(Variable): + value_type = float + entity = Person + label = "childcare consumption" + documentation = "Total amount spent on childcare" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/clothing_and_footwear_consumption.py b/policyengine_uk/variables/input/consumption/clothing_and_footwear_consumption.py new file mode 100644 index 000000000..b462ddd5e --- /dev/null +++ b/policyengine_uk/variables/input/consumption/clothing_and_footwear_consumption.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class clothing_and_footwear_consumption(Variable): + entity = Household + label = "clothing and footwear consumption" + documentation = "Total yearly expenditure on clothing and footwear" + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/coicop.py b/policyengine_uk/variables/input/consumption/coicop.py deleted file mode 100644 index db81458b0..000000000 --- a/policyengine_uk/variables/input/consumption/coicop.py +++ /dev/null @@ -1,192 +0,0 @@ -from policyengine_uk.model_api import * - -# The below variables follow the COICOP MECE categories. - -label = "General" -description = "General consumption categories" - - -class food_and_non_alcoholic_beverages_consumption(Variable): - entity = Household - label = "food and alcoholic beverage consumption" - documentation = "Total yearly expenditure on food and alcoholic beverages" - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - -class alcohol_and_tobacco_consumption(Variable): - entity = Household - label = "alcohol and tobacco consumption" - documentation = "Total yearly expenditure on alcohol and tobacco" - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - -class clothing_and_footwear_consumption(Variable): - entity = Household - label = "clothing and footwear consumption" - documentation = "Total yearly expenditure on clothing and footwear" - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - -class housing_water_and_electricity_consumption(Variable): - entity = Household - label = "housing, water and electricity consumption" - documentation = ( - "Total yearly expenditure on housing, water and electricity" - ) - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - -class household_furnishings_consumption(Variable): - entity = Household - label = "household furnishings consumption" - documentation = "Total yearly expenditure on household furnishings" - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - -class health_consumption(Variable): - entity = Household - label = "health consumption" - documentation = "Total yearly expenditure on health" - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - -class transport_consumption(Variable): - entity = Household - label = "transport consumption" - documentation = "Total yearly expenditure on transport" - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - -class communication_consumption(Variable): - entity = Household - label = "communication consumption" - documentation = "Total yearly expenditure on communication" - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - -class recreation_consumption(Variable): - entity = Household - label = "recreation consumption" - documentation = "Total yearly expenditure on recreation" - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - -class education_consumption(Variable): - entity = Household - label = "education consumption" - documentation = "Total yearly expenditure on education" - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - -class restaurants_and_hotels_consumption(Variable): - entity = Household - label = "restaurants and hotels consumption" - documentation = "Total yearly expenditure on restaurants and hotels" - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - -class miscellaneous_consumption(Variable): - entity = Household - label = "miscellaneous consumption" - documentation = "Total yearly expenditure on miscellaneous goods" - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - -class petrol_spending(Variable): - label = "petrol consumption" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - -class diesel_spending(Variable): - label = "diesel consumption" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - -class childcare_expenses(Variable): - value_type = float - entity = Person - label = "childcare consumption" - documentation = "Total amount spent on childcare" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.consumer_price_index" - - -class consumption(Variable): - label = "consumption" - entity = Household - definition_period = YEAR - value_type = float - unit = "currency-GBP" - adds = [ - "food_and_non_alcoholic_beverages_consumption", - "alcohol_and_tobacco_consumption", - "clothing_and_footwear_consumption", - "housing_water_and_electricity_consumption", - "household_furnishings_consumption", - "health_consumption", - "transport_consumption", - "communication_consumption", - "recreation_consumption", - "education_consumption", - "restaurants_and_hotels_consumption", - "miscellaneous_consumption", - ] diff --git a/policyengine_uk/variables/input/consumption/communication_consumption.py b/policyengine_uk/variables/input/consumption/communication_consumption.py new file mode 100644 index 000000000..12244a8f0 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/communication_consumption.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class communication_consumption(Variable): + entity = Household + label = "communication consumption" + documentation = "Total yearly expenditure on communication" + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/consumption.py b/policyengine_uk/variables/input/consumption/consumption.py new file mode 100644 index 000000000..ff6f1a7cd --- /dev/null +++ b/policyengine_uk/variables/input/consumption/consumption.py @@ -0,0 +1,25 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class consumption(Variable): + label = "consumption" + entity = Household + definition_period = YEAR + value_type = float + unit = "currency-GBP" + adds = [ + "food_and_non_alcoholic_beverages_consumption", + "alcohol_and_tobacco_consumption", + "clothing_and_footwear_consumption", + "housing_water_and_electricity_consumption", + "household_furnishings_consumption", + "health_consumption", + "transport_consumption", + "communication_consumption", + "recreation_consumption", + "education_consumption", + "restaurants_and_hotels_consumption", + "miscellaneous_consumption", + ] diff --git a/policyengine_uk/variables/input/consumption/diesel_spending.py b/policyengine_uk/variables/input/consumption/diesel_spending.py new file mode 100644 index 000000000..31de0e53f --- /dev/null +++ b/policyengine_uk/variables/input/consumption/diesel_spending.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class diesel_spending(Variable): + label = "diesel consumption" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/education_consumption.py b/policyengine_uk/variables/input/consumption/education_consumption.py new file mode 100644 index 000000000..c021fa04d --- /dev/null +++ b/policyengine_uk/variables/input/consumption/education_consumption.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class education_consumption(Variable): + entity = Household + label = "education consumption" + documentation = "Total yearly expenditure on education" + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/food_and_non_alcoholic_beverages_consumption.py b/policyengine_uk/variables/input/consumption/food_and_non_alcoholic_beverages_consumption.py new file mode 100644 index 000000000..d4620787f --- /dev/null +++ b/policyengine_uk/variables/input/consumption/food_and_non_alcoholic_beverages_consumption.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class food_and_non_alcoholic_beverages_consumption(Variable): + entity = Household + label = "food and alcoholic beverage consumption" + documentation = "Total yearly expenditure on food and alcoholic beverages" + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/health_consumption.py b/policyengine_uk/variables/input/consumption/health_consumption.py new file mode 100644 index 000000000..6d1e8504c --- /dev/null +++ b/policyengine_uk/variables/input/consumption/health_consumption.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class health_consumption(Variable): + entity = Household + label = "health consumption" + documentation = "Total yearly expenditure on health" + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/household_furnishings_consumption.py b/policyengine_uk/variables/input/consumption/household_furnishings_consumption.py new file mode 100644 index 000000000..150207cdd --- /dev/null +++ b/policyengine_uk/variables/input/consumption/household_furnishings_consumption.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class household_furnishings_consumption(Variable): + entity = Household + label = "household furnishings consumption" + documentation = "Total yearly expenditure on household furnishings" + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/housing_water_and_electricity_consumption.py b/policyengine_uk/variables/input/consumption/housing_water_and_electricity_consumption.py new file mode 100644 index 000000000..366a4e8a5 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/housing_water_and_electricity_consumption.py @@ -0,0 +1,16 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class housing_water_and_electricity_consumption(Variable): + entity = Household + label = "housing, water and electricity consumption" + documentation = ( + "Total yearly expenditure on housing, water and electricity" + ) + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/miscellaneous_consumption.py b/policyengine_uk/variables/input/consumption/miscellaneous_consumption.py new file mode 100644 index 000000000..39f686360 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/miscellaneous_consumption.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class miscellaneous_consumption(Variable): + entity = Household + label = "miscellaneous consumption" + documentation = "Total yearly expenditure on miscellaneous goods" + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/petrol_spending.py b/policyengine_uk/variables/input/consumption/petrol_spending.py new file mode 100644 index 000000000..70fa0d6c0 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/petrol_spending.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class petrol_spending(Variable): + label = "petrol consumption" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/property/council_tax.py b/policyengine_uk/variables/input/consumption/property/council_tax.py new file mode 100644 index 000000000..042d05ece --- /dev/null +++ b/policyengine_uk/variables/input/consumption/property/council_tax.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class council_tax(Variable): + value_type = float + entity = Household + label = "Council Tax" + documentation: str = "Gross amount spent on Council Tax, before discounts" + definition_period = YEAR + unit = GBP + quantity_type = FLOW + uprating: str = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/property/cumulative_non_residential_rent.py b/policyengine_uk/variables/input/consumption/property/cumulative_non_residential_rent.py new file mode 100644 index 000000000..f37edc3bc --- /dev/null +++ b/policyengine_uk/variables/input/consumption/property/cumulative_non_residential_rent.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class cumulative_non_residential_rent(Variable): + label = "Cumulative non-residential rent" + documentation = "Total rent paid over the lifetime of the non-residential property a tenancy is held for." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP diff --git a/policyengine_uk/variables/input/consumption/property/cumulative_residential_rent.py b/policyengine_uk/variables/input/consumption/property/cumulative_residential_rent.py new file mode 100644 index 000000000..c77b51936 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/property/cumulative_residential_rent.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class cumulative_residential_rent(Variable): + label = "Cumulative residential rent" + documentation = "Total rent paid over the lifetime of the residential property a tenancy is held for." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP diff --git a/policyengine_uk/variables/input/consumption/property/employee_pension_contributions.py b/policyengine_uk/variables/input/consumption/property/employee_pension_contributions.py new file mode 100644 index 000000000..27cda86b6 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/property/employee_pension_contributions.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class employee_pension_contributions(Variable): + label = "employee pension contributions" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + uprating = "gov.obr.per_capita.employment_income" diff --git a/policyengine_uk/variables/input/consumption/property/employer_pension_contributions.py b/policyengine_uk/variables/input/consumption/property/employer_pension_contributions.py new file mode 100644 index 000000000..805ba7585 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/property/employer_pension_contributions.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class employer_pension_contributions(Variable): + value_type = float + entity = Person + label = "Employer pension contributions" + documentation = "Total amount spent on employer pension contributions" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.per_capita.employment_income" diff --git a/policyengine_uk/variables/input/consumption/property/housing_service_charges.py b/policyengine_uk/variables/input/consumption/property/housing_service_charges.py new file mode 100644 index 000000000..53d857144 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/property/housing_service_charges.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class housing_service_charges(Variable): + value_type = float + entity = Household + label = "housing service charges" + documentation = "Total amount spent on housing service charges" + definition_period = YEAR + unit = GBP diff --git a/policyengine_uk/variables/input/consumption/property/maintenance.py b/policyengine_uk/variables/input/consumption/property/maintenance.py deleted file mode 100644 index 621e565f9..000000000 --- a/policyengine_uk/variables/input/consumption/property/maintenance.py +++ /dev/null @@ -1,86 +0,0 @@ -from policyengine_uk.model_api import * - -label = "Maintenance" - - -class housing_service_charges(Variable): - value_type = float - entity = Household - label = "housing service charges" - documentation = "Total amount spent on housing service charges" - definition_period = YEAR - unit = GBP - - -class water_and_sewerage_charges(Variable): - value_type = float - entity = Household - label = "water and sewerage charges" - documentation = "Total amount spent on water and sewerage charges" - definition_period = YEAR - - -class employer_pension_contributions(Variable): - value_type = float - entity = Person - label = "Employer pension contributions" - documentation = "Total amount spent on employer pension contributions" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.per_capita.employment_income" - - -class employee_pension_contributions(Variable): - label = "employee pension contributions" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - uprating = "gov.obr.per_capita.employment_income" - - -class personal_pension_contributions(Variable): - label = "personal pension contributions" - entity = Person - definition_period = YEAR - value_type = float - unit = GBP - uprating = "gov.obr.per_capita.employment_income" - - -class maintenance_expenses(Variable): - value_type = float - entity = Person - label = "maintenance expenses" - definition_period = YEAR - unit = GBP - - -class mortgage_interest_repayment(Variable): - value_type = float - entity = Household - label = "mortgage interest repayments" - documentation = "Total amount spent on mortgage interest repayments" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.mortgage_interest" - - -class mortgage_capital_repayment(Variable): - value_type = float - entity = Household - label = "mortgage capital repayments" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.house_prices" - - -class council_tax(Variable): - value_type = float - entity = Household - label = "Council Tax" - documentation: str = "Gross amount spent on Council Tax, before discounts" - definition_period = YEAR - unit = GBP - quantity_type = FLOW - uprating: str = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/property/maintenance_expenses.py b/policyengine_uk/variables/input/consumption/property/maintenance_expenses.py new file mode 100644 index 000000000..3cd219d70 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/property/maintenance_expenses.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class maintenance_expenses(Variable): + value_type = float + entity = Person + label = "maintenance expenses" + definition_period = YEAR + unit = GBP diff --git a/policyengine_uk/variables/input/consumption/property/mortgage_capital_repayment.py b/policyengine_uk/variables/input/consumption/property/mortgage_capital_repayment.py new file mode 100644 index 000000000..a885589f5 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/property/mortgage_capital_repayment.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class mortgage_capital_repayment(Variable): + value_type = float + entity = Household + label = "mortgage capital repayments" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.house_prices" diff --git a/policyengine_uk/variables/input/consumption/property/mortgage_interest_repayment.py b/policyengine_uk/variables/input/consumption/property/mortgage_interest_repayment.py new file mode 100644 index 000000000..5be8ff6a2 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/property/mortgage_interest_repayment.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class mortgage_interest_repayment(Variable): + value_type = float + entity = Household + label = "mortgage interest repayments" + documentation = "Total amount spent on mortgage interest repayments" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.mortgage_interest" diff --git a/policyengine_uk/variables/input/consumption/property/non_residential_rent.py b/policyengine_uk/variables/input/consumption/property/non_residential_rent.py new file mode 100644 index 000000000..878aa4004 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/property/non_residential_rent.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class non_residential_rent(Variable): + label = "Non-residential rent" + documentation = "The total amount of rent paid by the household in the year for non-residential property." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP diff --git a/policyengine_uk/variables/input/consumption/property/personal_pension_contributions.py b/policyengine_uk/variables/input/consumption/property/personal_pension_contributions.py new file mode 100644 index 000000000..d5c1713d3 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/property/personal_pension_contributions.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class personal_pension_contributions(Variable): + label = "personal pension contributions" + entity = Person + definition_period = YEAR + value_type = float + unit = GBP + uprating = "gov.obr.per_capita.employment_income" diff --git a/policyengine_uk/variables/input/consumption/property/property_purchased.py b/policyengine_uk/variables/input/consumption/property/property_purchased.py new file mode 100644 index 000000000..4ce674dfa --- /dev/null +++ b/policyengine_uk/variables/input/consumption/property/property_purchased.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class property_purchased(Variable): + label = "All property bought this year" + documentation = "Whether all property wealth was bought this year" + entity = Household + definition_period = YEAR + value_type = bool + default_value = True diff --git a/policyengine_uk/variables/input/consumption/property/transactions.py b/policyengine_uk/variables/input/consumption/property/transactions.py deleted file mode 100644 index 8a39b1a72..000000000 --- a/policyengine_uk/variables/input/consumption/property/transactions.py +++ /dev/null @@ -1,39 +0,0 @@ -from policyengine_uk.model_api import * - -label = "Transactions" - - -class property_purchased(Variable): - label = "All property bought this year" - documentation = "Whether all property wealth was bought this year" - entity = Household - definition_period = YEAR - value_type = bool - default_value = True - - -class cumulative_residential_rent(Variable): - label = "Cumulative residential rent" - documentation = "Total rent paid over the lifetime of the residential property a tenancy is held for." - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - -class cumulative_non_residential_rent(Variable): - label = "Cumulative non-residential rent" - documentation = "Total rent paid over the lifetime of the non-residential property a tenancy is held for." - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - -class non_residential_rent(Variable): - label = "Non-residential rent" - documentation = "The total amount of rent paid by the household in the year for non-residential property." - entity = Household - definition_period = YEAR - value_type = float - unit = GBP diff --git a/policyengine_uk/variables/input/consumption/property/water_and_sewerage_charges.py b/policyengine_uk/variables/input/consumption/property/water_and_sewerage_charges.py new file mode 100644 index 000000000..df05a0929 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/property/water_and_sewerage_charges.py @@ -0,0 +1,9 @@ +from policyengine_uk.model_api import * + + +class water_and_sewerage_charges(Variable): + value_type = float + entity = Household + label = "water and sewerage charges" + documentation = "Total amount spent on water and sewerage charges" + definition_period = YEAR diff --git a/policyengine_uk/variables/input/consumption/recreation_consumption.py b/policyengine_uk/variables/input/consumption/recreation_consumption.py new file mode 100644 index 000000000..fcfeca889 --- /dev/null +++ b/policyengine_uk/variables/input/consumption/recreation_consumption.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class recreation_consumption(Variable): + entity = Household + label = "recreation consumption" + documentation = "Total yearly expenditure on recreation" + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/restaurants_and_hotels_consumption.py b/policyengine_uk/variables/input/consumption/restaurants_and_hotels_consumption.py new file mode 100644 index 000000000..d20d2782a --- /dev/null +++ b/policyengine_uk/variables/input/consumption/restaurants_and_hotels_consumption.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class restaurants_and_hotels_consumption(Variable): + entity = Household + label = "restaurants and hotels consumption" + documentation = "Total yearly expenditure on restaurants and hotels" + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/consumption/transport_consumption.py b/policyengine_uk/variables/input/consumption/transport_consumption.py new file mode 100644 index 000000000..0174d13ec --- /dev/null +++ b/policyengine_uk/variables/input/consumption/transport_consumption.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + +# The below variables follow the COICOP MECE categories. + + +class transport_consumption(Variable): + entity = Household + label = "transport consumption" + documentation = "Total yearly expenditure on transport" + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" diff --git a/policyengine_uk/variables/input/corporate_wealth.py b/policyengine_uk/variables/input/corporate_wealth.py new file mode 100644 index 000000000..3f5815732 --- /dev/null +++ b/policyengine_uk/variables/input/corporate_wealth.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class corporate_wealth(Variable): + label = "corporate wealth" + documentation = "Total owned wealth in corporations" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + uprating = "household.wealth.corporate_wealth" + quantity_type = STOCK diff --git a/policyengine_uk/variables/input/housing.py b/policyengine_uk/variables/input/council_tax_band.py similarity index 56% rename from policyengine_uk/variables/input/housing.py rename to policyengine_uk/variables/input/council_tax_band.py index ebcaeb08d..568dd1596 100644 --- a/policyengine_uk/variables/input/housing.py +++ b/policyengine_uk/variables/input/council_tax_band.py @@ -1,20 +1,5 @@ from policyengine_uk.model_api import * -label = "Housing" - - -class rent(Variable): - label = "Rent" - documentation = ( - "The total amount of rent paid by the household in the year." - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - quantity_type = FLOW - uprating = "gov.obr.rent" - class CouncilTaxBand(Enum): A = "A" diff --git a/policyengine_uk/variables/input/disability.py b/policyengine_uk/variables/input/disability.py deleted file mode 100644 index 04c788b04..000000000 --- a/policyengine_uk/variables/input/disability.py +++ /dev/null @@ -1,130 +0,0 @@ -from policyengine_uk.model_api import * -from policyengine_uk.variables.misc.categories.lower_middle_or_higher import ( - LowerMiddleOrHigher, -) -from policyengine_uk.variables.misc.categories.lower_or_higher import ( - LowerOrHigher, -) -from policyengine_uk.variables.gov.dwp.pip.pip import PIPCategory - -label = "Disability" - - -class dla_sc_category(Variable): - label = "DLA (Self-care) category" - documentation = "If you receive the self-care component of Disability Living Allowance, you will be in one of the following categories: Lower, Middle, Higher. If not, select None." - entity = Person - definition_period = YEAR - value_type = Enum - possible_values = LowerMiddleOrHigher - default_value = LowerMiddleOrHigher.NONE - - def formula(person, period, parameters): - dla_sc = parameters(period).baseline.gov.dwp.dla.self_care - SAFETY_MARGIN = 0.1 # Survey reported values could be slightly below eligible values when they should be above due to data manipulation - reported_weekly_dla_sc = ( - person("dla_sc_reported", period) / WEEKS_IN_YEAR - ) - return select( - [ - reported_weekly_dla_sc >= dla_sc.higher * (1 - SAFETY_MARGIN), - reported_weekly_dla_sc >= dla_sc.middle * (1 - SAFETY_MARGIN), - reported_weekly_dla_sc >= dla_sc.lower * (1 - SAFETY_MARGIN), - True, - ], - [ - LowerMiddleOrHigher.HIGHER, - LowerMiddleOrHigher.MIDDLE, - LowerMiddleOrHigher.LOWER, - LowerMiddleOrHigher.NONE, - ], - ) - - -class dla_m_category(Variable): - label = "DLA (mobility) category" - documentation = "If you receive the mobility component of Disability Living Allowance, you will be in one of the following categories: Lower, Higher. If not, select None." - entity = Person - definition_period = YEAR - value_type = Enum - possible_values = LowerOrHigher - default_value = LowerOrHigher.NONE - - def formula(person, period, parameters): - dla_m = parameters(period).baseline.gov.dwp.dla.mobility - SAFETY_MARGIN = 0.1 # Survey reported values could be slightly below eligible values when they should be above due to data manipulation - reported_weekly_dla_m = ( - person("dla_m_reported", period) / WEEKS_IN_YEAR - ) - return select( - [ - reported_weekly_dla_m >= dla_m.higher * (1 - SAFETY_MARGIN), - reported_weekly_dla_m >= dla_m.lower * (1 - SAFETY_MARGIN), - True, - ], - [ - LowerOrHigher.HIGHER, - LowerOrHigher.LOWER, - LowerOrHigher.NONE, - ], - ) - - -class pip_m_category(Variable): - label = "PIP (mobility) category" - documentation = "If you receive the mobility component of the Personal Independence Payment, you will be in one of the following categories: Standard or Enhanced. If not, select None." - entity = Person - definition_period = YEAR - value_type = Enum - possible_values = PIPCategory - default_value = PIPCategory.NONE - - def formula(person, period, parameters): - pip_m = parameters(period).baseline.gov.dwp.pip.mobility - SAFETY_MARGIN = 0.1 # Survey reported values could be slightly below eligible values when they should be above due to data manipulation - reported_weekly_pip_m = ( - person("pip_m_reported", period) / WEEKS_IN_YEAR - ) - return select( - [ - reported_weekly_pip_m >= pip_m.enhanced * (1 - SAFETY_MARGIN), - reported_weekly_pip_m >= pip_m.standard * (1 - SAFETY_MARGIN), - True, - ], - [ - PIPCategory.ENHANCED, - PIPCategory.STANDARD, - PIPCategory.NONE, - ], - ) - - -class pip_dl_category(Variable): - label = "PIP (daily living) category" - documentation = "If you receive the daily living component of the Personal Independence Payment, you will be in one of the following categories: Standard or Enhanced. If not, select None." - entity = Person - definition_period = YEAR - value_type = Enum - possible_values = PIPCategory - default_value = PIPCategory.NONE - - def formula(person, period, parameters): - pip_dl = parameters(period).baseline.gov.dwp.pip.daily_living - SAFETY_MARGIN = 0.1 # Survey reported values could be slightly below eligible values when they should be above due to data manipulation - reported_weekly_pip_dl = ( - person("pip_dl_reported", period) / WEEKS_IN_YEAR - ) - return select( - [ - reported_weekly_pip_dl - >= pip_dl.enhanced * (1 - SAFETY_MARGIN), - reported_weekly_pip_dl - >= pip_dl.standard * (1 - SAFETY_MARGIN), - True, - ], - [ - PIPCategory.ENHANCED, - PIPCategory.STANDARD, - PIPCategory.NONE, - ], - ) diff --git a/policyengine_uk/variables/input/dividend_income.py b/policyengine_uk/variables/input/dividend_income.py new file mode 100644 index 000000000..0a4fe9690 --- /dev/null +++ b/policyengine_uk/variables/input/dividend_income.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class dividend_income(Variable): + value_type = float + entity = Person + label = "dividend income" + documentation = "Total income from dividends, gross of tax" + definition_period = YEAR + reference = "Income Tax (Trading and Other Income) Act 2005 s. 365(1)(b-d)" + unit = GBP + quantity_type = FLOW + uprating = "gov.obr.per_capita.non_labour_income" diff --git a/policyengine_uk/variables/input/dla_m_category.py b/policyengine_uk/variables/input/dla_m_category.py new file mode 100644 index 000000000..84b4b7737 --- /dev/null +++ b/policyengine_uk/variables/input/dla_m_category.py @@ -0,0 +1,33 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.misc.categories.lower_or_higher import ( + LowerOrHigher, +) + + +class dla_m_category(Variable): + label = "DLA (mobility) category" + documentation = "If you receive the mobility component of Disability Living Allowance, you will be in one of the following categories: Lower, Higher. If not, select None." + entity = Person + definition_period = YEAR + value_type = Enum + possible_values = LowerOrHigher + default_value = LowerOrHigher.NONE + + def formula(person, period, parameters): + dla_m = parameters(period).baseline.gov.dwp.dla.mobility + SAFETY_MARGIN = 0.1 # Survey reported values could be slightly below eligible values when they should be above due to data manipulation + reported_weekly_dla_m = ( + person("dla_m_reported", period) / WEEKS_IN_YEAR + ) + return select( + [ + reported_weekly_dla_m >= dla_m.higher * (1 - SAFETY_MARGIN), + reported_weekly_dla_m >= dla_m.lower * (1 - SAFETY_MARGIN), + True, + ], + [ + LowerOrHigher.HIGHER, + LowerOrHigher.LOWER, + LowerOrHigher.NONE, + ], + ) diff --git a/policyengine_uk/variables/input/dla_sc_category.py b/policyengine_uk/variables/input/dla_sc_category.py new file mode 100644 index 000000000..817d8f401 --- /dev/null +++ b/policyengine_uk/variables/input/dla_sc_category.py @@ -0,0 +1,35 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.misc.categories.lower_middle_or_higher import ( + LowerMiddleOrHigher, +) + + +class dla_sc_category(Variable): + label = "DLA (Self-care) category" + documentation = "If you receive the self-care component of Disability Living Allowance, you will be in one of the following categories: Lower, Middle, Higher. If not, select None." + entity = Person + definition_period = YEAR + value_type = Enum + possible_values = LowerMiddleOrHigher + default_value = LowerMiddleOrHigher.NONE + + def formula(person, period, parameters): + dla_sc = parameters(period).baseline.gov.dwp.dla.self_care + SAFETY_MARGIN = 0.1 # Survey reported values could be slightly below eligible values when they should be above due to data manipulation + reported_weekly_dla_sc = ( + person("dla_sc_reported", period) / WEEKS_IN_YEAR + ) + return select( + [ + reported_weekly_dla_sc >= dla_sc.higher * (1 - SAFETY_MARGIN), + reported_weekly_dla_sc >= dla_sc.middle * (1 - SAFETY_MARGIN), + reported_weekly_dla_sc >= dla_sc.lower * (1 - SAFETY_MARGIN), + True, + ], + [ + LowerMiddleOrHigher.HIGHER, + LowerMiddleOrHigher.MIDDLE, + LowerMiddleOrHigher.LOWER, + LowerMiddleOrHigher.NONE, + ], + ) diff --git a/policyengine_uk/variables/input/employment_income.py b/policyengine_uk/variables/input/employment_income.py new file mode 100644 index 000000000..1828e18bc --- /dev/null +++ b/policyengine_uk/variables/input/employment_income.py @@ -0,0 +1,18 @@ +from policyengine_uk.model_api import * + + +class employment_income(Variable): + value_type = float + entity = Person + label = "employment income" + documentation = "Total income from employment. Include wages, bonuses, tips, etc. This should be gross of all private pension contributions." + definition_period = YEAR + unit = GBP + reference = "Income Tax (Earnings and Pensions) Act 2003 s. 1(1)(a)" + quantity_type = FLOW + adds = [ + "employment_income_before_lsr", + "employment_income_behavioral_response", + "employer_ni_fixed_employer_cost_change", + ] + uprating = "gov.obr.per_capita.employment_income" diff --git a/policyengine_uk/variables/input/employment_income_before_lsr.py b/policyengine_uk/variables/input/employment_income_before_lsr.py new file mode 100644 index 000000000..e5c6b7e35 --- /dev/null +++ b/policyengine_uk/variables/input/employment_income_before_lsr.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class employment_income_before_lsr(Variable): + value_type = float + entity = Person + label = "employment income before labor supply responses" + unit = GBP + definition_period = YEAR + uprating = "gov.obr.per_capita.employment_income" diff --git a/policyengine_uk/variables/input/income.py b/policyengine_uk/variables/input/income.py deleted file mode 100644 index 06667787c..000000000 --- a/policyengine_uk/variables/input/income.py +++ /dev/null @@ -1,192 +0,0 @@ -from policyengine_uk.model_api import * - -label = "Income" -description = "Financial income received by individuals." - - -class employment_income(Variable): - value_type = float - entity = Person - label = "employment income" - documentation = "Total income from employment. Include wages, bonuses, tips, etc. This should be gross of all private pension contributions." - definition_period = YEAR - unit = GBP - reference = "Income Tax (Earnings and Pensions) Act 2003 s. 1(1)(a)" - quantity_type = FLOW - adds = [ - "employment_income_before_lsr", - "employment_income_behavioral_response", - "employer_ni_fixed_employer_cost_change", - ] - uprating = "gov.obr.per_capita.employment_income" - - -class employment_income_before_lsr(Variable): - value_type = float - entity = Person - label = "employment income before labor supply responses" - unit = GBP - definition_period = YEAR - uprating = "gov.obr.per_capita.employment_income" - - -class private_pension_income(Variable): - value_type = float - entity = Person - label = "pension income" - documentation = "Income from private or occupational pensions (not including the State Pension)" - definition_period = YEAR - unit = GBP - reference = "Income Tax (Earnings and Pensions) Act 2003 s. 1(1)(b)" - quantity_type = FLOW - uprating = "gov.obr.per_capita.non_labour_income" - - -class pension_income(Variable): - value_type = float - entity = Person - label = "pension income" - documentation = "Income from private or occupational pensions (not including the State Pension)" - definition_period = YEAR - unit = GBP - reference = "Income Tax (Earnings and Pensions) Act 2003 s. 1(1)(b)" - quantity_type = FLOW - uprating = "gov.obr.per_capita.non_labour_income" - - -class state_pension(Variable): - value_type = float - entity = Person - label = "State Pension" - definition_period = YEAR - unit = GBP - documentation = "Gross State Pension payments" - quantity_type = FLOW - uprating = "gov.obr.consumer_price_index" - - def formula(person, period, parameters): - gov = parameters(period).gov - if gov.contrib.abolish_state_pension: - return 0 - relative_increase = gov.contrib.cec.state_pension_increase - uprating = 1 + relative_increase - sp = gov.dwp.state_pension - gender = person("gender", period).decode_to_str() - is_sp_age = person("is_SP_age", period) - return add( - person, - period, - [ - "basic_state_pension", - "additional_state_pension", - "new_state_pension", - ], - ) - - -class self_employment_income(Variable): - value_type = float - entity = Person - label = "self-employment income" - documentation = "Income from self-employment profits. This should be net of self-employment expenses." - definition_period = YEAR - unit = GBP - reference = "Income Tax (Trading and Other Income) Act 2005 s. 1(1)(a)" - quantity_type = FLOW - uprating = "gov.obr.per_capita.mixed_income" - - -class property_income(Variable): - value_type = float - entity = Person - label = "rental income" - documentation = "Income from rental of property" - definition_period = YEAR - reference = "Income Tax (Trading and Other Income) Act 2005 s. 1(1)(b)" - unit = GBP - quantity_type = FLOW - uprating = "gov.obr.per_capita.non_labour_income" - - -class savings_interest_income(Variable): - value_type = float - entity = Person - label = "savings interest income" - documentation = "Income from interest on savings, gross of tax" - definition_period = YEAR - reference = "Income Tax (Trading and Other Income) Act 2005 s. 365(1)(a)" - unit = GBP - quantity_type = FLOW - uprating = "gov.obr.per_capita.non_labour_income" - - -class dividend_income(Variable): - value_type = float - entity = Person - label = "dividend income" - documentation = "Total income from dividends, gross of tax" - definition_period = YEAR - reference = "Income Tax (Trading and Other Income) Act 2005 s. 365(1)(b-d)" - unit = GBP - quantity_type = FLOW - uprating = "gov.obr.per_capita.non_labour_income" - - -class sublet_income(Variable): - value_type = float - entity = Person - label = "sublet income" - documentation = "Income from subletting properties" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.per_capita.non_labour_income" - - -class miscellaneous_income(Variable): - value_type = float - entity = Person - label = "miscellaneous income" - documentation = "Income from any other source" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.per_capita.non_labour_income" - - -class private_transfer_income(Variable): - value_type = float - entity = Person - label = "private transfer income" - documentation = "Income from private transfers" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.per_capita.non_labour_income" - - -class lump_sum_income(Variable): - value_type = float - entity = Person - label = "lump sum income" - documentation = "Income from lump sums" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.per_capita.non_labour_income" - - -class maintenance_income(Variable): - value_type = float - entity = Person - label = "maintenance payment income" - documentation = "Income from maintenance payments to you" - definition_period = YEAR - unit = GBP - uprating = "gov.obr.per_capita.non_labour_income" - - -class other_investment_income(Variable): - value_type = float - entity = Person - label = "other investment income" - documentation = "Investment income from sources other than dividends, property, and net interest on UK bank accounts; may include National Savings interest products, securities interest, interest from trusts or settlements, etc." - definition_period = YEAR - unit = GBP - uprating = "gov.obr.per_capita.non_labour_income" diff --git a/policyengine_uk/variables/input/lump_sum_income.py b/policyengine_uk/variables/input/lump_sum_income.py new file mode 100644 index 000000000..63ac5b59d --- /dev/null +++ b/policyengine_uk/variables/input/lump_sum_income.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class lump_sum_income(Variable): + value_type = float + entity = Person + label = "lump sum income" + documentation = "Income from lump sums" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.per_capita.non_labour_income" diff --git a/policyengine_uk/variables/input/main_residence_value.py b/policyengine_uk/variables/input/main_residence_value.py new file mode 100644 index 000000000..5d17f0dc6 --- /dev/null +++ b/policyengine_uk/variables/input/main_residence_value.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class main_residence_value(Variable): + label = "main residence value" + documentation = "Total value of the main residence" + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + quantity_type = STOCK + uprating = "household.wealth.financial_assets" diff --git a/policyengine_uk/variables/input/maintenance_income.py b/policyengine_uk/variables/input/maintenance_income.py new file mode 100644 index 000000000..97d9c1eeb --- /dev/null +++ b/policyengine_uk/variables/input/maintenance_income.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class maintenance_income(Variable): + value_type = float + entity = Person + label = "maintenance payment income" + documentation = "Income from maintenance payments to you" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.per_capita.non_labour_income" diff --git a/policyengine_uk/variables/input/miscellaneous_income.py b/policyengine_uk/variables/input/miscellaneous_income.py new file mode 100644 index 000000000..388f35290 --- /dev/null +++ b/policyengine_uk/variables/input/miscellaneous_income.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class miscellaneous_income(Variable): + value_type = float + entity = Person + label = "miscellaneous income" + documentation = "Income from any other source" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.per_capita.non_labour_income" diff --git a/policyengine_uk/variables/input/non_residential_property_value.py b/policyengine_uk/variables/input/non_residential_property_value.py new file mode 100644 index 000000000..dbb65a838 --- /dev/null +++ b/policyengine_uk/variables/input/non_residential_property_value.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + + +class non_residential_property_value(Variable): + label = "non-residential property value" + documentation = ( + "Total value of all non-residential property owned by the household" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + quantity_type = STOCK + uprating = "household.wealth.financial_assets" diff --git a/policyengine_uk/variables/input/other_investment_income.py b/policyengine_uk/variables/input/other_investment_income.py new file mode 100644 index 000000000..704a2b395 --- /dev/null +++ b/policyengine_uk/variables/input/other_investment_income.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class other_investment_income(Variable): + value_type = float + entity = Person + label = "other investment income" + documentation = "Investment income from sources other than dividends, property, and net interest on UK bank accounts; may include National Savings interest products, securities interest, interest from trusts or settlements, etc." + definition_period = YEAR + unit = GBP + uprating = "gov.obr.per_capita.non_labour_income" diff --git a/policyengine_uk/variables/input/other_residential_property_value.py b/policyengine_uk/variables/input/other_residential_property_value.py new file mode 100644 index 000000000..8550dc95e --- /dev/null +++ b/policyengine_uk/variables/input/other_residential_property_value.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + + +class other_residential_property_value(Variable): + label = "other residence value" + documentation = ( + "Total value of all residential property owned by the household" + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + quantity_type = STOCK + uprating = "household.wealth.financial_assets" diff --git a/policyengine_uk/variables/input/owned_land.py b/policyengine_uk/variables/input/owned_land.py new file mode 100644 index 000000000..e2d577050 --- /dev/null +++ b/policyengine_uk/variables/input/owned_land.py @@ -0,0 +1,12 @@ +from policyengine_uk.model_api import * + + +class owned_land(Variable): + entity = Household + label = "owned land value" + documentation = "Total value of all land-only plots owned by the household" + unit = GBP + definition_period = YEAR + value_type = float + quantity_type = STOCK + uprating = "household.wealth.financial_assets" diff --git a/policyengine_uk/variables/input/pension_income.py b/policyengine_uk/variables/input/pension_income.py new file mode 100644 index 000000000..76a993e09 --- /dev/null +++ b/policyengine_uk/variables/input/pension_income.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class pension_income(Variable): + value_type = float + entity = Person + label = "pension income" + documentation = "Income from private or occupational pensions (not including the State Pension)" + definition_period = YEAR + unit = GBP + reference = "Income Tax (Earnings and Pensions) Act 2003 s. 1(1)(b)" + quantity_type = FLOW + uprating = "gov.obr.per_capita.non_labour_income" diff --git a/policyengine_uk/variables/input/pip_dl_category.py b/policyengine_uk/variables/input/pip_dl_category.py new file mode 100644 index 000000000..fe1c561be --- /dev/null +++ b/policyengine_uk/variables/input/pip_dl_category.py @@ -0,0 +1,33 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.gov.dwp.pip.pip import PIPCategory + + +class pip_dl_category(Variable): + label = "PIP (daily living) category" + documentation = "If you receive the daily living component of the Personal Independence Payment, you will be in one of the following categories: Standard or Enhanced. If not, select None." + entity = Person + definition_period = YEAR + value_type = Enum + possible_values = PIPCategory + default_value = PIPCategory.NONE + + def formula(person, period, parameters): + pip_dl = parameters(period).baseline.gov.dwp.pip.daily_living + SAFETY_MARGIN = 0.1 # Survey reported values could be slightly below eligible values when they should be above due to data manipulation + reported_weekly_pip_dl = ( + person("pip_dl_reported", period) / WEEKS_IN_YEAR + ) + return select( + [ + reported_weekly_pip_dl + >= pip_dl.enhanced * (1 - SAFETY_MARGIN), + reported_weekly_pip_dl + >= pip_dl.standard * (1 - SAFETY_MARGIN), + True, + ], + [ + PIPCategory.ENHANCED, + PIPCategory.STANDARD, + PIPCategory.NONE, + ], + ) diff --git a/policyengine_uk/variables/input/pip_m_category.py b/policyengine_uk/variables/input/pip_m_category.py new file mode 100644 index 000000000..b4082ef1a --- /dev/null +++ b/policyengine_uk/variables/input/pip_m_category.py @@ -0,0 +1,31 @@ +from policyengine_uk.model_api import * +from policyengine_uk.variables.gov.dwp.pip.pip import PIPCategory + + +class pip_m_category(Variable): + label = "PIP (mobility) category" + documentation = "If you receive the mobility component of the Personal Independence Payment, you will be in one of the following categories: Standard or Enhanced. If not, select None." + entity = Person + definition_period = YEAR + value_type = Enum + possible_values = PIPCategory + default_value = PIPCategory.NONE + + def formula(person, period, parameters): + pip_m = parameters(period).baseline.gov.dwp.pip.mobility + SAFETY_MARGIN = 0.1 # Survey reported values could be slightly below eligible values when they should be above due to data manipulation + reported_weekly_pip_m = ( + person("pip_m_reported", period) / WEEKS_IN_YEAR + ) + return select( + [ + reported_weekly_pip_m >= pip_m.enhanced * (1 - SAFETY_MARGIN), + reported_weekly_pip_m >= pip_m.standard * (1 - SAFETY_MARGIN), + True, + ], + [ + PIPCategory.ENHANCED, + PIPCategory.STANDARD, + PIPCategory.NONE, + ], + ) diff --git a/policyengine_uk/variables/input/private_pension_income.py b/policyengine_uk/variables/input/private_pension_income.py new file mode 100644 index 000000000..ca8dcae81 --- /dev/null +++ b/policyengine_uk/variables/input/private_pension_income.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class private_pension_income(Variable): + value_type = float + entity = Person + label = "pension income" + documentation = "Income from private or occupational pensions (not including the State Pension)" + definition_period = YEAR + unit = GBP + reference = "Income Tax (Earnings and Pensions) Act 2003 s. 1(1)(b)" + quantity_type = FLOW + uprating = "gov.obr.per_capita.non_labour_income" diff --git a/policyengine_uk/variables/input/private_transfer_income.py b/policyengine_uk/variables/input/private_transfer_income.py new file mode 100644 index 000000000..7b081f531 --- /dev/null +++ b/policyengine_uk/variables/input/private_transfer_income.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class private_transfer_income(Variable): + value_type = float + entity = Person + label = "private transfer income" + documentation = "Income from private transfers" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.per_capita.non_labour_income" diff --git a/policyengine_uk/variables/input/property_income.py b/policyengine_uk/variables/input/property_income.py new file mode 100644 index 000000000..0739a4374 --- /dev/null +++ b/policyengine_uk/variables/input/property_income.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class property_income(Variable): + value_type = float + entity = Person + label = "rental income" + documentation = "Income from rental of property" + definition_period = YEAR + reference = "Income Tax (Trading and Other Income) Act 2005 s. 1(1)(b)" + unit = GBP + quantity_type = FLOW + uprating = "gov.obr.per_capita.non_labour_income" diff --git a/policyengine_uk/variables/input/rent.py b/policyengine_uk/variables/input/rent.py new file mode 100644 index 000000000..129ab9b87 --- /dev/null +++ b/policyengine_uk/variables/input/rent.py @@ -0,0 +1,14 @@ +from policyengine_uk.model_api import * + + +class rent(Variable): + label = "Rent" + documentation = ( + "The total amount of rent paid by the household in the year." + ) + entity = Household + definition_period = YEAR + value_type = float + unit = GBP + quantity_type = FLOW + uprating = "gov.obr.rent" diff --git a/policyengine_uk/variables/input/savings_interest_income.py b/policyengine_uk/variables/input/savings_interest_income.py new file mode 100644 index 000000000..5539037bc --- /dev/null +++ b/policyengine_uk/variables/input/savings_interest_income.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class savings_interest_income(Variable): + value_type = float + entity = Person + label = "savings interest income" + documentation = "Income from interest on savings, gross of tax" + definition_period = YEAR + reference = "Income Tax (Trading and Other Income) Act 2005 s. 365(1)(a)" + unit = GBP + quantity_type = FLOW + uprating = "gov.obr.per_capita.non_labour_income" diff --git a/policyengine_uk/variables/input/self_employment_income.py b/policyengine_uk/variables/input/self_employment_income.py new file mode 100644 index 000000000..04944ae90 --- /dev/null +++ b/policyengine_uk/variables/input/self_employment_income.py @@ -0,0 +1,13 @@ +from policyengine_uk.model_api import * + + +class self_employment_income(Variable): + value_type = float + entity = Person + label = "self-employment income" + documentation = "Income from self-employment profits. This should be net of self-employment expenses." + definition_period = YEAR + unit = GBP + reference = "Income Tax (Trading and Other Income) Act 2005 s. 1(1)(a)" + quantity_type = FLOW + uprating = "gov.obr.per_capita.mixed_income" diff --git a/policyengine_uk/variables/input/state_pension.py b/policyengine_uk/variables/input/state_pension.py new file mode 100644 index 000000000..bc7259bdf --- /dev/null +++ b/policyengine_uk/variables/input/state_pension.py @@ -0,0 +1,31 @@ +from policyengine_uk.model_api import * + + +class state_pension(Variable): + value_type = float + entity = Person + label = "State Pension" + definition_period = YEAR + unit = GBP + documentation = "Gross State Pension payments" + quantity_type = FLOW + uprating = "gov.obr.consumer_price_index" + + def formula(person, period, parameters): + gov = parameters(period).gov + if gov.contrib.abolish_state_pension: + return 0 + relative_increase = gov.contrib.cec.state_pension_increase + uprating = 1 + relative_increase + sp = gov.dwp.state_pension + gender = person("gender", period).decode_to_str() + is_sp_age = person("is_SP_age", period) + return add( + person, + period, + [ + "basic_state_pension", + "additional_state_pension", + "new_state_pension", + ], + ) diff --git a/policyengine_uk/variables/input/sublet_income.py b/policyengine_uk/variables/input/sublet_income.py new file mode 100644 index 000000000..554c0e2f0 --- /dev/null +++ b/policyengine_uk/variables/input/sublet_income.py @@ -0,0 +1,11 @@ +from policyengine_uk.model_api import * + + +class sublet_income(Variable): + value_type = float + entity = Person + label = "sublet income" + documentation = "Income from subletting properties" + definition_period = YEAR + unit = GBP + uprating = "gov.obr.per_capita.non_labour_income" diff --git a/policyengine_uk/variables/input/wealth.py b/policyengine_uk/variables/input/wealth.py deleted file mode 100644 index 8cb398673..000000000 --- a/policyengine_uk/variables/input/wealth.py +++ /dev/null @@ -1,63 +0,0 @@ -from policyengine_uk.model_api import * - -label = "Wealth" -description = "Wealth held by households." - - -class corporate_wealth(Variable): - label = "corporate wealth" - documentation = "Total owned wealth in corporations" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - uprating = "household.wealth.corporate_wealth" - quantity_type = STOCK - - -class owned_land(Variable): - entity = Household - label = "owned land value" - documentation = "Total value of all land-only plots owned by the household" - unit = GBP - definition_period = YEAR - value_type = float - quantity_type = STOCK - uprating = "household.wealth.financial_assets" - - -class main_residence_value(Variable): - label = "main residence value" - documentation = "Total value of the main residence" - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - quantity_type = STOCK - uprating = "household.wealth.financial_assets" - - -class other_residential_property_value(Variable): - label = "other residence value" - documentation = ( - "Total value of all residential property owned by the household" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - quantity_type = STOCK - uprating = "household.wealth.financial_assets" - - -class non_residential_property_value(Variable): - label = "non-residential property value" - documentation = ( - "Total value of all non-residential property owned by the household" - ) - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - quantity_type = STOCK - uprating = "household.wealth.financial_assets" diff --git a/policyengine_uk/variables/misc/in_original_frs.py b/policyengine_uk/variables/misc/in_original_frs.py new file mode 100644 index 000000000..a728e32b3 --- /dev/null +++ b/policyengine_uk/variables/misc/in_original_frs.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class in_original_frs(Variable): + label = "In original FRS" + entity = Household + documentation = "Whether this household appeared in the original FRS, or whether it has been modified." + definition_period = YEAR + value_type = float + unit = GBP diff --git a/policyengine_uk/variables/misc/simulation.py b/policyengine_uk/variables/misc/simulation.py deleted file mode 100644 index f20ae48a0..000000000 --- a/policyengine_uk/variables/misc/simulation.py +++ /dev/null @@ -1,28 +0,0 @@ -from policyengine_uk.model_api import * - - -class in_original_frs(Variable): - label = "In original FRS" - entity = Household - documentation = "Whether this household appeared in the original FRS, or whether it has been modified." - definition_period = YEAR - value_type = float - unit = GBP - - -class uc_migrated(Variable): - label = "UC migrated" - documentation = "Whether this household was generated by migrating an original FRS household to Universal Credit." - entity = Household - definition_period = YEAR - value_type = float - unit = GBP - - -class spi_imputed(Variable): - label = "SPI imputed" - documentation = "Whether this household was generated by replacing income variables with SPI imputations." - entity = Household - definition_period = YEAR - value_type = float - unit = GBP diff --git a/policyengine_uk/variables/misc/spi_imputed.py b/policyengine_uk/variables/misc/spi_imputed.py new file mode 100644 index 000000000..f8a1119a8 --- /dev/null +++ b/policyengine_uk/variables/misc/spi_imputed.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class spi_imputed(Variable): + label = "SPI imputed" + documentation = "Whether this household was generated by replacing income variables with SPI imputations." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP diff --git a/policyengine_uk/variables/misc/uc_migrated.py b/policyengine_uk/variables/misc/uc_migrated.py new file mode 100644 index 000000000..f81764917 --- /dev/null +++ b/policyengine_uk/variables/misc/uc_migrated.py @@ -0,0 +1,10 @@ +from policyengine_uk.model_api import * + + +class uc_migrated(Variable): + label = "UC migrated" + documentation = "Whether this household was generated by migrating an original FRS household to Universal Credit." + entity = Household + definition_period = YEAR + value_type = float + unit = GBP