From c092055fe2a8e760163a2822ff72494ad0ae742d Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Wed, 6 Dec 2023 10:43:15 +0100 Subject: [PATCH 1/4] sharing factor and pooling rate for DRT --- .../drt/analysis/DrtModeAnalysisModule.java | 3 + .../SharingFactorControlerListener.java | 144 ++++++++++++ .../sharingfactor/SharingFactorModule.java | 38 ++++ .../sharingfactor/SharingFactorTracker.java | 112 ++++++++++ .../drt/sharingfactor/SharingFactorTest.java | 205 ++++++++++++++++++ 5 files changed, 502 insertions(+) create mode 100644 contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorControlerListener.java create mode 100644 contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java create mode 100644 contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTracker.java create mode 100644 contribs/drt/src/test/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTest.java diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java index e362e4d4935..e5cd54688c2 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java @@ -34,6 +34,7 @@ import org.matsim.contrib.drt.schedule.DrtDriveTask; import org.matsim.contrib.drt.schedule.DrtStayTask; import org.matsim.contrib.drt.scheduler.EmptyVehicleRelocator; +import org.matsim.contrib.drt.sharingfactor.SharingFactorModule; import org.matsim.contrib.dvrp.analysis.ExecutedScheduleCollector; import org.matsim.contrib.dvrp.fleet.FleetSpecification; import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; @@ -136,5 +137,7 @@ public void install() { getter.getModal(DrtVehicleDistanceStats.class), getter.get(MatsimServices.class), getter.get(Network.class), getter.getModal(DrtEventSequenceCollector.class), getter.getModal(VehicleOccupancyProfileCalculator.class)))) .asEagerSingleton(); + + install(new SharingFactorModule(drtCfg)); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorControlerListener.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorControlerListener.java new file mode 100644 index 00000000000..2173b1c6840 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorControlerListener.java @@ -0,0 +1,144 @@ +package org.matsim.contrib.drt.sharingfactor; + +import com.google.inject.Inject; +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartUtils; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.renderer.category.BoxAndWhiskerRenderer; +import org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset; +import org.matsim.api.core.v01.Id; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.core.config.Config; +import org.matsim.core.controler.MatsimServices; +import org.matsim.core.controler.events.IterationEndsEvent; +import org.matsim.core.controler.listener.IterationEndsListener; +import org.matsim.core.utils.io.IOUtils; + +import java.io.BufferedWriter; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author nkuehnel / MOIA + */ +public class SharingFactorControlerListener implements IterationEndsListener { + private final MatsimServices matsimServices; + + private final DrtConfigGroup drtConfigGroup; + private final SharingFactorTracker sharingFactorTracker; + + private boolean headerWritten = false; + + private final String runId; + + private final String delimiter; + + private static final String notAvailableString = "NA"; + + + @Inject + public SharingFactorControlerListener(Config config, + DrtConfigGroup drtConfigGroup, + SharingFactorTracker sharingFactorTracker, + MatsimServices matsimServices) { + this.drtConfigGroup = drtConfigGroup; + this.sharingFactorTracker = sharingFactorTracker; + this.matsimServices = matsimServices; + runId = Optional.ofNullable(config.controller().getRunId()).orElse(notAvailableString); + this.delimiter = config.global().getDefaultDelimiter(); + + } + + @Override + public void notifyIterationEnds(IterationEndsEvent event) { + boolean createGraphs = event.getServices().getConfig().controller().isCreateGraphs(); + + Map, Double> sharingFactors = sharingFactorTracker.getSharingFactors(); + Map, Boolean> poolingRates = sharingFactorTracker.getPoolingRates(); + + writeAndPlotShiftEfficiency( + sharingFactors, + poolingRates, + filename(event, "sharingFactors", ".png"), + filename(event, "poolingRates", ".png"), + filename(event, "poolingMetrics", ".csv"), + createGraphs); + + double nPooled = poolingRates.values().stream().filter(b -> b).count(); + double nTotal = poolingRates.values().size(); + double meanPoolingRate = nPooled / nTotal; + double meanSharingFactor = sharingFactors.values().stream().mapToDouble(d -> d).average().orElse(Double.NaN); + + writeIterationPoolingStats(meanPoolingRate + delimiter + meanSharingFactor + delimiter + nPooled +delimiter + nTotal, event.getIteration()); + } + + private void writeAndPlotShiftEfficiency(Map, Double> sharingFactorByRequest, + Map, Boolean> rates, + String sharingFactors, + String poolingRates, + String csvFile, + boolean createGraphs) { + try (var bw = IOUtils.getBufferedWriter(csvFile)) { + bw.append(line("Request", "SharingFactor", "Pooled")); + + for (Map.Entry, Double> sharingFactorEntry : sharingFactorByRequest.entrySet()) { + bw.append(line(sharingFactorEntry.getKey(), sharingFactorEntry.getValue(),rates.get(sharingFactorEntry.getKey()))); + } + bw.flush(); + + if (createGraphs) { + final DefaultBoxAndWhiskerCategoryDataset sharingFactorDataset + = new DefaultBoxAndWhiskerCategoryDataset(); + + final DefaultBoxAndWhiskerCategoryDataset poolingRateDataset + = new DefaultBoxAndWhiskerCategoryDataset(); + + sharingFactorDataset.add(sharingFactorByRequest.values().stream().toList(), "", ""); + poolingRateDataset.add(rates.values().stream().toList(), "", ""); + + JFreeChart chartRides = ChartFactory.createBoxAndWhiskerChart("Sharing factor", "", "Factor", sharingFactorDataset, false); + JFreeChart chartPooling = ChartFactory.createBoxAndWhiskerChart("Pooling rate", "", "Rate", poolingRateDataset, false); + + ((BoxAndWhiskerRenderer) chartRides.getCategoryPlot().getRenderer()).setMeanVisible(true); + ((BoxAndWhiskerRenderer) chartPooling.getCategoryPlot().getRenderer()).setMeanVisible(true); + ChartUtils.writeChartAsPNG(new FileOutputStream(sharingFactors), chartRides, 1500, 1500); + ChartUtils.writeChartAsPNG(new FileOutputStream(poolingRates), chartPooling, 1500, 1500); + } + } catch ( + IOException e) { + throw new RuntimeException(e); + } + } + + private String filename(IterationEndsEvent event, String prefix, String extension) { + return matsimServices.getControlerIO() + .getIterationFilename(event.getIteration(), prefix + "_" + drtConfigGroup.getMode() + extension); + } + + private String line(Object... cells) { + return Arrays.stream(cells).map(Object::toString).collect(Collectors.joining(delimiter, "", "\n")); + } + + + private void writeIterationPoolingStats(String summarizePooling, int it) { + try (var bw = getAppendingBufferedWriter("drt_pooling_stats", ".csv")) { + if (!headerWritten) { + headerWritten = true; + bw.write(line("runId", "iteration", "poolingRate", "sharingFactor", "nPooled", "nTotal")); + } + bw.write(runId + delimiter + it + delimiter + summarizePooling); + bw.newLine(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private BufferedWriter getAppendingBufferedWriter(String prefix, String extension) { + return IOUtils.getAppendingBufferedWriter(matsimServices.getControlerIO().getOutputFilename(prefix + "_" + drtConfigGroup.getMode() + extension)); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java new file mode 100644 index 00000000000..3ce63eddc2e --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java @@ -0,0 +1,38 @@ +package org.matsim.contrib.drt.sharingfactor; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; +import org.matsim.core.controler.MatsimServices; + +/** + * @author nkuehnel / MOIA + */ +public class SharingFactorModule extends AbstractDvrpModeModule { + + private final DrtConfigGroup drtConfigGroup; + + public SharingFactorModule(DrtConfigGroup drtConfigGroup) { + super(drtConfigGroup.getMode()); + this.drtConfigGroup = drtConfigGroup; + } + + @Override + public void install() { + bindModal(SharingFactorTracker.class).toProvider(modalProvider(getter -> + new SharingFactorTracker(new SharingFactorTracker.GroupPredicate() { + @Override + public boolean isGroupRepresentative(Id personId) { + return SharingFactorTracker.GroupPredicate.super.isGroupRepresentative(personId); + } + }))).asEagerSingleton(); + addEventHandlerBinding().to(modalKey(SharingFactorTracker.class)); + bindModal(SharingFactorControlerListener.class).toProvider(modalProvider(getter -> + new SharingFactorControlerListener(getConfig(), drtConfigGroup, + getter.getModal(SharingFactorTracker.class), + getter.get(MatsimServices.class)) + )); + addControlerListenerBinding().to(modalKey(SharingFactorControlerListener.class)); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTracker.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTracker.java new file mode 100644 index 00000000000..cc28f6131a9 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTracker.java @@ -0,0 +1,112 @@ +package org.matsim.contrib.drt.sharingfactor; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEvent; +import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEventHandler; +import org.matsim.contrib.dvrp.passenger.PassengerPickedUpEvent; +import org.matsim.contrib.dvrp.passenger.PassengerPickedUpEventHandler; +import org.matsim.core.mobsim.framework.events.MobsimBeforeCleanupEvent; +import org.matsim.core.mobsim.framework.listeners.MobsimBeforeCleanupListener; + +import java.util.*; + +/** + * @author nkuehnel / MOIA + */ +public class SharingFactorTracker implements PassengerPickedUpEventHandler, PassengerDroppedOffEventHandler, MobsimBeforeCleanupListener { + + private final GroupPredicate groupPredicate; + + record Segment(double start, int occupancy) { + } + + private final Map, List>> map = new HashMap<>(); + + private final Map, List> segments = new HashMap<>(); + + private final Map, Double> sharingFactors = new HashMap<>(); + private final Map, Boolean> poolingRate = new HashMap<>(); + + public interface GroupPredicate { + default boolean isGroupRepresentative(Id personId) { + return true; + } + } + + public SharingFactorTracker(GroupPredicate groupPredicate) { + this.groupPredicate = groupPredicate; + } + + + @Override + public void handleEvent(PassengerDroppedOffEvent event) { + if (groupPredicate.isGroupRepresentative(event.getPersonId())) { + + List> occupancy = map.get(event.getVehicleId()); + occupancy.remove(event.getRequestId()); + occupancy.forEach(p -> segments.get(p).add(new Segment(event.getTime(), occupancy.size()))); + + + List finishedSegments = segments.remove(event.getRequestId()); + + double total = 0; + double portion = 0; + + boolean pooled = false; + + Segment last = finishedSegments.get(0); + if (last.occupancy > 1) { + pooled = true; + } + for (int i = 1; i < finishedSegments.size(); i++) { + Segment next = finishedSegments.get(i); + double duration = next.start - last.start; + total += duration; + portion += duration / last.occupancy; + last = next; + if (last.occupancy > 1) { + pooled = true; + } + } + + double duration = event.getTime() - last.start; + total += duration; + portion += duration / last.occupancy; + + double sharingFactor = total / portion; + sharingFactors.put(event.getRequestId(), sharingFactor); + poolingRate.put(event.getRequestId(), pooled); + } + } + + @Override + public void handleEvent(PassengerPickedUpEvent event) { + if (groupPredicate.isGroupRepresentative(event.getPersonId())) { + map.computeIfAbsent(event.getVehicleId(), vehicleId -> new ArrayList<>()); + List> occupancy = map.get(event.getVehicleId()); + occupancy.add(event.getRequestId()); + occupancy.forEach( + p -> segments.computeIfAbsent(p, requestId -> new ArrayList<>()).add(new Segment(event.getTime(), occupancy.size())) + ); + } + } + + @Override + public void notifyMobsimBeforeCleanup(MobsimBeforeCleanupEvent e) { + map.clear(); + segments.clear(); + poolingRate.clear(); + sharingFactors.clear(); + } + + public Map, Double> getSharingFactors() { + return Collections.unmodifiableMap(sharingFactors); + } + + public Map, Boolean> getPoolingRates() { + return Collections.unmodifiableMap(poolingRate); + } +} diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTest.java new file mode 100644 index 00000000000..fc2c8b96add --- /dev/null +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTest.java @@ -0,0 +1,205 @@ +package org.matsim.contrib.drt.sharingfactor; + +import org.junit.Assert; +import org.junit.Test; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEvent; +import org.matsim.contrib.dvrp.passenger.PassengerPickedUpEvent; +import org.matsim.core.events.ParallelEventsManager; +import org.matsim.testcases.MatsimTestUtils; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author nkuehnel / MOIA + */ +public class SharingFactorTest { + + /** + * Test method for {@link SharingFactorTracker}. + */ + @Test + public void testDrtSharingFactorHandler() { + String mode = "mode_0"; + + var vehicleId = Id.create("v1", DvrpVehicle.class); + var personId1 = Id.createPersonId("p1"); + var personId2 = Id.createPersonId("p2"); + + + Set> groupRepresentatives = new HashSet<>(); + + // two separate bookings + groupRepresentatives.add(personId1); + groupRepresentatives.add(personId2); + + + ParallelEventsManager events = new ParallelEventsManager(false); + SharingFactorTracker sharingFactorTracker = new SharingFactorTracker(new SharingFactorTracker.GroupPredicate() { + @Override + public boolean isGroupRepresentative(Id personId) { + return groupRepresentatives.contains(personId); + } + }); + events.addHandler(sharingFactorTracker); + + events.initProcessing(); + + + { + //single trip, no pooling + var requestId = Id.create(0, Request.class); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId)); + + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId, personId1, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId, personId1, vehicleId)); + events.flush(); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId)); + Assert.assertFalse(sharingFactorTracker.getPoolingRates().get(requestId)); + Assert.assertEquals(1., sharingFactorTracker.getSharingFactors().get(requestId), MatsimTestUtils.EPSILON); + } + + //clean up + sharingFactorTracker.notifyMobsimBeforeCleanup(null); + + { + //two trips exactly after each other, no pooling + var requestId1 = Id.create(0, Request.class); + var requestId2 = Id.create(1, Request.class); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerPickedUpEvent(300.0, mode, requestId2, personId2, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(500.0, mode, requestId2, personId2, vehicleId)); + events.flush(); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId1)); + Assert.assertFalse(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertEquals(1., sharingFactorTracker.getSharingFactors().get(requestId1), MatsimTestUtils.EPSILON); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId2)); + Assert.assertFalse(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertEquals(1., sharingFactorTracker.getSharingFactors().get(requestId2), MatsimTestUtils.EPSILON); + } + + //clean up + sharingFactorTracker.notifyMobsimBeforeCleanup(null); + + { + //two trips overlap half of the time + var requestId1 = Id.create(0, Request.class); + var requestId2 = Id.create(1, Request.class); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerPickedUpEvent(200.0, mode, requestId2, personId2, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(400.0, mode, requestId2, personId2, vehicleId)); + events.flush(); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId1)); + Assert.assertTrue(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertEquals((100. + 100.) / (100 + 50), sharingFactorTracker.getSharingFactors().get(requestId1), MatsimTestUtils.EPSILON); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId2)); + Assert.assertTrue(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertEquals((100. + 100.) / (50 + 100), sharingFactorTracker.getSharingFactors().get(requestId2), MatsimTestUtils.EPSILON); + } + + + //clean up + sharingFactorTracker.notifyMobsimBeforeCleanup(null); + + { + // second trip (sharing factor = 2) happens completely within first trip (sharing factor = 1.2) + var requestId1 = Id.create(0, Request.class); + var requestId2 = Id.create(1, Request.class); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerPickedUpEvent(200.0, mode, requestId2, personId2, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId2, personId2, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(400.0, mode, requestId1, personId1, vehicleId)); + events.flush(); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId1)); + Assert.assertTrue(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertEquals((100. + 100. + 100.) / (100 + 50 + 100), sharingFactorTracker.getSharingFactors().get(requestId1), MatsimTestUtils.EPSILON); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId2)); + Assert.assertTrue(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertEquals((100. ) / (50.), sharingFactorTracker.getSharingFactors().get(requestId2), MatsimTestUtils.EPSILON); + } + + //clean up + sharingFactorTracker.notifyMobsimBeforeCleanup(null); + + { + // two persons share exact same trip but not part of a group + + var requestId1 = Id.create(0, Request.class); + var requestId2 = Id.create(1, Request.class); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId2, personId2, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(200.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(200.0, mode, requestId2, personId2, vehicleId)); + events.flush(); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId1)); + Assert.assertTrue(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertEquals(2., sharingFactorTracker.getSharingFactors().get(requestId1), MatsimTestUtils.EPSILON); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId2)); + Assert.assertTrue(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertEquals(2., sharingFactorTracker.getSharingFactors().get(requestId2), MatsimTestUtils.EPSILON); + } + + + //clean up + sharingFactorTracker.notifyMobsimBeforeCleanup(null); + + { + // two persons part of a group, only person 1 is representative -> not pooled + groupRepresentatives.remove(personId2); + + var requestId1 = Id.create(0, Request.class); + var requestId2 = Id.create(1, Request.class); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId2, personId2, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(200.0, mode, requestId1, personId1, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(200.0, mode, requestId2, personId2, vehicleId)); + events.flush(); + + Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId1)); + Assert.assertFalse(sharingFactorTracker.getPoolingRates().get(requestId1)); + Assert.assertEquals(1., sharingFactorTracker.getSharingFactors().get(requestId1), MatsimTestUtils.EPSILON); + + Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + Assert.assertNull(sharingFactorTracker.getSharingFactors().get(requestId2)); + } + } +} From 4f97de8ae148b172f9a441d866a4dd50b6dbf49b Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Thu, 7 Dec 2023 13:24:43 +0100 Subject: [PATCH 2/4] update sharing metrics code --- .../drt/analysis/DrtModeAnalysisModule.java | 4 +- .../sharingfactor/SharingFactorModule.java | 38 ------------------- .../SharingMetricsControlerListener.java} | 18 ++++----- .../sharingmetrics/SharingMetricsModule.java | 33 ++++++++++++++++ .../SharingMetricsTracker.java} | 10 ++--- .../SharingFactorTest.java | 6 +-- 6 files changed, 51 insertions(+), 58 deletions(-) delete mode 100644 contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java rename contribs/drt/src/main/java/org/matsim/contrib/drt/{sharingfactor/SharingFactorControlerListener.java => sharingmetrics/SharingMetricsControlerListener.java} (90%) create mode 100644 contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsModule.java rename contribs/drt/src/main/java/org/matsim/contrib/drt/{sharingfactor/SharingFactorTracker.java => sharingmetrics/SharingMetricsTracker.java} (90%) rename contribs/drt/src/test/java/org/matsim/contrib/drt/{sharingfactor => sharingmetrics}/SharingFactorTest.java (97%) diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java index e5cd54688c2..67d38dad708 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java @@ -34,7 +34,7 @@ import org.matsim.contrib.drt.schedule.DrtDriveTask; import org.matsim.contrib.drt.schedule.DrtStayTask; import org.matsim.contrib.drt.scheduler.EmptyVehicleRelocator; -import org.matsim.contrib.drt.sharingfactor.SharingFactorModule; +import org.matsim.contrib.drt.sharingmetrics.SharingMetricsModule; import org.matsim.contrib.dvrp.analysis.ExecutedScheduleCollector; import org.matsim.contrib.dvrp.fleet.FleetSpecification; import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; @@ -138,6 +138,6 @@ public void install() { getter.getModal(DrtEventSequenceCollector.class), getter.getModal(VehicleOccupancyProfileCalculator.class)))) .asEagerSingleton(); - install(new SharingFactorModule(drtCfg)); + install(new SharingMetricsModule(drtCfg)); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java deleted file mode 100644 index 3ce63eddc2e..00000000000 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorModule.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.matsim.contrib.drt.sharingfactor; - -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.population.Person; -import org.matsim.contrib.drt.run.DrtConfigGroup; -import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; -import org.matsim.core.controler.MatsimServices; - -/** - * @author nkuehnel / MOIA - */ -public class SharingFactorModule extends AbstractDvrpModeModule { - - private final DrtConfigGroup drtConfigGroup; - - public SharingFactorModule(DrtConfigGroup drtConfigGroup) { - super(drtConfigGroup.getMode()); - this.drtConfigGroup = drtConfigGroup; - } - - @Override - public void install() { - bindModal(SharingFactorTracker.class).toProvider(modalProvider(getter -> - new SharingFactorTracker(new SharingFactorTracker.GroupPredicate() { - @Override - public boolean isGroupRepresentative(Id personId) { - return SharingFactorTracker.GroupPredicate.super.isGroupRepresentative(personId); - } - }))).asEagerSingleton(); - addEventHandlerBinding().to(modalKey(SharingFactorTracker.class)); - bindModal(SharingFactorControlerListener.class).toProvider(modalProvider(getter -> - new SharingFactorControlerListener(getConfig(), drtConfigGroup, - getter.getModal(SharingFactorTracker.class), - getter.get(MatsimServices.class)) - )); - addControlerListenerBinding().to(modalKey(SharingFactorControlerListener.class)); - } -} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorControlerListener.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java similarity index 90% rename from contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorControlerListener.java rename to contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java index 2173b1c6840..521be363f49 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorControlerListener.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java @@ -1,4 +1,4 @@ -package org.matsim.contrib.drt.sharingfactor; +package org.matsim.contrib.drt.sharingmetrics; import com.google.inject.Inject; import org.jfree.chart.ChartFactory; @@ -26,11 +26,11 @@ /** * @author nkuehnel / MOIA */ -public class SharingFactorControlerListener implements IterationEndsListener { +public class SharingMetricsControlerListener implements IterationEndsListener { private final MatsimServices matsimServices; private final DrtConfigGroup drtConfigGroup; - private final SharingFactorTracker sharingFactorTracker; + private final SharingMetricsTracker sharingFactorTracker; private boolean headerWritten = false; @@ -42,10 +42,10 @@ public class SharingFactorControlerListener implements IterationEndsListener { @Inject - public SharingFactorControlerListener(Config config, - DrtConfigGroup drtConfigGroup, - SharingFactorTracker sharingFactorTracker, - MatsimServices matsimServices) { + public SharingMetricsControlerListener(Config config, + DrtConfigGroup drtConfigGroup, + SharingMetricsTracker sharingFactorTracker, + MatsimServices matsimServices) { this.drtConfigGroup = drtConfigGroup; this.sharingFactorTracker = sharingFactorTracker; this.matsimServices = matsimServices; @@ -61,7 +61,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { Map, Double> sharingFactors = sharingFactorTracker.getSharingFactors(); Map, Boolean> poolingRates = sharingFactorTracker.getPoolingRates(); - writeAndPlotShiftEfficiency( + writeAndPlotSharingMetrics( sharingFactors, poolingRates, filename(event, "sharingFactors", ".png"), @@ -77,7 +77,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { writeIterationPoolingStats(meanPoolingRate + delimiter + meanSharingFactor + delimiter + nPooled +delimiter + nTotal, event.getIteration()); } - private void writeAndPlotShiftEfficiency(Map, Double> sharingFactorByRequest, + private void writeAndPlotSharingMetrics(Map, Double> sharingFactorByRequest, Map, Boolean> rates, String sharingFactors, String poolingRates, diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsModule.java new file mode 100644 index 00000000000..2ebe3777ac1 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsModule.java @@ -0,0 +1,33 @@ +package org.matsim.contrib.drt.sharingmetrics; + +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; +import org.matsim.core.controler.MatsimServices; + +/** + * @author nkuehnel / MOIA + */ +public class SharingMetricsModule extends AbstractDvrpModeModule { + + private final DrtConfigGroup drtConfigGroup; + + public SharingMetricsModule(DrtConfigGroup drtConfigGroup) { + super(drtConfigGroup.getMode()); + this.drtConfigGroup = drtConfigGroup; + } + + @Override + public void install() { + bindModal(SharingMetricsTracker.class).toProvider(modalProvider(getter -> + new SharingMetricsTracker(personId -> true))).asEagerSingleton(); + addEventHandlerBinding().to(modalKey(SharingMetricsTracker.class)); + bindModal(SharingMetricsControlerListener.class).toProvider(modalProvider(getter -> + new SharingMetricsControlerListener(getConfig(), drtConfigGroup, + getter.getModal(SharingMetricsTracker.class), + getter.get(MatsimServices.class)) + )); + addControlerListenerBinding().to(modalKey(SharingMetricsControlerListener.class)); + } +} diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTracker.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsTracker.java similarity index 90% rename from contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTracker.java rename to contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsTracker.java index cc28f6131a9..f5393b29144 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTracker.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsTracker.java @@ -1,4 +1,4 @@ -package org.matsim.contrib.drt.sharingfactor; +package org.matsim.contrib.drt.sharingmetrics; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.population.Person; @@ -16,7 +16,7 @@ /** * @author nkuehnel / MOIA */ -public class SharingFactorTracker implements PassengerPickedUpEventHandler, PassengerDroppedOffEventHandler, MobsimBeforeCleanupListener { +public class SharingMetricsTracker implements PassengerPickedUpEventHandler, PassengerDroppedOffEventHandler, MobsimBeforeCleanupListener { private final GroupPredicate groupPredicate; @@ -31,12 +31,10 @@ record Segment(double start, int occupancy) { private final Map, Boolean> poolingRate = new HashMap<>(); public interface GroupPredicate { - default boolean isGroupRepresentative(Id personId) { - return true; - } + boolean isGroupRepresentative(Id personId); } - public SharingFactorTracker(GroupPredicate groupPredicate) { + public SharingMetricsTracker(GroupPredicate groupPredicate) { this.groupPredicate = groupPredicate; } diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java similarity index 97% rename from contribs/drt/src/test/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTest.java rename to contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java index fc2c8b96add..e98a066d0ce 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingfactor/SharingFactorTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java @@ -1,4 +1,4 @@ -package org.matsim.contrib.drt.sharingfactor; +package org.matsim.contrib.drt.sharingmetrics; import org.junit.Assert; import org.junit.Test; @@ -20,7 +20,7 @@ public class SharingFactorTest { /** - * Test method for {@link SharingFactorTracker}. + * Test method for {@link SharingMetricsTracker}. */ @Test public void testDrtSharingFactorHandler() { @@ -39,7 +39,7 @@ public void testDrtSharingFactorHandler() { ParallelEventsManager events = new ParallelEventsManager(false); - SharingFactorTracker sharingFactorTracker = new SharingFactorTracker(new SharingFactorTracker.GroupPredicate() { + SharingMetricsTracker sharingFactorTracker = new SharingMetricsTracker(new SharingMetricsTracker.GroupPredicate() { @Override public boolean isGroupRepresentative(Id personId) { return groupRepresentatives.contains(personId); From 3f463412766be1ceef9034fda3f045e483a11c95 Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Thu, 7 Dec 2023 13:33:54 +0100 Subject: [PATCH 3/4] update to #2955 --- .../drt/sharingmetrics/SharingMetricsControlerListener.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java index 521be363f49..9257af67aee 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java @@ -56,7 +56,8 @@ public SharingMetricsControlerListener(Config config, @Override public void notifyIterationEnds(IterationEndsEvent event) { - boolean createGraphs = event.getServices().getConfig().controller().isCreateGraphs(); + int createGraphsInterval = event.getServices().getConfig().controller().getCreateGraphsInterval(); + boolean createGraphs = createGraphsInterval >0 && event.getIteration() % createGraphsInterval == 0; Map, Double> sharingFactors = sharingFactorTracker.getSharingFactors(); Map, Boolean> poolingRates = sharingFactorTracker.getPoolingRates(); From 787e0bb82687893c1537ea393b27760c3ed998ca Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Thu, 7 Dec 2023 13:50:00 +0100 Subject: [PATCH 4/4] change file names from pooling stats to sharing metrics --- .../drt/sharingmetrics/SharingMetricsControlerListener.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java index 9257af67aee..9884fce28cc 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java @@ -67,7 +67,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { poolingRates, filename(event, "sharingFactors", ".png"), filename(event, "poolingRates", ".png"), - filename(event, "poolingMetrics", ".csv"), + filename(event, "sharingMetrics", ".csv"), createGraphs); double nPooled = poolingRates.values().stream().filter(b -> b).count(); @@ -127,7 +127,7 @@ private String line(Object... cells) { private void writeIterationPoolingStats(String summarizePooling, int it) { - try (var bw = getAppendingBufferedWriter("drt_pooling_stats", ".csv")) { + try (var bw = getAppendingBufferedWriter("drt_sharing_metrics", ".csv")) { if (!headerWritten) { headerWritten = true; bw.write(line("runId", "iteration", "poolingRate", "sharingFactor", "nPooled", "nTotal"));