From abbfc70964446472d2a3868e4b3db9626e9a5d8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nico=20K=C3=BChnel?= Date: Thu, 13 Jun 2024 10:58:03 +0200 Subject: [PATCH] Add drt shift efficiency iteration stats (#3314) --- ...ftEfficiencyAnalysisControlerListener.java | 89 +++++++++++++++++-- .../efficiency/ShiftEfficiencyTracker.java | 2 +- .../shifts/run/RunShiftDrtScenarioIT.java | 6 ++ 3 files changed, 91 insertions(+), 6 deletions(-) diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyAnalysisControlerListener.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyAnalysisControlerListener.java index 9ea39e1a75f..6dc1a10ce81 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyAnalysisControlerListener.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyAnalysisControlerListener.java @@ -10,6 +10,7 @@ package org.matsim.contrib.drt.extension.operations.shifts.analysis.efficiency; import com.google.inject.Inject; +import jakarta.inject.Provider; import org.jfree.chart.ChartFactory; import org.jfree.chart.ChartUtils; import org.jfree.chart.JFreeChart; @@ -27,7 +28,7 @@ import org.matsim.core.controler.listener.IterationEndsListener; import org.matsim.core.utils.io.IOUtils; -import jakarta.inject.Provider; +import java.io.BufferedWriter; import java.io.FileOutputStream; import java.io.IOException; import java.util.*; @@ -44,6 +45,10 @@ public final class ShiftEfficiencyAnalysisControlerListener implements Iteration private final ShiftEfficiencyTracker shiftEfficiencyTracker; private final String delimiter; + private final String runId; + private boolean headerWritten = false; + private static final String notAvailableString = "NA"; + @Inject public ShiftEfficiencyAnalysisControlerListener(DrtConfigGroup drtConfigGroup, @@ -55,6 +60,7 @@ public ShiftEfficiencyAnalysisControlerListener(DrtConfigGroup drtConfigGroup, this.drtShiftsSpecification = drtShiftsSpecification; this.matsimServices = matsimServices; this.delimiter = matsimServices.getConfig().global().getDefaultDelimiter(); + this.runId = Optional.ofNullable(matsimServices.getConfig().controller().getRunId()).orElse(notAvailableString); } @Override @@ -62,14 +68,57 @@ public void notifyIterationEnds(IterationEndsEvent event) { int createGraphsInterval = event.getServices().getConfig().controller().getCreateGraphsInterval(); boolean createGraphs = createGraphsInterval >0 && event.getIteration() % createGraphsInterval == 0; - writeAndPlotShiftEfficiency( - shiftEfficiencyTracker.getCurrentRecord().getRevenueByShift(), - shiftEfficiencyTracker.getCurrentRecord().getRequestsByShift(), - shiftEfficiencyTracker.getCurrentRecord().getFinishedShifts(), + ShiftEfficiencyTracker.Record record = shiftEfficiencyTracker.getCurrentRecord(); + writeAndPlotShiftEfficiency( + record.getRevenueByShift(), + record.getRequestsByShift(), + record.getFinishedShifts(), filename(event, "shiftRevenue", ".png"), filename(event, "shiftRidesPerVrh", ".png"), filename(event, "shiftEfficiency", ".csv"), createGraphs); + + List finishedShifts = record.finishedShifts() + .keySet() + .stream() + .map(id -> drtShiftsSpecification.get().getShiftSpecifications().get(id)) + .toList(); + + double earliestShiftStart = finishedShifts.stream().map(DrtShiftSpecification::getStartTime).mapToDouble(d -> d).min().orElse(Double.NaN); + double latestShiftEnd = finishedShifts.stream().map(DrtShiftSpecification::getEndTime).mapToDouble(d -> d).min().orElse(Double.NaN); + + double numberOfShifts = finishedShifts.size(); + double numberOfShiftHours = finishedShifts. + stream() + .map(s -> (s.getEndTime() - s.getStartTime()) - (s.getBreak().isPresent() ? s.getBreak().get().getDuration() : 0.)) + .mapToDouble(d -> d) + .sum() / 3600.; + + long uniqueVehicles = record.getFinishedShifts().values().stream().distinct().count(); + + double totalRevenue = record.revenueByShift().values().stream().mapToDouble(d -> d).sum(); + double meanRevenuePerShift = record.revenueByShift().values().stream().mapToDouble(d -> d).average().orElse(Double.NaN); + double meanRevenuePerShiftHour = totalRevenue / numberOfShiftHours; + + double totalRides = record.getRequestsByShift().values().stream().mapToDouble(List::size).sum(); + double meanRidesPerShift = record.getRequestsByShift().values().stream().mapToDouble(List::size).average().orElse(Double.NaN); + double meanRidesPerShiftHour = totalRides / numberOfShiftHours; + + StringJoiner stringJoiner = new StringJoiner(delimiter); + stringJoiner + .add(earliestShiftStart + "") + .add(latestShiftEnd + "") + .add(numberOfShifts + "") + .add(numberOfShiftHours + "") + .add(uniqueVehicles + "") + .add(meanRevenuePerShift + "") + .add(meanRevenuePerShiftHour + "") + .add(totalRevenue + "") + .add(meanRidesPerShift + "") + .add(meanRidesPerShiftHour + "") + .add(totalRides + ""); + writeIterationShiftEfficiencyStats(stringJoiner.toString(), event.getIteration()); + } private void writeAndPlotShiftEfficiency(Map, Double> revenuePerShift, @@ -129,6 +178,32 @@ private void writeAndPlotShiftEfficiency(Map, Double> revenuePerShi } } + private void writeIterationShiftEfficiencyStats(String summarizeShiftEfficiency, int it) { + try (var bw = getAppendingBufferedWriter("drt_shift_efficiency_metrics", ".csv")) { + if (!headerWritten) { + headerWritten = true; + StringJoiner stringJoiner = new StringJoiner(delimiter); + stringJoiner + .add("earliestShiftStart") + .add("latestShiftEnd") + .add("numberOfShifts") + .add("numberOfShiftHours") + .add("uniqueVehicles") + .add("meanRevenuePerShift") + .add("meanRevenuePerShiftHour") + .add("totalRevenue") + .add("meanRidesPerShift") + .add("meanRidesPerShiftHour") + .add("totalRides"); + bw.write(line("runId", "iteration", stringJoiner.toString())); + } + bw.write(runId + delimiter + it + delimiter + summarizeShiftEfficiency); + bw.newLine(); + } 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); @@ -137,4 +212,8 @@ private String filename(IterationEndsEvent event, String prefix, String extensio private String line(Object... cells) { return Arrays.stream(cells).map(Object::toString).collect(Collectors.joining(delimiter, "", "\n")); } + + private BufferedWriter getAppendingBufferedWriter(String prefix, String extension) { + return IOUtils.getAppendingBufferedWriter(matsimServices.getControlerIO().getOutputFilename(prefix + "_" + drtConfigGroup.getMode() + extension)); + } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyTracker.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyTracker.java index ce4c39a882b..7ccb1edbbfc 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyTracker.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/shifts/analysis/efficiency/ShiftEfficiencyTracker.java @@ -45,7 +45,7 @@ public final class ShiftEfficiencyTracker implements PersonMoneyEventHandler, private Record currentRecord; - public static record Record(Map, Double> revenueByShift, + public record Record(Map, Double> revenueByShift, Map, Id> shiftByRequest, Map, Id> finishedShifts){ public Map, Double> getRevenueByShift() { diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunShiftDrtScenarioIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunShiftDrtScenarioIT.java index c2b671c90e7..522aff3db1b 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunShiftDrtScenarioIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/operations/shifts/run/RunShiftDrtScenarioIT.java @@ -9,6 +9,7 @@ import org.matsim.contrib.drt.extension.operations.DrtWithOperationsConfigGroup; import org.matsim.contrib.drt.extension.operations.operationFacilities.OperationFacilitiesParams; import org.matsim.contrib.drt.extension.operations.shifts.config.ShiftsParams; +import org.matsim.contrib.drt.fare.DrtFareParams; import org.matsim.contrib.drt.optimizer.DrtOptimizationConstraintsSet; import org.matsim.contrib.drt.optimizer.insertion.extensive.ExtensiveInsertionSearchParams; import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingParams; @@ -142,6 +143,11 @@ void test() { shiftsParams.allowInFieldChangeover = true; drtWithShiftsConfigGroup.addParameterSet(operationsParams); + DrtFareParams drtFareParams = new DrtFareParams(); + drtFareParams.baseFare = 1.; + drtFareParams.distanceFare_m = 1. / 1000; + drtWithShiftsConfigGroup.addParameterSet(drtFareParams); + final Controler run = DrtOperationsControlerCreator.createControler(config, false); run.run(); }