Skip to content

Commit 7985778

Browse files
authored
Merge pull request matsim-org#2984 from moia-oss/drtSharingFactor
DRT: Sharing factor and pooling rate
2 parents fcd4f03 + 787e0bb commit 7985778

File tree

5 files changed

+496
-0
lines changed

5 files changed

+496
-0
lines changed

contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtModeAnalysisModule.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.matsim.contrib.drt.schedule.DrtDriveTask;
3535
import org.matsim.contrib.drt.schedule.DrtStayTask;
3636
import org.matsim.contrib.drt.scheduler.EmptyVehicleRelocator;
37+
import org.matsim.contrib.drt.sharingmetrics.SharingMetricsModule;
3738
import org.matsim.contrib.dvrp.analysis.ExecutedScheduleCollector;
3839
import org.matsim.contrib.dvrp.fleet.FleetSpecification;
3940
import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule;
@@ -136,5 +137,7 @@ public void install() {
136137
getter.getModal(DrtVehicleDistanceStats.class), getter.get(MatsimServices.class), getter.get(Network.class),
137138
getter.getModal(DrtEventSequenceCollector.class), getter.getModal(VehicleOccupancyProfileCalculator.class))))
138139
.asEagerSingleton();
140+
141+
install(new SharingMetricsModule(drtCfg));
139142
}
140143
}
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
package org.matsim.contrib.drt.sharingmetrics;
2+
3+
import com.google.inject.Inject;
4+
import org.jfree.chart.ChartFactory;
5+
import org.jfree.chart.ChartUtils;
6+
import org.jfree.chart.JFreeChart;
7+
import org.jfree.chart.renderer.category.BoxAndWhiskerRenderer;
8+
import org.jfree.data.statistics.DefaultBoxAndWhiskerCategoryDataset;
9+
import org.matsim.api.core.v01.Id;
10+
import org.matsim.contrib.drt.run.DrtConfigGroup;
11+
import org.matsim.contrib.dvrp.optimizer.Request;
12+
import org.matsim.core.config.Config;
13+
import org.matsim.core.controler.MatsimServices;
14+
import org.matsim.core.controler.events.IterationEndsEvent;
15+
import org.matsim.core.controler.listener.IterationEndsListener;
16+
import org.matsim.core.utils.io.IOUtils;
17+
18+
import java.io.BufferedWriter;
19+
import java.io.FileOutputStream;
20+
import java.io.IOException;
21+
import java.util.Arrays;
22+
import java.util.Map;
23+
import java.util.Optional;
24+
import java.util.stream.Collectors;
25+
26+
/**
27+
* @author nkuehnel / MOIA
28+
*/
29+
public class SharingMetricsControlerListener implements IterationEndsListener {
30+
private final MatsimServices matsimServices;
31+
32+
private final DrtConfigGroup drtConfigGroup;
33+
private final SharingMetricsTracker sharingFactorTracker;
34+
35+
private boolean headerWritten = false;
36+
37+
private final String runId;
38+
39+
private final String delimiter;
40+
41+
private static final String notAvailableString = "NA";
42+
43+
44+
@Inject
45+
public SharingMetricsControlerListener(Config config,
46+
DrtConfigGroup drtConfigGroup,
47+
SharingMetricsTracker sharingFactorTracker,
48+
MatsimServices matsimServices) {
49+
this.drtConfigGroup = drtConfigGroup;
50+
this.sharingFactorTracker = sharingFactorTracker;
51+
this.matsimServices = matsimServices;
52+
runId = Optional.ofNullable(config.controller().getRunId()).orElse(notAvailableString);
53+
this.delimiter = config.global().getDefaultDelimiter();
54+
55+
}
56+
57+
@Override
58+
public void notifyIterationEnds(IterationEndsEvent event) {
59+
int createGraphsInterval = event.getServices().getConfig().controller().getCreateGraphsInterval();
60+
boolean createGraphs = createGraphsInterval >0 && event.getIteration() % createGraphsInterval == 0;
61+
62+
Map<Id<Request>, Double> sharingFactors = sharingFactorTracker.getSharingFactors();
63+
Map<Id<Request>, Boolean> poolingRates = sharingFactorTracker.getPoolingRates();
64+
65+
writeAndPlotSharingMetrics(
66+
sharingFactors,
67+
poolingRates,
68+
filename(event, "sharingFactors", ".png"),
69+
filename(event, "poolingRates", ".png"),
70+
filename(event, "sharingMetrics", ".csv"),
71+
createGraphs);
72+
73+
double nPooled = poolingRates.values().stream().filter(b -> b).count();
74+
double nTotal = poolingRates.values().size();
75+
double meanPoolingRate = nPooled / nTotal;
76+
double meanSharingFactor = sharingFactors.values().stream().mapToDouble(d -> d).average().orElse(Double.NaN);
77+
78+
writeIterationPoolingStats(meanPoolingRate + delimiter + meanSharingFactor + delimiter + nPooled +delimiter + nTotal, event.getIteration());
79+
}
80+
81+
private void writeAndPlotSharingMetrics(Map<Id<Request>, Double> sharingFactorByRequest,
82+
Map<Id<Request>, Boolean> rates,
83+
String sharingFactors,
84+
String poolingRates,
85+
String csvFile,
86+
boolean createGraphs) {
87+
try (var bw = IOUtils.getBufferedWriter(csvFile)) {
88+
bw.append(line("Request", "SharingFactor", "Pooled"));
89+
90+
for (Map.Entry<Id<Request>, Double> sharingFactorEntry : sharingFactorByRequest.entrySet()) {
91+
bw.append(line(sharingFactorEntry.getKey(), sharingFactorEntry.getValue(),rates.get(sharingFactorEntry.getKey())));
92+
}
93+
bw.flush();
94+
95+
if (createGraphs) {
96+
final DefaultBoxAndWhiskerCategoryDataset sharingFactorDataset
97+
= new DefaultBoxAndWhiskerCategoryDataset();
98+
99+
final DefaultBoxAndWhiskerCategoryDataset poolingRateDataset
100+
= new DefaultBoxAndWhiskerCategoryDataset();
101+
102+
sharingFactorDataset.add(sharingFactorByRequest.values().stream().toList(), "", "");
103+
poolingRateDataset.add(rates.values().stream().toList(), "", "");
104+
105+
JFreeChart chartRides = ChartFactory.createBoxAndWhiskerChart("Sharing factor", "", "Factor", sharingFactorDataset, false);
106+
JFreeChart chartPooling = ChartFactory.createBoxAndWhiskerChart("Pooling rate", "", "Rate", poolingRateDataset, false);
107+
108+
((BoxAndWhiskerRenderer) chartRides.getCategoryPlot().getRenderer()).setMeanVisible(true);
109+
((BoxAndWhiskerRenderer) chartPooling.getCategoryPlot().getRenderer()).setMeanVisible(true);
110+
ChartUtils.writeChartAsPNG(new FileOutputStream(sharingFactors), chartRides, 1500, 1500);
111+
ChartUtils.writeChartAsPNG(new FileOutputStream(poolingRates), chartPooling, 1500, 1500);
112+
}
113+
} catch (
114+
IOException e) {
115+
throw new RuntimeException(e);
116+
}
117+
}
118+
119+
private String filename(IterationEndsEvent event, String prefix, String extension) {
120+
return matsimServices.getControlerIO()
121+
.getIterationFilename(event.getIteration(), prefix + "_" + drtConfigGroup.getMode() + extension);
122+
}
123+
124+
private String line(Object... cells) {
125+
return Arrays.stream(cells).map(Object::toString).collect(Collectors.joining(delimiter, "", "\n"));
126+
}
127+
128+
129+
private void writeIterationPoolingStats(String summarizePooling, int it) {
130+
try (var bw = getAppendingBufferedWriter("drt_sharing_metrics", ".csv")) {
131+
if (!headerWritten) {
132+
headerWritten = true;
133+
bw.write(line("runId", "iteration", "poolingRate", "sharingFactor", "nPooled", "nTotal"));
134+
}
135+
bw.write(runId + delimiter + it + delimiter + summarizePooling);
136+
bw.newLine();
137+
} catch (IOException e) {
138+
throw new RuntimeException(e);
139+
}
140+
}
141+
142+
private BufferedWriter getAppendingBufferedWriter(String prefix, String extension) {
143+
return IOUtils.getAppendingBufferedWriter(matsimServices.getControlerIO().getOutputFilename(prefix + "_" + drtConfigGroup.getMode() + extension));
144+
}
145+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package org.matsim.contrib.drt.sharingmetrics;
2+
3+
import org.matsim.api.core.v01.Id;
4+
import org.matsim.api.core.v01.population.Person;
5+
import org.matsim.contrib.drt.run.DrtConfigGroup;
6+
import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule;
7+
import org.matsim.core.controler.MatsimServices;
8+
9+
/**
10+
* @author nkuehnel / MOIA
11+
*/
12+
public class SharingMetricsModule extends AbstractDvrpModeModule {
13+
14+
private final DrtConfigGroup drtConfigGroup;
15+
16+
public SharingMetricsModule(DrtConfigGroup drtConfigGroup) {
17+
super(drtConfigGroup.getMode());
18+
this.drtConfigGroup = drtConfigGroup;
19+
}
20+
21+
@Override
22+
public void install() {
23+
bindModal(SharingMetricsTracker.class).toProvider(modalProvider(getter ->
24+
new SharingMetricsTracker(personId -> true))).asEagerSingleton();
25+
addEventHandlerBinding().to(modalKey(SharingMetricsTracker.class));
26+
bindModal(SharingMetricsControlerListener.class).toProvider(modalProvider(getter ->
27+
new SharingMetricsControlerListener(getConfig(), drtConfigGroup,
28+
getter.getModal(SharingMetricsTracker.class),
29+
getter.get(MatsimServices.class))
30+
));
31+
addControlerListenerBinding().to(modalKey(SharingMetricsControlerListener.class));
32+
}
33+
}
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
package org.matsim.contrib.drt.sharingmetrics;
2+
3+
import org.matsim.api.core.v01.Id;
4+
import org.matsim.api.core.v01.population.Person;
5+
import org.matsim.contrib.dvrp.fleet.DvrpVehicle;
6+
import org.matsim.contrib.dvrp.optimizer.Request;
7+
import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEvent;
8+
import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEventHandler;
9+
import org.matsim.contrib.dvrp.passenger.PassengerPickedUpEvent;
10+
import org.matsim.contrib.dvrp.passenger.PassengerPickedUpEventHandler;
11+
import org.matsim.core.mobsim.framework.events.MobsimBeforeCleanupEvent;
12+
import org.matsim.core.mobsim.framework.listeners.MobsimBeforeCleanupListener;
13+
14+
import java.util.*;
15+
16+
/**
17+
* @author nkuehnel / MOIA
18+
*/
19+
public class SharingMetricsTracker implements PassengerPickedUpEventHandler, PassengerDroppedOffEventHandler, MobsimBeforeCleanupListener {
20+
21+
private final GroupPredicate groupPredicate;
22+
23+
record Segment(double start, int occupancy) {
24+
}
25+
26+
private final Map<Id<DvrpVehicle>, List<Id<Request>>> map = new HashMap<>();
27+
28+
private final Map<Id<Request>, List<Segment>> segments = new HashMap<>();
29+
30+
private final Map<Id<Request>, Double> sharingFactors = new HashMap<>();
31+
private final Map<Id<Request>, Boolean> poolingRate = new HashMap<>();
32+
33+
public interface GroupPredicate {
34+
boolean isGroupRepresentative(Id<Person> personId);
35+
}
36+
37+
public SharingMetricsTracker(GroupPredicate groupPredicate) {
38+
this.groupPredicate = groupPredicate;
39+
}
40+
41+
42+
@Override
43+
public void handleEvent(PassengerDroppedOffEvent event) {
44+
if (groupPredicate.isGroupRepresentative(event.getPersonId())) {
45+
46+
List<Id<Request>> occupancy = map.get(event.getVehicleId());
47+
occupancy.remove(event.getRequestId());
48+
occupancy.forEach(p -> segments.get(p).add(new Segment(event.getTime(), occupancy.size())));
49+
50+
51+
List<Segment> finishedSegments = segments.remove(event.getRequestId());
52+
53+
double total = 0;
54+
double portion = 0;
55+
56+
boolean pooled = false;
57+
58+
Segment last = finishedSegments.get(0);
59+
if (last.occupancy > 1) {
60+
pooled = true;
61+
}
62+
for (int i = 1; i < finishedSegments.size(); i++) {
63+
Segment next = finishedSegments.get(i);
64+
double duration = next.start - last.start;
65+
total += duration;
66+
portion += duration / last.occupancy;
67+
last = next;
68+
if (last.occupancy > 1) {
69+
pooled = true;
70+
}
71+
}
72+
73+
double duration = event.getTime() - last.start;
74+
total += duration;
75+
portion += duration / last.occupancy;
76+
77+
double sharingFactor = total / portion;
78+
sharingFactors.put(event.getRequestId(), sharingFactor);
79+
poolingRate.put(event.getRequestId(), pooled);
80+
}
81+
}
82+
83+
@Override
84+
public void handleEvent(PassengerPickedUpEvent event) {
85+
if (groupPredicate.isGroupRepresentative(event.getPersonId())) {
86+
map.computeIfAbsent(event.getVehicleId(), vehicleId -> new ArrayList<>());
87+
List<Id<Request>> occupancy = map.get(event.getVehicleId());
88+
occupancy.add(event.getRequestId());
89+
occupancy.forEach(
90+
p -> segments.computeIfAbsent(p, requestId -> new ArrayList<>()).add(new Segment(event.getTime(), occupancy.size()))
91+
);
92+
}
93+
}
94+
95+
@Override
96+
public void notifyMobsimBeforeCleanup(MobsimBeforeCleanupEvent e) {
97+
map.clear();
98+
segments.clear();
99+
poolingRate.clear();
100+
sharingFactors.clear();
101+
}
102+
103+
public Map<Id<Request>, Double> getSharingFactors() {
104+
return Collections.unmodifiableMap(sharingFactors);
105+
}
106+
107+
public Map<Id<Request>, Boolean> getPoolingRates() {
108+
return Collections.unmodifiableMap(poolingRate);
109+
}
110+
}

0 commit comments

Comments
 (0)