From c4936720b0257680bafcc6820466b202d455b3dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=B6rl?= Date: Tue, 17 Mar 2026 13:15:51 +0100 Subject: [PATCH 1/4] feat: use PersonInitializedEvent in activity analysis --- .../analysis/activities/ActivityListener.java | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/org/eqasim/core/analysis/activities/ActivityListener.java b/core/src/main/java/org/eqasim/core/analysis/activities/ActivityListener.java index f09e8b42f..1c18592a9 100644 --- a/core/src/main/java/org/eqasim/core/analysis/activities/ActivityListener.java +++ b/core/src/main/java/org/eqasim/core/analysis/activities/ActivityListener.java @@ -10,14 +10,17 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.ActivityEndEvent; import org.matsim.api.core.v01.events.ActivityStartEvent; +import org.matsim.api.core.v01.events.PersonInitializedEvent; import org.matsim.api.core.v01.events.handler.ActivityEndEventHandler; import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler; +import org.matsim.api.core.v01.events.handler.PersonInitializedEventHandler; import org.matsim.api.core.v01.population.Person; import org.matsim.core.router.TripStructureUtils; import com.google.common.base.Verify; -public class ActivityListener implements ActivityStartEventHandler, ActivityEndEventHandler { +public class ActivityListener + implements PersonInitializedEventHandler, ActivityStartEventHandler, ActivityEndEventHandler { final private Collection activities = new LinkedList<>(); final private Map, ActivityItem> ongoing = new HashMap<>(); final private Map, Integer> activityIndex = new HashMap<>(); @@ -39,20 +42,32 @@ public void reset(int iteration) { activityIndex.clear(); } + @Override + public void handleEvent(PersonInitializedEvent event) { + if (personFilter.analyzePerson(event.getPersonId())) { + ActivityItem activity = new ActivityItem(event.getPersonId(), 0, event.getActivityType(), + Double.NEGATIVE_INFINITY, + Double.POSITIVE_INFINITY, event.getCoord().getX(), event.getCoord().getY(), event.getFacilityId(), + event.getLinkId()); + + activities.add(activity); + ongoing.put(event.getPersonId(), activity); + } + } + @Override public void handleEvent(ActivityStartEvent event) { if (personFilter.analyzePerson(event.getPersonId())) { if (!TripStructureUtils.isStageActivityType(event.getActType())) { - int personActivityIndex = Objects.requireNonNull(activityIndex.get(event.getPersonId())) + 1; - activityIndex.put(event.getPersonId(), personActivityIndex); + int personActivityIndex = activityIndex.compute(event.getPersonId(), + (id, val) -> val == null ? 1 : val + 1); - // this is not the first one ActivityItem activity = new ActivityItem(event.getPersonId(), personActivityIndex, event.getActType(), event.getTime(), Double.POSITIVE_INFINITY, event.getCoord().getX(), event.getCoord().getY(), event.getFacilityId(), event.getLinkId()); activities.add(activity); - ongoing.put(event.getPersonId(), activity); + Verify.verify(ongoing.put(event.getPersonId(), activity) == null); } } } @@ -61,26 +76,8 @@ public void handleEvent(ActivityStartEvent event) { public void handleEvent(ActivityEndEvent event) { if (personFilter.analyzePerson(event.getPersonId())) { if (!TripStructureUtils.isStageActivityType(event.getActType())) { - ActivityItem activity = ongoing.remove(event.getPersonId()); - - if (activity == null) { - if (event.getCoord() == null) { - // can happen for BeforeVrpSchedule activities of DRT vehicles - return; - } - - // this is the first one - activity = new ActivityItem(event.getPersonId(), 0, event.getActType(), Double.NEGATIVE_INFINITY, - event.getTime(), event.getCoord().getX(), event.getCoord().getY(), event.getFacilityId(), - event.getLinkId()); - - Verify.verify(activityIndex.put(event.getPersonId(), 0) == null); - - activities.add(activity); - ongoing.put(event.getPersonId(), activity); - } else { - activity.endTime = event.getTime(); - } + ActivityItem activity = Objects.requireNonNull(ongoing.remove(event.getPersonId())); + activity.endTime = event.getTime(); } } } From e2617d779d388bc499fe0a2769553c8aff238ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=B6rl?= Date: Tue, 24 Mar 2026 12:49:28 +0100 Subject: [PATCH 2/4] update config --- .../eqasim/core/analysis/activities/ActivityListener.java | 3 ++- .../org/eqasim/core/scenario/config/GenerateConfig.java | 2 ++ .../core/simulation/analysis/EqasimAnalysisModule.java | 8 +++++++- core/src/main/resources/melun/config.xml | 1 + 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/eqasim/core/analysis/activities/ActivityListener.java b/core/src/main/java/org/eqasim/core/analysis/activities/ActivityListener.java index 1c18592a9..1064e1b2c 100644 --- a/core/src/main/java/org/eqasim/core/analysis/activities/ActivityListener.java +++ b/core/src/main/java/org/eqasim/core/analysis/activities/ActivityListener.java @@ -76,7 +76,8 @@ public void handleEvent(ActivityStartEvent event) { public void handleEvent(ActivityEndEvent event) { if (personFilter.analyzePerson(event.getPersonId())) { if (!TripStructureUtils.isStageActivityType(event.getActType())) { - ActivityItem activity = Objects.requireNonNull(ongoing.remove(event.getPersonId())); + ActivityItem activity = Objects.requireNonNull(ongoing.remove(event.getPersonId()), + "Are you running activity analysis on an instance without qsim.personInitializedEvents = 'all'?"); activity.endTime = event.getTime(); } } diff --git a/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java b/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java index 043acb645..d87e93468 100644 --- a/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java +++ b/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java @@ -16,6 +16,7 @@ import org.matsim.core.config.CommandLine.ConfigurationException; import org.matsim.core.config.Config; import org.matsim.core.config.groups.ControllerConfigGroup.RoutingAlgorithmType; +import org.matsim.core.config.groups.QSimConfigGroup.PersonInitializedEventsSetting; import org.matsim.core.config.groups.PlansConfigGroup; import org.matsim.core.config.groups.RoutingConfigGroup; import org.matsim.core.config.groups.RoutingConfigGroup.AccessEgressType; @@ -77,6 +78,7 @@ protected void adaptConfiguration(Config config) { config.qsim().setNumberOfThreads(Math.min(12, threads)); config.qsim().setFlowCapFactor(sampleSize); config.qsim().setStorageCapFactor(sampleSize); + config.qsim().setPersonInitializedEventsSetting(PersonInitializedEventsSetting.all); // Eqasim settings EqasimConfigGroup eqasimConfig = EqasimConfigGroup.get(config); diff --git a/core/src/main/java/org/eqasim/core/simulation/analysis/EqasimAnalysisModule.java b/core/src/main/java/org/eqasim/core/simulation/analysis/EqasimAnalysisModule.java index 0450fad7f..fcb34b2cf 100644 --- a/core/src/main/java/org/eqasim/core/simulation/analysis/EqasimAnalysisModule.java +++ b/core/src/main/java/org/eqasim/core/simulation/analysis/EqasimAnalysisModule.java @@ -20,6 +20,8 @@ import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.config.Config; import org.matsim.core.config.groups.ControllerConfigGroup; +import org.matsim.core.config.groups.QSimConfigGroup; +import org.matsim.core.config.groups.QSimConfigGroup.PersonInitializedEventsSetting; import org.matsim.core.config.groups.RoutingConfigGroup; import org.matsim.core.controler.AbstractModule; import org.matsim.core.controler.OutputDirectoryHierarchy; @@ -28,6 +30,7 @@ import org.matsim.core.utils.timing.TimeInterpretation; import org.matsim.pt.transitSchedule.api.TransitSchedule; +import com.google.common.base.Preconditions; import com.google.inject.Provides; import com.google.inject.Singleton; @@ -73,7 +76,10 @@ public PublicTransportLegListener providePublicTransportListener(Network network @Provides @Singleton - public ActivityListener provideActivityListener(PersonAnalysisFilter personFilter) { + public ActivityListener provideActivityListener(PersonAnalysisFilter personFilter, QSimConfigGroup qsimConfig) { + Preconditions.checkState( + qsimConfig.getPersonInitializedEventsSetting().equals(PersonInitializedEventsSetting.all), + "We now require to have qsim.personInitializedEvents to be set to 'all'."); return new ActivityListener(personFilter); } diff --git a/core/src/main/resources/melun/config.xml b/core/src/main/resources/melun/config.xml index 57ba7d3aa..7c2995003 100644 --- a/core/src/main/resources/melun/config.xml +++ b/core/src/main/resources/melun/config.xml @@ -390,6 +390,7 @@ + From f8a0ecfb3ed03106a6146b3dbecbe52bf39b1e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=B6rl?= Date: Tue, 24 Mar 2026 15:19:26 +0100 Subject: [PATCH 3/4] fixes --- .../eqasim/core/analysis/activities/ActivityListener.java | 7 +++++++ core/src/test/java/org/eqasim/TestEmissions.java | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/eqasim/core/analysis/activities/ActivityListener.java b/core/src/main/java/org/eqasim/core/analysis/activities/ActivityListener.java index 1064e1b2c..f514fb121 100644 --- a/core/src/main/java/org/eqasim/core/analysis/activities/ActivityListener.java +++ b/core/src/main/java/org/eqasim/core/analysis/activities/ActivityListener.java @@ -15,6 +15,7 @@ import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler; import org.matsim.api.core.v01.events.handler.PersonInitializedEventHandler; import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic; import org.matsim.core.router.TripStructureUtils; import com.google.common.base.Verify; @@ -74,6 +75,12 @@ public void handleEvent(ActivityStartEvent event) { @Override public void handleEvent(ActivityEndEvent event) { + if (event.getActType().startsWith(VrpAgentLogic.BEFORE_SCHEDULE_ACTIVITY_TYPE)) { + // special case that can not be covered by the person filter (event comes too + // early) + return; + } + if (personFilter.analyzePerson(event.getPersonId())) { if (!TripStructureUtils.isStageActivityType(event.getActType())) { ActivityItem activity = Objects.requireNonNull(ongoing.remove(event.getPersonId()), diff --git a/core/src/test/java/org/eqasim/TestEmissions.java b/core/src/test/java/org/eqasim/TestEmissions.java index f5481eb9f..79218af6f 100644 --- a/core/src/test/java/org/eqasim/TestEmissions.java +++ b/core/src/test/java/org/eqasim/TestEmissions.java @@ -165,7 +165,7 @@ private void runMelunEmissions() throws CommandLine.ConfigurationException, IOEx "sample_41_EFA_HOT_SubSegm_2020detailed.csv", "--eqasim-configurator", TestConfigurator.class.getName() }); - assertEquals(634526, countLines(new File("melun_test/output/output_emissions_events.xml.gz"))); + assertEquals(640230, countLines(new File("melun_test/output/output_emissions_events.xml.gz"))); RunExportEmissionsNetwork.main(new String[] { "--config-path", "melun_test/input/config.xml", "--pollutants", "PM,CO,NOx,Unknown", "--time-bin-size", "3600", From 3bcda43b464bf5212e768d9fe50a484261f80f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=B6rl?= Date: Thu, 26 Mar 2026 15:21:35 +0100 Subject: [PATCH 4/4] fix corsica --- ile_de_france/src/main/resources/corsica/corsica_config.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/ile_de_france/src/main/resources/corsica/corsica_config.xml b/ile_de_france/src/main/resources/corsica/corsica_config.xml index 435c2e328..49583dc1f 100644 --- a/ile_de_france/src/main/resources/corsica/corsica_config.xml +++ b/ile_de_france/src/main/resources/corsica/corsica_config.xml @@ -706,6 +706,7 @@ +