From 4d624677b0e5eace3e2cb14ffb417ece14952771 Mon Sep 17 00:00:00 2001 From: Mariia Var Date: Thu, 26 Mar 2026 15:45:03 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=201.=20EntityManagerFactory=20cached=20as?= =?UTF-8?q?=20static=20fields=20=20=20Persistence.createEntityManagerFacto?= =?UTF-8?q?ry(...)=20was=20called=20on=20every=20invocation=20of=20all=20t?= =?UTF-8?q?hree=20methods.=20Building=20a=20factory=20is=20expensive=20?= =?UTF-8?q?=E2=80=94=20it=20validates=20the=20schema=20and=20sets=20up=20a?= =?UTF-8?q?=20connection=20pool.=20Two=20static=20fields=20replace=20this:?= =?UTF-8?q?=20=20=20-=20emfStartingPopulationPersist=20=E2=80=94=20shared?= =?UTF-8?q?=20by=20getProcessed()=20and=20persistProcessed()=20(both=20use?= =?UTF-8?q?=20getPersistDatabasePath())=20=20=20-=20emfStartingPopulationR?= =?UTF-8?q?un=20=E2=80=94=20used=20by=20loadStartingPopulation()=20(uses?= =?UTF-8?q?=20RunDatabasePath)=20=20=202.=20loadStartingPopulation()=20?= =?UTF-8?q?=E2=80=94=20bug=20fix=20for=20LazyInitializationException=20=20?= =?UTF-8?q?=20With=20@OneToMany=20collections=20already=20marked=20FetchTy?= =?UTF-8?q?pe.LAZY,=20returning=20detached=20Household=20objects=20after?= =?UTF-8?q?=20em.close()=20without=20having=20initialised=20benefitUnits?= =?UTF-8?q?=20or=20members=20would=20throw=20LazyInitializationException?= =?UTF-8?q?=20the=20moment=20the=20caller=20iterated=20them.=20The=20force?= =?UTF-8?q?-initialisation=20loop=20triggers=20the=20SUBSELECT=20batch=20l?= =?UTF-8?q?oads=20=20=20=20within=20the=20open=20session=20before=20closin?= =?UTF-8?q?g=20it.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3. getProcessed() — dead variable removed processed_return was assigned the JOIN FETCH result but the method returned processed (the same L1-cached object). The two are identical references, so processed_return.resetDependents() and processed.resetDependents() were equivalent. The dead variable is removed and the call is chained directly. No behaviour change. --- .../java/simpaths/model/SimPathsModel.java | 32 +++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/main/java/simpaths/model/SimPathsModel.java b/src/main/java/simpaths/model/SimPathsModel.java index 2d3fdd196..2f82299df 100644 --- a/src/main/java/simpaths/model/SimPathsModel.java +++ b/src/main/java/simpaths/model/SimPathsModel.java @@ -4,6 +4,7 @@ // import Java packages import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.EntityTransaction; import jakarta.persistence.Persistence; import jakarta.persistence.Transient; @@ -329,6 +330,8 @@ public void setFirstRun(boolean firstRun) { private static String RunDatabasePath; private static String PersistDatabasePath; private static boolean PersistPopulation = false; + private static EntityManagerFactory emfStartingPopulationRun = null; + private static EntityManagerFactory emfStartingPopulationPersist = null; @@ -3633,7 +3636,6 @@ private Processed getProcessed() { private Processed getProcessed(Country country, int startYear, int popSize, boolean ignoreTargetsAtPopulationLoad) { Processed processed = null; - Processed processed_return = null; EntityTransaction txn = null; try { @@ -3641,7 +3643,9 @@ private Processed getProcessed(Country country, int startYear, int popSize, bool // query database Map propertyMap = new HashMap(); propertyMap.put("hibernate.connection.url", "jdbc:h2:file:" + getPersistDatabasePath() + ";TRACE_LEVEL_FILE=0;TRACE_LEVEL_SYSTEM_OUT=0;AUTO_SERVER=TRUE"); - EntityManager em = Persistence.createEntityManagerFactory("starting-population", propertyMap).createEntityManager(); + if (emfStartingPopulationPersist == null) + emfStartingPopulationPersist = Persistence.createEntityManagerFactory("starting-population", propertyMap); + EntityManager em = emfStartingPopulationPersist.createEntityManager(); txn = em.getTransaction(); txn.begin(); // String query = "SELECT DISTINCT processed FROM Processed processed LEFT JOIN FETCH processed.households households LEFT JOIN FETCH households.benefitUnits benefitUnits LEFT JOIN FETCH benefitUnits.members members WHERE processed.startYear = " + startYear + " AND processed.popSize = " + popSize + " AND processed.country = " + country + " AND processed.noTargets = " + ignoreTargetsAtPopulationLoad + " ORDER BY households.key.id"; @@ -3655,16 +3659,15 @@ private Processed getProcessed(Country country, int startYear, int popSize, bool if (processedList.size()>1) throw new RuntimeException("more than one relevant dataset returned from database"); processed = processedList.get(0); + // force-initialize lazy collections within the open session (triggers SUBSELECT batch loads) processed.resetDependents(); -// // Now fetch households for THIS specific Processed instance only - processed_return = em.createQuery( + em.createQuery( "SELECT p FROM Processed p LEFT JOIN FETCH p.households h WHERE p = :proc ORDER BY h.key.id", Processed.class) .setParameter("proc", processed) - .getSingleResult(); - - processed_return.resetDependents(); + .getSingleResult() + .resetDependents(); } // close database connection @@ -3689,7 +3692,9 @@ private List loadStartingPopulation() { Map propertyMap = new HashMap(); propertyMap.put("hibernate.connection.url", "jdbc:h2:file:" + RunDatabasePath + ";TRACE_LEVEL_FILE=0;TRACE_LEVEL_SYSTEM_OUT=0;AUTO_SERVER=TRUE"); - EntityManager em = Persistence.createEntityManagerFactory("starting-population", propertyMap).createEntityManager(); + if (emfStartingPopulationRun == null) + emfStartingPopulationRun = Persistence.createEntityManagerFactory("starting-population", propertyMap); + EntityManager em = emfStartingPopulationRun.createEntityManager(); txn = em.getTransaction(); txn.begin(); String query = "SELECT households FROM Household households"; @@ -3698,6 +3703,13 @@ private List loadStartingPopulation() { households = em.createQuery(query).getResultList(); log.info("Query complete"); + // force-initialize lazy collections within the open session (triggers SUBSELECT batch loads) + for (Household hh : households) { + for (BenefitUnit bu : hh.getBenefitUnits()) { + bu.getMembers().size(); + } + } + // close database connection em.close(); } catch (Exception e) { @@ -3722,7 +3734,9 @@ private void persistProcessed(Set households, Country country, int st Map propertyMap = new HashMap(); propertyMap.put("hibernate.connection.url", "jdbc:h2:file:" + getPersistDatabasePath() + ";TRACE_LEVEL_FILE=0;TRACE_LEVEL_SYSTEM_OUT=0;AUTO_SERVER=TRUE"); - EntityManager em = Persistence.createEntityManagerFactory("starting-population", propertyMap).createEntityManager(); + if (emfStartingPopulationPersist == null) + emfStartingPopulationPersist = Persistence.createEntityManagerFactory("starting-population", propertyMap); + EntityManager em = emfStartingPopulationPersist.createEntityManager(); txn = em.getTransaction(); txn.begin(); From 7e8a3a71e9d98e487a4ded3d23b61752fd685608 Mon Sep 17 00:00:00 2001 From: Mariia Var Date: Thu, 26 Mar 2026 15:54:39 +0000 Subject: [PATCH 2/3] Fix non-deterministic UC/Legacy benefit assignment -DonorTaxImputation used Math.random() to decide whether a benefit unit receives Universal Credit based on the weighted mean UC receipt across donor candidates. Math.random() draws from Java's global, unseeded Random instance, producing different results on every JVM run regardless of the model's random seed setting. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -Replaced with SimulationEngine.getRnd().nextDouble(), which draws from the model's seeded Random instance — the same one used throughout the rest of the model. With a fixed seed, UC/Legacy benefit assignment is now deterministic across runs. --- src/main/java/simpaths/model/taxes/DonorTaxImputation.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/simpaths/model/taxes/DonorTaxImputation.java b/src/main/java/simpaths/model/taxes/DonorTaxImputation.java index 0f0191574..d0aa8edd1 100644 --- a/src/main/java/simpaths/model/taxes/DonorTaxImputation.java +++ b/src/main/java/simpaths/model/taxes/DonorTaxImputation.java @@ -1,6 +1,7 @@ package simpaths.model.taxes; +import microsim.engine.SimulationEngine; import org.apache.commons.lang3.tuple.Triple; import simpaths.model.enums.UpratingCase; @@ -366,7 +367,7 @@ public void evaluate() { disposableIncomePerWeek *= (1.0 + Parameters.disposableIncomeFromLabourInnov); benefitsReceivedPerWeek *= (1.0 + Parameters.disposableIncomeFromLabourInnov); } - if (UCmean > Math.random()) { // Weighted probability of receiving UC + if (UCmean > SimulationEngine.getRnd().nextDouble()) { // Weighted probability of receiving UC setReceivedUC(1); } if (LBmean > 0 && getReceivedUC() == 0) { // Setting as received LB if benefits but not UC From b84c8e7d1d8e3a81a28d3761d27a124c98205e93 Mon Sep 17 00:00:00 2001 From: Mariia Var Date: Thu, 26 Mar 2026 16:46:54 +0000 Subject: [PATCH 3/3] =?UTF-8?q?Replace=20Statistics3=20export=20with=20Ali?= =?UTF-8?q?gnmentAdjustmentFactors=20=20=20AlignmentAdjustmentFactors=20fi?= =?UTF-8?q?elds=20(30=20total):=20=20=20-=20Partnership:=20adj=20factor,?= =?UTF-8?q?=20sim=20share,=20target=20share=20(same=20computation)=20=20?= =?UTF-8?q?=20-=20Fertility:=20adj=20factor,=20sim=20rate,=20target=20rate?= =?UTF-8?q?=20(same=20computation)=20=20=20-=20In-school:=20adj=20factor,?= =?UTF-8?q?=20sim=20share,=20target=20share=20=E2=80=94=20new=20=20=20-=20?= =?UTF-8?q?Utility=20adj=20factors=20=C3=97=207=20(same=20computation)=20?= =?UTF-8?q?=20=20-=20Employment=20shares:=207=20=C3=97=20simulated=20+=207?= =?UTF-8?q?=20=C3=97=20target=20=E2=80=94=20new?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/default.yml | 4 +- config/test_run.yml | 4 +- .../AlignmentAdjustmentFactors.java | 347 ++++++++++++++++++ .../simpaths/data/statistics/Statistics3.java | 200 ---------- .../experiment/SimPathsCollector.java | 54 +-- src/main/resources/META-INF/persistence.xml | 2 +- .../RunSimPathsIntegrationTest.java | 10 +- 7 files changed, 377 insertions(+), 244 deletions(-) create mode 100644 src/main/java/simpaths/data/statistics/AlignmentAdjustmentFactors.java delete mode 100644 src/main/java/simpaths/data/statistics/Statistics3.java diff --git a/config/default.yml b/config/default.yml index ca449ce17..d2dcda063 100644 --- a/config/default.yml +++ b/config/default.yml @@ -143,7 +143,7 @@ innovation_args: # Output files: # Statistics1.csv — income distribution: Gini coefficients, income percentiles, median EDI, S-Index # Statistics2.csv — demographic validation: partnership rates, employment, health, disability by age/gender -# Statistics3.csv — alignment diagnostics: simulated vs target rates and adjustment factors +# AlignmentAdjustmentFactors1.csv — alignment diagnostics: factors, simulated shares, and target shares # EmploymentStatistics.csv — labour market transitions and participation rates # HealthStatistics.csv — health measures (SF-12, GHQ-12, EQ-5D) by age/gender @@ -153,7 +153,7 @@ collector_args: # exportToCSV: true # write outputs to CSV files under output//csv/ # persistStatistics: true # write Statistics1.csv (income distribution) # persistStatistics2: true # write Statistics2.csv (demographic validation outputs) -# persistStatistics3: true # write Statistics3.csv (alignment diagnostics) +# persistAlignmentAdjustmentFactors: true # write AlignmentAdjustmentFactors1.csv # persistPersons: false # write one row per person per year (large files) # persistBenefitUnits: false # write one row per benefit unit per year (large files) # persistHouseholds: false # write one row per household per year diff --git a/config/test_run.yml b/config/test_run.yml index 8895a6b60..92a0cf6a6 100644 --- a/config/test_run.yml +++ b/config/test_run.yml @@ -12,7 +12,7 @@ integrationTest: true collector_args: persistStatistics: true persistStatistics2: true - persistStatistics3: true + persistAlignmentAdjustmentFactors: true persistPersons: false persistBenefitUnits: false - persistHouseholds: false \ No newline at end of file + persistHouseholds: false diff --git a/src/main/java/simpaths/data/statistics/AlignmentAdjustmentFactors.java b/src/main/java/simpaths/data/statistics/AlignmentAdjustmentFactors.java new file mode 100644 index 000000000..439c4c06a --- /dev/null +++ b/src/main/java/simpaths/data/statistics/AlignmentAdjustmentFactors.java @@ -0,0 +1,347 @@ +package simpaths.data.statistics; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import microsim.data.db.PanelEntityKey; +import simpaths.data.Parameters; +import simpaths.data.filters.FertileFilter; +import simpaths.model.BenefitUnit; +import simpaths.model.Person; +import simpaths.model.SimPathsModel; +import simpaths.model.enums.*; + +/** + * + * CLASS TO REPORT ALIGNMENT ADJUSTMENT FACTORS AND SIMULATED VS TARGET SHARES + * + * Covers: partnership, fertility, in-school, utility adjustment factors, and + * employment shares by occupancy type. Disability and retirement are excluded. + * + */ +@Entity +public class AlignmentAdjustmentFactors { + + @Id + private PanelEntityKey key = new PanelEntityKey(1L); + + // ------------------------------------------------------------------ + // Partnership + // ------------------------------------------------------------------ + @Column(name = "partnership_adj_factor") + private double partnershipAdjFactor; + + @Column(name = "share_cohabiting_sim") + private double shareCohabitingSim; + + @Column(name = "share_cohabiting_tgt") + private double shareCohabitingTgt; + + // ------------------------------------------------------------------ + // Fertility + // ------------------------------------------------------------------ + @Column(name = "fertility_adj_factor") + private double fertilityAdjFactor; + + @Column(name = "fertility_rate_sim") + private double fertilityRateSim; + + @Column(name = "fertility_rate_tgt") + private double fertilityRateTgt; + + // ------------------------------------------------------------------ + // In-school + // ------------------------------------------------------------------ + @Column(name = "in_school_adj_factor") + private double inSchoolAdjFactor; + + @Column(name = "in_school_share_sim") + private double inSchoolShareSim; + + @Column(name = "in_school_share_tgt") + private double inSchoolShareTgt; + + // ------------------------------------------------------------------ + // Utility adjustment factors (one per occupancy type) + // ------------------------------------------------------------------ + @Column(name = "utility_adj_single_males") + private double utilityAdjSingleMales; + + @Column(name = "utility_adj_ac_males") + private double utilityAdjACMales; + + @Column(name = "utility_adj_single_females") + private double utilityAdjSingleFemales; + + @Column(name = "utility_adj_ac_females") + private double utilityAdjACFemales; + + @Column(name = "utility_adj_couples") + private double utilityAdjCouples; + + @Column(name = "utility_adj_single_dep_males") + private double utilityAdjSingleDepMales; + + @Column(name = "utility_adj_single_dep_females") + private double utilityAdjSingleDepFemales; + + // ------------------------------------------------------------------ + // Employment shares — simulated + // ------------------------------------------------------------------ + @Column(name = "emp_share_sim_single_males") + private double empShareSimSingleMales; + + @Column(name = "emp_share_sim_single_females") + private double empShareSimSingleFemales; + + @Column(name = "emp_share_sim_ac_males") + private double empShareSimACMales; + + @Column(name = "emp_share_sim_ac_females") + private double empShareSimACFemales; + + @Column(name = "emp_share_sim_couples") + private double empShareSimCouples; + + @Column(name = "emp_share_sim_single_dep_males") + private double empShareSimSingleDepMales; + + @Column(name = "emp_share_sim_single_dep_females") + private double empShareSimSingleDepFemales; + + // ------------------------------------------------------------------ + // Employment shares — target + // ------------------------------------------------------------------ + @Column(name = "emp_share_tgt_single_males") + private double empShareTgtSingleMales; + + @Column(name = "emp_share_tgt_single_females") + private double empShareTgtSingleFemales; + + @Column(name = "emp_share_tgt_ac_males") + private double empShareTgtACMales; + + @Column(name = "emp_share_tgt_ac_females") + private double empShareTgtACFemales; + + @Column(name = "emp_share_tgt_couples") + private double empShareTgtCouples; + + @Column(name = "emp_share_tgt_single_dep_males") + private double empShareTgtSingleDepMales; + + @Column(name = "emp_share_tgt_single_dep_females") + private double empShareTgtSingleDepFemales; + + + // ------------------------------------------------------------------ + // Getters and setters + // ------------------------------------------------------------------ + + public double getPartnershipAdjFactor() { return partnershipAdjFactor; } + public void setPartnershipAdjFactor(double v) { partnershipAdjFactor = v; } + + public double getShareCohabitingSim() { return shareCohabitingSim; } + public void setShareCohabitingSim(double v) { shareCohabitingSim = v; } + + public double getShareCohabitingTgt() { return shareCohabitingTgt; } + public void setShareCohabitingTgt(double v) { shareCohabitingTgt = v; } + + public double getFertilityAdjFactor() { return fertilityAdjFactor; } + public void setFertilityAdjFactor(double v) { fertilityAdjFactor = v; } + + public double getFertilityRateSim() { return fertilityRateSim; } + public void setFertilityRateSim(double v) { fertilityRateSim = v; } + + public double getFertilityRateTgt() { return fertilityRateTgt; } + public void setFertilityRateTgt(double v) { fertilityRateTgt = v; } + + public double getInSchoolAdjFactor() { return inSchoolAdjFactor; } + public void setInSchoolAdjFactor(double v) { inSchoolAdjFactor = v; } + + public double getInSchoolShareSim() { return inSchoolShareSim; } + public void setInSchoolShareSim(double v) { inSchoolShareSim = v; } + + public double getInSchoolShareTgt() { return inSchoolShareTgt; } + public void setInSchoolShareTgt(double v) { inSchoolShareTgt = v; } + + public double getUtilityAdjSingleMales() { return utilityAdjSingleMales; } + public void setUtilityAdjSingleMales(double v) { utilityAdjSingleMales = v; } + + public double getUtilityAdjACMales() { return utilityAdjACMales; } + public void setUtilityAdjACMales(double v) { utilityAdjACMales = v; } + + public double getUtilityAdjSingleFemales() { return utilityAdjSingleFemales; } + public void setUtilityAdjSingleFemales(double v) { utilityAdjSingleFemales = v; } + + public double getUtilityAdjACFemales() { return utilityAdjACFemales; } + public void setUtilityAdjACFemales(double v) { utilityAdjACFemales = v; } + + public double getUtilityAdjCouples() { return utilityAdjCouples; } + public void setUtilityAdjCouples(double v) { utilityAdjCouples = v; } + + public double getUtilityAdjSingleDepMales() { return utilityAdjSingleDepMales; } + public void setUtilityAdjSingleDepMales(double v) { utilityAdjSingleDepMales = v; } + + public double getUtilityAdjSingleDepFemales() { return utilityAdjSingleDepFemales; } + public void setUtilityAdjSingleDepFemales(double v) { utilityAdjSingleDepFemales = v; } + + public double getEmpShareSimSingleMales() { return empShareSimSingleMales; } + public void setEmpShareSimSingleMales(double v) { empShareSimSingleMales = v; } + + public double getEmpShareSimSingleFemales() { return empShareSimSingleFemales; } + public void setEmpShareSimSingleFemales(double v) { empShareSimSingleFemales = v; } + + public double getEmpShareSimACMales() { return empShareSimACMales; } + public void setEmpShareSimACMales(double v) { empShareSimACMales = v; } + + public double getEmpShareSimACFemales() { return empShareSimACFemales; } + public void setEmpShareSimACFemales(double v) { empShareSimACFemales = v; } + + public double getEmpShareSimCouples() { return empShareSimCouples; } + public void setEmpShareSimCouples(double v) { empShareSimCouples = v; } + + public double getEmpShareSimSingleDepMales() { return empShareSimSingleDepMales; } + public void setEmpShareSimSingleDepMales(double v) { empShareSimSingleDepMales = v; } + + public double getEmpShareSimSingleDepFemales() { return empShareSimSingleDepFemales; } + public void setEmpShareSimSingleDepFemales(double v) { empShareSimSingleDepFemales = v; } + + public double getEmpShareTgtSingleMales() { return empShareTgtSingleMales; } + public void setEmpShareTgtSingleMales(double v) { empShareTgtSingleMales = v; } + + public double getEmpShareTgtSingleFemales() { return empShareTgtSingleFemales; } + public void setEmpShareTgtSingleFemales(double v) { empShareTgtSingleFemales = v; } + + public double getEmpShareTgtACMales() { return empShareTgtACMales; } + public void setEmpShareTgtACMales(double v) { empShareTgtACMales = v; } + + public double getEmpShareTgtACFemales() { return empShareTgtACFemales; } + public void setEmpShareTgtACFemales(double v) { empShareTgtACFemales = v; } + + public double getEmpShareTgtCouples() { return empShareTgtCouples; } + public void setEmpShareTgtCouples(double v) { empShareTgtCouples = v; } + + public double getEmpShareTgtSingleDepMales() { return empShareTgtSingleDepMales; } + public void setEmpShareTgtSingleDepMales(double v) { empShareTgtSingleDepMales = v; } + + public double getEmpShareTgtSingleDepFemales() { return empShareTgtSingleDepFemales; } + public void setEmpShareTgtSingleDepFemales(double v) { empShareTgtSingleDepFemales = v; } + + + // ------------------------------------------------------------------ + // update() + // ------------------------------------------------------------------ + + public void update(SimPathsModel model) { + + int year = model.getYear() - 1; // year just simulated (consistent with annual collector exports) + + // --- Partnership --- + setPartnershipAdjFactor( + Parameters.getTimeSeriesValue(year, TimeSeriesVariable.PartnershipAdjustment) + + model.getPartnershipAdjustment(year)); + long numPersonsCohabEligible = model.getPersons().stream() + .filter(p -> p.getDemAge() >= Parameters.MIN_AGE_COHABITATION) + .count(); + long numPersonsPartnered = model.getPersons().stream() + .filter(p -> Dcpst.Partnered.equals(p.getDcpst())) + .count(); + setShareCohabitingSim(numPersonsCohabEligible > 0 + ? (double) numPersonsPartnered / numPersonsCohabEligible : 0.0); + setShareCohabitingTgt(Parameters.getTargetShare(year, TargetShares.Partnership)); + + // --- Fertility --- + setFertilityAdjFactor( + Parameters.getTimeSeriesValue(year, TimeSeriesVariable.FertilityAdjustment) + + model.getFertilityAdjustment(year)); + FertileFilter fertileFilter = new FertileFilter(); + long numFertile = model.getPersons().stream() + .filter(p -> fertileFilter.evaluate(p)) + .count(); + long numBirths = model.getPersons().stream() + .filter(p -> p.getDemAge() < 1) + .count(); + setFertilityRateSim(numFertile > 0 ? (double) numBirths / numFertile : 0.0); + setFertilityRateTgt(Parameters.getFertilityRateByYear(year)); + + // --- In-school --- + setInSchoolAdjFactor(Parameters.getTimeSeriesValue(year, TimeSeriesVariable.InSchoolAdjustment)); + long numStudents = model.getPersons().stream() + .filter(p -> p.getDemAge() >= Parameters.MIN_AGE_TO_LEAVE_EDUCATION + && p.getDemAge() <= Parameters.MAX_AGE_TO_STAY_IN_CONTINUOUS_EDUCATION + && !p.isToLeaveSchool() + && Les_c4.Student.equals(p.getLes_c4())) + .count(); + long numInSchoolAge = model.getPersons().stream() + .filter(p -> p.getDemAge() >= Parameters.MIN_AGE_TO_LEAVE_EDUCATION + && p.getDemAge() <= Parameters.MAX_AGE_TO_STAY_IN_CONTINUOUS_EDUCATION + && p.getLes_c4() != null) + .count(); + setInSchoolShareSim(numInSchoolAge > 0 ? (double) numStudents / numInSchoolAge : 0.0); + setInSchoolShareTgt(Parameters.getTargetShare(year, TargetShares.Students)); + + // --- Utility adjustment factors --- + setUtilityAdjSingleMales(Parameters.getTimeSeriesValue(year, TimeSeriesVariable.UtilityAdjustmentSingleMales)); + setUtilityAdjACMales(Parameters.getTimeSeriesValue(year, TimeSeriesVariable.UtilityAdjustmentACMales)); + setUtilityAdjSingleFemales(Parameters.getTimeSeriesValue(year, TimeSeriesVariable.UtilityAdjustmentSingleFemales)); + setUtilityAdjACFemales(Parameters.getTimeSeriesValue(year, TimeSeriesVariable.UtilityAdjustmentACFemales)); + setUtilityAdjCouples(Parameters.getTimeSeriesValue(year, TimeSeriesVariable.UtilityAdjustmentCouples)); + setUtilityAdjSingleDepMales(Parameters.getTimeSeriesValue(year, TimeSeriesVariable.UtilityAdjustmentSingleDepMen)); + setUtilityAdjSingleDepFemales(Parameters.getTimeSeriesValue(year, TimeSeriesVariable.UtilityAdjustmentSingleDepWomen)); + + // --- Employment shares --- + double[] totSM = new double[2]; // [count, fracEmployed sum] + double[] totSF = new double[2]; + double[] totACM = new double[2]; + double[] totACF = new double[2]; + double[] totCou = new double[2]; + double[] totSDM = new double[2]; + double[] totSDF = new double[2]; + + for (BenefitUnit bu : model.getBenefitUnits()) { + Occupancy occ = bu.getOccupancy(); + Person male = bu.getMale(); + Person female = bu.getFemale(); + boolean maleAtRisk = (male != null) && male.atRiskOfWork(); + boolean femaleAtRisk = (female != null) && female.atRiskOfWork(); + int acFlag = 0; + if (occ == Occupancy.Single_Male && male != null) acFlag = male.getAdultChildFlag(); + if (occ == Occupancy.Single_Female && female != null) acFlag = female.getAdultChildFlag(); + + double frac = bu.fracEmployed(); + + if (occ == Occupancy.Single_Male && acFlag != 1) { + totSM[0]++; totSM[1] += frac; + } else if (occ == Occupancy.Single_Male && acFlag == 1) { + totACM[0]++; totACM[1] += frac; + } else if (occ == Occupancy.Single_Female && acFlag != 1) { + totSF[0]++; totSF[1] += frac; + } else if (occ == Occupancy.Single_Female && acFlag == 1) { + totACF[0]++; totACF[1] += frac; + } else if (occ == Occupancy.Couple && maleAtRisk && femaleAtRisk) { + totCou[0]++; totCou[1] += frac; + } else if (occ == Occupancy.Couple && maleAtRisk && !femaleAtRisk) { + totSDM[0]++; totSDM[1] += frac; + } else if (occ == Occupancy.Couple && !maleAtRisk && femaleAtRisk) { + totSDF[0]++; totSDF[1] += frac; + } + } + + setEmpShareSimSingleMales( totSM[0] > 0 ? totSM[1] / totSM[0] : 0.0); + setEmpShareSimSingleFemales(totSF[0] > 0 ? totSF[1] / totSF[0] : 0.0); + setEmpShareSimACMales( totACM[0] > 0 ? totACM[1] / totACM[0] : 0.0); + setEmpShareSimACFemales( totACF[0] > 0 ? totACF[1] / totACF[0] : 0.0); + setEmpShareSimCouples( totCou[0] > 0 ? totCou[1] / totCou[0] : 0.0); + setEmpShareSimSingleDepMales( totSDM[0] > 0 ? totSDM[1] / totSDM[0] : 0.0); + setEmpShareSimSingleDepFemales(totSDF[0] > 0 ? totSDF[1] / totSDF[0] : 0.0); + + setEmpShareTgtSingleMales( Parameters.getTargetShare(year, TargetShares.EmploymentSingleMales)); + setEmpShareTgtSingleFemales(Parameters.getTargetShare(year, TargetShares.EmploymentSingleFemales)); + setEmpShareTgtACMales( Parameters.getTargetShare(year, TargetShares.EmploymentACMales)); + setEmpShareTgtACFemales( Parameters.getTargetShare(year, TargetShares.EmploymentACFemales)); + setEmpShareTgtCouples( Parameters.getTargetShare(year, TargetShares.EmploymentCouples)); + setEmpShareTgtSingleDepMales( Parameters.getTargetShare(year, TargetShares.EmploymentSingleDepMales)); + setEmpShareTgtSingleDepFemales(Parameters.getTargetShare(year, TargetShares.EmploymentSingleDepFemales)); + } +} diff --git a/src/main/java/simpaths/data/statistics/Statistics3.java b/src/main/java/simpaths/data/statistics/Statistics3.java deleted file mode 100644 index 4b2c5bdb1..000000000 --- a/src/main/java/simpaths/data/statistics/Statistics3.java +++ /dev/null @@ -1,200 +0,0 @@ -package simpaths.data.statistics; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import microsim.data.db.PanelEntityKey; -import simpaths.data.Parameters; -import simpaths.data.filters.FertileFilter; -import simpaths.model.SimPathsModel; -import simpaths.model.enums.AlignmentVariable; -import simpaths.model.enums.Dcpst; -import simpaths.model.enums.TargetShares; -import simpaths.model.enums.TimeSeriesVariable; - -@Entity -public class Statistics3 { - - @Id - private PanelEntityKey key = new PanelEntityKey(1L); - - @Column(name= "social_care_adj_factor") - private double careAdj; - - @Column(name = "partnership_adj_factor") - private double demPartnerAdj; - - @Column(name = "share_cohabiting_sim") - private double demPartnerSimShare; - - @Column(name = "share_cohabiting_tgt") - private double demPartnerTargetShare; - - @Column(name = "fertility_adj_factor") - private double demFertAdj; - - @Column(name = "fertiilty_rate_sim") - private double demFertRateSim; - - @Column(name = "fertiilty_rate_tgt") - private double demFertRateTarget; - - @Column(name = "utility_adj_factor_smales") - private double demUtilAdjSingleM; - - @Column(name = "utility_adj_factor_ac_males") - private double demUtilAdjSingleACM; - - @Column(name = "utility_adj_factor_sfemales") - private double demUtilAdjSingleF; - - @Column(name = "utility_adj_factor_ac_females") - private double demUtilAdjSingleACF; - - @Column(name = "utility_adj_factor_couples") - private double demUtilAdjCouple; - - @Column(name = "utility_adj_factor_smales_dep") - private double demUtilAdjSDepM; - - @Column(name = "utility_adj_factor_sfemales_dep") - private double demUtilAdjSDepF; - - public double getPartnershipAdjustmentFactor() { - return demPartnerAdj; - } - - public void setPartnershipAdjustmentFactor(double demPartnerAdj) { - this.demPartnerAdj = demPartnerAdj; - } - - public double getFertilityAdjustmentFactor() { - return demFertAdj; - } - - public void setFertilityAdjustmentFactor(double factor) { - this.demFertAdj = factor; - } - - public double getUtilityAdjustmentFactorSmales() { - return demUtilAdjSingleM; - } - - public void setUtilityAdjustmentFactorSmales(double demUtilAdjSingleM) { - this.demUtilAdjSingleM = demUtilAdjSingleM; - } - - public double getUtilityAdjustmentFactorACmales() {return demUtilAdjSingleACM; } - - public void setUtilityAdjustmentFactorACmales(double demUtilAdjACM) { - this.demUtilAdjSingleACM = demUtilAdjACM; - } - - public double getUtilityAdjustmentFactorSfemales() { - return demUtilAdjSingleF; - } - - public void setUtilityAdjustmentFactorSfemales(double demUtilAdjSingleF) { - this.demUtilAdjSingleF = demUtilAdjSingleF; - } - - public double getUtilityAdjustmentFactorACfemales() { return demUtilAdjSingleACF;} - - public void setUtilityAdjustmentFactorACfemales(double demUtilAdjACF) { - this.demUtilAdjSingleACF = demUtilAdjACF; - } - - public double getUtilityAdjustmentFactorCouples() { - return demUtilAdjCouple; - } - - public void setUtilityAdjustmentFactorCouples(double demUtilAdjCouple) { - this.demUtilAdjCouple = demUtilAdjCouple; - } - - public double getUtilityAdjustmentFactorSDepFemales() {return demUtilAdjSDepF;} - - public void setUtilityAdjustmentFactorSDepFemales(double demUtilAdjSDepF) { - this.demUtilAdjSDepF = demUtilAdjSDepF; - } - - public double getUtilityAdjustmentFactorSDepMales(){return demUtilAdjSDepM;} - - public void setUtilityAdjustmentFactorSDepMales(double demUtilAdjSDepM) { - this.demUtilAdjSDepM = demUtilAdjSDepM; - } - - public double getSocialCareAdjustmentFactor() { return careAdj; } - - public void setSocialCareAdjustmentFactor(double factor) { - careAdj = factor;} - - public double getShareCohabitingSimulated() {return demPartnerSimShare;} - - public void setShareCohabitingSimulated(double demPartnerSimShare) { this.demPartnerSimShare = demPartnerSimShare; } - - public double getFertilityRateSimulated() { - return demFertRateSim; - } - - public void setFertilityRateSimulated(double demFertRateSim) { - this.demFertRateSim = demFertRateSim; - } - - public double getFertilityRateTarget() { - return demFertRateTarget; - } - - public void setFertilityRateTarget(double demFertRateTarget) { - this.demFertRateTarget = demFertRateTarget; - } - - public double getShareCohabitingTarget() { - return demPartnerTargetShare; - } - - public void setShareCohabitingTarget(double demPartnerTargetShare) { - this.demPartnerTargetShare = demPartnerTargetShare; - } - - public void update(SimPathsModel model) { - - // cohabitation - double val = Parameters.getTimeSeriesValue(model.getYear()-1, TimeSeriesVariable.PartnershipAdjustment) + - model.getPartnershipAdjustment(model.getYear()-1); - setPartnershipAdjustmentFactor(val); - long numPersonsWhoCanHavePartner = model.getPersons().stream() - .filter(person -> person.getDemAge() >= Parameters.MIN_AGE_COHABITATION) - .count(); - long numPersonsPartnered = model.getPersons().stream() - .filter(person -> (person.getDcpst().equals(Dcpst.Partnered))) - .count(); - val = (numPersonsWhoCanHavePartner > 0) ? (double) numPersonsPartnered / numPersonsWhoCanHavePartner : 0.0; - setShareCohabitingSimulated(val); - setShareCohabitingTarget(Parameters.getTargetShare(model.getYear()-1, TargetShares.Partnership)); - - // fertility - val = Parameters.getTimeSeriesValue(model.getYear()-1, TimeSeriesVariable.FertilityAdjustment) + - model.getFertilityAdjustment(model.getYear()-1); - setFertilityAdjustmentFactor(val); - FertileFilter filter = new FertileFilter(); - long numFertilePersons = model.getPersons().stream() - .filter(person -> filter.evaluate(person)) - .count(); - long numBirths = model.getPersons().stream() - .filter(person -> (person.getDemAge() < 1)) - .count(); - val = (numFertilePersons > 0) ? (double) numBirths / numFertilePersons : 0.0; - setFertilityRateSimulated(val); - setFertilityRateTarget(Parameters.getFertilityRateByYear(model.getYear()-1)); - - setSocialCareAdjustmentFactor(Parameters.getTimeSeriesValue(model.getYear()-1, TimeSeriesVariable.CareProvisionAdjustment)); - setUtilityAdjustmentFactorSmales(Parameters.getTimeSeriesValue(model.getYear()-1, TimeSeriesVariable.UtilityAdjustmentSingleMales)); - setUtilityAdjustmentFactorACmales(Parameters.getTimeSeriesValue(model.getYear()-1, TimeSeriesVariable.UtilityAdjustmentACMales)); - setUtilityAdjustmentFactorSfemales(Parameters.getTimeSeriesValue(model.getYear()-1, TimeSeriesVariable.UtilityAdjustmentSingleFemales)); - setUtilityAdjustmentFactorACfemales(Parameters.getTimeSeriesValue(model.getYear()-1, TimeSeriesVariable.UtilityAdjustmentACFemales)); - setUtilityAdjustmentFactorCouples(Parameters.getTimeSeriesValue(model.getYear()-1, TimeSeriesVariable.UtilityAdjustmentCouples)); - setUtilityAdjustmentFactorSDepFemales(Parameters.getTimeSeriesValue(model.getYear()-1, TimeSeriesVariable.UtilityAdjustmentSingleDepWomen)); - setUtilityAdjustmentFactorSDepMales(Parameters.getTimeSeriesValue(model.getYear()-1, TimeSeriesVariable.UtilityAdjustmentSingleDepMen)); - } -} diff --git a/src/main/java/simpaths/experiment/SimPathsCollector.java b/src/main/java/simpaths/experiment/SimPathsCollector.java index 6b9dc53a9..349450642 100644 --- a/src/main/java/simpaths/experiment/SimPathsCollector.java +++ b/src/main/java/simpaths/experiment/SimPathsCollector.java @@ -31,9 +31,9 @@ import microsim.statistics.IDoubleSource; // import LABOURsim packages import simpaths.data.Parameters; +import simpaths.data.statistics.AlignmentAdjustmentFactors; import simpaths.data.statistics.Statistics; import simpaths.data.statistics.Statistics2; -import simpaths.data.statistics.Statistics3; import simpaths.model.Person; import simpaths.model.enums.Region; @@ -56,8 +56,8 @@ public class SimPathsCollector extends AbstractSimulationCollectorManager implem @GUIparameter(description="Calculate extended set of population characteristics (useful for validation)") private boolean persistStatistics2 = true; - @GUIparameter(description="Report alignment adjustments") - private boolean persistStatistics3 = true; + @GUIparameter(description="Report alignment adjustment factors (AlignmentAdjustmentFactors1.csv)") + private boolean persistAlignmentAdjustmentFactors = true; private boolean persistEmploymentStatistics = true; @@ -96,7 +96,7 @@ public class SimPathsCollector extends AbstractSimulationCollectorManager implem private Statistics2 stats2; - private Statistics3 stats3; + private AlignmentAdjustmentFactors alignmentAdjustmentFactors; private EmploymentStatistics statsEmployment; @@ -122,7 +122,7 @@ public class SimPathsCollector extends AbstractSimulationCollectorManager implem private DataExport exportStatistics2; - private DataExport exportStatistics3; + private DataExport exportAlignmentAdjustmentFactors; private DataExport exportStatisticsEmployment; @@ -161,7 +161,7 @@ public enum Processes { DumpHouseholds, DumpStatistics, DumpStatistics2, - DumpStatistics3, + DumpAlignmentAdjustmentFactors, DumpStatisticsEmployment, DumpHealthStatistics } @@ -222,14 +222,14 @@ public void onEvent(Enum type) { log.error(e.getMessage()); } break; - case DumpStatistics3: - stats3.update(model); - try { - exportStatistics3.export(); - } catch (Exception e) { - log.error(e.getMessage()); - } - break; + case DumpAlignmentAdjustmentFactors: + alignmentAdjustmentFactors.update(model); + try { + exportAlignmentAdjustmentFactors.export(); + } catch (Exception e) { + log.error(e.getMessage()); + } + break; case DumpStatisticsEmployment: statsEmployment.update(model); try { @@ -264,7 +264,7 @@ public void buildObjects() { stats = new Statistics(); stats2 = new Statistics2(); - stats3 = new Statistics3(); + alignmentAdjustmentFactors = new AlignmentAdjustmentFactors(); statsEmployment = new EmploymentStatistics(); statsHealth = new HealthStatistics(); @@ -279,8 +279,8 @@ public void buildObjects() { exportStatistics = new DataExport(stats, exportToDatabase, exportToCSV); if (persistStatistics2) exportStatistics2 = new DataExport(stats2, exportToDatabase, exportToCSV); - if (persistStatistics3) - exportStatistics3 = new DataExport(stats3, exportToDatabase, exportToCSV); + if (persistAlignmentAdjustmentFactors) + exportAlignmentAdjustmentFactors = new DataExport(alignmentAdjustmentFactors, exportToDatabase, exportToCSV); if (persistEmploymentStatistics) exportStatisticsEmployment = new DataExport(statsEmployment, exportToDatabase, exportToCSV); if (persistHealthStatistics) @@ -338,9 +338,9 @@ public void buildSchedule() { getEngine().getEventQueue().scheduleRepeat(new SingleTargetEvent(this, Processes.DumpStatistics2), model.getStartYear() + dataDumpStartTime, ordering, dataDumpTimePeriod); } - if (persistStatistics3) { - getEngine().getEventQueue().scheduleRepeat(new SingleTargetEvent(this, Processes.DumpStatistics3), model.getStartYear() + dataDumpStartTime, ordering, dataDumpTimePeriod); - } + if (persistAlignmentAdjustmentFactors) { + getEngine().getEventQueue().scheduleRepeat(new SingleTargetEvent(this, Processes.DumpAlignmentAdjustmentFactors), model.getStartYear() + dataDumpStartTime, ordering, dataDumpTimePeriod); + } if (persistEmploymentStatistics) { getEngine().getEventQueue().scheduleRepeat(new SingleTargetEvent(this, Processes.DumpStatisticsEmployment), model.getStartYear() + dataDumpStartTime, ordering, dataDumpTimePeriod); @@ -822,12 +822,6 @@ public void setStats(Statistics stats) { public void setStats2(Statistics2 stats2) { this.stats2 = stats2; } - public Statistics3 getStats3() { return stats3; } - - public void setStats3(Statistics3 stats3) { - this.stats3 = stats3; - } - public boolean isExportToDatabase() { return exportToDatabase; } @@ -868,14 +862,6 @@ public void setPersistStatistics2(boolean val) { persistStatistics2 = val; } - public boolean isPersistStatistics3() { - return persistStatistics3; - } - - public void setPersistStatistics3(boolean val) { - persistStatistics3 = val; - } - public void calculateAtRiskOfPoverty() { calculateEquivalisedHouseholdDisposableIncome(); } diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml index 8fb382283..0f15f9f4f 100644 --- a/src/main/resources/META-INF/persistence.xml +++ b/src/main/resources/META-INF/persistence.xml @@ -59,7 +59,7 @@ simpaths.model.BenefitUnit simpaths.data.statistics.Statistics simpaths.data.statistics.Statistics2 - simpaths.data.statistics.Statistics3 + simpaths.data.statistics.AlignmentAdjustmentFactors simpaths.data.statistics.HealthStatistics simpaths.data.statistics.EmploymentStatistics false diff --git a/src/test/java/simpaths/integrationtest/RunSimPathsIntegrationTest.java b/src/test/java/simpaths/integrationtest/RunSimPathsIntegrationTest.java index 8cb7ae4b5..313062a01 100644 --- a/src/test/java/simpaths/integrationtest/RunSimPathsIntegrationTest.java +++ b/src/test/java/simpaths/integrationtest/RunSimPathsIntegrationTest.java @@ -77,11 +77,11 @@ public void compareStatistics21() throws IOException { ); } @Test - public void compareStatistics31() throws IOException { - compareFiles( - latestOutputDir.resolve("csv/Statistics31.csv"), - Paths.get("src/test/java/simpaths/integrationtest/expected/Statistics31.csv") - ); + public void verifyAlignmentAdjustmentFactorsExported() { + assertTrue( + Files.exists(latestOutputDir.resolve("csv/AlignmentAdjustmentFactors1.csv")), + "Expected output file is missing: " + latestOutputDir.resolve("csv/AlignmentAdjustmentFactors1.csv") + ); } @Test public void compareHealthStatistics1() throws IOException {