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..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,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.sharingmetrics.SharingMetricsModule; 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 SharingMetricsModule(drtCfg)); } } 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 new file mode 100644 index 00000000000..9884fce28cc --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsControlerListener.java @@ -0,0 +1,145 @@ +package org.matsim.contrib.drt.sharingmetrics; + +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 SharingMetricsControlerListener implements IterationEndsListener { + private final MatsimServices matsimServices; + + private final DrtConfigGroup drtConfigGroup; + private final SharingMetricsTracker sharingFactorTracker; + + private boolean headerWritten = false; + + private final String runId; + + private final String delimiter; + + private static final String notAvailableString = "NA"; + + + @Inject + public SharingMetricsControlerListener(Config config, + DrtConfigGroup drtConfigGroup, + SharingMetricsTracker 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) { + 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(); + + writeAndPlotSharingMetrics( + sharingFactors, + poolingRates, + filename(event, "sharingFactors", ".png"), + filename(event, "poolingRates", ".png"), + filename(event, "sharingMetrics", ".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 writeAndPlotSharingMetrics(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_sharing_metrics", ".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/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/sharingmetrics/SharingMetricsTracker.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsTracker.java new file mode 100644 index 00000000000..f5393b29144 --- /dev/null +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsTracker.java @@ -0,0 +1,110 @@ +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.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 SharingMetricsTracker 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 { + boolean isGroupRepresentative(Id personId); + } + + public SharingMetricsTracker(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/sharingmetrics/SharingFactorTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java new file mode 100644 index 00000000000..e98a066d0ce --- /dev/null +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java @@ -0,0 +1,205 @@ +package org.matsim.contrib.drt.sharingmetrics; + +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 SharingMetricsTracker}. + */ + @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); + SharingMetricsTracker sharingFactorTracker = new SharingMetricsTracker(new SharingMetricsTracker.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)); + } + } +}