diff --git a/.gitignore b/.gitignore index 9cff59870..03935f796 100644 --- a/.gitignore +++ b/.gitignore @@ -87,6 +87,8 @@ $RECYCLE.BIN/ input/input.h2.db input/input.trace.db /input/input.mv.db +/input_full/ +/input_temp/ src/main/resources/log4j/log.out @@ -141,6 +143,108 @@ input/EUROMODoutput/baseline/* .Rproj.user /input/EUROMODoutput/test /input/InitialPopulations/test +input/EUROMODoutput/expanded/uk_2011_std.txt +input/EUROMODoutput/expanded/uk_2012_std.txt +input/EUROMODoutput/expanded/uk_2013_std.txt +input/EUROMODoutput/expanded/uk_2014_std.txt +input/EUROMODoutput/expanded/uk_2015_std.txt +input/EUROMODoutput/expanded/uk_2016_std.txt +input/EUROMODoutput/expanded/uk_2017_std.txt +input/EUROMODoutput/expanded/uk_2018_std.txt +input/EUROMODoutput/expanded/uk_2019_std.txt +input/EUROMODoutput/expanded/uk_2020_std.txt +input/EUROMODoutput/expanded/uk_2021_std.txt +input/EUROMODoutput/expanded/uk_2022_std.txt +input/EUROMODoutput/expanded/uk_2023_std.txt +input/EUROMODoutput/expanded/uk_2024_std.txt +input/EUROMODoutput/expanded/uk_2025_std.txt +input/EUROMODoutput/expanded/uk_2026_std.txt +input/EUROMODoutput/expanded/uk_2027_std.txt +input/EUROMODoutput/initial/uk_2011_std.txt +input/EUROMODoutput/initial/uk_2012_std.txt +input/EUROMODoutput/initial/uk_2013_std.txt +input/EUROMODoutput/initial/uk_2014_std.txt +input/EUROMODoutput/initial/uk_2015_std.txt +input/EUROMODoutput/initial/uk_2016_std.txt +input/EUROMODoutput/initial/uk_2017_std.txt +input/EUROMODoutput/initial/uk_2018_std.txt +input/EUROMODoutput/initial/uk_2019_std.txt +input/EUROMODoutput/initial/uk_2020_std.txt +input/EUROMODoutput/initial/uk_2021_std.txt +input/EUROMODoutput/initial/uk_2022_std.txt +input/EUROMODoutput/initial/uk_2023_std.txt +input/EUROMODoutput/initial/uk_2024_std.txt +input/EUROMODoutput/initial/uk_2025_std.txt +input/EUROMODoutput/initial/uk_2026_std.txt +input/EUROMODoutput/initial/uk_2027_std.txt +input/EUROMODoutput/testing/uk_2015_std.txt +input/EUROMODoutput/testing/uk_2019_std.txt +input/InitialPopulations/current/population_initial_UK_2011.csv +input/InitialPopulations/current/population_initial_UK_2012.csv +input/InitialPopulations/current/population_initial_UK_2013.csv +input/InitialPopulations/current/population_initial_UK_2014.csv +input/InitialPopulations/current/population_initial_UK_2015.csv +input/InitialPopulations/current/population_initial_UK_2016.csv +input/InitialPopulations/current/population_initial_UK_2017.csv +input/InitialPopulations/current/population_initial_UK_2018.csv +input/InitialPopulations/current/population_initial_UK_2019.csv +input/InitialPopulations/current/population_initial_UK_2020.csv +input/InitialPopulations/current/population_initial_UK_2021.csv +input/InitialPopulations/testing/population_initial_UK_2019.csv +input/EUROMODoutput/testing/uk_2015_std.txt +input/EUROMODoutput/testing/uk_2019_std.txt +input/EUROMODoutput/extended/uk_2015_std.txt +input/EUROMODoutput/extended/uk_2019_std.txt +input/EUROMODoutput/extended/uk_2020_std.txt +input/EUROMODoutput/extended/uk_2021_std.txt +input/EUROMODoutput/extended/uk_2022_std.txt +input/EUROMODoutput/extended/uk_2023_std.txt +input/EUROMODoutput/extended/uk_2024_std.txt +input/EUROMODoutput/extended/uk_2025_std.txt +input/EUROMODoutput/testing/DatabaseCountryYear.xlsx +input/EUROMODoutput/testing/EUROMODpolicySchedule.xlsx +input/EUROMODoutput/sc expanded/uk_2011_std.txt +input/EUROMODoutput/sc expanded/uk_2012_std.txt +input/EUROMODoutput/sc expanded/uk_2013_std.txt +input/EUROMODoutput/sc expanded/uk_2014_std.txt +input/EUROMODoutput/sc expanded/uk_2015_std.txt +input/EUROMODoutput/sc expanded/uk_2016_std.txt +input/EUROMODoutput/sc expanded/uk_2017_std.txt +input/EUROMODoutput/sc expanded/uk_2018_std.txt +input/EUROMODoutput/sc expanded/uk_2019_std.txt +input/EUROMODoutput/sc expanded/uk_2020_std.txt +input/EUROMODoutput/sc expanded/uk_2021_std.txt +input/EUROMODoutput/sc expanded/uk_2022_std.txt +input/EUROMODoutput/sc expanded/uk_2023_std.txt +input/EUROMODoutput/sc expanded/uk_2024_std.txt +input/EUROMODoutput/sc expanded/uk_2025_std.txt +input/EUROMODoutput/sc expanded/uk_2026_std.txt +input/EUROMODoutput/sc expanded/uk_2027_std.txt +input/EUROMODoutput/current/DatabaseCountryYear.xlsx +input/EUROMODoutput/current/EUROMODpolicySchedule.xlsx +input/EUROMODoutput/current/uk_2011_std.txt +input/EUROMODoutput/current/uk_2012_std.txt +input/EUROMODoutput/current/uk_2013_std.txt +input/EUROMODoutput/current/uk_2014_std.txt +input/EUROMODoutput/current/uk_2015_std.txt +input/EUROMODoutput/current/uk_2016_std.txt +input/EUROMODoutput/current/uk_2017_std.txt +input/EUROMODoutput/current/uk_2018_std.txt +input/EUROMODoutput/current/uk_2019_std.txt +input/EUROMODoutput/current/uk_2020_std.txt +input/EUROMODoutput/current/uk_2021_std.txt +input/EUROMODoutput/current/uk_2022_std.txt +input/EUROMODoutput/current/uk_2023_std.txt +input/EUROMODoutput/current/uk_2024_std.txt +input/EUROMODoutput/current/uk_2025_std.txt +input/EUROMODoutput/current/uk_2026_std.txt +input/EUROMODoutput/current/uk_2027_std.txt +input/InitialPopulations/current/* +input/InitialPopulations/testing/* +input/EUROMODoutput/working/* +.Rproj.user + +input/DatabaseCountryYear.xlsx # macOS .DS_Store diff --git a/config/lba_test.yml b/config/lba_test.yml new file mode 100644 index 000000000..4941b1c66 --- /dev/null +++ b/config/lba_test.yml @@ -0,0 +1,70 @@ +# This file can be used to override defaults for multirun arguments. +# Arguments of the SimPathsMultiRun object overridden by the command-line + +maxNumberOfRuns: 1 +countryString: "United Kingdom" +executeWithGui: false +startYear: 2015 +endYear: 2035 +popSize: 200000 +persistCountryYear: false + +# Arguments passed to the SimPathsModel +model_args: +# maxAge: 130 +# fixTimeTrend: true +# timeTrendStopsIn: 2017 +# fixRandomSeed: true +# sIndexTimeWindow: 5 +# sIndexAlpha: 2 +# sIndexDelta: 0 +# savingRate: 0 +# initialisePotentialEarningsFromDatabase: true +# useWeights: false +# useSBAMMatching: +# projectMortality: true +# alignFertility: true +# labourMarketCovid19On: false +# projectFormalChildcare: true +# donorPoolAveraging: true +# alignEmployment: false +# projectSocialCare: false +# enableIntertemporalOptimisations: true +# responsesToLowWageOffer: true +# saveImperfectTaxDBMatches: false +# useSavedBehaviour: false +# readGrid: "laptop serial" +# saveBehaviour: true +# employmentOptionsOfPrincipalWorker: 3 +# employmentOptionsOfSecondaryWorker: 3 +# responsesToEducation: true +# responsesToRetirement: false +# responsesToHealth: true +# responsesToDisability: false +# minAgeForPoorHealth: 50 +# responsesToRegion: false + +# Arguments that alter processing of the SimPathsMultiRun object +innovation_args: +# randomSeedInnov: false +# intertemporalElasticityInnov: false +# labourSupplyElasticityInnov: true + +collector_args: + # calculateGiniCoefficients: false + # exportToDatabase: false + # exportToCSV: true + # persistStatistics: true + # persistStatistics2: true + persistPersons: true + persistBenefitUnits: true + persistHouseholds: true +# dataDumpStartTime: 0L +# dataDumpTimePeriod: 1.0 + +parameter_args: + scenario: "Legacy Benefits retained" + EUROMODpolicyScheduleFilename: "EUROMODpolicySchedule_UC" + euromod_output_directory: EUROMODoutput_LBA + # input_directory: input_full + # input_directory_initial_populations: input/InitialPopulations diff --git a/config/test_create_database.yml b/config/test_create_database.yml index 870eb77ba..9363dac80 100644 --- a/config/test_create_database.yml +++ b/config/test_create_database.yml @@ -9,5 +9,6 @@ startYear: 2019 endYear: 2030 popSize: 40000 + parameter_args: - trainingFlag: true \ No newline at end of file + trainingFlag: true \ No newline at end of file diff --git a/config/test_run.yml b/config/test_run.yml index ad0c29019..67bccde5d 100644 --- a/config/test_run.yml +++ b/config/test_run.yml @@ -8,6 +8,8 @@ startYear: 2019 endYear: 2022 popSize: 20000 integrationTest: true +countryString: United Kingdom +persistCountryYear: false collector_args: persistWealthIncomeStatistics: true @@ -16,3 +18,6 @@ collector_args: persistPersons: false persistBenefitUnits: false persistHouseholds: false + +parameter_args: + trainingFlag: true diff --git a/config/uc_full.yml b/config/uc_full.yml new file mode 100644 index 000000000..cb280f368 --- /dev/null +++ b/config/uc_full.yml @@ -0,0 +1,71 @@ +# This file can be used to override defaults for multirun arguments. +# Arguments of the SimPathsMultiRun object overridden by the command-line + +maxNumberOfRuns: 1 +executeWithGui: false +countryString: "United Kingdom" +randomSeed: 100 +startYear: 2011 +endYear: 2035 +popSize: 170000 + +# Arguments passed to the SimPathsModel +model_args: +# maxAge: 130 +# fixTimeTrend: true +# timeTrendStopsIn: 2017 +# fixRandomSeed: true +# sIndexTimeWindow: 5 +# sIndexAlpha: 2 +# sIndexDelta: 0 +# savingRate: 0 +# initialisePotentialEarningsFromDatabase: true +# useWeights: false +# useSBAMMatching: +# projectMortality: true +# alignFertility: true +# labourMarketCovid19On: false +# projectFormalChildcare: true +# donorPoolAveraging: true +# alignEmployment: false +# projectSocialCare: false +# enableIntertemporalOptimisations: true +# responsesToLowWageOffer: true +# saveImperfectTaxDBMatches: false +# useSavedBehaviour: false +# readGrid: "laptop serial" +# saveBehaviour: true +# employmentOptionsOfPrincipalWorker: 3 +# employmentOptionsOfSecondaryWorker: 3 +# responsesToEducation: true +# responsesToRetirement: false +# responsesToHealth: true +# responsesToDisability: false +# minAgeForPoorHealth: 50 +# responsesToRegion: false + +# Arguments that alter processing of the SimPathsMultiRun object +innovation_args: +# randomSeedInnov: false +# intertemporalElasticityInnov: false +# labourSupplyElasticityInnov: true + +collector_args: +# calculateGiniCoefficients: false +# exportToDatabase: false +# exportToCSV: true +# persistStatistics: true +# persistStatistics2: true +# persistPersons: false +# persistBenefitUnits: false +# persistHouseholds: false +# dataDumpStartTime: 0L +# dataDumpTimePeriod: 1.0 + +parameter_args: +# input_directory: input +# input_directory_initial_populations: input/InitialPopulations + euromod_output_directory: /home/abaxter/euromod_outs/uca + MIN_START_YEAR: 2011 + MAX_START_YEAR: 2011 + scenario: "UC only available benefit from 2014" diff --git a/config/uc_full_on.yml b/config/uc_full_on.yml new file mode 100644 index 000000000..c489cc405 --- /dev/null +++ b/config/uc_full_on.yml @@ -0,0 +1,69 @@ +# This file can be used to override defaults for multirun arguments. +# Arguments of the SimPathsMultiRun object overridden by the command-line + +maxNumberOfRuns: 1 +countryString: "United Kingdom" +executeWithGui: false +startYear: 2015 +endYear: 2035 +popSize: 200000 +persistCountryYear: false + +# Arguments passed to the SimPathsModel +model_args: +# maxAge: 130 +# fixTimeTrend: true +# timeTrendStopsIn: 2017 +# fixRandomSeed: true +# sIndexTimeWindow: 5 +# sIndexAlpha: 2 +# sIndexDelta: 0 +# savingRate: 0 +# initialisePotentialEarningsFromDatabase: true +# useWeights: false +# useSBAMMatching: +# projectMortality: true +# alignFertility: true +# labourMarketCovid19On: false +# projectFormalChildcare: true +# donorPoolAveraging: true +# alignEmployment: false +# projectSocialCare: false +# enableIntertemporalOptimisations: true +# responsesToLowWageOffer: true +# saveImperfectTaxDBMatches: false +# useSavedBehaviour: false +# readGrid: "laptop serial" +# saveBehaviour: true +# employmentOptionsOfPrincipalWorker: 3 +# employmentOptionsOfSecondaryWorker: 3 +# responsesToEducation: true +# responsesToRetirement: false +# responsesToHealth: true +# responsesToDisability: false +# minAgeForPoorHealth: 50 +# responsesToRegion: false + +# Arguments that alter processing of the SimPathsMultiRun object +innovation_args: +# randomSeedInnov: false +# intertemporalElasticityInnov: false +# labourSupplyElasticityInnov: true + +collector_args: +# calculateGiniCoefficients: false +# exportToDatabase: false +# exportToCSV: true +# persistStatistics: true +# persistStatistics2: true + persistPersons: true + persistBenefitUnits: true + persistHouseholds: true +# dataDumpStartTime: 0L +# dataDumpTimePeriod: 1.0 + +parameter_args: + scenario: "UC on full" + EUROMODpolicyScheduleFilename: "EUROMODpolicySchedule_UC" +# input_directory_initial_populations: input/InitialPopulations +# euromod_output_directory: input/EUROMODoutput diff --git a/config/uc_no_direct.yml b/config/uc_no_direct.yml new file mode 100644 index 000000000..64d1e1d8c --- /dev/null +++ b/config/uc_no_direct.yml @@ -0,0 +1,72 @@ +# This file can be used to override defaults for multirun arguments. +# Arguments of the SimPathsMultiRun object overridden by the command-line + +maxNumberOfRuns: 1 +executeWithGui: false +countryString: "United Kingdom" +randomSeed: 100 +startYear: 2011 +endYear: 2035 +popSize: 170000 + +# Arguments passed to the SimPathsModel +model_args: +# maxAge: 130 +# fixTimeTrend: true +# timeTrendStopsIn: 2017 +# fixRandomSeed: true +# sIndexTimeWindow: 5 +# sIndexAlpha: 2 +# sIndexDelta: 0 +# savingRate: 0 +# initialisePotentialEarningsFromDatabase: true +# useWeights: false +# useSBAMMatching: +# projectMortality: true +# alignFertility: true +# labourMarketCovid19On: false +# projectFormalChildcare: true +# donorPoolAveraging: true +# alignEmployment: false +# projectSocialCare: false +# enableIntertemporalOptimisations: true +# responsesToLowWageOffer: true +# saveImperfectTaxDBMatches: false +# useSavedBehaviour: false +# readGrid: "laptop serial" +# saveBehaviour: true +# employmentOptionsOfPrincipalWorker: 3 +# employmentOptionsOfSecondaryWorker: 3 +# responsesToEducation: true +# responsesToRetirement: false +# responsesToHealth: true +# responsesToDisability: false +# minAgeForPoorHealth: 50 +# responsesToRegion: false + +# Arguments that alter processing of the SimPathsMultiRun object +innovation_args: +# randomSeedInnov: false +# intertemporalElasticityInnov: false +# labourSupplyElasticityInnov: true + +collector_args: +# calculateGiniCoefficients: false +# exportToDatabase: false +# exportToCSV: true +# persistStatistics: true +# persistStatistics2: true +# persistPersons: false +# persistBenefitUnits: false +# persistHouseholds: false +# dataDumpStartTime: 0L +# dataDumpTimePeriod: 1.0 + +parameter_args: +# input_directory: input +# input_directory_initial_populations: input/InitialPopulations + euromod_output_directory: /home/abaxter/euromod_outs/standard + UC_ROLLOUT: false + MIN_START_YEAR: 2011 + MAX_START_YEAR: 2011 + scenario: "No UC direct effects" diff --git a/config/uc_no_effect.yml b/config/uc_no_effect.yml new file mode 100644 index 000000000..fd438230a --- /dev/null +++ b/config/uc_no_effect.yml @@ -0,0 +1,70 @@ +# This file can be used to override defaults for multirun arguments. +# Arguments of the SimPathsMultiRun object overridden by the command-line + +maxNumberOfRuns: 1 +countryString: "United Kingdom" +executeWithGui: false +startYear: 2015 +endYear: 2035 +popSize: 200000 +persistCountryYear: false + +# Arguments passed to the SimPathsModel +model_args: +# maxAge: 130 +# fixTimeTrend: true +# timeTrendStopsIn: 2017 +# fixRandomSeed: true +# sIndexTimeWindow: 5 +# sIndexAlpha: 2 +# sIndexDelta: 0 +# savingRate: 0 +# initialisePotentialEarningsFromDatabase: true +# useWeights: false +# useSBAMMatching: +# projectMortality: true +# alignFertility: true +# labourMarketCovid19On: false +# projectFormalChildcare: true +# donorPoolAveraging: true +# alignEmployment: false +# projectSocialCare: false +# enableIntertemporalOptimisations: true +# responsesToLowWageOffer: true +# saveImperfectTaxDBMatches: false +# useSavedBehaviour: false +# readGrid: "laptop serial" +# saveBehaviour: true +# employmentOptionsOfPrincipalWorker: 3 +# employmentOptionsOfSecondaryWorker: 3 +# responsesToEducation: true +# responsesToRetirement: false +# responsesToHealth: true +# responsesToDisability: false +# minAgeForPoorHealth: 50 +# responsesToRegion: false + +# Arguments that alter processing of the SimPathsMultiRun object +innovation_args: +# randomSeedInnov: false +# intertemporalElasticityInnov: false +# labourSupplyElasticityInnov: true + +collector_args: +# calculateGiniCoefficients: false +# exportToDatabase: false +# exportToCSV: true +# persistStatistics: true +# persistStatistics2: true + persistPersons: true + persistBenefitUnits: true + persistHouseholds: true +# dataDumpStartTime: 0L +# dataDumpTimePeriod: 1.0 + +parameter_args: + scenario: "UC with no direct effects" + uc_direct_effects: false + EUROMODpolicyScheduleFilename: "EUROMODpolicySchedule_UC" +# input_directory_initial_populations: input/InitialPopulations +# euromod_output_directory: input/EUROMODoutput diff --git a/config/uc_no_lsu.yml b/config/uc_no_lsu.yml new file mode 100644 index 000000000..f527e3703 --- /dev/null +++ b/config/uc_no_lsu.yml @@ -0,0 +1,71 @@ +# This file can be used to override defaults for multirun arguments. +# Arguments of the SimPathsMultiRun object overridden by the command-line + +maxNumberOfRuns: 1 +countryString: "United Kingdom" +executeWithGui: false +startYear: 2015 +endYear: 2035 +popSize: 200000 +persistCountryYear: false + +# Arguments passed to the SimPathsModel +model_args: +# maxAge: 130 +# fixTimeTrend: true +# timeTrendStopsIn: 2017 +# fixRandomSeed: true +# sIndexTimeWindow: 5 +# sIndexAlpha: 2 +# sIndexDelta: 0 +# savingRate: 0 +# initialisePotentialEarningsFromDatabase: true +# useWeights: false +# useSBAMMatching: +# projectMortality: true +# alignFertility: true +# labourMarketCovid19On: false +# projectFormalChildcare: true +# donorPoolAveraging: true +# alignEmployment: false +# projectSocialCare: false +# enableIntertemporalOptimisations: true +# responsesToLowWageOffer: true +# saveImperfectTaxDBMatches: false +# useSavedBehaviour: false +# readGrid: "laptop serial" +# saveBehaviour: true +# employmentOptionsOfPrincipalWorker: 3 +# employmentOptionsOfSecondaryWorker: 3 +# responsesToEducation: true +# responsesToRetirement: false +# responsesToHealth: true +# responsesToDisability: false +# minAgeForPoorHealth: 50 +# responsesToRegion: false + +# Arguments that alter processing of the SimPathsMultiRun object +innovation_args: +# randomSeedInnov: false +# intertemporalElasticityInnov: false +# labourSupplyElasticityInnov: true + +collector_args: + # calculateGiniCoefficients: false + # exportToDatabase: false + # exportToCSV: true + # persistStatistics: true + # persistStatistics2: true + persistPersons: true + persistBenefitUnits: true + persistHouseholds: true +# dataDumpStartTime: 0L +# dataDumpTimePeriod: 1.0 + +parameter_args: + scenario: "UC with no effects on labour supply" + uc_direct_effects: false + euromod_output_directory: EUROMODoutput_noLSU + EUROMODpolicyScheduleFilename: "EUROMODpolicySchedule_UC" + # input_directory: input_full + # input_directory_initial_populations: input/InitialPopulations diff --git a/config/uc_off.yml b/config/uc_off.yml new file mode 100644 index 000000000..a20b9592b --- /dev/null +++ b/config/uc_off.yml @@ -0,0 +1,71 @@ +# This file can be used to override defaults for multirun arguments. +# Arguments of the SimPathsMultiRun object overridden by the command-line + +maxNumberOfRuns: 1 +executeWithGui: false +countryString: "United Kingdom" +randomSeed: 100 +startYear: 2011 +endYear: 2035 +popSize: 170000 + +# Arguments passed to the SimPathsModel +model_args: +# maxAge: 130 +# fixTimeTrend: true +# timeTrendStopsIn: 2017 +# fixRandomSeed: true +# sIndexTimeWindow: 5 +# sIndexAlpha: 2 +# sIndexDelta: 0 +# savingRate: 0 +# initialisePotentialEarningsFromDatabase: true +# useWeights: false +# useSBAMMatching: +# projectMortality: true +# alignFertility: true +# labourMarketCovid19On: false +# projectFormalChildcare: true +# donorPoolAveraging: true +# alignEmployment: false +# projectSocialCare: false +# enableIntertemporalOptimisations: true +# responsesToLowWageOffer: true +# saveImperfectTaxDBMatches: false +# useSavedBehaviour: false +# readGrid: "laptop serial" +# saveBehaviour: true +# employmentOptionsOfPrincipalWorker: 3 +# employmentOptionsOfSecondaryWorker: 3 +# responsesToEducation: true +# responsesToRetirement: false +# responsesToHealth: true +# responsesToDisability: false +# minAgeForPoorHealth: 50 +# responsesToRegion: false + +# Arguments that alter processing of the SimPathsMultiRun object +innovation_args: +# randomSeedInnov: false +# intertemporalElasticityInnov: false +# labourSupplyElasticityInnov: true + +collector_args: +# calculateGiniCoefficients: false +# exportToDatabase: false +# exportToCSV: true +# persistStatistics: true +# persistStatistics2: true +# persistPersons: false +# persistBenefitUnits: false +# persistHouseholds: false +# dataDumpStartTime: 0L +# dataDumpTimePeriod: 1.0 + +parameter_args: +# input_directory: input +# input_directory_initial_populations: input/InitialPopulations + euromod_output_directory: /home/abaxter/euromod_outs/lba + MIN_START_YEAR: 2011 + MAX_START_YEAR: 2011 + scenario: "No UC rollout" \ No newline at end of file diff --git a/config/uc_standard.yml b/config/uc_standard.yml new file mode 100644 index 000000000..ba9530e3a --- /dev/null +++ b/config/uc_standard.yml @@ -0,0 +1,71 @@ +# This file can be used to override defaults for multirun arguments. +# Arguments of the SimPathsMultiRun object overridden by the command-line + +maxNumberOfRuns: 1 +executeWithGui: false +countryString: "United Kingdom" +randomSeed: 100 +startYear: 2011 +endYear: 2035 +popSize: 170000 + +# Arguments passed to the SimPathsModel +model_args: +# maxAge: 130 +# fixTimeTrend: true +# timeTrendStopsIn: 2017 +# fixRandomSeed: true +# sIndexTimeWindow: 5 +# sIndexAlpha: 2 +# sIndexDelta: 0 +# savingRate: 0 +# initialisePotentialEarningsFromDatabase: true +# useWeights: false +# useSBAMMatching: +# projectMortality: true +# alignFertility: true +# labourMarketCovid19On: false +# projectFormalChildcare: true +# donorPoolAveraging: true +# alignEmployment: false +# projectSocialCare: false +# enableIntertemporalOptimisations: true +# responsesToLowWageOffer: true +# saveImperfectTaxDBMatches: false +# useSavedBehaviour: false +# readGrid: "laptop serial" +# saveBehaviour: true +# employmentOptionsOfPrincipalWorker: 3 +# employmentOptionsOfSecondaryWorker: 3 +# responsesToEducation: true +# responsesToRetirement: false +# responsesToHealth: true +# responsesToDisability: false +# minAgeForPoorHealth: 50 +# responsesToRegion: false + +# Arguments that alter processing of the SimPathsMultiRun object +innovation_args: +# randomSeedInnov: false +# intertemporalElasticityInnov: false +# labourSupplyElasticityInnov: true + +collector_args: +# calculateGiniCoefficients: false +# exportToDatabase: false +# exportToCSV: true +# persistStatistics: true +# persistStatistics2: true +# persistPersons: false +# persistBenefitUnits: false +# persistHouseholds: false +# dataDumpStartTime: 0L +# dataDumpTimePeriod: 1.0 + +parameter_args: +# input_directory: input +# input_directory_initial_populations: input/InitialPopulations + euromod_output_directory: /home/abaxter/euromod_outs/standard + MIN_START_YEAR: 2011 + MAX_START_YEAR: 2011 + scenario: "UC rollout as-is" diff --git a/config/uc_static_as_observed.yml b/config/uc_static_as_observed.yml new file mode 100644 index 000000000..a912d25d8 --- /dev/null +++ b/config/uc_static_as_observed.yml @@ -0,0 +1,93 @@ +# This file can be used to override defaults for multirun arguments. +# Arguments of the SimPathsMultiRun object overridden by the command-line + +maxNumberOfRuns: 1 +countryString: "United Kingdom" +executeWithGui: false +startYear: 2015 +endYear: 2035 +popSize: 200000 +persistCountryYear: false + +# Arguments passed to the SimPathsModel +model_args: +# maxAge: 130 +# fixTimeTrend: true +# timeTrendStopsIn: 2017 +# fixRandomSeed: true +# sIndexTimeWindow: 5 +# sIndexAlpha: 2 +# sIndexDelta: 0 +# savingRate: 0 +# initialisePotentialEarningsFromDatabase: true +# useWeights: false +# useSBAMMatching: +# projectMortality: true +# alignFertility: true +# labourMarketCovid19On: false +# projectFormalChildcare: true +# donorPoolAveraging: true +# alignEmployment: false +# projectSocialCare: false +# enableIntertemporalOptimisations: true +# responsesToLowWageOffer: true +# saveImperfectTaxDBMatches: false +# useSavedBehaviour: false +# readGrid: "laptop serial" +# saveBehaviour: true +# employmentOptionsOfPrincipalWorker: 3 +# employmentOptionsOfSecondaryWorker: 3 +# responsesToEducation: true +# responsesToRetirement: false +# responsesToHealth: true +# responsesToDisability: false +# minAgeForPoorHealth: 50 +# responsesToRegion: false + +# Arguments that alter processing of the SimPathsMultiRun object +innovation_args: +# randomSeedInnov: false +# intertemporalElasticityInnov: false +# labourSupplyElasticityInnov: true + +collector_args: +# calculateGiniCoefficients: false +# exportToDatabase: false +# exportToCSV: true +# persistStatistics: true +# persistStatistics2: true + persistPersons: true + persistBenefitUnits: true + persistHouseholds: true +# dataDumpStartTime: 0L +# dataDumpTimePeriod: 1.0 + +parameter_args: + scenario: "UC static - as observed" + uc_direct_effects: true + EUROMODpolicyScheduleFilename: "EUROMODpolicySchedule_UC" + euromod_output_directory: "EUROMODoutput_std" + healthWellbeingExcelFileName: "reg_health_wellbeing_static_uc" + healthMentalExcelFileName: "reg_health_mental_static_uc" + labourSupplyExcelFileName: "reg_labourSupplyUtility_static_uc" + columnsHealthHM1: 43 + columnsHealthHM2Males: 11 + columnsHealthHM2Females: 11 + columnsHealthMCS1: 43 + columnsHealthMCS2Males: 11 + columnsHealthMCS2Females: 11 + columnsHealthPCS1: 43 + columnsHealthPCS2Males: 11 + columnsHealthPCS2Females: 11 + columnsLifeSatisfaction1: 43 + columnsLifeSatisfaction2Males: 11 + columnsLifeSatisfaction2Females: 11 + columnsLabourSupplyUtilityMales: 19 + columnsLabourSupplyUtilityFemales: 12 + columnsLabourSupplyUtilityMalesWithDependent: 15 + columnsLabourSupplyUtilityFemalesWithDependent: 15 + columnsLabourSupplyUtilityACMales: 31 + columnsLabourSupplyUtilityACFemales: 31 + columnsLabourSupplyUtilityCouples: 64 +# input_directory_initial_populations: input/InitialPopulations +# euromod_output_directory: input/EUROMODoutput diff --git a/config/uc_static_no_effect.yml b/config/uc_static_no_effect.yml new file mode 100644 index 000000000..3f84e0add --- /dev/null +++ b/config/uc_static_no_effect.yml @@ -0,0 +1,93 @@ +# This file can be used to override defaults for multirun arguments. +# Arguments of the SimPathsMultiRun object overridden by the command-line + +maxNumberOfRuns: 1 +countryString: "United Kingdom" +executeWithGui: false +startYear: 2015 +endYear: 2035 +popSize: 200000 +persistCountryYear: false + +# Arguments passed to the SimPathsModel +model_args: +# maxAge: 130 +# fixTimeTrend: true +# timeTrendStopsIn: 2017 +# fixRandomSeed: true +# sIndexTimeWindow: 5 +# sIndexAlpha: 2 +# sIndexDelta: 0 +# savingRate: 0 +# initialisePotentialEarningsFromDatabase: true +# useWeights: false +# useSBAMMatching: +# projectMortality: true +# alignFertility: true +# labourMarketCovid19On: false +# projectFormalChildcare: true +# donorPoolAveraging: true +# alignEmployment: false +# projectSocialCare: false +# enableIntertemporalOptimisations: true +# responsesToLowWageOffer: true +# saveImperfectTaxDBMatches: false +# useSavedBehaviour: false +# readGrid: "laptop serial" +# saveBehaviour: true +# employmentOptionsOfPrincipalWorker: 3 +# employmentOptionsOfSecondaryWorker: 3 +# responsesToEducation: true +# responsesToRetirement: false +# responsesToHealth: true +# responsesToDisability: false +# minAgeForPoorHealth: 50 +# responsesToRegion: false + +# Arguments that alter processing of the SimPathsMultiRun object +innovation_args: +# randomSeedInnov: false +# intertemporalElasticityInnov: false +# labourSupplyElasticityInnov: true + +collector_args: +# calculateGiniCoefficients: false +# exportToDatabase: false +# exportToCSV: true +# persistStatistics: true +# persistStatistics2: true + persistPersons: true + persistBenefitUnits: true + persistHouseholds: true +# dataDumpStartTime: 0L +# dataDumpTimePeriod: 1.0 + +parameter_args: + scenario: "UC static - no direct effects" + uc_direct_effects: false + EUROMODpolicyScheduleFilename: "EUROMODpolicySchedule_UC" + euromod_output_directory: "EUROMODoutput_std" + healthWellbeingExcelFileName: "reg_health_wellbeing_static_uc" + healthMentalExcelFileName: "reg_health_mental_static_uc" + labourSupplyExcelFileName: "reg_labourSupplyUtility_static_uc" + columnsHealthHM1: 43 + columnsHealthHM2Males: 11 + columnsHealthHM2Females: 11 + columnsHealthMCS1: 43 + columnsHealthMCS2Males: 11 + columnsHealthMCS2Females: 11 + columnsHealthPCS1: 43 + columnsHealthPCS2Males: 11 + columnsHealthPCS2Females: 11 + columnsLifeSatisfaction1: 43 + columnsLifeSatisfaction2Males: 11 + columnsLifeSatisfaction2Females: 11 + columnsLabourSupplyUtilityMales: 19 + columnsLabourSupplyUtilityFemales: 12 + columnsLabourSupplyUtilityMalesWithDependent: 15 + columnsLabourSupplyUtilityFemalesWithDependent: 15 + columnsLabourSupplyUtilityACMales: 31 + columnsLabourSupplyUtilityACFemales: 31 + columnsLabourSupplyUtilityCouples: 64 +# input_directory_initial_populations: input/InitialPopulations +# euromod_output_directory: input/EUROMODoutput diff --git a/config/uc_test.yml b/config/uc_test.yml new file mode 100644 index 000000000..93c265cef --- /dev/null +++ b/config/uc_test.yml @@ -0,0 +1,91 @@ +# This file can be used to override defaults for multirun arguments. +# Arguments of the SimPathsMultiRun object overridden by the command-line + +maxNumberOfRuns: 1 +executeWithGui: false +randomSeed: 600 +startYear: 2015 +endYear: 2020 +popSize: 80000 + +# Arguments passed to the SimPathsModel +model_args: +# maxAge: 130 +# fixTimeTrend: true +# timeTrendStopsIn: 2017 +# fixRandomSeed: true +# sIndexTimeWindow: 5 +# sIndexAlpha: 2 +# sIndexDelta: 0 +# savingRate: 0 +# initialisePotentialEarningsFromDatabase: true +# useWeights: false +# useSBAMMatching: +# projectMortality: true +# alignFertility: true +# labourMarketCovid19On: false +# projectFormalChildcare: true +# donorPoolAveraging: true +# alignEmployment: false +# projectSocialCare: false +# enableIntertemporalOptimisations: true +# responsesToLowWageOffer: true +# saveImperfectTaxDBMatches: false +# useSavedBehaviour: false +# readGrid: "laptop serial" +# saveBehaviour: true +# employmentOptionsOfPrincipalWorker: 3 +# employmentOptionsOfSecondaryWorker: 3 +# responsesToEducation: true +# responsesToRetirement: false +# responsesToHealth: true +# responsesToDisability: false +# minAgeForPoorHealth: 50 +# responsesToRegion: false + +# Arguments that alter processing of the SimPathsMultiRun object +innovation_args: +# randomSeedInnov: false +# intertemporalElasticityInnov: false +# labourSupplyElasticityInnov: true + +collector_args: +# calculateGiniCoefficients: false +# exportToDatabase: false +# exportToCSV: true +# persistStatistics: true +# persistStatistics2: true + persistPersons: true + persistBenefitUnits: true + persistHouseholds: true +# dataDumpStartTime: 0L +# dataDumpTimePeriod: 1.0 + +parameter_args: + scenario: "UC testing" + EUROMODpolicyScheduleFilename: "EUROMODpolicySchedule_UC_test" + healthWellbeingExcelFileName: "reg_health_wellbeing_static_uc" + healthMentalExcelFileName: "reg_health_mental_static_uc" + labourSupplyExcelFileName: "reg_labourSupplyUtility_static_uc" + columnsHealthHM1: 43 + columnsHealthHM2Males: 11 + columnsHealthHM2Females: 11 + columnsHealthMCS1: 43 + columnsHealthMCS2Males: 11 + columnsHealthMCS2Females: 11 + columnsHealthPCS1: 43 + columnsHealthPCS2Males: 11 + columnsHealthPCS2Females: 11 + columnsLifeSatisfaction1: 43 + columnsLifeSatisfaction2Males: 11 + columnsLifeSatisfaction2Females: 11 + columnsLabourSupplyUtilityMales: 19 + columnsLabourSupplyUtilityFemales: 12 + columnsLabourSupplyUtilityMalesWithDependent: 15 + columnsLabourSupplyUtilityFemalesWithDependent: 15 + columnsLabourSupplyUtilityACMales: 31 + columnsLabourSupplyUtilityACFemales: 31 + columnsLabourSupplyUtilityCouples: 64 + +# input_directory_initial_populations: input/InitialPopulations +# euromod_output_directory: input/EUROMODoutput diff --git a/input/EUROMODpolicySchedule_UC.xlsx b/input/EUROMODpolicySchedule_UC.xlsx new file mode 100644 index 000000000..296d50876 Binary files /dev/null and b/input/EUROMODpolicySchedule_UC.xlsx differ diff --git a/src/main/java/simpaths/data/ManagerRegressions.java b/src/main/java/simpaths/data/ManagerRegressions.java index 2b29f9e87..d17b5b9ac 100644 --- a/src/main/java/simpaths/data/ManagerRegressions.java +++ b/src/main/java/simpaths/data/ManagerRegressions.java @@ -5,6 +5,7 @@ import microsim.statistics.regression.*; import org.apache.commons.collections4.keyvalue.MultiKey; import org.apache.commons.collections4.map.MultiKeyMap; +import org.apache.commons.lang3.tuple.Triple; import simpaths.model.Person; import simpaths.model.enums.Labour; @@ -447,4 +448,57 @@ private static int getMultiKeyValue(MultiKey ee) { } return val; } + + public static Triple multiEvent(Map, Double> probs, double rand) { + + double cprob = 0.0; + List> keys = new ArrayList<>(); + for (Triple ee : probs.keySet()) { + if (probs.get(ee) != null) { + cprob += probs.get(ee); + int eeVal = getTripleKeyValue(ee); + for (int ii=0; ii ee1 = keys.get(ii); + int ee1Val = getTripleKeyValue(ee1); + if (ee1Val > eeVal) { + keys.add(ii, ee); + break; + } + } + if (!keys.contains(ee)) + keys.add(ee); + } else { + throw new RuntimeException("problem identifying probabilities for multinomial object (3)"); + } + } + + double prob = 0.0; + for (Triple ee : keys) { + prob += probs.get(ee) / cprob; + if (rand < prob) { + return ee; + } + } + throw new RuntimeException("failed to identify new enumerator for multi-event (3)"); + } + + private static int getTripleKeyValue(Triple key) { + // Replicates the old positional weighting (factor *= 100) while adding UC as the 3rd position. + int maleVal = key.getLeft().getValue(); + int femaleVal = key.getMiddle().getValue(); + int ucVal = key.getRight(); // assumed 0/1 + + int val = 0; + int fctr = 1; + + val += maleVal * fctr; // position 0 + fctr *= 100; + + val += femaleVal * fctr; // position 1 + fctr *= 100; + + val += ucVal * fctr; // position 2 (UC) + return val; + } + } diff --git a/src/main/java/simpaths/data/Parameters.java b/src/main/java/simpaths/data/Parameters.java index ac40f8372..1f9c189b8 100644 --- a/src/main/java/simpaths/data/Parameters.java +++ b/src/main/java/simpaths/data/Parameters.java @@ -8,6 +8,7 @@ import microsim.statistics.regression.*; // import plug-in packages import org.apache.commons.io.FileUtils; +import org.apache.commons.math3.linear.SingularMatrixException; import simpaths.data.startingpop.DataParser; import simpaths.model.AnnuityRates; import simpaths.model.BenefitUnit; @@ -330,7 +331,8 @@ else if(numberOfChildren <= 5) { public static double PROB_NEWBORN_IS_MALE = 0.5; // Must be strictly greater than 0.0 and less than 1.0 - public static boolean UC_ROLLOUT = true; // Whether UC is available in population or not + public static boolean uc_direct_effects = true; // Whether UC effects population or not + public static boolean uc_labour_supply_effects = true; public static final boolean systemOut = true; @@ -388,6 +390,8 @@ else if(numberOfChildren <= 5) { public static String eq5dConversionParameters = "lawrence"; + public static String scenario = "None"; // Label for testing competing scenarios + /////////////////////////////////////////////////////////////////// INITIALISATION OF DATA STRUCTURES ////////////////////////////////// public static Map EUROMODpolicySchedule = new TreeMap(); @@ -713,8 +717,8 @@ else if(numberOfChildren <= 5) { //Gross earnings yearly by education and demSex (for employed persons) private static MultiKeyCoefficientMap validationGrossEarningsByGenderAndEducation; - //Hourly wages by education and demSex (for employed persons) - private static MultiKeyCoefficientMap validationLhwByGenderAndEducation; + //Hourly wages by education and gender (for employed persons) + private static MultiKeyCoefficientMap validationLhwByGenderAndEducation, validationLhwByGender; //Hours worked weekly by education and demSex (for employed persons) private static MultiKeyCoefficientMap hourlyWageByGenderAndEducation; @@ -871,6 +875,7 @@ else if(numberOfChildren <= 5) { public static boolean flagSocialCare; public static boolean flagSuppressChildcareCosts; public static boolean flagSuppressSocialCareCosts; + public static boolean flagSuppressUCTakeup; public static boolean donorPoolAveraging; public static boolean taxDonorUpratingByWage; public static boolean lifetimeIncomeImpute; @@ -906,7 +911,7 @@ public static void loadParameters(Country country, int maxAgeModel, boolean enab boolean taxDBMatches, Integer timeTrendStops, int startYearModel, int endYearModel, double interestRateInnov1, double disposableIncomeFromLabourInnov1, boolean flagSuppressChildcareCosts1, boolean flagSuppressSocialCareCosts1, - boolean lifetimeIncomeImpute1) { + boolean lifetimeIncomeImpute1, boolean flagSuppressUCTakeup1) { // display a dialog box to let the user know what is happening System.out.println("Loading model parameters"); @@ -937,6 +942,7 @@ public static void loadParameters(Country country, int maxAgeModel, boolean enab flagSocialCare = projectSocialCare; flagSuppressChildcareCosts = flagSuppressChildcareCosts1; flagSuppressSocialCareCosts = flagSuppressSocialCareCosts1; + flagSuppressUCTakeup = flagSuppressUCTakeup1; donorPoolAveraging = donorPoolAveraging1; taxDonorUpratingByWage = taxDonorUpratingByWage1; realInterestRateInnov = interestRateInnov1; @@ -1154,7 +1160,7 @@ public static void loadParameters(Country country, int maxAgeModel, boolean enab coeffCovarianceDLS2Males = ExcelAssistant.loadCoefficientMap(Parameters.getInputDirectory() + "reg_health_wellbeing.xlsx", "DLS2_Males", 1); coeffCovarianceDLS2Females = ExcelAssistant.loadCoefficientMap(Parameters.getInputDirectory() + "reg_health_wellbeing.xlsx", "DLS2_Females", 1); - loadEQ5DParameters(countryString); + loadEQ5DParameters(); //Life satisfaction // coeffCovarianceDLS1 = ExcelAssistant.loadCoefficientMap(Parameters.getInputDirectory() + "reg_lifesatisfaction.xlsx", "DLS1", 1, columnsLifeSatisfaction1); @@ -2096,6 +2102,7 @@ public static LinearRegression getRegLabourSupplyUtilityMales() { return regLabourSupplyUtilityMales; } + public static LinearRegression getRegLabourSupplyUtilitySingleDep() { return regLabourSupplyUtilitySingleDep; } @@ -3320,11 +3327,11 @@ public static MultiKeyCoefficientMap getCoeffLabourSupplyUtilityFemales() { public static MultiKeyCoefficientMap getCoeffLabourSupplyUtilityCouples() { return coeffLabourSupplyUtilityCouples; } + public static MultiKeyCoefficientMap getCoeffLabourSupplyUtilitySingleDep() { return coeffLabourSupplyUtilitySingleDep; } - public static double getLiquidWealthDiscount() { return 0.0; } @@ -3455,11 +3462,12 @@ private static void safeDelete(String filePath) { } } - public static void loadEQ5DParameters(String countryString) { + public static void loadEQ5DParameters() { - coeffCovarianceEQ5D = ExcelAssistant.loadCoefficientMap(getInputDirectory() + "reg_eq5d.xlsx", countryString + "_EQ5D_" + eq5dConversionParameters, 1); + coeffCovarianceEQ5D = ExcelAssistant.loadCoefficientMap(getInputDirectory() + "reg_eq5d.xlsx", "EQ5D_" + eq5dConversionParameters, 1); regHealthEQ5D = new LinearRegression(coeffCovarianceEQ5D); } + public static void setWorkingDirectory(String workingDirectory) { WORKING_DIRECTORY = workingDirectory; INPUT_DIRECTORY = WORKING_DIRECTORY + File.separator + "input" + File.separator; diff --git a/src/main/java/simpaths/experiment/SimPathsCollector.java b/src/main/java/simpaths/experiment/SimPathsCollector.java index 4213fa394..4fc92a129 100644 --- a/src/main/java/simpaths/experiment/SimPathsCollector.java +++ b/src/main/java/simpaths/experiment/SimPathsCollector.java @@ -8,6 +8,7 @@ import java.util.List; import java.util.Map; +import microsim.data.db.PanelEntityKey; import simpaths.data.filters.Filters; import simpaths.data.statistics.AgeBandAggregates; import simpaths.data.statistics.LabourStatistics; @@ -15,6 +16,8 @@ import simpaths.data.statistics.WellbeingByGender; import simpaths.model.BenefitUnit; import simpaths.model.SimPathsModel; +import simpaths.model.enums.Education; +import simpaths.model.enums.Gender; import simpaths.model.enums.Quintiles; // import plug-in packages import org.apache.commons.math3.util.Pair; @@ -105,9 +108,15 @@ public class SimPathsCollector extends AbstractSimulationCollectorManager implem private AlignmentStatistics alignmentStats; - private LabourStatistics labourStats; + private HealthStatistics statsHealthGender; + + private HealthStatistics statsHealthAgeGrps; - private HealthStatistics healthStats; + private HealthStatistics statsHealthHousehold; + + private HealthStatistics statsHealthEducation; + + private LabourStatistics labourStats; private WellbeingByGender wellbeingByGender; @@ -137,7 +146,29 @@ public class SimPathsCollector extends AbstractSimulationCollectorManager implem private DataExport exportLabourStatistics; - private DataExport exportHealthStatistics; + private DataExport exportStatisticsEmploymentAgeGrps; + + private DataExport exportHealthStatisticsGender; + + private DataExport exportHealthStatisticsAgeGrps; + + private DataExport exportHealthStatisticsHousehold; + + private DataExport exportHealthStatisticsEducation; + + public record AgeRange(int lowerBound, int upperBound) { + @Override + public String toString() { + return lowerBound + "-" + upperBound; + } + } + + public record HouseholdStructure(boolean coupled, boolean children, Gender gender) { + @Override + public String toString() { + return gender.toString() + "-" + (coupled ? "Partnered" : "Single") + "-" + (children ? "Children" : "No children"); + } + } private DataExport exportWellbeingByGender; @@ -170,6 +201,28 @@ public enum Processes { DumpWellbeingByGender } + String[] genders = {"Total", "Male", "Female"}; + + List ageGroups = List.of( + new AgeRange(16, 17), + new AgeRange(18, 24), + new AgeRange(25, 34), + new AgeRange(35, 49), + new AgeRange(50, 64), + new AgeRange(65, 130) + ); + + List householdStructures = List.of( + new HouseholdStructure(true, true, Gender.Male), + new HouseholdStructure(true, false, Gender.Male), + new HouseholdStructure(false, true, Gender.Male), + new HouseholdStructure(false, false, Gender.Male), + new HouseholdStructure(true, true, Gender.Female), + new HouseholdStructure(true, false, Gender.Female), + new HouseholdStructure(false, true, Gender.Female), + new HouseholdStructure(false, false, Gender.Female) + ); + @Override public void onEvent(Enum type) { @@ -237,16 +290,7 @@ public void onEvent(Enum type) { log.error(e.getMessage()); } break; - case DumpHealthStatistics: - healthStats.update(ageBands()); - try { - exportHealthStatistics.export(); - } catch (Exception e) { - log.error(e.getMessage()); - } - break; case DumpWellbeingByGender: - String[] genders = {"Total", "Male", "Female"}; for (String gender_s: genders) { wellbeingByGender.update(model, gender_s); try { @@ -255,6 +299,51 @@ public void onEvent(Enum type) { log.error(e.getMessage()); } } + + break; + case DumpHealthStatistics: + for (String gender_s: genders) { + // FIXME: adapt to new stats + // statsHealthGender.update(model, gender_s, new AgeRange(18, 64)); + try { + exportHealthStatisticsGender.export(); + } catch (Exception e) { + log.error(e.getMessage()); + } + } + + for (AgeRange ageGroup: ageGroups) { + for (String gender_s: genders) { + // FIXME: adapt to new stats + // statsHealthAgeGrps.update(model, gender_s, ageGroup); + try { + exportHealthStatisticsAgeGrps.export(); + } catch (Exception e) { + log.error(e.getMessage()); + } + } + } + + for (HouseholdStructure householdStructure: householdStructures) { + // FIXME: adapt to new stats + // statsHealthHousehold.update(model, householdStructure); + try { + exportHealthStatisticsHousehold.export(); + } catch (Exception e) { + log.error(e.getMessage()); + } + } + + for (Education education: Education.values()) { + // FIXME: adapt to new stats + // statsHealthEducation.update(model, education); + try { + exportHealthStatisticsEducation.export(); + } catch (Exception e) { + log.error(e.getMessage()); + } + } + break; } } @@ -269,11 +358,15 @@ public void buildObjects() { model = (SimPathsModel) getManager(); + statsHealthGender = new HealthStatistics(); + statsHealthAgeGrps = new HealthStatistics(); + statsHealthHousehold = new HealthStatistics(); + statsHealthEducation = new HealthStatistics(); + wealthIncomeStats = new WealthIncomeStatistics(); demographicStats = new DemographicStatistics(); alignmentStats = new AlignmentStatistics(); labourStats = new LabourStatistics(); - healthStats = new HealthStatistics(); wellbeingByGender = new WellbeingByGender(); //For export to database or .csv files. @@ -283,6 +376,14 @@ public void buildObjects() { exportBenefitUnits = new DataExport(model.getBenefitUnits(), exportToDatabase, exportToCSV); if (persistHouseholds) exportHouseholds = new DataExport(model.getHouseholds(), exportToDatabase, exportToCSV); + + if (persistHealthStatistics) { + exportHealthStatisticsGender = new DataExport(statsHealthGender, exportToDatabase, exportToCSV); + exportHealthStatisticsAgeGrps = new DataExport(statsHealthAgeGrps, exportToDatabase, exportToCSV); + exportHealthStatisticsHousehold = new DataExport(statsHealthHousehold, exportToDatabase, exportToCSV); + exportHealthStatisticsEducation = new DataExport(statsHealthEducation, exportToDatabase, exportToCSV); + } + if (persistWealthIncomeStatistics) exportWealthIncomeStatistics = new DataExport(List.of(wealthIncomeStats), exportToDatabase, exportToCSV); if (persistDemographicStatistics) @@ -291,8 +392,6 @@ public void buildObjects() { exportAlignmentStatistics = new DataExport(List.of(alignmentStats), exportToDatabase, exportToCSV); if (persistLabourStatistics) exportLabourStatistics = new DataExport(List.of(labourStats), exportToDatabase, exportToCSV); - if (persistHealthStatistics) - exportHealthStatistics = new DataExport(List.of(healthStats), exportToDatabase, exportToCSV); if (persistWellbeingByGender) exportWellbeingByGender = new DataExport(List.of(wellbeingByGender), exportToDatabase, exportToCSV); @@ -857,5 +956,4 @@ public boolean isPersistWellbeingByGender() { public void setPersistWellbeingByGender(boolean persistWellbeingByGender) { this.persistWellbeingByGender = persistWellbeingByGender; } - } diff --git a/src/main/java/simpaths/experiment/SimPathsMultiRun.java b/src/main/java/simpaths/experiment/SimPathsMultiRun.java index 5594d02e2..44bc2453e 100644 --- a/src/main/java/simpaths/experiment/SimPathsMultiRun.java +++ b/src/main/java/simpaths/experiment/SimPathsMultiRun.java @@ -71,6 +71,8 @@ public class SimPathsMultiRun extends MultiRun { private static boolean integrationTest = false; + private static boolean persistCountryYear = true; + /** * * MAIN PROGRAM ENTRY FOR MULTI-SIMULATION @@ -88,20 +90,20 @@ public static void main(String[] args) { if (parameterArgs != null) updateParameters(parameterArgs); - // set default values for country and start year - MultiKeyCoefficientMap lastDatabaseCountryAndYear = ExcelAssistant.loadCoefficientMap(Parameters.getInputDirectory() + File.separator + Parameters.DatabaseCountryYearFilename + ".xlsx", "Data", 1); - try { - countryString = "United Kingdom"; - country = Country.UK; - String valueYear = lastDatabaseCountryAndYear.getValue(country.toString()).toString(); - startYear = Integer.parseInt(valueYear); - } catch (NullPointerException e) { - System.out.println("No last database country and year found."); - countryString = "United Kingdom"; - startYear = 2019; - } - - country = Country.getCountryFromNameString(countryString); + if (persistCountryYear) { + // set default values for country and start year + MultiKeyCoefficientMap lastDatabaseCountryAndYear = ExcelAssistant.loadCoefficientMap(Parameters.getInputDirectory() + File.separator + Parameters.DatabaseCountryYearFilename + ".xlsx", "Data", 1); + try { + countryString = "United Kingdom"; + country = Country.UK; + String valueYear = lastDatabaseCountryAndYear.getValue(country.toString()).toString(); + startYear = Integer.parseInt(valueYear); + } catch (NullPointerException e) { + System.out.println("No last database country and year found."); + countryString = "United Kingdom"; + startYear = 2019; + } + } if (innovationArgs!=null) updateLocalParameters(innovationArgs); @@ -118,11 +120,12 @@ public static void main(String[] args) { Parameters.validateStartYear(startYear); //Save the last selected country and year to Excel to use in the model + System.setProperty("java.io.tmpdir", "~/.simpathstmp"); String[] columnNames = {"Country", "Year"}; Object[][] data = new Object[1][columnNames.length]; data[0][0] = country.toString(); data[0][1] = startYear; - XLSXfileWriter.createXLSX(Parameters.INPUT_DIRECTORY, Parameters.DatabaseCountryYearFilename, "Data", columnNames, data); + if (persistCountryYear) XLSXfileWriter.createXLSX(Parameters.INPUT_DIRECTORY, Parameters.DatabaseCountryYearFilename, "Data", columnNames, data); if (flagDatabaseSetup) { @@ -208,6 +211,9 @@ private static boolean parseCommandLineArgs(String[] args) { Option fileOption = new Option("f", "Output to file"); options.addOption(fileOption); + Option logOption = new Option("l", "Output log file"); + options.addOption(logOption); + Option persistRoot = new Option("P", "persist", true, "Write and read processed database to root or run-specific database. Accepted arguments:" + "\n - root: persist to root output folder (input/)" + diff --git a/src/main/java/simpaths/experiment/SimPathsObserver.java b/src/main/java/simpaths/experiment/SimPathsObserver.java index d7c42ae3e..ea6e2e019 100644 --- a/src/main/java/simpaths/experiment/SimPathsObserver.java +++ b/src/main/java/simpaths/experiment/SimPathsObserver.java @@ -155,6 +155,9 @@ public class SimPathsObserver extends AbstractSimulationObserverManager implemen @GUIparameter(description="Toggle to turn chart on/off") private boolean educationOfAdults = true; + @GUIparameter(description="Toggle to turn chart on/off") + private boolean benefits = true; + @GUIparameter(description="Toggle to turn chart on/off") private boolean employmentByAge = true; @@ -883,6 +886,31 @@ else if(edu.equals(Education.High)) { tabSet.add(plot); } + // Benefits graphs + if (benefits) { + var plots = new LinkedHashSet(); + // FIXME: why 16 when all others are 18+? + var inAgeRange = new FilteredCollection<>(model::getPersons, Filters.ageRange(16, 64)); + var ucCs = new WeightedCrossSection<>(inAgeRange, p -> Parameters.uc_direct_effects && p.isReceivesBenefitsFlagUC() ? 1.0 : 0.0, Person::getWeight); + var lbCs = new WeightedCrossSection<>(inAgeRange, p -> p.isReceivesBenefitsFlagNonUC() ? 1.0 : 0.0, Person::getWeight); + var ucStats = WeightedStats.supplier(ucCs); + var lbStats = WeightedStats.supplier(lbCs); + + var ucPlot = new TimeSeriesSimulationPlotter("Universal Credit receipt", ""); + ucPlot.addSource("Recipients", () -> ucStats.get().mean(), colorArrayList.get(0), false); + updateChartSet.add(ucPlot); + ucPlot.setName("Universal Credit receipt"); + plots.add(ucPlot); + + var lbPlot = new TimeSeriesSimulationPlotter("Legacy Benefits receipt", ""); + lbPlot.addSource("Recipients", () -> lbStats.get().mean(), colorArrayList.get(1), false); + updateChartSet.add(lbPlot); + lbPlot.setName("Legacy Benefits receipt"); + plots.add(lbPlot); + + tabSet.add(createScrollPaneFromPlots(plots, "Benefits", 2)); + } + // Male/Female employment rates by age groups if(employmentByAge) { ageGenderPlots("Employment rate", this.decades, Person::getEmployed, AgeRange::employmentValidation); @@ -1505,8 +1533,15 @@ public void setEducationOfAdults(boolean educationOfAdults) { this.educationOfAdults = educationOfAdults; } + public boolean isBenefits() { + return benefits; + } + + public void setBenefits(boolean benefits) { + this.benefits = benefits; + } - public boolean isEmploymentByAge() { + public boolean isEmploymentByAge() { return employmentByAge; } diff --git a/src/main/java/simpaths/model/BenefitUnit.java b/src/main/java/simpaths/model/BenefitUnit.java index 92c14b28d..2ad582a31 100644 --- a/src/main/java/simpaths/model/BenefitUnit.java +++ b/src/main/java/simpaths/model/BenefitUnit.java @@ -75,8 +75,10 @@ public class BenefitUnit implements EventListener, IDoubleSource, Weight, Compar @NullInitialised private Double yDispMonth; @NullInitialised private Double yGrossMonth; @NullInitialised private Double yBenAmountMonth; + private Double universalCreditMonthly; + private Double nonUCMonthly; @NullInitialised private Integer yBenUCReceivedFlag; - @NullInitialised private Integer yBenLegacyReceivedFlag; + @NullInitialised private Integer yBenNonUCReceivedFlag; @NullInitialised private Double yDispEquivYear; @Lag(getter = "getEquivalisedDisposableIncomeYearly") @Transient private Double yDispEquivYearL1; @NullInitialised @Transient private Double yDiffDispEquivPrevYear; @@ -147,6 +149,8 @@ private static class LabourEval { } } + private Integer uc_takeup; + /********************************************************************* * CONSTRUCTOR FOR OBJECT USED ONLY TO INTERACT WITH REGRESSION MODELS @@ -176,6 +180,12 @@ public BenefitUnit(boolean regressionModel) { } } + public BenefitUnit(boolean testModel, long id) { + model = null; + key = new PanelEntityKey(id); + collector = null; + } + // USED BY EXPECTATIONS OBJECT TO INTERACT WITH REGRESSION MODELS public BenefitUnit(BenefitUnit originalBenefitUnit, boolean regressionModel) { @@ -211,6 +221,8 @@ public BenefitUnit(Long id, long statSeed) { this.yGrossMonth = 0.; this.yDispEquivYear = 0.; this.yBenAmountMonth = 0.; + this.universalCreditMonthly = 0.; + this.nonUCMonthly = 0.; this.demCreatedByConstructor = "LongID"; if (Parameters.projectLiquidWealth) setWealthTotValue(0.); @@ -281,6 +293,8 @@ public BenefitUnit(BenefitUnit originalBenefitUnit, long benefitUnitInnov, Sampl yDispMonth = Objects.requireNonNullElse(originalBenefitUnit.getDisposableIncomeMonthly(),0.0); + universalCreditMonthly = Objects.requireNonNullElse(originalBenefitUnit.getUniversalCreditMonthly(),0.0); + nonUCMonthly = Objects.requireNonNullElse(originalBenefitUnit.getNonUCMonthly(),0.0); xDiscretionaryYear = Objects.requireNonNullElse(originalBenefitUnit.xDiscretionaryYear, 0.0); yGrossMonth = Objects.requireNonNullElse(originalBenefitUnit.getGrossIncomeMonthly(),0.0); yDispEquivYear = Objects.requireNonNullElse(originalBenefitUnit.yDispEquivYear,0.0); @@ -326,6 +340,7 @@ public enum Processes { CalculateChangeInEDI, //Calculate change in equivalised disposable income Homeownership, ReceivesBenefits, + ReceivesBenefitsUC, UpdateStates, UpdateInvestmentIncome, ProjectDiscretionaryConsumption, @@ -353,6 +368,10 @@ public void onEvent(Enum type) { } case ReceivesBenefits -> { setReceivesBenefitsFlag(); + setReceivesBenefitsFlagUCNonUC(); + } + case ReceivesBenefitsUC -> { + setReceivesBenefitsFlagUCNonUC(); } case UpdateStates -> { setStates(); @@ -475,6 +494,77 @@ public LinkedHashSet> findPossibleLabourCombinations() { return combinationsToReturn; } + /** + * Identifies possible labour supply combinations for a benefit unit (single or couple) + * alongside their Universal Credit (UC) take-up status. + * This method considers various scenarios based on individual characteristics, + * such as whether each partner in a couple is at risk of work, retired, or unable to work. + * It generates combinations of labour supply levels for both individuals, including + * Universal Credit take-up status. + * + * @return A set of triplets (Triple<Labour, Labour, Integer>) where each triplet represents: + * - Labour supply level for the male partner. + * - Labour supply level for the female partner. + * - Universal Credit take-up status (1 for taken, 0 for not taken). + */ + public LinkedHashSet> findPossibleLabourCombinationsWithUniversalCredit() { + LinkedHashSet> combinationsToReturn = new LinkedHashSet<>(); + Integer[] UC_TakeUp = Parameters.uc_labour_supply_effects ? new Integer[]{1, 0} : new Integer[]{0}; + Person male = getMale(); + Person female = getFemale(); + if (male!=null && female!=null) { + //Need to use both partners individual characteristics to determine similar benefitUnits + //Sometimes one of the occupants of the couple will be retired (or even under the age to work, + // which is currently the age to leave home). For this case, the person (not at risk of work)'s + // labour supply will always be zero, while the other person at risk of work has a choice over the + // single person Labour Supply set. + Labour[] labourMaleValues; + if (male.atRiskOfWork()) { + labourMaleValues = Labour.values(); + } else { + labourMaleValues = new Labour[]{Labour.ZERO}; + } + + Labour[] labourFemaleValues; + if (female.atRiskOfWork()) { + labourFemaleValues = Labour.values(); + } else { + labourFemaleValues = new Labour[]{Labour.ZERO}; + } + + for (Labour labourMale: labourMaleValues) { + for(Labour labourFemale: labourFemaleValues) { + for (Integer UC: UC_TakeUp){ + if ((labourMale == Labour.ZERO || labourMale == Labour.TEN) && + (labourFemale == Labour.ZERO || labourFemale == Labour.TEN) || + UC == 0.0) { + combinationsToReturn.add(Triple.of(labourMale, labourFemale, UC)); + } + } + } + } + } else { + //For single benefitUnits, no need to check for at risk of work (i.e. retired, sick or student activity status), + // as this has already been done when passing this household to the labour supply module (see first loop over benefitUnits + // in LabourMarket#update()). + if (male!=null) { + for (Labour labour : Labour.values()) { + for (Integer UC: UC_TakeUp) { + combinationsToReturn.add(Triple.of(labour, Labour.ZERO, UC)); + } + } + } else { //Must be single female + for (Labour labour : Labour.values()) { + for (Integer UC: UC_TakeUp){ + combinationsToReturn.add(Triple.of(Labour.ZERO, labour, UC)); + } + } + } + } + + return combinationsToReturn; + } + public void setReceivesBenefitsFlag() { @@ -498,24 +588,28 @@ public void setReceivesBenefitsFlag() { public void setReceivesBenefitsFlagUCNonUC() { - // TO DO: this will require an additional test of whether UC is received or not - for now all receive UC if any ben - boolean receivesBenefitsFlagUC = getReceivedUC() == 1 && Parameters.UC_ROLLOUT; - boolean receivesLegacyBenefitsFlag = !receivesBenefitsFlagUC && getReceivedLegacyBenefits() == 1; + boolean receivesBenefitsFlagUC = getReceivedUC() == 1; +// boolean receivesLegacyBenefitsFlag = !receivesBenefitsFlagUC && getReceivedLegacyBenefits() == 1; + boolean receivesBenefitsNonUC = !receivesBenefitsFlagUC && getBenefitsReceivedPerMonth() > 0; Occupancy occupancy = getOccupancy(); switch (occupancy) { case Couple -> { getMale().setReceivesBenefitsFlagUC(receivesBenefitsFlagUC); - getMale().setReceivesBenefitsFlagNonUC(receivesLegacyBenefitsFlag); +// getMale().setReceivesBenefitsFlagLB(receivesLegacyBenefitsFlag); + getMale().setReceivesBenefitsFlagNonUC(receivesBenefitsNonUC); getFemale().setReceivesBenefitsFlagUC(receivesBenefitsFlagUC); - getFemale().setReceivesBenefitsFlagNonUC(receivesLegacyBenefitsFlag); +// getFemale().setReceivesBenefitsFlagLB(receivesLegacyBenefitsFlag); + getFemale().setReceivesBenefitsFlagNonUC(receivesBenefitsNonUC); } case Single_Male -> { getMale().setReceivesBenefitsFlagUC(receivesBenefitsFlagUC); - getMale().setReceivesBenefitsFlagNonUC(receivesLegacyBenefitsFlag); +// getMale().setReceivesBenefitsFlagLB(receivesLegacyBenefitsFlag); + getMale().setReceivesBenefitsFlagNonUC(receivesBenefitsNonUC); } case Single_Female -> { getFemale().setReceivesBenefitsFlagUC(receivesBenefitsFlagUC); - getFemale().setReceivesBenefitsFlagNonUC(receivesLegacyBenefitsFlag); +// getFemale().setReceivesBenefitsFlagLB(receivesLegacyBenefitsFlag); + getFemale().setReceivesBenefitsFlagNonUC(receivesBenefitsNonUC); } default -> throw new IllegalStateException("Benefit Unit with the following ID has no recognised occupancy: " + getKey().getId()); @@ -536,7 +630,7 @@ protected void updateDisposableIncomeIfNotAtRiskOfWork() { double hoursWorkedPerWeekM = 0.0; double hoursWorkedPerWeekF = 0.0; - int dlltsdM = -1, dlltsdF = -1; + int dlltsdM = -1, dlltsdF = -1, ucTakeUp = 1; double originalIncomePerMonth, secondIncomePerMonth = 0.0; Person male = getMale(); Person female = getFemale(); @@ -565,7 +659,7 @@ protected void updateDisposableIncomeIfNotAtRiskOfWork() { throw new RuntimeException("Benefit Unit with the following ID has no recognised occupancy: " + getKey().getId()); // update disposable income - TaxEvaluation evaluatedTransfers = taxWrapper(hoursWorkedPerWeekM, hoursWorkedPerWeekF, dlltsdM, dlltsdF, originalIncomePerMonth, secondIncomePerMonth); + TaxEvaluation evaluatedTransfers = taxWrapper(hoursWorkedPerWeekM, hoursWorkedPerWeekF, dlltsdM, dlltsdF, ucTakeUp, originalIncomePerMonth, secondIncomePerMonth); yDispMonth = evaluatedTransfers.getDisposableIncomePerMonth(); yBenAmountMonth = evaluatedTransfers.getBenefitsReceivedPerMonth(); @@ -578,7 +672,7 @@ protected void updateDisposableIncomeIfNotAtRiskOfWork() { } } - private TaxEvaluation taxWrapper(double hoursWorkedPerWeekM, double hoursWorkedPerWeekF, int dlltsdM, int dlltsdF, double originalIncomePerMonth, double secondIncomePerMonth) { + private TaxEvaluation taxWrapper(double hoursWorkedPerWeekM, double hoursWorkedPerWeekF, int dlltsdM, int dlltsdF, Integer ucTakeUp, double originalIncomePerMonth, double secondIncomePerMonth) { xChildCareWeek = 0.0; double childcareCostPerMonth = 0.0; @@ -606,7 +700,7 @@ private TaxEvaluation taxWrapper(double hoursWorkedPerWeekM, double hoursWorkedP double taxInnov = (Parameters.donorPoolAveraging) ? -1.0 : statInnovations.getDoubleDraw(8); evaluatedTransfers = new TaxEvaluation(model.getYear(), getRefPersonForDecisions().getDemAge(), getIntValue(Regressors.NumberMembersOver17), getIntValue(Regressors.NumberChildren04), getIntValue(Regressors.NumberChildren59), getIntValue(Regressors.NumberChildren1017), - hoursWorkedPerWeekM, hoursWorkedPerWeekF, dlltsdM, dlltsdF, careProvidedFlag, originalIncomePerMonth, secondIncomePerMonth, + hoursWorkedPerWeekM, hoursWorkedPerWeekF, dlltsdM, dlltsdF, careProvidedFlag, ucTakeUp, originalIncomePerMonth, secondIncomePerMonth, childcareCostPerMonth, socialCareCostPerMonth, getWealthTotValue(Parameters.enableIntertemporalOptimisations), taxInnov); return evaluatedTransfers; @@ -672,10 +766,13 @@ protected void chooseRandomMonthlyOutcomeCovid19() { // Find best donor and convert gross income to disposable MultiKey labourKey = new MultiKey<>(male.getLabourSupplyWeekly(), female.getLabourSupplyWeekly()); - TaxEvaluation evaluatedTransfers; - double taxInnov = (Parameters.donorPoolAveraging) ? -1.0 : taxRandomUniform; - evaluatedTransfers = new TaxEvaluation(model.getYear(), getIntValue(Regressors.MaximumAge), getIntValue(Regressors.NumberMembersOver17), getIntValue(Regressors.NumberChildren04), getIntValue(Regressors.NumberChildren59), getIntValue(Regressors.NumberChildren1017), labourKey.getKey(0).getHours(male), labourKey.getKey(1).getHours(female), male.getDisability(), female.getDisability(), 0, simulatedIncomeToConvertPerMonth, 0.0, 0.0, taxInnov); + double hoursWorkedPerWeekM = labourKey.getKey(0).getHours(male); + double hoursWorkedPerWeekF = labourKey.getKey(1).getHours(female); + int dlltsdM = male.getDisability(); + int dlltsdF = female.getDisability(); + int ucTakeUp = 1; + TaxEvaluation evaluatedTransfers = taxWrapper(hoursWorkedPerWeekM, hoursWorkedPerWeekF, dlltsdM, dlltsdF, ucTakeUp, simulatedIncomeToConvertPerMonth, 0.0); yDispMonth = evaluatedTransfers.getDisposableIncomePerMonth(); yBenAmountMonth = evaluatedTransfers.getBenefitsReceivedPerMonth(); @@ -698,9 +795,13 @@ protected void chooseRandomMonthlyOutcomeCovid19() { // Find best donor and convert gross income to disposable MultiKey labourKey = new MultiKey<>(male.getLabourSupplyWeekly(), Labour.ZERO); - TaxEvaluation evaluatedTransfers; - double taxInnov = (Parameters.donorPoolAveraging) ? -1.0 : taxRandomUniform; - evaluatedTransfers = new TaxEvaluation(model.getYear(), getIntValue(Regressors.MaximumAge), getIntValue(Regressors.NumberMembersOver17), getIntValue(Regressors.NumberChildren04), getIntValue(Regressors.NumberChildren59), getIntValue(Regressors.NumberChildren1017), labourKey.getKey(0).getHours(male), labourKey.getKey(1).getHours(female), male.getDisability(), female.getDisability(), 0, simulatedIncomeToConvertPerMonth, 0.0, 0.0, taxInnov); + double hoursWorkedPerWeekM = labourKey.getKey(0).getHours(male); + double hoursWorkedPerWeekF = labourKey.getKey(1).getHours(female); + int dlltsdM = male.getDisability(); + int dlltsdF = female.getDisability(); + int ucTakeUp = 1; + TaxEvaluation evaluatedTransfers = taxWrapper(hoursWorkedPerWeekM, hoursWorkedPerWeekF, dlltsdM, dlltsdF, ucTakeUp, simulatedIncomeToConvertPerMonth, 0.0); + yDispMonth = evaluatedTransfers.getDisposableIncomePerMonth(); yBenAmountMonth = evaluatedTransfers.getBenefitsReceivedPerMonth(); yGrossMonth = evaluatedTransfers.getGrossIncomePerMonth(); @@ -724,9 +825,13 @@ protected void chooseRandomMonthlyOutcomeCovid19() { // Find best donor and convert gross income to disposable MultiKey labourKey = new MultiKey<>(Labour.ZERO, female.getLabourSupplyWeekly()); - TaxEvaluation evaluatedTransfers; - double taxInnov = (Parameters.donorPoolAveraging) ? -1.0 : taxRandomUniform; - evaluatedTransfers = new TaxEvaluation(model.getYear(), getIntValue(Regressors.MaximumAge), getIntValue(Regressors.NumberMembersOver17), getIntValue(Regressors.NumberChildren04), getIntValue(Regressors.NumberChildren59), getIntValue(Regressors.NumberChildren1017), labourKey.getKey(0).getHours(male), labourKey.getKey(1).getHours(female), male.getDisability(), female.getDisability(), 0, simulatedIncomeToConvertPerMonth, 0.0, 0.0, taxInnov); + double hoursWorkedPerWeekM = labourKey.getKey(0).getHours(male); + double hoursWorkedPerWeekF = labourKey.getKey(1).getHours(female); + int dlltsdM = male.getDisability(); + int dlltsdF = female.getDisability(); + int ucTakeUp = 1; + TaxEvaluation evaluatedTransfers = taxWrapper(hoursWorkedPerWeekM, hoursWorkedPerWeekF, dlltsdM, dlltsdF, ucTakeUp, simulatedIncomeToConvertPerMonth, 0.0); + yDispMonth = evaluatedTransfers.getDisposableIncomePerMonth(); yBenAmountMonth = evaluatedTransfers.getBenefitsReceivedPerMonth(); yGrossMonth = evaluatedTransfers.getGrossIncomePerMonth(); @@ -747,9 +852,13 @@ protected void chooseRandomMonthlyOutcomeCovid19() { // Find best donor and convert gross income to disposable MultiKey labourKey = new MultiKey<>(male.getLabourSupplyWeekly(), Labour.ZERO); - TaxEvaluation evaluatedTransfers; - double taxInnov = (Parameters.donorPoolAveraging) ? -1.0 : taxRandomUniform; - evaluatedTransfers = new TaxEvaluation(model.getYear(), getIntValue(Regressors.MaximumAge), getIntValue(Regressors.NumberMembersOver17), getIntValue(Regressors.NumberChildren04), getIntValue(Regressors.NumberChildren59), getIntValue(Regressors.NumberChildren1017), labourKey.getKey(0).getHours(male), labourKey.getKey(1).getHours(female), male.getDisability(), -1, 0, simulatedIncomeToConvertPerMonth, 0.0, 0.0, taxInnov); + double hoursWorkedPerWeekM = labourKey.getKey(0).getHours(male); + double hoursWorkedPerWeekF = 0.0; + int dlltsdM = male.getDisability(); + int dlltsdF = -1; + int ucTakeUp = 1; + TaxEvaluation evaluatedTransfers = taxWrapper(hoursWorkedPerWeekM, hoursWorkedPerWeekF, dlltsdM, dlltsdF, ucTakeUp, simulatedIncomeToConvertPerMonth, 0.0); + yDispMonth = evaluatedTransfers.getDisposableIncomePerMonth(); yBenAmountMonth = evaluatedTransfers.getBenefitsReceivedPerMonth(); yGrossMonth = evaluatedTransfers.getGrossIncomePerMonth(); @@ -767,9 +876,13 @@ protected void chooseRandomMonthlyOutcomeCovid19() { // Find best donor and convert gross income to disposable MultiKey labourKey = new MultiKey<>(Labour.ZERO, female.getLabourSupplyWeekly()); - TaxEvaluation evaluatedTransfers; - double taxInnov = (Parameters.donorPoolAveraging) ? -1.0 : taxRandomUniform; - evaluatedTransfers = new TaxEvaluation(model.getYear(), getIntValue(Regressors.MaximumAge), getIntValue(Regressors.NumberMembersOver17), getIntValue(Regressors.NumberChildren04), getIntValue(Regressors.NumberChildren59), getIntValue(Regressors.NumberChildren1017), labourKey.getKey(0).getHours(male), labourKey.getKey(1).getHours(female), -1, female.getDisability(), 0, simulatedIncomeToConvertPerMonth, 0.0, 0.0, taxInnov); + double hoursWorkedPerWeekM = 0.0; + double hoursWorkedPerWeekF = labourKey.getKey(1).getHours(female); + int dlltsdM = -1; + int dlltsdF = female.getDisability(); + int ucTakeUp = 1; + TaxEvaluation evaluatedTransfers = taxWrapper(hoursWorkedPerWeekM, hoursWorkedPerWeekF, dlltsdM, dlltsdF, ucTakeUp, simulatedIncomeToConvertPerMonth, 0.0); + yDispMonth = evaluatedTransfers.getDisposableIncomePerMonth(); yBenAmountMonth = evaluatedTransfers.getBenefitsReceivedPerMonth(); yGrossMonth = evaluatedTransfers.getGrossIncomePerMonth(); @@ -1032,7 +1145,7 @@ public void updateLabourChoices() { double originalIncomePerMonth = maleIncome + femaleIncome; double secondIncomePerMonth = Math.min(maleIncome, femaleIncome); - TaxEvaluation ev = taxWrapper(labourKey.getKey(0).getHours(male), labourKey.getKey(1).getHours(female), male.getDisability(), female.getDisability(), originalIncomePerMonth, secondIncomePerMonth); + TaxEvaluation ev = taxWrapper(labourKey.getKey(0).getHours(male), labourKey.getKey(1).getHours(female), male.getDisability(), female.getDisability(), uc_takeup, originalIncomePerMonth, secondIncomePerMonth); cachedEvalByLabourPairs.put(labourKey, new LabourEval(ev)); } @@ -1043,7 +1156,7 @@ public void updateLabourChoices() { male.setLabourSupplyWeekly(labourKey.getKey(0)); double originalIncomePerMonth = Parameters.WEEKS_PER_MONTH * male.getEarningsWeekly() + Math.sinh(male.getYMiscPersGrossMonth()); - TaxEvaluation ev = taxWrapper(labourKey.getKey(0).getHours(male), 0.0, male.getDisability(), -1, originalIncomePerMonth, 0.0); + TaxEvaluation ev = taxWrapper(labourKey.getKey(0).getHours(male), 0.0, male.getDisability(), -1, uc_takeup, originalIncomePerMonth, 0.0); cachedEvalByLabourPairs.put(labourKey, new LabourEval(ev)); } @@ -1054,7 +1167,7 @@ public void updateLabourChoices() { female.setLabourSupplyWeekly(labourKey.getKey(1)); double originalIncomePerMonth = Parameters.WEEKS_PER_MONTH * female.getEarningsWeekly() + Math.sinh(female.getYMiscPersGrossMonth()); - TaxEvaluation ev = taxWrapper(0.0, labourKey.getKey(1).getHours(female), -1, female.getDisability(), originalIncomePerMonth, 0.0); + TaxEvaluation ev = taxWrapper(0.0, labourKey.getKey(1).getHours(female), -1, female.getDisability(), uc_takeup, originalIncomePerMonth, 0.0); cachedEvalByLabourPairs.put(labourKey, new LabourEval(ev)); } @@ -1573,16 +1686,17 @@ protected void updateLabourSupplyAndIncome() { double originalIncomePerMonth = Parameters.WEEKS_PER_MONTH * (labourIncomeWeeklyM + labourIncomeWeeklyF) + yInvestYear /12.0 + yPensYear /12.0; double secondIncomePerMonth = Math.min(labourIncomeWeeklyM, labourIncomeWeeklyF) * Parameters.WEEKS_PER_MONTH; + int ucTakeUp = 1; - TaxEvaluation evaluatedTransfers = taxWrapper(hoursWorkedPerWeekM, hoursWorkedPerWeekF, dlltsdM, dlltsdF, originalIncomePerMonth, secondIncomePerMonth); + TaxEvaluation evaluatedTransfers = taxWrapper(hoursWorkedPerWeekM, hoursWorkedPerWeekF, dlltsdM, dlltsdF, ucTakeUp, originalIncomePerMonth, secondIncomePerMonth); yDispMonth = evaluatedTransfers.getDisposableIncomePerMonth(); yBenAmountMonth = evaluatedTransfers.getBenefitsReceivedPerMonth(); yGrossMonth = evaluatedTransfers.getGrossIncomePerMonth(); demDbMatchTax = evaluatedTransfers.getMatch(); idtaxDbDonor = demDbMatchTax.getCandidateID(); - setReceivedUC(evaluatedTransfers.getReceivedUC()); - setReceivedLegacyBenefits(evaluatedTransfers.getReceivedLegacyBenefit()); + setReceivedBenefitsUC(evaluatedTransfers.getReceivedUC()); + setReceivedBenefitsNonUC(evaluatedTransfers.getReceivedLegacyBenefit()); } else { // intertemporal optimisations disabled @@ -1612,14 +1726,15 @@ protected void updateLabourSupplyAndIncome() { double femaleIncome = Parameters.WEEKS_PER_MONTH * female.getEarningsWeekly() + Math.sinh(female.getYMiscPersGrossMonth()); double originalIncomePerMonth = maleIncome + femaleIncome; double secondIncomePerMonth = Math.min(maleIncome, femaleIncome); + int ucTakeUp = 1; - TaxEvaluation evaluatedTransfers = taxWrapper(labourKey.getKey(0).getHours(male), labourKey.getKey(1).getHours(female), male.getDisability(), female.getDisability(), originalIncomePerMonth, secondIncomePerMonth); + TaxEvaluation evaluatedTransfers = taxWrapper(labourKey.getKey(0).getHours(male), labourKey.getKey(1).getHours(female), male.getDisability(), female.getDisability(), ucTakeUp, originalIncomePerMonth, secondIncomePerMonth); yDispMonth = evaluatedTransfers.getDisposableIncomePerMonth(); yBenAmountMonth = evaluatedTransfers.getBenefitsReceivedPerMonth(); yGrossMonth = evaluatedTransfers.getGrossIncomePerMonth(); - setReceivedUC(evaluatedTransfers.getReceivedUC()); - setReceivedLegacyBenefits(evaluatedTransfers.getReceivedLegacyBenefit()); + setReceivedBenefitsUC(evaluatedTransfers.getReceivedUC()); + setReceivedBenefitsNonUC(evaluatedTransfers.getReceivedLegacyBenefit()); // Note that only benefitUnits at risk of work are considered, so at least one partner is at risk of work double regressionScore = 0.; @@ -1667,13 +1782,14 @@ protected void updateLabourSupplyAndIncome() { male.setLabourSupplyWeekly(labourKey.getKey(0)); double originalIncomePerMonth = Parameters.WEEKS_PER_MONTH * male.getEarningsWeekly() + Math.sinh(male.getYMiscPersGrossMonth()); - TaxEvaluation evaluatedTransfers = taxWrapper(labourKey.getKey(0).getHours(male), 0.0, male.getDisability(), -1, originalIncomePerMonth, 0.0); + int ucTakeUp = 1; + TaxEvaluation evaluatedTransfers = taxWrapper(labourKey.getKey(0).getHours(male), 0.0, male.getDisability(), -1, ucTakeUp, originalIncomePerMonth, 0.0); yDispMonth = evaluatedTransfers.getDisposableIncomePerMonth(); yBenAmountMonth = evaluatedTransfers.getBenefitsReceivedPerMonth(); yGrossMonth = evaluatedTransfers.getGrossIncomePerMonth(); - setReceivedUC(evaluatedTransfers.getReceivedUC()); - setReceivedLegacyBenefits(evaluatedTransfers.getReceivedLegacyBenefit()); + setReceivedBenefitsUC(evaluatedTransfers.getReceivedUC()); + setReceivedBenefitsNonUC(evaluatedTransfers.getReceivedLegacyBenefit()); double regressionScore = 0.; if (male.getAdultChildFlag() == 1) { //If adult children use labour supply estimates for male adult children @@ -1697,13 +1813,14 @@ protected void updateLabourSupplyAndIncome() { female.setLabourSupplyWeekly(labourKey.getKey(1)); double originalIncomePerMonth = Parameters.WEEKS_PER_MONTH * female.getEarningsWeekly() + Math.sinh(female.getYMiscPersGrossMonth()); - TaxEvaluation evaluatedTransfers = taxWrapper(0.0, labourKey.getKey(1).getHours(female), -1, female.getDisability(), originalIncomePerMonth, 0.0); + int ucTakeUp = 1; + TaxEvaluation evaluatedTransfers = taxWrapper(0.0, labourKey.getKey(1).getHours(female), -1, female.getDisability(), ucTakeUp, originalIncomePerMonth, 0.0); yDispMonth = evaluatedTransfers.getDisposableIncomePerMonth(); yBenAmountMonth = evaluatedTransfers.getBenefitsReceivedPerMonth(); yGrossMonth = evaluatedTransfers.getGrossIncomePerMonth(); - setReceivedUC(evaluatedTransfers.getReceivedUC()); - setReceivedLegacyBenefits(evaluatedTransfers.getReceivedLegacyBenefit()); + setReceivedBenefitsUC(evaluatedTransfers.getReceivedUC()); + setReceivedBenefitsNonUC(evaluatedTransfers.getReceivedLegacyBenefit()); double regressionScore = 0.; if (female.getAdultChildFlag() == 1) { //If adult children use labour supply estimates for female adult children @@ -1799,6 +1916,244 @@ protected void updateLabourSupplyAndIncome() { calculateBUIncome(); } + protected void updateLabourSupplyAndIncomeWithUniversalCredit() { + + resetLabourStates(); + Occupancy occupancy = getOccupancy(); + Person male = getMale(); + Person female = getFemale(); + // intertemporal optimisations disabled all the time + + updateNonLabourIncome(); + + // prepare temporary storage variables + Triple labourSupplyChoice = null; + Map, Double> disposableIncomeMonthlyByLabourPairs = new LinkedHashMap<>(); + Map, Double> benefitsReceivedMonthlyByLabourPairs = new LinkedHashMap<>(); + Map, Double> universalCreditByLabourPairs = new LinkedHashMap<>(); + Map, Double> nonUniversalCreditByLabourPairs = new LinkedHashMap<>(); + Map, Double> grossIncomeMonthlyByLabourPairs = new LinkedHashMap<>(); + Map, Match> taxDbMatchByLabourPairs = new LinkedHashMap<>(); + LinkedHashSet> possibleLabourCombinations = findPossibleLabourCombinationsWithUniversalCredit(); // Find possible labour combinations for this benefit unit + Map, Double> labourSupplyUtilityRegressionScoresByLabourPairs = new LinkedHashMap<>(); + + //Sometimes one of the occupants of the couple will be retired (or even under the age to work, which is currently the age to leave home). For this case, the person (not at risk of work)'s labour supply will always be zero, while the other person at risk of work has a choice over the single person Labour Supply set. + if (Occupancy.Couple.equals(occupancy)) { + + for (Triple labourKey : possibleLabourCombinations) { //PB: for each possible discrete number of hours + + //Sets values for regression score calculation + male.setLabourSupplyWeekly(labourKey.getLeft()); + female.setLabourSupplyWeekly(labourKey.getMiddle()); + this.setUC_takeup(labourKey.getRight()); + + //Earnings are composed of the labour income and non-benefit non-employment income Yptciihs_dv() (this is monthly, so no need to multiply by WEEKS_PER_MONTH_RATIO) + double maleIncome = Parameters.WEEKS_PER_MONTH * male.getEarningsWeekly() + Math.sinh(male.getYMiscPersGrossMonth()); + double femaleIncome = Parameters.WEEKS_PER_MONTH * female.getEarningsWeekly() + Math.sinh(female.getYMiscPersGrossMonth()); + double originalIncomePerMonth = maleIncome + femaleIncome; + double secondIncomePerMonth = Math.min(maleIncome, femaleIncome); + + TaxEvaluation evaluatedTransfers = taxWrapper(labourKey.getLeft().getHours(male), labourKey.getMiddle().getHours(female), male.getDisability(), female.getDisability(), labourKey.getRight(), originalIncomePerMonth, secondIncomePerMonth); + + yDispMonth = evaluatedTransfers.getDisposableIncomePerMonth(); + yBenAmountMonth = evaluatedTransfers.getBenefitsReceivedPerMonth(); + yGrossMonth = evaluatedTransfers.getGrossIncomePerMonth(); + universalCreditMonthly = evaluatedTransfers.getUniversalCreditPerMonth(); + nonUCMonthly = evaluatedTransfers.getLegacyBenefitPerMonth(); + + // Note that only benefitUnits at risk of work are considered, so at least one partner is at risk of work + double regressionScore = 0.; + if (male.atRiskOfWork()) { //If male has flexible labour supply + if (female.atRiskOfWork()) { //And female has flexible labour supply + //Follow utility process for couples + regressionScore = Parameters.getRegLabourSupplyUtilityCouples().getScore(this, BenefitUnit.Regressors.class); + } else if (!female.atRiskOfWork()) { //Male has flexible labour supply, female doesn't + //Male is at risk of work and has dependent female + regressionScore = Parameters.getRegLabourSupplyUtilitySingleDep().getScore(this, BenefitUnit.Regressors.class); + + var reg = Parameters.getRegLabourSupplyUtilitySingleDep(); + double betaWomen = reg.getCoefficient("AlignmentFixedCostWomen"); + double xWomen = this.getDoubleValue(Enum.valueOf(BenefitUnit.Regressors.class, "AlignmentFixedCostWomen")); + + regressionScore = regressionScore - (betaWomen * xWomen); //term "(betaWomen * xWomen)" should be zero but this is just a precaution + } + } else if (female.atRiskOfWork() && !male.atRiskOfWork()) { //Male not at risk of work - female must be at risk of work since only benefitUnits at risk are considered here + //Female is at risk of work and has dependent male + regressionScore = Parameters.getRegLabourSupplyUtilitySingleDep().getScore(this, BenefitUnit.Regressors.class); + + var reg = Parameters.getRegLabourSupplyUtilitySingleDep(); + double betaMen = reg.getCoefficient("AlignmentFixedCostMen"); + double xMen = this.getDoubleValue(Enum.valueOf(BenefitUnit.Regressors.class, "AlignmentFixedCostMen")); + + regressionScore = regressionScore - (betaMen * xMen); //term "(betaMen * xMen)" should be zero but this is just a precaution + + } else throw new IllegalArgumentException("None of the partners are at risk of work! HHID " + getKey().getId()); + if (!Parameters.checkFinite(regressionScore)) { + regressionScore = -700.0; + } + + disposableIncomeMonthlyByLabourPairs.put(labourKey, getDisposableIncomeMonthly()); + benefitsReceivedMonthlyByLabourPairs.put(labourKey, getBenefitsReceivedPerMonth()); + universalCreditByLabourPairs.put(labourKey, getUniversalCreditMonthly()); + nonUniversalCreditByLabourPairs.put(labourKey, getNonUCMonthly()); + grossIncomeMonthlyByLabourPairs.put(labourKey, getGrossIncomeMonthly()); + taxDbMatchByLabourPairs.put(labourKey, evaluatedTransfers.getMatch()); + labourSupplyUtilityRegressionScoresByLabourPairs.put(labourKey, regressionScore); //XXX: Adult children could contribute their income to the hh, but then utility would have to be joint for a household with adult children, and they couldn't be treated separately as they are at the moment? + } + } else { + // single adult + + if (Occupancy.Single_Male.equals(occupancy)) { + + for (Triple labourKey : possibleLabourCombinations) { + + male.setLabourSupplyWeekly(labourKey.getLeft()); + this.setUC_takeup(labourKey.getRight()); + + double originalIncomePerMonth = Parameters.WEEKS_PER_MONTH * male.getEarningsWeekly() + Math.sinh(male.getYMiscPersGrossMonth()); + TaxEvaluation evaluatedTransfers = taxWrapper(labourKey.getLeft().getHours(male), 0.0, male.getDisability(), -1, labourKey.getRight(), originalIncomePerMonth, 0.0); + + yDispMonth = evaluatedTransfers.getDisposableIncomePerMonth(); + yBenAmountMonth = evaluatedTransfers.getBenefitsReceivedPerMonth(); + yGrossMonth = evaluatedTransfers.getGrossIncomePerMonth(); + universalCreditMonthly = evaluatedTransfers.getUniversalCreditPerMonth(); + nonUCMonthly = evaluatedTransfers.getLegacyBenefitPerMonth(); + + double regressionScore = 0.; + if (male.getAdultChildFlag() == 1) { //If adult children use labour supply estimates for male adult children + regressionScore = Parameters.getRegLabourSupplyUtilityACMales().getScore(this, Regressors.class); + } else { + regressionScore = Parameters.getRegLabourSupplyUtilityMales().getScore(this, Regressors.class); + } + if (Double.isNaN(regressionScore) || Double.isInfinite(regressionScore)) { + throw new RuntimeException("problem evaluating exponential regression score in labour supply module (2)"); + } + + disposableIncomeMonthlyByLabourPairs.put(labourKey, getDisposableIncomeMonthly()); + benefitsReceivedMonthlyByLabourPairs.put(labourKey, getBenefitsReceivedPerMonth()); + universalCreditByLabourPairs.put(labourKey, getUniversalCreditMonthly()); + nonUniversalCreditByLabourPairs.put(labourKey, getNonUCMonthly()); + grossIncomeMonthlyByLabourPairs.put(labourKey, getGrossIncomeMonthly()); + taxDbMatchByLabourPairs.put(labourKey, evaluatedTransfers.getMatch()); + labourSupplyUtilityRegressionScoresByLabourPairs.put(labourKey, regressionScore); + } + } else if (Occupancy.Single_Female.equals(occupancy)) { //Occupant must be a single female + + for (Triple labourKey : possibleLabourCombinations) { + + female.setLabourSupplyWeekly(labourKey.getMiddle()); + this.setUC_takeup(labourKey.getRight()); + + double originalIncomePerMonth = Parameters.WEEKS_PER_MONTH * female.getEarningsWeekly() + Math.sinh(female.getYMiscPersGrossMonth()); + TaxEvaluation evaluatedTransfers = taxWrapper(0.0, labourKey.getMiddle().getHours(female), -1, female.getDisability(), labourKey.getRight(), originalIncomePerMonth, 0.0); + + yDispMonth = evaluatedTransfers.getDisposableIncomePerMonth(); + yBenAmountMonth = evaluatedTransfers.getBenefitsReceivedPerMonth(); + yGrossMonth = evaluatedTransfers.getGrossIncomePerMonth(); + universalCreditMonthly = evaluatedTransfers.getUniversalCreditPerMonth(); + nonUCMonthly = evaluatedTransfers.getLegacyBenefitPerMonth(); + + double regressionScore = 0.; + if (female.getAdultChildFlag() == 1) { //If adult children use labour supply estimates for female adult children + regressionScore = Parameters.getRegLabourSupplyUtilityACFemales().getScore(this, BenefitUnit.Regressors.class); + } else { + regressionScore = Parameters.getRegLabourSupplyUtilityFemales().getScore(this, BenefitUnit.Regressors.class); + } + if (Double.isNaN(regressionScore) || Double.isInfinite(regressionScore)) { + throw new RuntimeException("problem evaluating exponential regression score in labour supply module (3)"); + } + disposableIncomeMonthlyByLabourPairs.put(labourKey, getDisposableIncomeMonthly()); + benefitsReceivedMonthlyByLabourPairs.put(labourKey, getBenefitsReceivedPerMonth()); + universalCreditByLabourPairs.put(labourKey, getUniversalCreditMonthly()); + nonUniversalCreditByLabourPairs.put(labourKey, getNonUCMonthly()); + grossIncomeMonthlyByLabourPairs.put(labourKey, getGrossIncomeMonthly()); + taxDbMatchByLabourPairs.put(labourKey, evaluatedTransfers.getMatch()); + labourSupplyUtilityRegressionScoresByLabourPairs.put(labourKey, regressionScore); + } + } + } + if (labourSupplyUtilityRegressionScoresByLabourPairs.isEmpty()) { + // error check + + System.out.print("\nlabourSupplyUtilityExponentialRegressionScoresByLabourPairs for household " + key.getId() + " with occupants "); + if (male != null) { + System.out.print("male : " + male.getKey().getId() + ", "); + } + if (female != null) { + System.out.print("female : " + female.getKey().getId() + ", "); + } + System.out.print("is empty!"); + } + + //Sample labour supply from possible labour (pairs of) values + double labourInnov = 0; + + // Check if: + // - single occupant is employed + // - or male of couple is employed + // - or male is not at risk of work and female is employed + // -> then persist employment at prob_{persist_employment} + // Otherwise + // -> persist unemployment at prob_{persist_unemployment} + + if (occupancy.Single_Male.equals(occupancy) && male.atRiskOfWork() && male.getEmployed() == 1) { + labourInnov = getLabourInnovation(Parameters.labour_innovation_employment_persistence_probability); + } else if (occupancy.Single_Female.equals(occupancy) && female.atRiskOfWork() && female.getEmployed() == 1) { + labourInnov = getLabourInnovation(Parameters.labour_innovation_employment_persistence_probability); + } else if (occupancy.equals(Occupancy.Couple) && + ((male.atRiskOfWork() && male.getEmployed() == 1) || + (!male.atRiskOfWork() && female.atRiskOfWork() && female.getEmployed() == 1))) { + labourInnov = getLabourInnovation(Parameters.labour_innovation_employment_persistence_probability); + } else { + labourInnov = getLabourInnovation(Parameters.labour_innovation_notinemployment_persistence_probability); + } + + try { + Map, Double> labourSupplyUtilityRegressionProbabilitiesByLabourPairs = + convertRegressionScoresToProbabilities(labourSupplyUtilityRegressionScoresByLabourPairs); + labourSupplyChoice = ManagerRegressions.multiEvent(labourSupplyUtilityRegressionProbabilitiesByLabourPairs, labourInnov); + // labourRandomUniform is not updated here to avoid issues with search routine for labour market alignment + } catch (RuntimeException e) { + System.out.print("Could not determine labour supply choice for BU with ID: " + getKey().getId()); + } + // populate labour supply + if(model.debugCommentsOn && labourSupplyChoice!=null) { + log.trace("labour supply choice " + labourSupplyChoice); + } + this.setUC_takeup(labourSupplyChoice.getRight()); + if(Occupancy.Couple.equals(occupancy)) { + male.setLabourSupplyWeekly(labourSupplyChoice.getLeft()); + female.setLabourSupplyWeekly(labourSupplyChoice.getMiddle()); + } else { + if(Occupancy.Single_Male.equals(occupancy)) { + male.setLabourSupplyWeekly(labourSupplyChoice.getLeft()); + } else { //Occupant must be single female + female.setLabourSupplyWeekly(labourSupplyChoice.getMiddle()); + } + } + + // allow for formal childcare costs + if (Parameters.flagFormalChildcare && !Parameters.flagSuppressChildcareCosts) { + updateChildcareCostPerWeek(model.getYear(), getRefPersonForDecisions().getDemAge()); + } + if (Parameters.flagSocialCare && !Parameters.flagSuppressSocialCareCosts) { + updateSocialCareCostPerWeek(); + } + + // populate disposable income + yDispMonth = disposableIncomeMonthlyByLabourPairs.get(labourSupplyChoice); + yBenAmountMonth = benefitsReceivedMonthlyByLabourPairs.get(labourSupplyChoice); + universalCreditMonthly = universalCreditByLabourPairs.get(labourSupplyChoice); + nonUCMonthly = nonUniversalCreditByLabourPairs.get(labourSupplyChoice); + setUC_takeup(labourSupplyChoice.getRight()); + setReceivedBenefitsUC(getUniversalCreditMonthly() > 0. ? 1 : 0); + setReceivedBenefitsNonUC(getNonUCMonthly() > 0. ? 1 : 0); + yGrossMonth = grossIncomeMonthlyByLabourPairs.get(labourSupplyChoice); + + //Update gross income variables for the household and all occupants: + calculateBUIncome(); + } + private MultiKeyMap convertRegressionScoresToProbabilities(MultiKeyMap regressionScoresMap) { MultiKeyMap labourSupplyUtilityRegressionProbabilitiesByLabourPairs = MultiKeyMap.multiKeyMap(new LinkedMap<>()); // Output map to update and return double logSumExp = getLogSumExp(regressionScoresMap); @@ -1813,7 +2168,21 @@ private MultiKeyMap convertRegressionScoresToProbabilities(Multi return labourSupplyUtilityRegressionProbabilitiesByLabourPairs; } - private static double getLogSumExp(MultiKeyMap regressionScoresMap) { + private Map, Double> convertRegressionScoresToProbabilities(Map, Double> regressionScoresMap) { + Map, Double> labourSupplyUtilityRegressionProbabilitiesByLabourPairs = new LinkedHashMap<>(); + double logSumExp = getLogSumExp(regressionScoresMap); + + // Transform using the log-sum-exp + for (Triple key : regressionScoresMap.keySet()) { + double regressionScore = regressionScoresMap.get(key); + double regressionProbability = Math.exp(regressionScore - logSumExp); + labourSupplyUtilityRegressionProbabilitiesByLabourPairs.put(key, regressionProbability); + } + + return labourSupplyUtilityRegressionProbabilitiesByLabourPairs; + } + + private static double getLogSumExp(Map, Double> regressionScoresMap) { double maxRegressionScore = Double.NEGATIVE_INFINITY; double sumExpRegScoreMinusMax = 0.; double logSumExp = 0.; @@ -1835,16 +2204,38 @@ private static double getLogSumExp(MultiKeyMap regressionScoresM return logSumExp; } - protected void calculateBUIncome() { + private static double getLogSumExp(MultiKeyMap regressionScoresMap) { + double maxRegressionScore = Double.NEGATIVE_INFINITY; + double sumExpRegScoreMinusMax = 0.; + double logSumExp = 0.; - /* - * This method updates income variables for responsible persons in the household - * - * BenefitUnit income quintiles are based on labour, pension, miscellaneous (see comment for definition), - * Trade Union / Friendly Society Payment and maintenace or alimony. - * - * Gross household income combination of spouses income if partnered or personal income if single. Adjusted for - * household composition using OECD-modified scale and inflation. + // Find maximum of regression scores + for (double val : regressionScoresMap.values()) { + if(val > maxRegressionScore){ + maxRegressionScore = val; + } + } + + // Calculate sum of exp() differences between each element and max + for (double val : regressionScoresMap.values()) { + sumExpRegScoreMinusMax += Math.exp(val - maxRegressionScore); + } + + // Calculate log sum exp + logSumExp = maxRegressionScore + Math.log(sumExpRegScoreMinusMax); + return logSumExp; + } + + protected void calculateBUIncome() { + + /* + * This method updates income variables for responsible persons in the household + * + * BenefitUnit income quintiles are based on labour, pension, miscellaneous (see comment for definition), + * Trade Union / Friendly Society Payment and maintenace or alimony. + * + * Gross household income combination of spouses income if partnered or personal income if single. Adjusted for + * household composition using OECD-modified scale and inflation. * Normalised using inverse hyperbolic sine. * * labour income = yplgrs_dv at person level @@ -2000,6 +2391,8 @@ public enum Regressors { FemaleEduM_4, FemaleEduM_40, FemaleHoursAbove40, + FemaleIncomeDiv100, + FemaleIncomeSqDiv10000, FemaleLeisure, //24*7 - labour supply weekly for Female FemaleLeisure_DChildren017, //Male leisure interacted with dummy for presence of children aged 0 - 17 FemaleLeisure_DChildren1317, //Male leisure interacted with dummy for presence of children aged 13-17 @@ -2158,17 +2551,22 @@ public enum Regressors { L1_lhw_Male_43, L1_lhw_Male_44, Liwwh_1, - Liwwh_10, - Liwwh_2, - Liwwh_20, - Liwwh_3, - Liwwh_30, - Liwwh_4, - Liwwh_40, - Liwwh_5, - Liwwh_50, - Liwwh_6, - Liwwh_60, + Liwwh_010, + Liwwh_011, + Liwwh_020, + Liwwh_021, + Liwwh_030, + Liwwh_031, + Liwwh_040, + Liwwh_041, + Liwwh_100, + Liwwh_101, + Liwwh_200, + Liwwh_201, + Liwwh_300, + Liwwh_301, + Liwwh_400, + Liwwh_401, Liwwh_Female_01, Liwwh_Female_02, Liwwh_Female_03, @@ -2297,6 +2695,8 @@ public enum Regressors { MaleEduM_4, MaleEduM_40, MaleHoursAbove40, + MaleIncomeDiv100, + MaleIncomeSqDiv10000, MaleLeisure, //24*7 - labour supply weekly for male MaleLeisure_DChildren017, //Male leisure interacted with dummy for presence of children aged 0 - 17 MaleLeisure_DChildren1317, //Male leisure interacted with dummy for presence of children aged 13-17 @@ -2319,12 +2719,194 @@ public enum Regressors { MaleLeisure_UKF, MaleLeisure_UKG, MaleLeisure_UKH, + MaleLeisure_UKI, MaleLeisure_UKJ, MaleLeisure_UKK, MaleLeisure_UKL, MaleLeisure_UKM, MaleLeisure_UKN, MaleLeisureSq, + + //Enums added for Universal Credit effects on LS + UC_TakeUp, + Hrs_36Plus_Female, + Hrs_36Plus_Male, + + UKC_1, + UKD_1, + UKE_1, + UKF_1, + UKG_1, + UKH_1, + UKI_1, + UKJ_1, + UKK_1, + UKL_1, + UKM_1, + UKN_1, + UKC_10, + UKD_10, + UKE_10, + UKF_10, + UKG_10, + UKH_10, + UKI_10, + UKJ_10, + UKK_10, + UKL_10, + UKM_10, + UKN_10, + UKC_11, + UKD_11, + UKE_11, + UKF_11, + UKG_11, + UKH_11, + UKI_11, + UKJ_11, + UKK_11, + UKL_11, + UKM_11, + UKN_11, + UKC_20, + UKD_20, + UKE_20, + UKF_20, + UKG_20, + UKH_20, + UKI_20, + UKJ_20, + UKK_20, + UKL_20, + UKM_20, + UKN_20, + UKC_21, + UKD_21, + UKE_21, + UKF_21, + UKG_21, + UKH_21, + UKI_21, + UKJ_21, + UKK_21, + UKL_21, + UKM_21, + UKN_21, + UKC_30, + UKD_30, + UKE_30, + UKF_30, + UKG_30, + UKH_30, + UKI_30, + UKJ_30, + UKK_30, + UKL_30, + UKM_30, + UKN_30, + UKC_31, + UKD_31, + UKE_31, + UKF_31, + UKG_31, + UKH_31, + UKI_31, + UKJ_31, + UKK_31, + UKL_31, + UKM_31, + UKN_31, + UKC_40, + UKD_40, + UKE_40, + UKF_40, + UKG_40, + UKH_40, + UKI_40, + UKJ_40, + UKK_40, + UKL_40, + UKM_40, + UKN_40, + UKC_41, + UKD_41, + UKE_41, + UKF_41, + UKG_41, + UKH_41, + UKI_41, + UKJ_41, + UKK_41, + UKL_41, + UKM_41, + UKN_41, + + Liwwh_Male_100, + Liwwh_Female_100, + Liwwh_Male_101, + Liwwh_Female_101, + Liwwh_Male_110, + Liwwh_Female_110, + Liwwh_Male_111, + Liwwh_Female_111, + Liwwh_Male_120, + Liwwh_Female_120, + Liwwh_Male_121, + Liwwh_Female_121, + Liwwh_Male_130, + Liwwh_Female_130, + Liwwh_Male_131, + Liwwh_Female_131, + Liwwh_Male_140, + Liwwh_Female_140, + Liwwh_Male_141, + Liwwh_Female_141, + Liwwh_Male_200, + Liwwh_Female_200, + Liwwh_Male_201, + Liwwh_Female_201, + Liwwh_Male_210, + Liwwh_Female_210, + Liwwh_Male_211, + Liwwh_Female_211, + Liwwh_Male_220, + Liwwh_Female_220, + Liwwh_Male_230, + Liwwh_Female_230, + Liwwh_Male_240, + Liwwh_Female_240, + Liwwh_Male_300, + Liwwh_Female_300, + Liwwh_Male_301, + Liwwh_Female_301, + Liwwh_Male_310, + Liwwh_Female_310, + Liwwh_Male_311, + Liwwh_Female_311, + Liwwh_Male_320, + Liwwh_Female_320, + Liwwh_Male_330, + Liwwh_Female_330, + Liwwh_Male_340, + Liwwh_Female_340, + Liwwh_Male_400, + Liwwh_Female_400, + Liwwh_Male_401, + Liwwh_Female_401, + Liwwh_Male_410, + Liwwh_Female_410, + Liwwh_Male_411, + Liwwh_Female_411, + Liwwh_Male_420, + Liwwh_Female_420, + Liwwh_Male_430, + Liwwh_Female_430, + Liwwh_Male_440, + Liwwh_Female_440, + + + + //Enums for use with the tax-benefit matching method MaximumAge, // Returns maximum age of responsible individuals in the benefit (tax) unit MinimumAge, // Returns maximum age of all people in benefit unit n_children_0, @@ -2460,7 +3042,6 @@ public double getDoubleValue(Enum variableID) { return (getDisposableIncomeMonthlyUpratedToBasePriceYear() - getNonDiscretionaryExpenditureMonthlyUpratedToBasePriceYear()) * getIndicatorChildren(0,2).ordinal() * 1.e-2; } - case AlignmentFixedCostMen -> { return (getMale() != null && (!getMale().getLabourSupplyWeekly().equals(Labour.ZERO))) ? 1. :0.; // Note != ZERO condition } @@ -2563,6 +3144,13 @@ public double getDoubleValue(Enum variableID) { } else return 0.; } else throw new IllegalArgumentException("Error - the region used in regression doesn't match the country in the simulation!"); } + case MaleLeisure_UKI -> { + if(model.getCountry().equals(Country.UK)) { + if(getRegion().equals(Region.UKI)) { + return (Parameters.HOURS_IN_WEEK - getMale().getLabourSupplyHoursWeekly()); + } else return 0.; + } else throw new IllegalArgumentException("Error - the region used in regression doesn't match the country in the simulation!"); + } case MaleLeisure_UKJ -> { if(model.getCountry().equals(Country.UK)) { if(getRegion().equals(Region.UKJ)) { @@ -2627,6 +3215,15 @@ public double getDoubleValue(Enum variableID) { return getFemaleLeisureHoursWeekly() * getDisposableIncomeMonthlyUpratedToBasePriceYear() * 1.e-2; } else return 0.; } + case FemaleIncomeDiv100 -> { + return (getFemale() == null ? 0. : getDisposableIncomeMonthlyUpratedToBasePriceYear() * 1.e-2); + } + case FemaleIncomeSqDiv10000 -> { + return (getFemale() == null ? 0. : (getDisposableIncomeMonthlyUpratedToBasePriceYear() - + getNonDiscretionaryExpenditureMonthlyUpratedToBasePriceYear()) * + (getDisposableIncomeMonthlyUpratedToBasePriceYear() - + getNonDiscretionaryExpenditureMonthlyUpratedToBasePriceYear()) * 1.e-4); + } case FemaleLeisure -> { //24*7 - labour supply weekly for Female return getFemaleLeisureHoursWeekly(); } @@ -3270,97 +3867,7 @@ public double getDoubleValue(Enum variableID) { case L1_lhw_Female_44 -> { return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT)) ? getFemale().getL1LabourSupplyHoursWeekly() : 0.; } - case Liwwh_1 -> { - if (Occupancy.Single_Female.equals(getOccupancy())) { - return (getFemale().getLabourSupplyWeekly().equals(Labour.TEN)) ? getFemale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_1 for non-single woman"); - } - } - case Liwwh_10 -> { - if (Occupancy.Single_Male.equals(getOccupancy())) { - return (getMale().getLabourSupplyWeekly().equals(Labour.TEN)) ? getMale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_10 for non-single man"); - } - } - case Liwwh_2 -> { - if (Occupancy.Single_Female.equals(getOccupancy())) { - return (getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY)) ? getFemale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_2 for non-single woman"); - } - } - case Liwwh_20 -> { - if (Occupancy.Single_Male.equals(getOccupancy())) { - return (getMale().getLabourSupplyWeekly().equals(Labour.TWENTY)) ? getMale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_20 for non-single man"); - } - } - case Liwwh_3 -> { - if (Occupancy.Single_Female.equals(getOccupancy())) { - return (getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY)) ? getFemale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_3 for non-single woman"); - } - } - case Liwwh_30 -> { - if (Occupancy.Single_Male.equals(getOccupancy())) { - return (getMale().getLabourSupplyWeekly().equals(Labour.THIRTY)) ? getMale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_30 for non-single man"); - } - } - case Liwwh_4 -> { - if (Occupancy.Single_Female.equals(getOccupancy())) { - return (getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT)) ? getFemale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_4 for non-single woman"); - } - } - case Liwwh_40 -> { - if (Occupancy.Single_Male.equals(getOccupancy())) { - return (getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT)) ? getMale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_40 for non-single man"); - } - } - case Liwwh_5 -> { - if (Occupancy.Single_Female.equals(getOccupancy())) { - return (getFemale().getLabourSupplyWeekly().equals(Labour.FORTY_FIVE)) ? getFemale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_5 for non-single woman"); - } - } - case Liwwh_50 -> { - if (Occupancy.Single_Male.equals(getOccupancy())) { - return (getMale().getLabourSupplyWeekly().equals(Labour.FORTY_FIVE)) ? getMale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_50 for non-single man"); - } - } - case Liwwh_6 -> { - if (Occupancy.Single_Female.equals(getOccupancy())) { - return (getFemale().getLabourSupplyWeekly().equals(Labour.FIFTY_FIVE)) ? getFemale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_6 for non-single woman"); - } - } - case Liwwh_60 -> { - if (Occupancy.Single_Male.equals(getOccupancy())) { - return (getMale().getLabourSupplyWeekly().equals(Labour.FIFTY_FIVE)) ? getMale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_60 for non-single man"); - } - } - case Liwwh_Female_1 -> { - if (Occupancy.Couple.equals(getOccupancy()) && !getMale().atRiskOfWork()) { - return (getFemale().getLabourSupplyWeekly().equals(Labour.TEN)) ? getFemale().getLiwwh() : 0.; - } else { - return 0.; - } - } + case Liwwh_Female_2 -> { if (Occupancy.Couple.equals(getOccupancy()) && !getMale().atRiskOfWork()) { return (getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY)) ? getFemale().getLiwwh() : 0.; @@ -3396,20 +3903,20 @@ public double getDoubleValue(Enum variableID) { return 0.; } } - case Liwwh_Female_10, Liwwh_Female_20, Liwwh_Female_30, Liwwh_Female_40, Liwwh_Female_50, Liwwh_Female_60 -> { - if (Occupancy.Couple.equals(getOccupancy())) { - return (getFemale().getLabourSupplyWeekly().equals(Labour.ZERO)) ? getFemale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_Female_XY for non-couple"); - } - } - case Liwwh_Female_01, Liwwh_Female_11, Liwwh_Female_21, Liwwh_Female_31, Liwwh_Female_41, Liwwh_Female_51, Liwwh_Female_61 -> { - if (Occupancy.Couple.equals(getOccupancy())) { - return (getFemale().getLabourSupplyWeekly().equals(Labour.TEN)) ? getFemale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_Female_XY for non-couple"); - } - } +// case Liwwh_Female_10, Liwwh_Female_20, Liwwh_Female_30, Liwwh_Female_40, Liwwh_Female_50, Liwwh_Female_60 -> { +// if (Occupancy.Couple.equals(getOccupancy())) { +// return (getFemale().getLabourSupplyWeekly().equals(Labour.ZERO)) ? getFemale().getLiwwh() : 0.; +// } else { +// throw new RuntimeException("request for parameter Liwwh_Female_XY for non-couple"); +// } +// } +// case Liwwh_Female_01, Liwwh_Female_11, Liwwh_Female_21, Liwwh_Female_31, Liwwh_Female_41, Liwwh_Female_51, Liwwh_Female_61 -> { +// if (Occupancy.Couple.equals(getOccupancy())) { +// return (getFemale().getLabourSupplyWeekly().equals(Labour.TEN)) ? getFemale().getLiwwh() : 0.; +// } else { +// throw new RuntimeException("request for parameter Liwwh_Female_XY for non-couple"); +// } +// } case Liwwh_Female_02, Liwwh_Female_12, Liwwh_Female_22, Liwwh_Female_32, Liwwh_Female_42, Liwwh_Female_52, Liwwh_Female_62 -> { if (Occupancy.Couple.equals(getOccupancy())) { return (getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY)) ? getFemale().getLiwwh() : 0.; @@ -3445,13 +3952,13 @@ public double getDoubleValue(Enum variableID) { throw new RuntimeException("request for parameter Liwwh_Female_XY for non-couple"); } } - case Liwwh_Male_1 -> { - if (Occupancy.Couple.equals(getOccupancy()) && !getFemale().atRiskOfWork()) { - return (getMale().getLabourSupplyWeekly().equals(Labour.TEN)) ? getMale().getLiwwh() : 0.; - } else { - return 0.; - } - } +// case Liwwh_Male_1 -> { +// if (Occupancy.Couple.equals(getOccupancy()) && !getFemale().atRiskOfWork()) { +// return (getMale().getLabourSupplyWeekly().equals(Labour.TEN)) ? getMale().getLiwwh() : 0.; +// } else { +// return 0.; +// } +// } case Liwwh_Male_2 -> { if (Occupancy.Couple.equals(getOccupancy()) && !getFemale().atRiskOfWork()) { return (getMale().getLabourSupplyWeekly().equals(Labour.TWENTY)) ? getMale().getLiwwh() : 0.; @@ -3494,34 +4001,34 @@ public double getDoubleValue(Enum variableID) { throw new RuntimeException("request for parameter Liwwh_Male_XY for non-couple"); } } - case Liwwh_Male_10, Liwwh_Male_11, Liwwh_Male_12, Liwwh_Male_13, Liwwh_Male_14, Liwwh_Male_15, Liwwh_Male_16 -> { - if (Occupancy.Couple.equals(getOccupancy())) { - return (getMale().getLabourSupplyWeekly().equals(Labour.TEN)) ? getMale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_Male_XY for non-couple"); - } - } - case Liwwh_Male_20, Liwwh_Male_21, Liwwh_Male_22, Liwwh_Male_23, Liwwh_Male_24, Liwwh_Male_25, Liwwh_Male_26 -> { - if (Occupancy.Couple.equals(getOccupancy())) { - return (getMale().getLabourSupplyWeekly().equals(Labour.TWENTY)) ? getMale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_Male_XY for non-couple"); - } - } - case Liwwh_Male_30, Liwwh_Male_31, Liwwh_Male_32, Liwwh_Male_33, Liwwh_Male_34, Liwwh_Male_35, Liwwh_Male_36 -> { - if (Occupancy.Couple.equals(getOccupancy())) { - return (getMale().getLabourSupplyWeekly().equals(Labour.THIRTY)) ? getMale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_Male_XY for non-couple"); - } - } - case Liwwh_Male_40, Liwwh_Male_41, Liwwh_Male_42, Liwwh_Male_43, Liwwh_Male_44, Liwwh_Male_45, Liwwh_Male_46 -> { - if (Occupancy.Couple.equals(getOccupancy())) { - return (getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT)) ? getMale().getLiwwh() : 0.; - } else { - throw new RuntimeException("request for parameter Liwwh_Male_XY for non-couple"); - } - } +// case Liwwh_Male_10, Liwwh_Male_11, Liwwh_Male_12, Liwwh_Male_13, Liwwh_Male_14, Liwwh_Male_15, Liwwh_Male_16 -> { +// if (Occupancy.Couple.equals(getOccupancy())) { +// return (getMale().getLabourSupplyWeekly().equals(Labour.TEN)) ? getMale().getLiwwh() : 0.; +// } else { +// throw new RuntimeException("request for parameter Liwwh_Male_XY for non-couple"); +// } +// } +// case Liwwh_Male_20, Liwwh_Male_21, Liwwh_Male_22, Liwwh_Male_23, Liwwh_Male_24, Liwwh_Male_25, Liwwh_Male_26 -> { +// if (Occupancy.Couple.equals(getOccupancy())) { +// return (getMale().getLabourSupplyWeekly().equals(Labour.TWENTY)) ? getMale().getLiwwh() : 0.; +// } else { +// throw new RuntimeException("request for parameter Liwwh_Male_XY for non-couple"); +// } +// } +// case Liwwh_Male_30, Liwwh_Male_31, Liwwh_Male_32, Liwwh_Male_33, Liwwh_Male_34, Liwwh_Male_35, Liwwh_Male_36 -> { +// if (Occupancy.Couple.equals(getOccupancy())) { +// return (getMale().getLabourSupplyWeekly().equals(Labour.THIRTY)) ? getMale().getLiwwh() : 0.; +// } else { +// throw new RuntimeException("request for parameter Liwwh_Male_XY for non-couple"); +// } +// } +// case Liwwh_Male_40, Liwwh_Male_41, Liwwh_Male_42, Liwwh_Male_43, Liwwh_Male_44, Liwwh_Male_45, Liwwh_Male_46 -> { +// if (Occupancy.Couple.equals(getOccupancy())) { +// return (getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT)) ? getMale().getLiwwh() : 0.; +// } else { +// throw new RuntimeException("request for parameter Liwwh_Male_XY for non-couple"); +// } +// } case Liwwh_Male_50, Liwwh_Male_51, Liwwh_Male_52, Liwwh_Male_53, Liwwh_Male_54, Liwwh_Male_55, Liwwh_Male_56 -> { if (Occupancy.Couple.equals(getOccupancy())) { return (getMale().getLabourSupplyWeekly().equals(Labour.FORTY_FIVE)) ? getMale().getLiwwh() : 0.; @@ -3648,6 +4155,86 @@ public double getDoubleValue(Enum variableID) { case Year_transformed -> { return (Parameters.isFixTimeTrend && getYear() >= Parameters.timeTrendStopsIn) ? (double) Parameters.timeTrendStopsIn - 2000 : (double) getYear() - 2000; } + + // --------------------------------------------------------------------------------- + // LIWWH case statements for single male/female households with UC takeup + // Pattern: Liwwh_{male work index}{female work index}{UC takeup} + // In Single_Male households: any enum with femaleWorkIndex != 0 returns 0 + // In Single_Female households: any enum with maleWorkIndex != 0 returns 0 + // --------------------------------------------------------------------------------- + case Liwwh_1 -> { + // Baseline: for singles return the single adult's liwwh if UC takeup, else 0 + Person male = getMale(); + Person female = getFemale(); + if (male != null && female == null && uc_takeup == 1) { + return male.getLiwwh(); + } + if (female != null && male == null && uc_takeup == 1) { + return female.getLiwwh(); + } + return 0.; + } + + case Liwwh_010, Liwwh_011, Liwwh_020, Liwwh_021, Liwwh_030, Liwwh_031, Liwwh_040, Liwwh_041, + Liwwh_100, Liwwh_101, Liwwh_200, Liwwh_201, Liwwh_300, Liwwh_301, Liwwh_400, Liwwh_401 -> { + + Person male = getMale(); + Person female = getFemale(); + + // If not a single-adult BU, these parameters aren't defined (per user request) + if ((male != null && female != null) || (male == null && female == null)) { + return 0.; + } + + // Decode indices from enum name: Liwwh_{m}{f}{uc} + // m/f indices: 0..4 correspond to Labour: ZERO/TEN/TWENTY/THIRTY/THIRTY_EIGHT + // uc: 0/1 corresponds to uc_takeup + final String name = ((Regressors) variableID).name(); + final String code = name.substring("Liwwh_".length()); + final int maleIndex = Character.digit(code.charAt(0), 10); + final int femaleIndex = Character.digit(code.charAt(1), 10); + final int ucIndex = Character.digit(code.charAt(2), 10); + final int takeUp = (uc_takeup == null ? 0 : uc_takeup); + + if (takeUp != ucIndex) { + return 0.; + } + + // Single male BU + if (male != null) { + if (femaleIndex != 0) { + return 0.; + } + // maleIndex 0 implies ZERO hours; 1..4 imply TEN/TWENTY/THIRTY/THIRTY_EIGHT + final Labour expected = switch (maleIndex) { + case 0 -> Labour.ZERO; + case 1 -> Labour.TEN; + case 2 -> Labour.TWENTY; + case 3 -> Labour.THIRTY; + case 4 -> Labour.THIRTY_EIGHT; + default -> null; + }; + return (expected != null && expected.equals(male.getLabourSupplyWeekly())) ? male.getLiwwh() : 0.; + } + + // Single female BU + if (female != null) { + if (maleIndex != 0) { + return 0.; + } + final Labour expected = switch (femaleIndex) { + case 0 -> Labour.ZERO; + case 1 -> Labour.TEN; + case 2 -> Labour.TWENTY; + case 3 -> Labour.THIRTY; + case 4 -> Labour.THIRTY_EIGHT; + default -> null; + }; + return (expected != null && expected.equals(female.getLabourSupplyWeekly())) ? female.getLiwwh() : 0.; + } + + return 0.; + } case couple_emp_2ft -> { return (getCoupleBoolean() && (getMinWeeklyHoursWorked() >= Parameters.MIN_HOURS_FULL_TIME_EMPLOYED)) ? 1.0 : 0.0; } @@ -3801,6 +4388,572 @@ public double getDoubleValue(Enum variableID) { case Cut10 -> { return 0.; } + // UC labour supply case statements + case UC_TakeUp -> { + return (uc_takeup == null ? 0. : uc_takeup.doubleValue()); + } + case Hrs_36Plus_Female -> { + return getFemale().getLabourSupplyHoursWeekly() >= 36 ? 1. : 0.; + } + case Hrs_36Plus_Male -> { + return getMale().getLabourSupplyHoursWeekly() >= 36 ? 1. : 0.; + } + case UKC_1 -> { + return (region.equals(Region.UKC) && uc_takeup == 1) ? 1. : 0.; + } + case UKD_1 -> { + return (region.equals(Region.UKD) && uc_takeup == 1) ? 1. : 0.; + } + case UKE_1 -> { + return (region.equals(Region.UKE) && uc_takeup == 1) ? 1. : 0.; + } + case UKF_1 -> { + return (region.equals(Region.UKF) && uc_takeup == 1) ? 1. : 0.; + } + case UKG_1 -> { + return (region.equals(Region.UKG) && uc_takeup == 1) ? 1. : 0.; + } + case UKH_1 -> { + return (region.equals(Region.UKH) && uc_takeup == 1) ? 1. : 0.; + } + case UKI_1 -> { + return (region.equals(Region.UKI) && uc_takeup == 1) ? 1. : 0.; + } + case UKJ_1 -> { + return (region.equals(Region.UKJ) && uc_takeup == 1) ? 1. : 0.; + } + case UKK_1 -> { + return (region.equals(Region.UKK) && uc_takeup == 1) ? 1. : 0.; + } + case UKL_1 -> { + return (region.equals(Region.UKL) && uc_takeup == 1) ? 1. : 0.; + } + case UKM_1 -> { + return (region.equals(Region.UKM) && uc_takeup == 1) ? 1. : 0.; + } + case UKN_1 -> { + return (region.equals(Region.UKN) && uc_takeup == 1) ? 1. : 0.; + } + case UKC_10 -> { + return (region.equals(Region.UKC) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? 1. : 0.; + } + case UKC_11 -> { + return (region.equals(Region.UKC) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? 1. : 0.; + } + case UKD_10 -> { + return (region.equals(Region.UKD) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? 1. : 0.; + } + case UKD_20 -> { + return (region.equals(Region.UKD) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKD_11 -> { + return (region.equals(Region.UKD) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? 1. : 0.; + } + case UKD_21 -> { + return (region.equals(Region.UKD) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKD_30 -> { + return (region.equals(Region.UKD) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKD_31 -> { + return (region.equals(Region.UKD) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKD_40 -> { + return (region.equals(Region.UKD) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? 1. : 0.; + } + case UKD_41 -> { + return (region.equals(Region.UKD) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? 1. : 0.; + } + + case UKE_10 -> { + return (region.equals(Region.UKE) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? 1. : 0.; + } + case UKE_20 -> { + return (region.equals(Region.UKE) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKE_11 -> { + return (region.equals(Region.UKE) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? 1. : 0.; + } + case UKE_21 -> { + return (region.equals(Region.UKE) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKE_30 -> { + return (region.equals(Region.UKE) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKE_31 -> { + return (region.equals(Region.UKE) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKE_40 -> { + return (region.equals(Region.UKE) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? 1. : 0.; + } + case UKE_41 -> { + return (region.equals(Region.UKE) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? 1. : 0.; + } + + case UKF_10 -> { + return (region.equals(Region.UKF) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? 1. : 0.; + } + case UKF_20 -> { + return (region.equals(Region.UKF) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKF_11 -> { + return (region.equals(Region.UKF) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? 1. : 0.; + } + case UKF_21 -> { + return (region.equals(Region.UKF) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKF_30 -> { + return (region.equals(Region.UKF) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKF_31 -> { + return (region.equals(Region.UKF) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKF_40 -> { + return (region.equals(Region.UKF) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? 1. : 0.; + } + case UKF_41 -> { + return (region.equals(Region.UKF) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? 1. : 0.; + } + + case UKG_10 -> { + return (region.equals(Region.UKG) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? 1. : 0.; + } + case UKG_20 -> { + return (region.equals(Region.UKG) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKG_11 -> { + return (region.equals(Region.UKG) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? 1. : 0.; + } + case UKG_21 -> { + return (region.equals(Region.UKG) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKG_30 -> { + return (region.equals(Region.UKG) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKG_31 -> { + return (region.equals(Region.UKG) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKG_40 -> { + return (region.equals(Region.UKG) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? 1. : 0.; + } + case UKG_41 -> { + return (region.equals(Region.UKG) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? 1. : 0.; + } + + case UKH_10 -> { + return (region.equals(Region.UKH) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? 1. : 0.; + } + case UKH_20 -> { + return (region.equals(Region.UKH) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKH_11 -> { + return (region.equals(Region.UKH) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? 1. : 0.; + } + case UKH_21 -> { + return (region.equals(Region.UKH) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKH_30 -> { + return (region.equals(Region.UKH) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKH_31 -> { + return (region.equals(Region.UKH) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKH_40 -> { + return (region.equals(Region.UKH) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? 1. : 0.; + } + case UKH_41 -> { + return (region.equals(Region.UKH) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? 1. : 0.; + } + + case UKI_10 -> { + return (region.equals(Region.UKI) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? 1. : 0.; + } + case UKI_20 -> { + return (region.equals(Region.UKI) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKI_11 -> { + return (region.equals(Region.UKI) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? 1. : 0.; + } + case UKI_21 -> { + return (region.equals(Region.UKI) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKI_30 -> { + return (region.equals(Region.UKI) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKI_31 -> { + return (region.equals(Region.UKI) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKI_40 -> { + return (region.equals(Region.UKI) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? 1. : 0.; + } + case UKI_41 -> { + return (region.equals(Region.UKI) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? 1. : 0.; + } + + case UKJ_10 -> { + return (region.equals(Region.UKJ) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? 1. : 0.; + } + case UKJ_20 -> { + return (region.equals(Region.UKJ) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKJ_11 -> { + return (region.equals(Region.UKJ) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? 1. : 0.; + } + case UKJ_21 -> { + return (region.equals(Region.UKJ) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKJ_30 -> { + return (region.equals(Region.UKJ) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKJ_31 -> { + return (region.equals(Region.UKJ) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKJ_40 -> { + return (region.equals(Region.UKJ) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? 1. : 0.; + } + case UKJ_41 -> { + return (region.equals(Region.UKJ) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? 1. : 0.; + } + + case UKK_10 -> { + return (region.equals(Region.UKK) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? 1. : 0.; + } + case UKK_20 -> { + return (region.equals(Region.UKK) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKK_11 -> { + return (region.equals(Region.UKK) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? 1. : 0.; + } + case UKK_21 -> { + return (region.equals(Region.UKK) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKK_30 -> { + return (region.equals(Region.UKK) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKK_31 -> { + return (region.equals(Region.UKK) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKK_40 -> { + return (region.equals(Region.UKK) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? 1. : 0.; + } + case UKK_41 -> { + return (region.equals(Region.UKK) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? 1. : 0.; + } + + case UKL_10 -> { + return (region.equals(Region.UKL) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? 1. : 0.; + } + case UKL_20 -> { + return (region.equals(Region.UKL) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKL_11 -> { + return (region.equals(Region.UKL) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? 1. : 0.; + } + case UKL_21 -> { + return (region.equals(Region.UKL) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKL_30 -> { + return (region.equals(Region.UKL) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKL_31 -> { + return (region.equals(Region.UKL) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKL_40 -> { + return (region.equals(Region.UKL) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? 1. : 0.; + } + case UKL_41 -> { + return (region.equals(Region.UKL) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? 1. : 0.; + } + + case UKM_10 -> { + return (region.equals(Region.UKM) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? 1. : 0.; + } + case UKM_20 -> { + return (region.equals(Region.UKM) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKM_11 -> { + return (region.equals(Region.UKM) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? 1. : 0.; + } + case UKM_21 -> { + return (region.equals(Region.UKM) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKM_30 -> { + return (region.equals(Region.UKM) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKM_31 -> { + return (region.equals(Region.UKM) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKM_40 -> { + return (region.equals(Region.UKM) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? 1. : 0.; + } + case UKM_41 -> { + return (region.equals(Region.UKM) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? 1. : 0.; + } + + case UKN_10 -> { + return (region.equals(Region.UKN) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? 1. : 0.; + } + case UKN_20 -> { + return (region.equals(Region.UKN) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKN_11 -> { + return (region.equals(Region.UKN) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? 1. : 0.; + } + case UKN_21 -> { + return (region.equals(Region.UKN) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKN_30 -> { + return (region.equals(Region.UKN) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? 1. : 0.; + } + case UKN_31 -> { + return (region.equals(Region.UKN) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? 1. : 0.; + } + case UKN_40 -> { + return (region.equals(Region.UKN) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? 1. : 0.; + } + case UKN_41 -> { + return (region.equals(Region.UKN) && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? 1. : 0.; + } + case Liwwh_Male_1 -> { + return (getMale() != null && getFemale() != null && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_1 -> { + return (getMale() != null && getFemale() != null && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_10 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_10 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_11 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_11 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_20 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_20 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_21 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_21 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_30 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_30 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_31 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_31 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_40 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_40 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_41 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_41 -> { + return (getMale() != null && getFemale() != null && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_100 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_100 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_101 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_101 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_110 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_110 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_111 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_111 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_120 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_120 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_121 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_121 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_130 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_130 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_131 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_131 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_140 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_140 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_141 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_141 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TEN) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_200 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_200 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_201 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_201 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_210 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_210 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_211 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_211 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_220 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_220 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_230 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_230 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_240 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_240 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.TWENTY) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_300 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_300 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_301 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_301 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_310 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_310 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_311 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_311 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_320 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_320 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_330 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_330 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_340 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_340 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_400 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_400 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_401 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_401 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.ZERO) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_410 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_410 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_411 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_411 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.TEN) && uc_takeup == 1) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_420 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_420 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.TWENTY) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_430 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_430 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Male_440 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? (Double) getMale().getLiwwh().doubleValue() : 0.; + } + case Liwwh_Female_440 -> { + return (getMale() != null && getFemale() != null && getMale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && getFemale().getLabourSupplyWeekly().equals(Labour.THIRTY_EIGHT) && uc_takeup == 0) ? (Double) getFemale().getLiwwh().doubleValue() : 0.; + } default -> { throw new IllegalArgumentException("Unsupported regressor " + variableID.name() + " in BenefitUnit"); } @@ -4152,6 +5305,22 @@ public Double getDisposableIncomeMonthly() { return yDispMonth; } + public Double getNonUCMonthly() { + return nonUCMonthly; + } + + public void setNonUCMonthly(Double nonUCMonthly) { + this.nonUCMonthly = nonUCMonthly; + } + + public Double getUniversalCreditMonthly() { + return universalCreditMonthly; + } + + public void setUniversalCreditMonthly(Double universalCreditMonthly) { + this.universalCreditMonthly = universalCreditMonthly; + } + public Double getGrossIncomeMonthly() { return yGrossMonth; } @@ -4164,16 +5333,16 @@ public Integer getReceivedUC() { return Objects.requireNonNullElse(yBenUCReceivedFlag, 0); } - public void setReceivedUC(Integer yBenUCReceivedFlag) { + public void setReceivedBenefitsUC(Integer yBenUCReceivedFlag) { this.yBenUCReceivedFlag = yBenUCReceivedFlag; } public Integer getReceivedLegacyBenefits() { - return Objects.requireNonNullElse(yBenLegacyReceivedFlag, 0); + return Objects.requireNonNullElse(yBenNonUCReceivedFlag, 0); } - public void setReceivedLegacyBenefits(Integer yBenLegacyReceivedFlag) { - this.yBenLegacyReceivedFlag = yBenLegacyReceivedFlag; + public void setReceivedBenefitsNonUC(Integer yBenLegacyReceivedFlag) { + this.yBenNonUCReceivedFlag = yBenLegacyReceivedFlag; } @@ -4804,6 +5973,14 @@ public void setProcessedId(long id) { public long getIdHhOriginal() {return idHhOriginal;} + public Integer getUC_takeup() { + return uc_takeup; + } + + public void setUC_takeup(Integer uc_takeup) { + this.uc_takeup = uc_takeup; + } + public Person getFemale() { for (Person member : members) { if (member.getDemAge()>=Parameters.AGE_TO_BECOME_RESPONSIBLE && Gender.Female.equals(member.getDemMaleFlag())) diff --git a/src/main/java/simpaths/model/Person.java b/src/main/java/simpaths/model/Person.java index d37260fec..9ff1895cd 100644 --- a/src/main/java/simpaths/model/Person.java +++ b/src/main/java/simpaths/model/Person.java @@ -118,6 +118,15 @@ public class Person implements EventListener, IDoubleSource, IIntSource, Weight, @Lag(getter="getAgeDifferencePartner") @Transient private Integer demAgePartnerDiffL1; //Lag(1) of difference between ages of partners in union @Lag(getter="getYPersAndPartnerGrossDiffMonth") @Transient private Double yPersAndPartnerGrossDiffMonthL1; //Lag(1) of difference between own and partner's gross personal non-benefit income + @Transient private Boolean receivesBenefitsFlag_L1; // Lag(1) of whether person receives benefits + @Transient private Boolean receivesBenefitsFlag; // Does person receive benefits + @Column(name="econ_benefits_uc") private Boolean receivesBenefitsFlagUC; // Person receives UC + @Transient private Boolean receivesBenefitsFlagUC_L1; + @Column(name="econ_benefits_nonuc") private Boolean receivesBenefitsFlagNonUC; // Person receives a benefit which is not UC + @Transient private Boolean receivesBenefitsFlagNonUC_L1; + @Column(name="lifetime_income") private Double lifetimeIncome; // mean annual equivalised household disposable income by age + @Column(name="econ_benefits_lb") private Boolean receivesBenefitsFlagLB = false; + @Transient private Boolean receivesBenefitsFlagLB_L1; @Enumerated(EnumType.STRING) private Indicator eduExitSampleFlag; // year left education @NullInitialised @Transient private Boolean demGiveBirthFlag; @NullInitialised @Transient private Boolean eduLeaveSchoolFlag; @@ -250,6 +259,12 @@ public Person(boolean regressionModel) { } } + public Person(boolean testModel, long id, long seed) { + model = null; + key = new PanelEntityKey(id); + setAllSocialCareVariablesToFalse(); + } + // used to create new people who enter the simulation during UpdateMaternityStatus public Person(Gender gender, Person mother) { @@ -297,6 +312,12 @@ public Person(Gender gender, Person mother) { labHrsWorkWeek = getLabourSupplyWeekly().getHours(this); idHh = mother.getBenefitUnit().getHousehold().getId(); // setDeviationFromMeanRetirementAge(); //This would normally be done within initialisation, but the line above has been commented out for reasons given... + lifetimeIncome = 0.; + // Redundant - to be removed +// receivesBenefitsFlag = false; +// receivesBenefitsFlagNonUC = false; +// receivesBenefitsFlagUC = false; +// receivesBenefitsFlagLB = false; xEquivYear = 0.; yLifeTime = 0.; demBornInSimFlag = true; @@ -524,6 +545,15 @@ else if (demAge > Parameters.MAX_AGE_TO_STAY_IN_CONTINUOUS_EDUCATION) yBenUCReceivedFlagL1 = originalPerson.yBenUCReceivedFlagL1; yFinDstrssFlag = originalPerson.yFinDstrssFlag; + // Redundant - to be removed +// receivesBenefitsFlag = originalPerson.receivesBenefitsFlag; +// receivesBenefitsFlag_L1 = originalPerson.receivesBenefitsFlag_L1; +// receivesBenefitsFlagNonUC = originalPerson.receivesBenefitsFlagNonUC; +// receivesBenefitsFlagNonUC_L1 = originalPerson.receivesBenefitsFlagNonUC_L1; +// receivesBenefitsFlagUC = originalPerson.receivesBenefitsFlagUC; +// receivesBenefitsFlagUC_L1 = originalPerson.receivesBenefitsFlagUC_L1; +// receivesBenefitsFlagLB = originalPerson.receivesBenefitsFlagLB; +// receivesBenefitsFlagLB_L1 = originalPerson.receivesBenefitsFlagLB_L1; if (originalPerson.labWageFullTimeHrly > Parameters.MIN_HOURLY_WAGE_RATE) { labWageFullTimeHrly = Math.min(Parameters.MAX_HOURLY_WAGE_RATE, Math.max(Parameters.MIN_HOURLY_WAGE_RATE, originalPerson.labWageFullTimeHrly)); } else { @@ -609,6 +639,13 @@ public void setAdditionalFieldsInInitialPopulation() { labHrsWorkWeek = labHrsWorkEnumWeek.getValue(); } } + // Redundant - To be removed + // receivesBenefitsFlag_L1 = receivesBenefitsFlag; + // receivesBenefitsFlagNonUC_L1 = receivesBenefitsFlagNonUC; + // receivesBenefitsFlagUC_L1 = receivesBenefitsFlagUC; + // receivesBenefitsFlagNonUC_L1 = receivesBenefitsFlagNonUC; + // receivesBenefitsFlagUC_L1 = receivesBenefitsFlagUC; + // receivesBenefitsFlagLB_L1 = receivesBenefitsFlagLB; if(UnionMatchingMethod.SBAM.equals(model.getUnionMatchingMethod())) { updateAgeGroup(); @@ -2203,9 +2240,16 @@ protected void updateAttributes() { private void updateOutputVariables() { idPartner = getPartnerID(); - demPartnerStatus = getDemPartnerStatus(); } + // Redundant - to be removed +// private void updateLaggedVariables(boolean initialUpdate) { +// receivesBenefitsFlag_L1 = receivesBenefitsFlag; // Lag(1) of flag indicating if individual receives benefits +// receivesBenefitsFlagNonUC_L1 = receivesBenefitsFlagNonUC; // Lag(1) of flag indicating if individual receives non-UC benefits +// receivesBenefitsFlagUC_L1 = receivesBenefitsFlagUC; // Lag(1) of flag indicating if individual receives UC +// receivesBenefitsFlagLB_L1 = receivesBenefitsFlagLB; // Lag(1) of flag indicating if individual receives UC +// } + // used when children leave home protected void setupNewHousehold() { @@ -2519,6 +2563,7 @@ public enum DoublesVariables { D_children_13_17, D_children_18over, //Currently this will return 0 (false) as children leave home when they are 18 D_Econ_benefits, + D_Econ_benefits_LB, D_Econ_benefits_NonUC, D_Econ_benefits_UC, D_Econ_benefits_UC_Lhw_ZERO, @@ -2526,6 +2571,16 @@ public enum DoublesVariables { D_Econ_benefits_UC_Lhw_TWENTY, D_Econ_benefits_UC_Lhw_THIRTY, D_Econ_benefits_UC_Lhw_FORTY, + Lhw_ZERO, + Lhw_TEN, + Lhw_TWENTY, + Lhw_THIRTY, + Lhw_FORTY, + Lhwsp_c6_ZERO, + Lhwsp_c6_TEN, + Lhwsp_c6_TWENTY, + Lhwsp_c6_THIRTY, + Lhwsp_c6_FORTY, D_Home_owner, D_Home_owner_L1, Dag, @@ -2703,6 +2758,7 @@ public enum DoublesVariables { Dhe_pcs_c, //MCS centralised by subtracting population mean Dhe_pcs_c_sq, //Square of centralised MCS Dhe_pcssp_L1, //Physical well-being status of the partner + Dhmghq, Dhmghq_L1, Dlltsd, //Long-term sick or disabled Dlltsd01, @@ -2719,6 +2775,7 @@ public enum DoublesVariables { EquivalisedConsumptionYearly, EquivalisedIncomeYearly, //Equivalised income for use with the security index Ethn_White, + EthnicityWhite, // Ethn_Mixed, Ethn_Asian, EthnicityAsian, @@ -2729,7 +2786,8 @@ public enum DoublesVariables { Ethn_Other_, EthnicityOther, EthnicityMixed, - // Ethn_Missing, + EthnicityMissing, +// Ethn_Missing, Female, FertilityRate, FinancialDistress, @@ -4496,9 +4554,6 @@ else throw new IllegalArgumentException( case EthnicityMixed -> { return demEthnC6.equals(Ethnicity.Mixed) ? 1. : 0.; } - // case Ethn_Missing -> { - // return dot01.equals(Ethnicity.Missing) ? 1. : 0.; - // } case FertilityRate -> { if (demIoFlag) return Parameters.getFertilityProjectionsByYear(getYear()); @@ -5213,23 +5268,56 @@ else throw new IllegalArgumentException( case D_Econ_benefits_NonUC -> { return isReceivesBenefitsFlagNonUC() ? 1. : 0.; } + case D_Econ_benefits_LB -> { + return isReceivesBenefitsFlagNonUC() ? 1. : 0.; + } case D_Econ_benefits_UC -> { - return isReceivesBenefitsFlagUC() ? 1. : 0.; + return Parameters.uc_direct_effects && isReceivesBenefitsFlagUC() ? 1. : 0.; } case D_Econ_benefits_UC_Lhw_ZERO -> { - return isReceivesBenefitsFlagUC() && getLabourSupplyWeekly() == Labour.ZERO ? 1. : 0.; + return Parameters.uc_direct_effects && isReceivesBenefitsFlagUC() && getLabourSupplyWeekly() == Labour.ZERO ? 1. : 0.; } case D_Econ_benefits_UC_Lhw_TEN -> { - return isReceivesBenefitsFlagUC() && getLabourSupplyWeekly() == Labour.TEN ? 1. : 0.; + return Parameters.uc_direct_effects && isReceivesBenefitsFlagUC() && getLabourSupplyWeekly() == Labour.TEN ? 1. : 0.; } case D_Econ_benefits_UC_Lhw_TWENTY -> { - return isReceivesBenefitsFlagUC() && getLabourSupplyWeekly() == Labour.TWENTY ? 1. : 0.; + return Parameters.uc_direct_effects && isReceivesBenefitsFlagUC() && getLabourSupplyWeekly() == Labour.TWENTY ? 1. : 0.; } case D_Econ_benefits_UC_Lhw_THIRTY -> { - return isReceivesBenefitsFlagUC() && getLabourSupplyWeekly() == Labour.THIRTY ? 1. : 0.; + return Parameters.uc_direct_effects && isReceivesBenefitsFlagUC() && getLabourSupplyWeekly() == Labour.THIRTY ? 1. : 0.; } case D_Econ_benefits_UC_Lhw_FORTY -> { - return isReceivesBenefitsFlagUC() && getLabourSupplyWeekly() == Labour.THIRTY_EIGHT ? 1. : 0.; + return Parameters.uc_direct_effects && isReceivesBenefitsFlagUC() && getLabourSupplyWeekly() == Labour.THIRTY_EIGHT ? 1. : 0.; + } + case Lhw_ZERO -> { + return getLabourSupplyWeekly() == Labour.ZERO ? 1. : 0.; + } + case Lhw_TEN -> { + return getLabourSupplyWeekly() == Labour.TEN ? 1. : 0.; + } + case Lhw_TWENTY -> { + return getLabourSupplyWeekly() == Labour.TWENTY ? 1. : 0.; + } + case Lhw_THIRTY -> { + return getLabourSupplyWeekly() == Labour.THIRTY ? 1. : 0.; + } + case Lhw_FORTY -> { + return getLabourSupplyWeekly() == Labour.THIRTY_EIGHT ? 1. : 0.; + } + case Lhwsp_c6_ZERO -> { + return null != getPartner() && getPartner().getLabourSupplyWeekly() == Labour.ZERO ? 1. : 0.; + } + case Lhwsp_c6_TEN -> { + return null != getPartner() && getPartner().getLabourSupplyWeekly() == Labour.TEN ? 1. : 0.; + } + case Lhwsp_c6_TWENTY -> { + return null != getPartner() && getPartner().getLabourSupplyWeekly() == Labour.TWENTY ? 1. : 0.; + } + case Lhwsp_c6_THIRTY -> { + return null != getPartner() && getPartner().getLabourSupplyWeekly() == Labour.THIRTY ? 1. : 0.; + } + case Lhwsp_c6_FORTY -> { + return null != getPartner() && getPartner().getLabourSupplyWeekly() == Labour.THIRTY_EIGHT ? 1. : 0.; } case D_Home_owner -> { return getBenefitUnit().isHousingOwned() ? 1. : 0.; @@ -7152,7 +7240,6 @@ public void setLabWageFullTimeHrlyL1(double potentialHourlyEarnings) { labWageFullTimeHrlyL1 = potentialHourlyEarnings; } - public void setLiwwh(Integer labEmpNyear) { this.labEmpNyear = labEmpNyear; } @@ -7256,8 +7343,16 @@ public boolean isReceivesBenefitsFlagUC() { return yBenUCReceivedFlag; } - public void setReceivesBenefitsFlagUC(boolean yBenUCReceivedFlag) { - this.yBenUCReceivedFlag = yBenUCReceivedFlag; + public Double isReceivesBenefitsUCDouble() { + return yBenUCReceivedFlag ? 1.0 : 0.0; + } + + public Double isReceivesBenefitsLBDouble() { + return (null != receivesBenefitsFlagLB && receivesBenefitsFlagLB) ? 1.0 : 0.0; + } + + public void setReceivesBenefitsFlagUC(boolean receivesBenefitsFlagUC) { + this.yBenUCReceivedFlag = receivesBenefitsFlagUC; } public boolean isReceivesBenefitsFlagUC_L1() { @@ -7276,6 +7371,8 @@ public void setReceivesBenefitsFlagNonUC(boolean yBenNonUCReceivedFlag) { this.yBenNonUCReceivedFlag = yBenNonUCReceivedFlag; } + + public boolean isReceivesBenefitsFlagNonUC_L1() { return (null != yBenNonUCReceivedFlagL1) ? yBenNonUCReceivedFlagL1 : false; } @@ -7284,6 +7381,21 @@ public void setYBenNonUCReceivedFlagL1(boolean yBenNonUCReceivedFlagL1) { this.yBenNonUCReceivedFlagL1 = yBenNonUCReceivedFlagL1; } + public Boolean isReceivesBenefitsFlagLB() { + return (null != yBenNonUCReceivedFlag ? yBenNonUCReceivedFlag : false); + } + + public void setReceivesBenefitsFlagLB(Boolean receivesBenefitsFlagLB) { + this.yBenNonUCReceivedFlag = yBenNonUCReceivedFlag; + } + + public Boolean getReceivesBenefitsFlagLB_L1() { + return yBenNonUCReceivedFlagL1; + } + + public void setReceivesBenefitsFlagLB_L1(Boolean receivesBenefitsFlagLB_L1) { + this.yBenNonUCReceivedFlagL1 = receivesBenefitsFlagLB_L1; + } public double getEquivalisedDisposableIncomeYearly() { return benefitUnit.getEquivalisedDisposableIncomeYearly(); diff --git a/src/main/java/simpaths/model/SimPathsModel.java b/src/main/java/simpaths/model/SimPathsModel.java index 0e9703ef4..a1b4a962e 100644 --- a/src/main/java/simpaths/model/SimPathsModel.java +++ b/src/main/java/simpaths/model/SimPathsModel.java @@ -265,6 +265,8 @@ public void setFirstRun(boolean firstRun) { private boolean flagSuppressSocialCareCosts = false; + private boolean flagSuppressUCTakeup = false; + @GUIparameter(description = "tick to enable intertemporal optimised consumption and labour decisions") private boolean enableIntertemporalOptimisations = false; @@ -385,7 +387,7 @@ public void buildObjects() { Parameters.loadParameters(country, maxAge, enableIntertemporalOptimisations, projectFormalChildcare, projectSocialCare, donorPoolAveraging, taxDonorUpratingByWage, fixTimeTrend, flagDefaultToTimeSeriesAverages, saveImperfectTaxDBMatches, timeTrendStopsIn, startYear, endYear, interestRateInnov, - disposableIncomeFromLabourInnov, flagSuppressChildcareCosts, flagSuppressSocialCareCosts, lifetimeIncomeImpute); + disposableIncomeFromLabourInnov, flagSuppressChildcareCosts, flagSuppressSocialCareCosts, lifetimeIncomeImpute, flagSuppressUCTakeup); if (lifetimeIncomeGenerate) { ManagerProjectLifetimeIncomes.run(log, lifetimeIncomeStartBirthYear, lifetimeIncomeEndBirthYear, lifetimeIncomeEndAge, lifetimeIncomeCohortSize, lifetimeIncomeWriteToCSV, @@ -589,6 +591,7 @@ public void buildSchedule() { // Assign benefit status to individuals in benefit units, from donors. Based on donor tax unit status. yearlySchedule.addCollectionEvent(benefitUnits, BenefitUnit.Processes.ReceivesBenefits); + yearlySchedule.addCollectionEvent(benefitUnits, BenefitUnit.Processes.ReceivesBenefitsUC); // CONSUMPTION AND SAVINGS MODULE if (enableIntertemporalOptimisations) diff --git a/src/main/java/simpaths/model/TaxEvaluation.java b/src/main/java/simpaths/model/TaxEvaluation.java index 9baf7c226..4d972de63 100644 --- a/src/main/java/simpaths/model/TaxEvaluation.java +++ b/src/main/java/simpaths/model/TaxEvaluation.java @@ -27,10 +27,11 @@ public class TaxEvaluation { * CONSTRUCTORS */ public TaxEvaluation(){} - public TaxEvaluation(int simYear, int age, int numberMembersOver17, int numberChildrenUnder5, int numberChildren5To9, - int numberChildren10To17, double hoursWorkedPerWeekMan, double hoursWorkedPerWeekWoman, - int disabilityMan, int disabilityWoman, int careProvision, double originalIncomePerMonth, double secondIncomePerMonth, - double childcareCostPerMonth, double randomDraw) { + // used for expectations and benefit unit evaluations + public TaxEvaluation(int year, int age, int numberMembersOver17, int numberChildrenUnder5, int numberChildren5To9, int numberChildren10To17, + double hoursWorkedPerWeekMan, double hoursWorkedPerWeekWoman, int disabilityMan, int disabilityWoman, int careProvision, Integer ucTakeUp, + double originalIncomePerMonth, double secondIncomePerMonth, double childcareCostPerMonth, double socialCareCostPerMonth, + Double liquidWealth, double randomDraw) { // evaluate imputed transfer payments keys = new DonorKeys(randomDraw); @@ -39,20 +40,10 @@ public TaxEvaluation(int simYear, int age, int numberMembersOver17, int numberCh int priceYear = Parameters.BASE_PRICE_YEAR; // evaluate transfer payments - evaluateImputedTaxes(simYear, priceYear, age, numberMembersOver17, numberChildrenUnder5, numberChildren5To9, numberChildren10To17, - hoursWorkedPerWeekMan, hoursWorkedPerWeekWoman, disabilityMan, disabilityWoman, careProvision, originalIncomePerMonth, + evaluateImputedTaxes(year, priceYear, age, numberMembersOver17, numberChildrenUnder5, numberChildren5To9, numberChildren10To17, + hoursWorkedPerWeekMan, hoursWorkedPerWeekWoman, disabilityMan, disabilityWoman, careProvision, ucTakeUp, originalIncomePerMonth, secondIncomePerMonth, childcareCostPerMonth); - } - // used for expectations and benefit unit evaluations - public TaxEvaluation(int year, int age, int numberMembersOver17, int numberChildrenUnder5, int numberChildren5To9, int numberChildren10To17, - double hoursWorkedPerWeekMan, double hoursWorkedPerWeekWoman, int disabilityMan, int disabilityWoman, int careProvision, - double originalIncomePerMonth, double secondIncomePerMonth, double childcareCostPerMonth, double socialCareCostPerMonth, - Double liquidWealth, double randomDraw) { - - this(year, age, numberMembersOver17, numberChildrenUnder5, numberChildren5To9, numberChildren10To17, hoursWorkedPerWeekMan, - hoursWorkedPerWeekWoman, disabilityMan, disabilityWoman, careProvision, originalIncomePerMonth, secondIncomePerMonth, childcareCostPerMonth, randomDraw); - if (Parameters.flagSocialCare) { // consider social support for formal care expenditure @@ -64,13 +55,12 @@ public TaxEvaluation(int year, int age, int numberMembersOver17, int numberChild } } - /** * WORKER METHODS */ private void evaluateImputedTaxes(int simYear, int priceYear, int age, int numberMembersOver17, int numberChildrenUnder5, int numberChildren5To9, int numberChildren10To17, double hoursWorkedPerWeekMan, double hoursWorkedPerWeekWoman, - int disabilityMan, int disabilityWoman, int careProvision, double originalIncomePerMonth, double secondIncomePerMonth, + int disabilityMan, int disabilityWoman, int careProvision, Integer ucTakeUp, double originalIncomePerMonth, double secondIncomePerMonth, double childcareCostPerMonth) { double originalIncomePerWeek = originalIncomePerMonth / Parameters.WEEKS_PER_MONTH; // can be negative @@ -78,7 +68,7 @@ private void evaluateImputedTaxes(int simYear, int priceYear, int age, int numbe double childcareCostPerWeek = childcareCostPerMonth / Parameters.WEEKS_PER_MONTH; keyFunction = new KeyFunction(simYear, priceYear, age, numberMembersOver17, numberChildrenUnder5, numberChildren5To9, numberChildren10To17, hoursWorkedPerWeekMan, hoursWorkedPerWeekWoman, disabilityMan, - disabilityWoman, careProvision, originalIncomePerWeek, secondIncomePerWeek, childcareCostPerWeek); + disabilityWoman, careProvision, ucTakeUp, originalIncomePerWeek, secondIncomePerWeek, childcareCostPerWeek); keys.evaluate(keyFunction); imputedTransfers = new DonorTaxImputation(keys); imputedTransfers.evaluate(); @@ -96,6 +86,15 @@ public Match getMatch() { public double getDisposableIncomePerMonth() { return imputedTransfers.getDisposableIncomePerMonth() + socialCareSupportPerMonth; } + + public double getUniversalCreditPerMonth() { + return imputedTransfers.getUniversalCreditPerMonth(); + } + + public double getLegacyBenefitPerMonth() { + return imputedTransfers.getLegacyBenefitPerMonth(); + } + public double getDisposableIncomePerWeek() { return imputedTransfers.getDisposableIncomePerWeek() + socialCareSupportPerMonth / Parameters.WEEKS_PER_MONTH; } diff --git a/src/main/java/simpaths/model/decisions/Expectations.java b/src/main/java/simpaths/model/decisions/Expectations.java index 88ddd86e5..461744367 100644 --- a/src/main/java/simpaths/model/decisions/Expectations.java +++ b/src/main/java/simpaths/model/decisions/Expectations.java @@ -560,11 +560,12 @@ public double taxBenefitFunction(Integer labourHours1Weekly, } double childcareCostPerMonth = childcareCostAnnual / 12.0; double socialCareCostPerMonth = socialCareCostAnnual / 12.0; + int ucTakeUp = 1; // evaluate disposable income double originalIncomePerMonth = originalIncomePerWeek * Parameters.WEEKS_PER_MONTH; TaxEvaluation evaluatedTransfers = new TaxEvaluation(year, ageYearsNextPeriod, numberAdults, numberChildrenUnder5, numberChildrenAged5To9, - numberChildrenAged10To17, hoursWorkPerWeek1, hoursWorkPerWeek2, disability1, disability2, careProvision, originalIncomePerMonth, secondIncomePerMonth, + numberChildrenAged10To17, hoursWorkPerWeek1, hoursWorkPerWeek2, disability1, disability2, careProvision, ucTakeUp, originalIncomePerMonth, secondIncomePerMonth, childcareCostPerMonth, socialCareCostPerMonth, wealthLiqValue, -1.0); Match match = evaluatedTransfers.getMatch(); diff --git a/src/main/java/simpaths/model/taxes/DonorTaxImputation.java b/src/main/java/simpaths/model/taxes/DonorTaxImputation.java index 2b67b503e..6a70d6d34 100644 --- a/src/main/java/simpaths/model/taxes/DonorTaxImputation.java +++ b/src/main/java/simpaths/model/taxes/DonorTaxImputation.java @@ -44,6 +44,9 @@ public class DonorTaxImputation { private double grossIncomePerWeek; private double targetNormalisedOriginalIncome; + private Double universalCreditPerMonth; + private Double legacyBenefitPerMonth; + private Integer receivedUC; private Integer yBenUCFlag; private Integer receivedLegacyBenefit; @@ -96,6 +99,21 @@ public void setReceivedLegacyBenefit(Integer receivedLegacyBenefit) { this.receivedLegacyBenefit = receivedLegacyBenefit; } + public Double getLegacyBenefitPerMonth() { + return legacyBenefitPerMonth; + } + + public void setLegacyBenefitPerMonth(Double legacyBenefitPerMonth) { + this.legacyBenefitPerMonth = legacyBenefitPerMonth; + } + + public Double getUniversalCreditPerMonth() { + return universalCreditPerMonth; + } + + public void setUniversalCreditPerMonth(Double universalCreditPerMonth) { + this.universalCreditPerMonth = universalCreditPerMonth; + } /** * METHOD TO PERFORM IMPUTATION AND GENERATE OUTPUTS @@ -111,6 +129,8 @@ public void evaluate() { int matchRegime = -1; int systemYear = getSystemYear(keys.getSimYear()); boolean flagSecondIncome = false, flagChildcareCost = false; + + for (int ii=0; ii key = Triple.of(systemYear,ii,keys.getKey(ii)); @@ -301,7 +321,11 @@ public void evaluate() { double infAdj = 1.0; double UCmean = 0.; // Take a weighted mean of whether received UC or not across all candidates double LBmean = 0.; // Take a weighted mean of whether received LB or not across all candidates + double UCperMonth = 0.0; // Calculating UC award amount + double LBperMonth = 0.0; // Calculating LB award amount setReceivedUC(0); + setUniversalCreditPerMonth(0.0); + setLegacyBenefitPerMonth(0.0); setReceivedLegacyBenefit(0); if (systemYear != keys.getPriceYear()) infAdj = Parameters.getTimeSeriesValue(keys.getPriceYear(), TimeSeriesVariable.Inflation) / @@ -329,15 +353,15 @@ public void evaluate() { // impute based on observed disposable income disposableIncomePerWeek += candidate.getPolicyBySystemYear(systemYear).getDisposableIncomePerMonth() / Parameters.WEEKS_PER_MONTH * weight * infAdj; benefitsReceivedPerWeek += (candidate.getPolicyBySystemYear(systemYear).getBenMeansTestPerMonth() + candidate.getPolicyBySystemYear(systemYear).getBenNonMeansTestPerMonth()) / Parameters.WEEKS_PER_MONTH * weight * infAdj; - UCmean += candidate.getPolicyBySystemYear(systemYear).getReceivesUC() * weight * infAdj; - LBmean += candidate.getPolicyBySystemYear(systemYear).getReceivesLegacyBenefit() * weight * infAdj; } else { // impute based on ratio of disposable to original income disposableIncomePerWeek += candidate.getPolicyBySystemYear(systemYear).getDisposableIncomePerMonth() / candidate.getPolicyBySystemYear(systemYear).getOriginalIncomePerMonth() * weight; benefitsReceivedPerWeek += (candidate.getPolicyBySystemYear(systemYear).getBenMeansTestPerMonth() + candidate.getPolicyBySystemYear(systemYear).getBenNonMeansTestPerMonth()) / candidate.getPolicyBySystemYear(systemYear).getOriginalIncomePerMonth() * weight; - UCmean += candidate.getPolicyBySystemYear(systemYear).getReceivesUC() * weight * infAdj; - LBmean += candidate.getPolicyBySystemYear(systemYear).getReceivesLegacyBenefit() * weight * infAdj; } + UCperMonth += candidate.getPolicyBySystemYear(systemYear).getBenefitUCPerMonth() * weight * infAdj; + LBperMonth += candidate.getPolicyBySystemYear(systemYear).getLegacyBenefitPerMonth() * weight * infAdj; + UCmean += candidate.getPolicyBySystemYear(systemYear).getReceivesUC() * weight * infAdj; + LBmean += candidate.getPolicyBySystemYear(systemYear).getReceivesLegacyBenefit() * weight * infAdj; if (keys.getRandomDraw()>0.0 || Math.abs(keys.getRandomDraw()+2.0)<1.0E-2) { donorID = candidate.getId(); break; @@ -379,9 +403,14 @@ public void evaluate() { } if (UCmean > SimulationEngine.getRnd().nextDouble()) { // Weighted probability of receiving UC setReceivedUC(1); + setUniversalCreditPerMonth(UCperMonth); + } else { + setReceivedUC(0); + setUniversalCreditPerMonth(0.0); } if (LBmean > 0 && getReceivedUC() == 0) { // Setting as received LB if benefits but not UC setReceivedLegacyBenefit(1); + setLegacyBenefitPerMonth(LBperMonth); } } diff --git a/src/main/java/simpaths/model/taxes/DonorTaxUnit.java b/src/main/java/simpaths/model/taxes/DonorTaxUnit.java index ab04d4d69..32ad8ebe9 100644 --- a/src/main/java/simpaths/model/taxes/DonorTaxUnit.java +++ b/src/main/java/simpaths/model/taxes/DonorTaxUnit.java @@ -21,7 +21,8 @@ public class DonorTaxUnit { @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "taxUnit") private Set policies = new LinkedHashSet<>(); - @Column(name = "WEIGHT") private Double dem; + private Integer ucTakeUp = 0; // FIXME: should be read from db + @Column(name = "WEIGHT") private Double weight; /** @@ -34,8 +35,9 @@ public DonorTaxUnit(){} */ public long getId() {return this.id;} public void setId(long id) {this.id = id;} - public Double getWeight() {return this.dem;} - public void setWeight(double dem) { this.dem = dem;} + public Double getWeight() {return this.weight;} + public Integer getUcTakeUp() {return this.ucTakeUp;} + public void setWeight(double weight) { this.weight = weight;} public DonorTaxUnitPolicy getPolicyByFromYear(int fromYear) { for (DonorTaxUnitPolicy policy : policies) { diff --git a/src/main/java/simpaths/model/taxes/DonorTaxUnitPolicy.java b/src/main/java/simpaths/model/taxes/DonorTaxUnitPolicy.java index 6c3d33e1e..44ddfafe7 100644 --- a/src/main/java/simpaths/model/taxes/DonorTaxUnitPolicy.java +++ b/src/main/java/simpaths/model/taxes/DonorTaxUnitPolicy.java @@ -34,6 +34,8 @@ public class DonorTaxUnitPolicy { @Column(name="ILS_BENMT") private Double benMeansTestPerMonth; @Column(name="ILS_BENNT") private Double benNonMeansTestPerMonth; @Column(name="SECOND_INCOME") private Double secondIncomePerMonth; + @Column(name="BSAUC_S") private Double benefitUCPerMonth; //bsauc_s + @Column(name="LEGACY_BENEFITS") private Double legacyBenefitPerMonth; @Column(name="XCC") private Double childcareCostPerMonth; @Column(name = "DONOR_KEY0") private Integer donorKey0; @Column(name = "DONOR_KEY1") private Integer donorKey1; @@ -113,6 +115,22 @@ public double getBenMeansTestPerMonth() { return benMeansTestPerMonth; } public void setBenMeansTestPerMonth(Double ils_benmt) { this.benMeansTestPerMonth = ils_benmt; } + + public double getBenefitUCPerMonth() { + if (benefitUCPerMonth ==null) + throw new RuntimeException("attempt to get benefit amnount before instantiated"); + return benefitUCPerMonth; + } + public void setBenefitUCPerMonth(Double ils_benmt) { this.benefitUCPerMonth = ils_benmt; } + + public double getLegacyBenefitPerMonth() { + if (legacyBenefitPerMonth ==null) + throw new RuntimeException("attempt to get benefit amnount before instantiated"); + return legacyBenefitPerMonth; + } + + public void setLegacyBenefitPerMonth(Double ils_benmt) { this.legacyBenefitPerMonth = ils_benmt; } + public double getBenNonMeansTestPerMonth() { if (!Parameters.checkFinite(benNonMeansTestPerMonth)) throw new RuntimeException("attempt to get benefit amount before instantiated"); diff --git a/src/main/java/simpaths/model/taxes/IKeyFunction.java b/src/main/java/simpaths/model/taxes/IKeyFunction.java index 32ae3c06e..ef71b509f 100644 --- a/src/main/java/simpaths/model/taxes/IKeyFunction.java +++ b/src/main/java/simpaths/model/taxes/IKeyFunction.java @@ -5,7 +5,7 @@ public interface IKeyFunction { Integer[] evaluateKeys(int simYear, int priceYear, int age, int numberMembersOver17, int numberChildrenUnder5, int numberChildren5To9, int numberChildren10To17, double hoursWorkedPerWeekMan, double hoursWorkedPerWeekWoman, int dlltsdMan, int dlltsdWoman, - int careProvision, double originalIncomePerWeek, double secondIncomePerWeek, double childcareCostPerWeek); + int careProvision, int ucTakeUp, double originalIncomePerWeek, double secondIncomePerWeek, double childcareCostPerWeek); int getMatchFeatureIndex(MatchFeature feature, int taxDBRegime, int keyValue); boolean[] isLowIncome(Integer[] keys); Map> getTaxdbCounter(); diff --git a/src/main/java/simpaths/model/taxes/KeyFunction.java b/src/main/java/simpaths/model/taxes/KeyFunction.java index 0cc15d917..47bf88901 100644 --- a/src/main/java/simpaths/model/taxes/KeyFunction.java +++ b/src/main/java/simpaths/model/taxes/KeyFunction.java @@ -16,8 +16,8 @@ public class KeyFunction { * ATTRIBUTES */ private int simYear = -999, priceYear = -999, age, numberMembersOver17, numberChildrenUnder5, numberChildren5To9, numberChildren10To17; - private int healthDsblLongtermFlagMan = -1, healthDsblLongtermFlagWoman = -1, careProvision = -1; - private double hoursWorkedPerWeekMan, hoursWorkedPerWeekWoman, originalIncomePerWeek, secondIncomePerWeek, xChildCareWeek; + private int healthDsblLongtermFlagMan = -1, healthDsblLongtermFlagWoman = -1, careProvision = -1, ucTakeUp = -1; + private double hoursWorkedPerWeekMan, hoursWorkedPerWeekWoman, originalIncomePerWeek, secondIncomePerWeek, childcareCostPerWeek; // define key function here - switchable private IKeyFunction keyFunction; @@ -27,10 +27,11 @@ public class KeyFunction { * CONSTRUCTORS */ public KeyFunction() { - this.keyFunction = new KeyFunction4(); + this.keyFunction = new KeyFunction5(); } public KeyFunction(int simYear, int priceYear, int age, int numberMembersOver17, int numberChildrenUnder5, int numberChildren5To9, int numberChildren10To17, - double hoursWorkedPerWeekMan, double hoursWorkedPerWeekWoman, int dlltsdMan, int dlltsdWoman, int careProvision, double originalIncomePerWeek) { + double hoursWorkedPerWeekMan, double hoursWorkedPerWeekWoman, int dlltsdMan, int dlltsdWoman, int careProvision, double originalIncomePerWeek, + double secondIncomePerWeek, double childcareCostPerWeek) { this(); @@ -74,15 +75,15 @@ public KeyFunction(int simYear, int priceYear, int age, int numberMembersOver17, this.healthDsblLongtermFlagMan = dlltsdMan; this.healthDsblLongtermFlagWoman = dlltsdWoman; this.careProvision = careProvision; + this.childcareCostPerWeek = childcareCostPerWeek; } public KeyFunction(int simYear, int priceYear, int age, int numberMembersOver17, int numberChildrenUnder5, int numberChildren5To9, int numberChildren10To17, - double hoursWorkedPerWeekMan, double hoursWorkedPerWeekWoman, int dlltsdMan, int dlltsdWoman, int careProvision, double originalIncomePerWeek, - double secondIncomePerWeek, double xChildCareWeek) { + double hoursWorkedPerWeekMan, double hoursWorkedPerWeekWoman, int dlltsdMan, int dlltsdWoman, int careProvision, int ucTakeUp, + double originalIncomePerWeek, double secondIncomePerWeek, double childcareCostPerWeek) { this(simYear, priceYear, age, numberMembersOver17, numberChildrenUnder5, numberChildren5To9, numberChildren10To17, - hoursWorkedPerWeekMan, hoursWorkedPerWeekWoman, dlltsdMan, dlltsdWoman, careProvision, originalIncomePerWeek); - this.xChildCareWeek = xChildCareWeek; - this.secondIncomePerWeek = Math.max(0.0, Math.min(secondIncomePerWeek, originalIncomePerWeek - secondIncomePerWeek)); + hoursWorkedPerWeekMan, hoursWorkedPerWeekWoman, dlltsdMan, dlltsdWoman, careProvision, originalIncomePerWeek, secondIncomePerWeek, childcareCostPerWeek); + this.ucTakeUp = ucTakeUp; } @@ -186,7 +187,7 @@ public void setSecondIncomePerWeek(double secondIncomePerWeek) { } public void setChildcareCostPerWeek(double xChildCareWeek) { - this.xChildCareWeek = xChildCareWeek; + this.childcareCostPerWeek = xChildCareWeek; } /** @@ -203,7 +204,7 @@ public Integer[] evaluateKeys() { } //return keyFunction.evaluateKeys(simYear, priceYear, age, numberMembersOver17, numberChildrenUnder5, numberChildren5To17, hoursWorkedPerWeekMan, hoursWorkedPerWeekWoman, healthDsblLongtermFlagMan, healthDsblLongtermFlagWoman, originalIncomePerWeek); return keyFunction.evaluateKeys(simYear, priceYear, age, numberMembersOver17, numberChildrenUnder5, numberChildren5To9, numberChildren10To17, - hoursWorkedPerWeekMan, hoursWorkedPerWeekWoman, healthDsblLongtermFlagMan, healthDsblLongtermFlagWoman, careProvision, originalIncomePerWeek, secondIncomePerWeek, xChildCareWeek); + hoursWorkedPerWeekMan, hoursWorkedPerWeekWoman, healthDsblLongtermFlagMan, healthDsblLongtermFlagWoman, careProvision, ucTakeUp, originalIncomePerWeek, secondIncomePerWeek, childcareCostPerWeek); } public boolean[] isLowIncome(Integer[] keys) { @@ -220,5 +221,5 @@ public Map> getTaxdbCounter() { public double getOriginalIncomePerWeek() { return originalIncomePerWeek; } public double getSecondIncomePerWeek() { return secondIncomePerWeek; } - public double getChildcareCostPerWeek() { return xChildCareWeek; } + public double getChildcareCostPerWeek() { return childcareCostPerWeek; } } diff --git a/src/main/java/simpaths/model/taxes/KeyFunction1.java b/src/main/java/simpaths/model/taxes/KeyFunction1.java index dae4e5fe8..b734ace42 100644 --- a/src/main/java/simpaths/model/taxes/KeyFunction1.java +++ b/src/main/java/simpaths/model/taxes/KeyFunction1.java @@ -37,7 +37,7 @@ public KeyFunction1() {} */ public Integer[] evaluateKeys(int simYear, int priceYear, int age, int numberMembersOver17, int numberChildrenUnder5, int numberChildren5To9, int numberChildren10To17, double hoursWorkedPerWeekMan, double hoursWorkedPerWeekWoman, int dlltsdMan, - int dlltsdWoman, int careProvision, double originalIncomePerWeek, double secondIncomePerWeek, double childcareCostPerWeek) { + int dlltsdWoman, int careProvision, int ucTakeUp, double originalIncomePerWeek, double secondIncomePerWeek, double childcareCostPerWeek) { // initialise working variables int spa = getStatePensionAge(age, simYear); diff --git a/src/main/java/simpaths/model/taxes/KeyFunction2.java b/src/main/java/simpaths/model/taxes/KeyFunction2.java index b91c7f500..e56c04874 100644 --- a/src/main/java/simpaths/model/taxes/KeyFunction2.java +++ b/src/main/java/simpaths/model/taxes/KeyFunction2.java @@ -37,7 +37,7 @@ public KeyFunction2() {} */ public Integer[] evaluateKeys(int simYear, int priceYear, int age, int numberMembersOver17, int numberChildrenUnder5, int numberChildren5To9, int numberChildren10To17, double hoursWorkedPerWeekMan, double hoursWorkedPerWeekWoman, int dlltsdMan, - int dlltsdWoman, int careProvision, double originalIncomePerWeek, double secondIncomePerWeek, double childcareCostPerWeek) { + int dlltsdWoman, int careProvision, int ucTakeUp, double originalIncomePerWeek, double secondIncomePerWeek, double childcareCostPerWeek) { // initialise working variables int spa = getStatePensionAge(age, simYear); diff --git a/src/main/java/simpaths/model/taxes/KeyFunction3.java b/src/main/java/simpaths/model/taxes/KeyFunction3.java index 8d00fe3c1..7997b25b6 100644 --- a/src/main/java/simpaths/model/taxes/KeyFunction3.java +++ b/src/main/java/simpaths/model/taxes/KeyFunction3.java @@ -42,12 +42,13 @@ public KeyFunction3() {} * @param hoursWorkedPerWeekWoman employment hours per week of adult female * @param dlltsdMan disability status of man * @param dlltsdWoman disability status of woman + * @param ucTakeUp indicator that at least one member of household is a UC member * @param originalIncomePerWeek original income per week of family (possibly negative) * @return Integer list of keys, ordered from most fine (0) to most coarse (2) */ public Integer[] evaluateKeys(int simYear, int priceYear, int age, int numberMembersOver17, int numberChildrenUnder5, int numberChildren5To9, int numberChildren10To17, double hoursWorkedPerWeekMan, double hoursWorkedPerWeekWoman, int dlltsdMan, int dlltsdWoman, - int careProvision, double originalIncomePerWeek, double secondIncomePerWeek, double childcareCostPerWeek) { + int careProvision, int ucTakeUp, double originalIncomePerWeek, double secondIncomePerWeek, double childcareCostPerWeek) { // initialise working variables int spa = getStatePensionAge(age, simYear); diff --git a/src/main/java/simpaths/model/taxes/KeyFunction4.java b/src/main/java/simpaths/model/taxes/KeyFunction4.java index ad3a3c121..6ae459e2c 100644 --- a/src/main/java/simpaths/model/taxes/KeyFunction4.java +++ b/src/main/java/simpaths/model/taxes/KeyFunction4.java @@ -46,12 +46,13 @@ public KeyFunction4() {} * @param dlltsdMan disability status of man * @param dlltsdWoman disability status of woman * @param careProvision indicator that at least one member of household provides social care + * @param ucTakeUp indicator that at least one member of household is a UC member * @param originalIncomePerWeek original income per week of family (possibly negative) * @return Integer list of keys, ordered from most fine (0) to most coarse (2) */ public Integer[] evaluateKeys(int simYear, int priceYear, int age, int numberMembersOver17, int numberChildrenUnder5, int numberChildren5To9, int numberChildren10To17, double hoursWorkedPerWeekMan, double hoursWorkedPerWeekWoman, int dlltsdMan, int dlltsdWoman, - int careProvision, double originalIncomePerWeek, double secondIncomePerWeek, double childcareCostPerWeek) { + int careProvision, int ucTakeUp, double originalIncomePerWeek, double secondIncomePerWeek, double childcareCostPerWeek) { // initialise working variables int spa = Parameters.getStatePensionAge(simYear, age); diff --git a/src/main/java/simpaths/model/taxes/KeyFunction5.java b/src/main/java/simpaths/model/taxes/KeyFunction5.java new file mode 100644 index 000000000..7a0fa6817 --- /dev/null +++ b/src/main/java/simpaths/model/taxes/KeyFunction5.java @@ -0,0 +1,456 @@ +package simpaths.model.taxes; + +import simpaths.data.Parameters; +import simpaths.model.decisions.DecisionParams; +import simpaths.model.enums.UpratingCase; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + + +/** + * + * CLASS TO MANAGE ONE SPECIFICATION FOR EVALUATING DONOR KEYS USED TO IMPUTE TAX AND BENEFIT PAYMENTS + * + */ +public class KeyFunction5 implements IKeyFunction { + + + /** + * ATTRIBUTES + */ + private final int MID_AGE = 45; + private final int INCOME_REF_YEAR = 2017; + private final double LO_INCOME = 225.0; + private final double HI_INCOME = 710.0; + + + /** + * CONSTRUCTORS + */ + public KeyFunction5() {} + + + /** + * METHOD TO EVALUATE DONOR KEYS FOR COARSE EXACT MATCHING + * @param simYear simulated year + * @param priceYear year of prices used to measure financial statistics + * @param age simulated age + * @param numberMembersOver17 family members aged 18+ + * @param numberChildrenUnder5 family members under age 5 + * @param numberChildren5To9 family members aged 5 to 9 + * @param numberChildren10To17 family members aged 10 to 17 + * @param hoursWorkedPerWeekMan employment hours per week of adult male + * @param hoursWorkedPerWeekWoman employment hours per week of adult female + * @param dlltsdMan disability status of man + * @param dlltsdWoman disability status of woman + * @param careProvision indicator that at least one member of household provides social care + * @param ucTakeUp indicator that at least one member of household is a UC member + * @param originalIncomePerWeek original income per week of family (possibly negative) + * @return Integer list of keys, ordered from most fine (0) to most coarse (2) + */ + public Integer[] evaluateKeys(int simYear, int priceYear, int age, int numberMembersOver17, int numberChildrenUnder5, int numberChildren5To9, + int numberChildren10To17, double hoursWorkedPerWeekMan, double hoursWorkedPerWeekWoman, int dlltsdMan, int dlltsdWoman, + int careProvision, int ucTakeUp, double originalIncomePerWeek, double secondIncomePerWeek, double childcareCostPerWeek) { + + // initialise working variables + int spa = Parameters.getStatePensionAge(simYear, age); + Map> taxdbCounter = getTaxdbCounter(); + Map> units = new HashMap<>(); + Integer[] result = new Integer[Parameters.TAXDB_REGIMES]; + Map localMap; + + // discretise hours worked variables + int partTimeEmployed = 0, fullTimeEmployed = 0; + if (hoursWorkedPerWeekMan >= DecisionParams.PARTTIME_HOURS_WEEKLY) { + fullTimeEmployed += 1; + } else if (hoursWorkedPerWeekMan > DecisionParams.MIN_WORK_HOURS_WEEKLY) { + partTimeEmployed += 1; + } + if (hoursWorkedPerWeekWoman >= DecisionParams.PARTTIME_HOURS_WEEKLY) { + fullTimeEmployed += 1; + } else if (hoursWorkedPerWeekWoman > DecisionParams.MIN_WORK_HOURS_WEEKLY) { + partTimeEmployed += 1; + } + + //------------------------------------------------------ + // evaluate characteristic-specific steps + //------------------------------------------------------ + + // age + localMap = new HashMap<>(); + if (age >= spa) { + localMap.put(0,2); + localMap.put(1,2); + localMap.put(2,2); + localMap.put(3,1); + localMap.put(4,1); + } else if (age >= MID_AGE) { + localMap.put(0,1); + localMap.put(1,1); + localMap.put(2,1); + localMap.put(3,0); + localMap.put(4,0); + } else { + localMap.put(0,0); + localMap.put(1,0); + localMap.put(2,0); + localMap.put(3,0); + localMap.put(4,0); + } + units.put(MatchFeature.Age, localMap); + + // adults + localMap = new HashMap<>(); + if (numberMembersOver17 > 1) { + localMap.put(0,1); + localMap.put(1,1); + localMap.put(2,1); + localMap.put(3,1); + localMap.put(4,1); + } else { + localMap.put(0,0); + localMap.put(1,0); + localMap.put(2,0); + localMap.put(3,0); + localMap.put(4,0); + } + units.put(MatchFeature.Adults, localMap); + + // children + localMap = new HashMap<>(); + if ( age < spa ) { + localMap.put(0, Math.min(numberChildrenUnder5,2) + 3 * Math.min(numberChildren5To9,2) + 9 * Math.min(numberChildren10To17,1)); + localMap.put(1, Math.min(numberChildrenUnder5,2) + 3 * Math.min(numberChildren5To9,2) + 9 * Math.min(numberChildren10To17,1)); + localMap.put(2, Math.min(numberChildrenUnder5,2) + 3 * Math.min(numberChildren5To9,2) + 9 * Math.min(numberChildren10To17,1)); + localMap.put(3, Math.min(numberChildrenUnder5,2) + 3 * Math.min(numberChildren5To9 + numberChildren10To17,2)); + localMap.put(4, Math.min(numberChildrenUnder5 + numberChildren5To9 + numberChildren10To17,3)); + } else { + localMap.put(0, 0); + localMap.put(1, 0); + localMap.put(2, 0); + localMap.put(3, 0); + localMap.put(4, 0); + } + units.put(MatchFeature.Children, localMap); + + // employment + localMap = new HashMap<>(); + if (partTimeEmployed + fullTimeEmployed == 0) { + // no employment + localMap.put(0,0); + localMap.put(1,0); + localMap.put(2,0); + localMap.put(3,0); + localMap.put(4,0); + } else if ( fullTimeEmployed == 0 ){ + // only part-time employed + localMap.put(0,1); + localMap.put(1,1); + localMap.put(2,1); + localMap.put(3,1); + localMap.put(4,0); + } else if ( partTimeEmployed + fullTimeEmployed == 1 ){ + // one full-time employed + localMap.put(0,2); + localMap.put(1,2); + localMap.put(2,2); + localMap.put(3,1); + localMap.put(4,0); + } else if ( partTimeEmployed == 1 & fullTimeEmployed == 1 ){ + // one full-time and one part-time employed + localMap.put(0,3); + localMap.put(1,3); + localMap.put(2,3); + localMap.put(3,2); + localMap.put(4,0); + } else { + // two full-time employed + localMap.put(0,4); + localMap.put(1,4); + localMap.put(2,4); + localMap.put(3,2); + localMap.put(4,0); + } + units.put(MatchFeature.Employment, localMap); + + // long-term sick and disabled + localMap = new HashMap<>(); + if ((dlltsdMan > 0 || dlltsdWoman > 0) && !Parameters.flagSuppressSocialCareCosts) { + // one adult disabled and one able-bodied + localMap.put(0,1); + localMap.put(1,1); + localMap.put(2,0); + localMap.put(3,0); + localMap.put(4,0); + } else { + // no disabled + localMap.put(0,0); + localMap.put(1,0); + localMap.put(2,0); + localMap.put(3,0); + localMap.put(4,0); + } + units.put(MatchFeature.Disability, localMap); + + // social care provision + localMap = new HashMap<>(); + if (careProvision > 0 && !Parameters.flagSuppressSocialCareCosts) { + localMap.put(0,1); + localMap.put(1,1); + localMap.put(2,1); + localMap.put(3,0); + localMap.put(4,0); + } else { + localMap.put(0,0); + localMap.put(1,0); + localMap.put(2,0); + localMap.put(3,0); + localMap.put(4,0); + } + units.put(MatchFeature.CareProvision, localMap); + + // UC take-up + localMap = new HashMap<>(); + if (ucTakeUp > 0 && !Parameters.flagSuppressUCTakeup) { + localMap.put(0,1); + localMap.put(1,1); + localMap.put(2,1); + localMap.put(3,0); + localMap.put(4,0); + } else { + localMap.put(0,0); + localMap.put(1,0); + localMap.put(2,0); + localMap.put(3,0); + localMap.put(4,0); + } + units.put(MatchFeature.UCTakeUp, localMap); + + // original income + localMap = new HashMap<>(); + double originalIncomePerWeekAdjusted = originalIncomePerWeek * Parameters.getTimeSeriesIndex(INCOME_REF_YEAR, UpratingCase.TaxDonor) / + Parameters.getTimeSeriesIndex(priceYear, UpratingCase.TaxDonor); + if (originalIncomePerWeekAdjusted < -LO_INCOME) { + // substantial negative income + localMap.put(0,0); + localMap.put(1,0); + localMap.put(2,0); + localMap.put(3,0); + localMap.put(4,0); + } else if (originalIncomePerWeekAdjusted < LO_INCOME) { + // low income + localMap.put(0,1); + localMap.put(1,1); + localMap.put(2,0); + localMap.put(3,0); + localMap.put(4,0); + } else if ( originalIncomePerWeekAdjusted < HI_INCOME ) { + // mid income + localMap.put(0,2); + localMap.put(1,2); + localMap.put(2,1); + localMap.put(3,1); + localMap.put(4,1); + } else { + // high income + localMap.put(0,3); + localMap.put(1,3); + localMap.put(2,2); + localMap.put(3,1); + localMap.put(4,1); + } + units.put(MatchFeature.Income, localMap); + + // second income + localMap = new HashMap<>(); + if (secondIncomePerWeek > 0.01) { + localMap.put(0,1); + localMap.put(1,0); + localMap.put(2,0); + localMap.put(3,0); + localMap.put(4,0); + } else { + localMap.put(0,0); + localMap.put(1,0); + localMap.put(2,0); + localMap.put(3,0); + localMap.put(4,0); + } + units.put(MatchFeature.DualIncome, localMap); + + // childcare costs + localMap = new HashMap<>(); + if (childcareCostPerWeek > 0.01) { + localMap.put(0,1); + localMap.put(1,0); + localMap.put(2,0); + localMap.put(3,0); + localMap.put(4,0); + } else { + localMap.put(0,0); + localMap.put(1,0); + localMap.put(2,0); + localMap.put(3,0); + localMap.put(4,0); + } + units.put(MatchFeature.Childcare, localMap); + + //------------------------------------------------------ + // compile results + //------------------------------------------------------ + for (int ii=0; ii> taxdbCounter = getTaxdbCounter(); + int keyLocal = keyValue; + for (int ii = MatchFeature.values().length-1; ii>=0; ii--) { + + MatchFeature featureHere = MatchFeature.values()[ii]; + try { + int size = taxdbCounter.get(featureHere).get(taxDBRegime); + int index = keyLocal / size; + if (feature.equals(featureHere)) + return index; + else + keyLocal -= index * size; + } catch (Exception e) { + System.out.println("Issue retrieving feature" + featureHere + "for regime " + taxDBRegime); + e.printStackTrace(); + } + } + throw new RuntimeException("failed to identify match feature for indexing"); + } + + + /** + * METHOD TO INDICATE IF TAX UNIT IS MEMBER OF 'LOW INCOME' CATEGORY FOR DATABASE MATCHING + */ + public boolean[] isLowIncome(Integer[] keys) { + + boolean[] lowIncome = new boolean[Parameters.TAXDB_REGIMES]; + for (int regime=0; regime 1 && index == 0); + } + return lowIncome; + } + + + /** + * WORKER METHOD TO CALL OR INITIALISE THE COUNTER MAPPING FOR DONOR KEYS + * @return taxdbCounter populated as implied by current matching function + */ + public Map> getTaxdbCounter() { + + Map> taxdbCounter = Parameters.getTaxdbCounter(); + if (taxdbCounter.isEmpty()) { + + // initialise working variables + Map mapLocal; + int[] ptsLocal; + + // initialise starting values + ptsLocal = new int[Parameters.TAXDB_REGIMES]; + Arrays.fill(ptsLocal, 1); + mapLocal = new HashMap<>(); + for ( int ii=0; ii updateMap(Map mapPrev, int[] ptsPrev) { + + Map mapHere = new HashMap<>(); + for ( int ii=0; ii Parameters.validateRegressors(goodMap, "A map designed to contain valid values", "Sheet1")); } -} \ No newline at end of file +} diff --git a/src/test/java/simpaths/model/PersonTest.java b/src/test/java/simpaths/model/PersonTest.java index 64e2b4b68..bca2095f4 100644 --- a/src/test/java/simpaths/model/PersonTest.java +++ b/src/test/java/simpaths/model/PersonTest.java @@ -401,7 +401,7 @@ public void setupLawrenceCoefficients() { Parameters.setInputDirectory("src/test/java/simpaths/testinput"); Parameters.eq5dConversionParameters = "lawrence"; - Parameters.loadEQ5DParameters("UK"); + Parameters.loadEQ5DParameters(); } @Test @@ -438,7 +438,7 @@ public void setupFranksCoefficients() { Parameters.setInputDirectory("src/test/java/simpaths/testinput"); Parameters.eq5dConversionParameters = "franks"; - Parameters.loadEQ5DParameters("UK"); + Parameters.loadEQ5DParameters(); } @Test diff --git a/src/test/java/simpaths/testinput/reg_eq5d.xlsx b/src/test/java/simpaths/testinput/reg_eq5d.xlsx index 744768a83..0f357c66d 100644 Binary files a/src/test/java/simpaths/testinput/reg_eq5d.xlsx and b/src/test/java/simpaths/testinput/reg_eq5d.xlsx differ