diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/BasicDrtEstimator.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/BasicDrtEstimator.java index 3d7555020e1..d446d5f34bc 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/BasicDrtEstimator.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/BasicDrtEstimator.java @@ -5,7 +5,9 @@ import org.apache.commons.math3.stat.regression.SimpleRegression; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.PersonMoneyEvent; +import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; import org.matsim.contrib.drt.extension.estimator.run.DrtEstimatorConfigGroup; import org.matsim.contrib.drt.fare.DrtFareParams; @@ -16,6 +18,7 @@ import org.matsim.core.controler.listener.IterationEndsListener; import org.matsim.core.utils.misc.OptionalTime; +import java.util.Map; import java.util.SplittableRandom; /** @@ -64,19 +67,21 @@ public void notifyIterationEnds(IterationEndsEvent event) { for (DrtEventSequenceCollector.EventSequence seq : collector.getPerformedRequestSequences().values()) { - if (seq.getPickedUp().isPresent() && seq.getDroppedOff().isPresent()) { + Map, DrtEventSequenceCollector.EventSequence.PersonEvents> personEvents = seq.getPersonEvents(); + for (Map.Entry, DrtEventSequenceCollector.EventSequence.PersonEvents> entry : personEvents.entrySet()) { + if (entry.getValue().getPickedUp().isPresent() && entry.getValue().getDroppedOff().isPresent()) { + double waitTime = entry.getValue().getPickedUp().get().getTime() - seq.getSubmitted().getTime(); + est.waitTime.addValue(waitTime); - double waitTime = seq.getPickedUp().get().getTime() - seq.getSubmitted().getTime(); - est.waitTime.addValue(waitTime); + double unsharedTime = seq.getSubmitted().getUnsharedRideTime(); + double travelTime = entry.getValue().getDroppedOff().get().getTime() - entry.getValue().getPickedUp().get().getTime(); - double unsharedTime = seq.getSubmitted().getUnsharedRideTime(); - double travelTime = seq.getDroppedOff().get().getTime() - seq.getPickedUp().get().getTime(); + est.detour.addValue(travelTime / unsharedTime); - est.detour.addValue(travelTime / unsharedTime); - - double fare = seq.getDrtFares().stream().mapToDouble(PersonMoneyEvent::getAmount).sum(); - est.fare.addData(seq.getSubmitted().getUnsharedRideDistance(), fare); - n++; + double fare = seq.getDrtFares().stream().mapToDouble(PersonMoneyEvent::getAmount).sum(); + est.fare.addData(seq.getSubmitted().getUnsharedRideDistance(), fare); + n++; + } } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimateAnalyzer.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimateAnalyzer.java index 2d87f7ea7b8..d095a0037bd 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimateAnalyzer.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/estimator/DrtEstimateAnalyzer.java @@ -5,7 +5,9 @@ import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.PersonMoneyEvent; +import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; import org.matsim.contrib.drt.extension.estimator.run.DrtEstimatorConfigGroup; import org.matsim.contrib.drt.routing.DrtRoute; @@ -23,6 +25,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import java.util.Map; /** * Analyzes and outputs drt estimates errors metrics based on daily requests. @@ -96,22 +99,25 @@ private Iterable calcMetrics(int iteration) { DescriptiveStatistics fare = new DescriptiveStatistics(); for (DrtEventSequenceCollector.EventSequence seq : collector.getPerformedRequestSequences().values()) { - if (seq.getPickedUp().isPresent() && seq.getDroppedOff().isPresent()) { + Map, DrtEventSequenceCollector.EventSequence.PersonEvents> personEvents = seq.getPersonEvents(); + for (Map.Entry, DrtEventSequenceCollector.EventSequence.PersonEvents> entry : personEvents.entrySet()) { + if (entry.getValue().getPickedUp().isPresent() && entry.getValue().getDroppedOff().isPresent()) { - // many attributes are not filled, when using the constructor - DrtRoute route = new DrtRoute(seq.getSubmitted().getFromLinkId(), seq.getSubmitted().getToLinkId()); - route.setDirectRideTime(seq.getSubmitted().getUnsharedRideTime()); - route.setDistance(seq.getSubmitted().getUnsharedRideDistance()); + // many attributes are not filled, when using the constructor + DrtRoute route = new DrtRoute(seq.getSubmitted().getFromLinkId(), seq.getSubmitted().getToLinkId()); + route.setDirectRideTime(seq.getSubmitted().getUnsharedRideTime()); + route.setDistance(seq.getSubmitted().getUnsharedRideDistance()); - double valWaitTime = seq.getPickedUp().get().getTime() - seq.getSubmitted().getTime(); - double valTravelTime = seq.getDroppedOff().get().getTime() - seq.getPickedUp().get().getTime(); - double valFare = seq.getDrtFares().stream().mapToDouble(PersonMoneyEvent::getAmount).sum(); + double valWaitTime = entry.getValue().getPickedUp().get().getTime() - seq.getSubmitted().getTime(); + double valTravelTime = entry.getValue().getDroppedOff().get().getTime() - entry.getValue().getPickedUp().get().getTime(); + double valFare = seq.getDrtFares().stream().mapToDouble(PersonMoneyEvent::getAmount).sum(); - DrtEstimator.Estimate estimate = estimator.estimate(route, OptionalTime.defined(seq.getSubmitted().getTime())); + DrtEstimator.Estimate estimate = estimator.estimate(route, OptionalTime.defined(seq.getSubmitted().getTime())); - waitTime.addValue(Math.abs(estimate.waitingTime() - valWaitTime)); - travelTime.addValue(Math.abs(estimate.travelTime() - valTravelTime)); - fare.addValue(Math.abs(estimate.fare() - valFare)); + waitTime.addValue(Math.abs(estimate.waitingTime() - valWaitTime)); + travelTime.addValue(Math.abs(estimate.travelTime() - valTravelTime)); + fare.addValue(Math.abs(estimate.fare() - valFare)); + } } } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/charging/ChargingBreakActivity.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/charging/ChargingBreakActivity.java index f535c15d5ca..1a008c63cdd 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/charging/ChargingBreakActivity.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/operations/eshifts/charging/ChargingBreakActivity.java @@ -1,6 +1,7 @@ package org.matsim.contrib.drt.extension.operations.eshifts.charging; import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Identifiable; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.drt.passenger.AcceptedDrtRequest; import org.matsim.contrib.dvrp.optimizer.Request; @@ -15,7 +16,10 @@ import org.matsim.contrib.drt.extension.operations.shifts.schedule.ShiftBreakTask; import org.matsim.core.mobsim.framework.MobsimPassengerAgent; +import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; /** * based on {@link DrtStopActivity} and {@link ChargingActivity} @@ -50,7 +54,7 @@ public ChargingBreakActivity(ChargingTask chargingTask, PassengerHandler passeng protected boolean isLastStep(double now) { if(chargingDelegate.getEndTime() < now && now >= endTime) { for (var request : pickupRequests.values()) { - if (passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now)) { + if (passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now)) { passengersPickedUp++; } } @@ -64,13 +68,13 @@ public void finalizeAction(double now) { } @Override - public void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, double now) { + public void notifyPassengersAreReadyForDeparture(List passengers, double now) { if (!isLastStep(now)) { return;// pick up only at the end of stop activity } - var request = getRequestForPassenger(passenger.getId()); - if (passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now)) { + var request = getRequestForPassengers(passengers.stream().map(Identifiable::getId).toList()); + if (passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now)) { passengersPickedUp++; } else { throw new IllegalStateException("The passenger is not on the link or not available for departure!"); @@ -81,7 +85,7 @@ public void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, d protected void beforeFirstStep(double now) { // TODO probably we should simulate it more accurately (passenger by passenger, not all at once...) for (var request : dropoffRequests.values()) { - passengerHandler.dropOffPassenger(driver, request.getId(), now); + passengerHandler.dropOffPassengers(driver, request.getId(), now); } } @@ -95,9 +99,9 @@ protected void simStep(double now) { chargingDelegate.doSimStep(now); } - private AcceptedDrtRequest getRequestForPassenger(Id passengerId) { + private AcceptedDrtRequest getRequestForPassengers(List> passengerIds) { return pickupRequests.values().stream() - .filter(r -> passengerId.equals(r.getPassengerId())) + .filter(r -> r.getPassengerIds().size() == passengerIds.size() && r.getPassengerIds().containsAll(passengerIds)) .findAny() .orElseThrow(() -> new IllegalArgumentException("I am waiting for different passengers!")); } diff --git a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtOptimizer.java b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtOptimizer.java index 0103e5bb66a..5dd5f1c8bf7 100644 --- a/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtOptimizer.java +++ b/contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/preplanned/optimizer/PreplannedDrtOptimizer.java @@ -22,9 +22,7 @@ import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.STAY; -import java.util.HashMap; -import java.util.Map; -import java.util.Queue; +import java.util.*; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; @@ -118,7 +116,7 @@ public void requestSubmitted(Request request) { "Pre-planned request (%s) not assigned to any vehicle and not marked as unassigned.", preplannedRequest); eventsManager.processEvent(new PassengerRequestRejectedEvent(timer.getTimeOfDay(), mode, request.getId(), - drtRequest.getPassengerId(), "Marked as unassigned")); + drtRequest.getPassengerIds(), "Marked as unassigned")); return; } @@ -130,7 +128,7 @@ public void requestSubmitted(Request request) { //TODO in the current implementation we do not know the scheduled pickup and dropoff times eventsManager.processEvent( new PassengerRequestScheduledEvent(timer.getTimeOfDay(), drtRequest.getMode(), drtRequest.getId(), - drtRequest.getPassengerId(), vehicleId, Double.NaN, Double.NaN)); + drtRequest.getPassengerIds(), vehicleId, Double.NaN, Double.NaN)); } @Override @@ -203,7 +201,7 @@ public record PreplannedSchedules(Map> pre Map unassignedRequests) { } - public record PreplannedRequestKey(Id passengerId, Id fromLinkId, Id toLinkId) { + public record PreplannedRequestKey(Set> passengerIds, Id fromLinkId, Id toLinkId) { } // also input to the external optimiser @@ -212,7 +210,7 @@ public record PreplannedRequest(PreplannedRequestKey key, double earliestStartTi } static PreplannedRequest createFromRequest(DrtRequest request) { - return new PreplannedRequest(new PreplannedRequestKey(request.getPassengerId(), request.getFromLink().getId(), + return new PreplannedRequest(new PreplannedRequestKey(Set.copyOf(request.getPassengerIds()), request.getFromLink().getId(), request.getToLink().getId()), request.getEarliestStartTime(), request.getLatestStartTime(), request.getLatestArrivalTime()); } diff --git a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/preplanned/optimizer/RunPreplannedDrtExampleIT.java b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/preplanned/optimizer/RunPreplannedDrtExampleIT.java index 11bd8d5328b..cd0b743f9f2 100644 --- a/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/preplanned/optimizer/RunPreplannedDrtExampleIT.java +++ b/contribs/drt-extensions/src/test/java/org/matsim/contrib/drt/extension/preplanned/optimizer/RunPreplannedDrtExampleIT.java @@ -25,11 +25,7 @@ import static org.matsim.contrib.drt.extension.preplanned.optimizer.PreplannedDrtOptimizer.PreplannedStop; import java.net.URL; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; +import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -53,34 +49,34 @@ public void testRun() { // create preplanned requests (they will be mapped to drt requests created during simulation) var preplannedRequest_0 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_0"), Id.createLinkId("114"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_0")), Id.createLinkId("114"), Id.createLinkId("349")), 0.0, 900.0, 844.4); var preplannedRequest_1 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_1"), Id.createLinkId("144"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_1")), Id.createLinkId("144"), Id.createLinkId("437")), 300.0, 1200.0, 1011.8); var preplannedRequest_2 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_2"), Id.createLinkId("223"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_2")), Id.createLinkId("223"), Id.createLinkId("347")), 600.0, 1500.0, 1393.7); var preplannedRequest_3 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_3"), Id.createLinkId("234"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_3")), Id.createLinkId("234"), Id.createLinkId("119")), 900.0, 1800.0, 1825.0); var preplannedRequest_4 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_4"), Id.createLinkId("314"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_4")), Id.createLinkId("314"), Id.createLinkId("260")), 1200.0, 2100.0, 1997.6); var preplannedRequest_5 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_5"), Id.createLinkId("333"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_5")), Id.createLinkId("333"), Id.createLinkId("438")), 1500.0, 2400.0, 2349.6); var preplannedRequest_6 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_6"), Id.createLinkId("325"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_6")), Id.createLinkId("325"), Id.createLinkId("111")), 1800.0, 2700.0, 2600.2); var preplannedRequest_7 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_7"), Id.createLinkId("412"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_7")), Id.createLinkId("412"), Id.createLinkId("318")), 2100.0, 3000.0, 2989.9); var preplannedRequest_8 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_8"), Id.createLinkId("455"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_8")), Id.createLinkId("455"), Id.createLinkId("236")), 2400.0, 3300.0, 3110.5); var preplannedRequest_9 = new PreplannedRequest( - new PreplannedRequestKey(Id.createPersonId("passenger_9"), Id.createLinkId("139"), + new PreplannedRequestKey(Set.of(Id.createPersonId("passenger_9")), Id.createLinkId("139"), Id.createLinkId("330")), 2700.0, 3600.0, 3410.5); var preplannedRequests = List.of(preplannedRequest_0, preplannedRequest_1, preplannedRequest_2, preplannedRequest_3, preplannedRequest_4, preplannedRequest_5, preplannedRequest_6); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java index cfb6d20ba50..1f33b8cf008 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtAnalysisControlerListener.java @@ -35,7 +35,6 @@ import org.jfree.data.xy.XYSeries; import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.events.PersonDepartureEvent; import org.matsim.api.core.v01.events.PersonMoneyEvent; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; @@ -126,31 +125,37 @@ private record DrtLeg(Id request, double submissionTime, double departu double arrivalTime, double fare, double earliestDepartureTime, double latestDepartureTime, double latestArrivalTime) { } - private static DrtLeg newDrtLeg(EventSequence sequence, Function, ? extends Link> linkProvider) { + private static List newDrtLegs(EventSequence sequence, Function, ? extends Link> linkProvider) { Preconditions.checkArgument(sequence.isCompleted()); + List legs = new ArrayList<>(); DrtRequestSubmittedEvent submittedEvent = sequence.getSubmitted(); - PersonDepartureEvent departureEvent = sequence.getDeparture().get(); - PassengerPickedUpEvent pickedUpEvent = sequence.getPickedUp().get(); + + Map, EventSequence.PersonEvents> personEvents = sequence.getPersonEvents(); + var request = submittedEvent.getRequestId(); var submissionTime = submittedEvent.getTime(); - var departureTime = departureEvent.getTime(); - var person = submittedEvent.getPersonId(); - var vehicle = pickedUpEvent.getVehicleId(); var fromLinkId = submittedEvent.getFromLinkId(); var fromCoord = linkProvider.apply(fromLinkId).getToNode().getCoord(); var toLinkId = submittedEvent.getToLinkId(); var toCoord = linkProvider.apply(toLinkId).getToNode().getCoord(); - var waitTime = pickedUpEvent.getTime() - departureEvent.getTime(); var unsharedDistanceEstimate_m = submittedEvent.getUnsharedRideDistance(); var unsharedTimeEstimate_m = submittedEvent.getUnsharedRideTime(); - var arrivalTime = sequence.getDroppedOff().get().getTime(); // PersonMoneyEvent has negative amount because the agent's money is reduced -> for the operator that is a positive amount var fare = sequence.getDrtFares().stream().mapToDouble(PersonMoneyEvent::getAmount).sum(); var earliestDepartureTime = sequence.getSubmitted().getEarliestDepartureTime(); var latestDepartureTime = sequence.getSubmitted().getLatestPickupTime(); var latestArrivalTime = sequence.getSubmitted().getLatestDropoffTime(); - return new DrtLeg(request, submissionTime, departureTime, person, vehicle, fromLinkId, fromCoord, toLinkId, toCoord, waitTime, unsharedDistanceEstimate_m, - unsharedTimeEstimate_m, arrivalTime, fare, earliestDepartureTime, latestDepartureTime, latestArrivalTime); + + for (Id person : submittedEvent.getPersonIds()) { + var departureTime = personEvents.get(person).getDeparture().get().getTime(); + PassengerPickedUpEvent pickedUp = personEvents.get(person).getPickedUp().get(); + var vehicle = pickedUp.getVehicleId(); + var waitTime = pickedUp.getTime() - personEvents.get(person).getDeparture().get().getTime(); + var arrivalTime = personEvents.get(person).getDroppedOff().get().getTime(); + legs.add(new DrtLeg(request, submissionTime, departureTime, person, vehicle, fromLinkId, fromCoord, toLinkId, toCoord, waitTime, unsharedDistanceEstimate_m, + unsharedTimeEstimate_m, arrivalTime, fare, earliestDepartureTime, latestDepartureTime, latestArrivalTime)); + } + return legs; } @Override @@ -164,25 +169,26 @@ public void notifyIterationEnds(IterationEndsEvent event) { .values() .stream() .filter(EventSequence::isCompleted) - .map(sequence -> newDrtLeg(sequence, network.getLinks()::get)) + .map(sequence -> newDrtLegs(sequence, network.getLinks()::get)) + .flatMap(Collection::stream) .sorted(Comparator.comparing(leg -> leg.departureTime)) .collect(toList()); List rejectionEvents = drtEventSequenceCollector.getRejectedRequestSequences() - .values() - .stream() - .map(eventSequence -> eventSequence.getRejected().get()) - .sorted(Comparator.comparing(rejectionEvent -> rejectionEvent.getTime())) - .collect(toList()); + .values() + .stream() + .map(eventSequence -> eventSequence.getRejected().get()) + .sorted(Comparator.comparing(rejectionEvent -> rejectionEvent.getTime())) + .collect(toList()); collection2Text(drtEventSequenceCollector.getRejectedRequestSequences().values(), filename(event, "drt_rejections", ".csv"), - String.join(delimiter, "time", "personId", "requestId", "fromLinkId", "toLinkId", "fromX", "fromY", "toX", "toY", "cause"), seq -> { + String.join(delimiter, "time", "personIds", "requestId", "fromLinkId", "toLinkId", "fromX", "fromY", "toX", "toY", "cause"), seq -> { DrtRequestSubmittedEvent submission = seq.getSubmitted(); Coord fromCoord = network.getLinks().get(submission.getFromLinkId()).getToNode().getCoord(); Coord toCoord = network.getLinks().get(submission.getToLinkId()).getToNode().getCoord(); PassengerRequestRejectedEvent rejection = seq.getRejected().get(); return String.join(delimiter, submission.getTime() + "",// - submission.getPersonId() + "",// + submission.getPersonIds().stream().map(Object::toString).collect(Collectors.joining("-")) + "",// submission.getRequestId() + "",// submission.getFromLinkId() + "",// submission.getToLinkId() + "",// @@ -193,7 +199,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { rejection.getCause()); }); - double rejectionRate = (double)drtEventSequenceCollector.getRejectedRequestSequences().size() + double rejectionRate = (double) drtEventSequenceCollector.getRejectedRequestSequences().size() / drtEventSequenceCollector.getRequestSubmissions().size(); String legsSummarize = summarizeLegs(legs, drtVehicleStats.getTravelDistances(), drtEventSequenceCollector.getDrtFarePersonMoneyEvents(), delimiter); @@ -214,8 +220,8 @@ public void notifyIterationEnds(IterationEndsEvent event) { String occStats = summarizeDetailedOccupancyStats(drtVehicleStats.getVehicleStates(), delimiter, maxcap); writeIterationVehicleStats(vehStats, occStats, event.getIteration()); if (drtCfg.plotDetailedCustomerStats) { - String header = String.join(delimiter, // - "submissionTime", // + String header = String.join(delimiter, // + "submissionTime", // "departureTime",// "personId",// "requestId",// @@ -261,7 +267,7 @@ public void notifyIterationEnds(IterationEndsEvent event) { writeVehicleDistances(drtVehicleStats.getVehicleStates(), filename(event, "vehicleDistanceStats", ".csv"), delimiter); analyseDetours(network, legs, drtVehicleStats.getTravelDistances(), drtCfg, filename(event, "drt_detours"), createGraphs, delimiter); analyseWaitTimes(filename(event, "waitStats"), legs, 1800, createGraphs, delimiter); - analyseRejections(filename(event,"drt_rejections_perTimeBin"), rejectionEvents,1800, createGraphs, delimiter); + analyseRejections(filename(event, "drt_rejections_perTimeBin"), rejectionEvents, 1800, createGraphs, delimiter); analyseConstraints(filename(event, "constraints"), legs, createGraphs); double endTime = qSimCfg.getEndTime() @@ -343,7 +349,7 @@ private void writeIterationPassengerStats(String summarizeLegs, int it) { private void writeIterationVehicleStats(String summarizeVehicles, String vehOcc, int it) { try (var bw = getAppendingBufferedWriter("drt_vehicle_stats", ".csv")) { if (!vheaderWritten) { - bw.write(line("runId", "iteration", "vehicles", "totalServiceDuration", "totalDistance", "totalEmptyDistance", "emptyRatio", "totalPassengerDistanceTraveled", + bw.write(line("runId", "iteration", "vehicles", "totalServiceDuration", "totalDistance", "totalEmptyDistance", "emptyRatio", "totalPassengerDistanceTraveled", "averageDrivenDistance", "averageEmptyDistance", "averagePassengerDistanceTraveled", "d_p/d_t", "l_det", "minShareIdleVehicles", "minCountIdleVehicles")); } @@ -375,11 +381,17 @@ private void writeAndPlotWaitTimeEstimateComparison(Collection pe bw.append(line("RequestId", "actualWaitTime", "estimatedWaitTime", "deviate")); for (EventSequence seq : performedRequestEventSequences) { - if (seq.getPickedUp().isPresent()) { - double actualWaitTime = seq.getPickedUp().get().getTime() - seq.getDeparture().get().getTime(); - double estimatedWaitTime = seq.getScheduled().get().getPickupTime() - seq.getSubmitted().getEarliestDepartureTime(); - bw.append(line(seq.getSubmitted().getRequestId(), actualWaitTime, estimatedWaitTime, actualWaitTime - estimatedWaitTime)); - times.add(actualWaitTime, estimatedWaitTime); + List> personIds = seq.getSubmitted().getPersonIds(); + for (Id person : personIds) { + if(seq.getPersonEvents().containsKey(person)) { + EventSequence.PersonEvents personEvents = seq.getPersonEvents().get(person); + if(personEvents.getPickedUp().isPresent() && personEvents.getDeparture().isPresent()) { + double actualWaitTime = personEvents.getPickedUp().get().getTime() - personEvents.getDeparture().get().getTime(); + double estimatedWaitTime = seq.getScheduled().get().getPickupTime() - seq.getSubmitted().getEarliestDepartureTime(); + bw.append(line(seq.getSubmitted().getRequestId(), actualWaitTime, estimatedWaitTime, actualWaitTime - estimatedWaitTime)); + times.add(actualWaitTime, estimatedWaitTime); + } + } } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtEventSequenceCollector.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtEventSequenceCollector.java index 04a89f30c36..b8b095cd1dc 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtEventSequenceCollector.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/DrtEventSequenceCollector.java @@ -29,6 +29,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -85,6 +86,28 @@ public class DrtEventSequenceCollector PersonMoneyEventHandler, PersonDepartureEventHandler { public static class EventSequence { + + public static class PersonEvents { + + @Nullable + private PersonDepartureEvent departure; + @Nullable + private PassengerPickedUpEvent pickedUp; + @Nullable + private PassengerDroppedOffEvent droppedOff; + + public Optional getDeparture() { + return Optional.ofNullable(departure); + } + + public Optional getPickedUp() { + return Optional.ofNullable(pickedUp); + } + + public Optional getDroppedOff() { + return Optional.ofNullable(droppedOff); + } + } private final DrtRequestSubmittedEvent submitted; @Nullable @@ -92,12 +115,8 @@ public static class EventSequence { @Nullable private PassengerRequestRejectedEvent rejected; - @Nullable - private PersonDepartureEvent departure; - @Nullable - private PassengerPickedUpEvent pickedUp; - @Nullable - private PassengerDroppedOffEvent droppedOff; + private final Map, PersonEvents> personEvents = new HashMap<>(); + @Nullable private List drtFares = new LinkedList<>(); @@ -105,15 +124,17 @@ public static class EventSequence { this.submitted = Objects.requireNonNull(submitted); } - public EventSequence(PersonDepartureEvent departure, DrtRequestSubmittedEvent submitted, + public EventSequence(Id personId, PersonDepartureEvent departure, DrtRequestSubmittedEvent submitted, PassengerRequestScheduledEvent scheduled, PassengerPickedUpEvent pickedUp, PassengerDroppedOffEvent droppedOff, List drtFares) { this.submitted = Objects.requireNonNull(submitted); this.scheduled = scheduled; - this.departure = departure; - this.pickedUp = pickedUp; - this.droppedOff = droppedOff; this.drtFares = new ArrayList<>(drtFares); + PersonEvents personEvents = new PersonEvents(); + personEvents.departure = departure; + personEvents.pickedUp = pickedUp; + personEvents.droppedOff = droppedOff; + this.personEvents.put(personId, personEvents); } public DrtRequestSubmittedEvent getSubmitted() { @@ -128,24 +149,17 @@ public Optional getRejected() { return Optional.ofNullable(rejected); } - public Optional getDeparture() { - return Optional.ofNullable(departure); + public Map, PersonEvents> getPersonEvents() { + return personEvents; } - public Optional getPickedUp() { - return Optional.ofNullable(pickedUp); - } - - public Optional getDroppedOff() { - return Optional.ofNullable(droppedOff); - } public List getDrtFares() { return Collections.unmodifiableList(drtFares); } public boolean isCompleted() { - return droppedOff != null; + return personEvents.values().stream().allMatch(pe -> pe.droppedOff != null); } } @@ -155,7 +169,7 @@ public boolean isCompleted() { private final List drtFarePersonMoneyEvents = new ArrayList<>(); private final Map, PersonDepartureEvent> latestDepartures = new HashMap<>(); - private final Map, PersonDepartureEvent> waitingForSubmission = new HashMap<>(); + private final Map, List> waitingForSubmission = new HashMap<>(); public DrtEventSequenceCollector(String mode) { this.mode = mode; @@ -175,8 +189,8 @@ public Map, EventSequence> getRejectedRequestSequences() { public Map, EventSequence> getPerformedRequestSequences() { return sequences.entrySet().stream() // .filter(e -> e.getValue().getRejected().isEmpty()) // - .filter(e -> e.getValue().getDeparture().isPresent()) // - .filter(e -> e.getValue().getPickedUp().isPresent()) // + .filter(e -> e.getValue().personEvents.values().stream().allMatch(pe -> pe.departure != null)) // + .filter(e -> e.getValue().personEvents.values().stream().allMatch(pe -> pe.pickedUp != null)) // .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue())); } @@ -197,9 +211,16 @@ public void handleEvent(DrtRequestSubmittedEvent event) { if (event.getMode().equals(mode)) { EventSequence sequence = new EventSequence(event); sequences.put(event.getRequestId(), sequence); - + // if we already have a departure - sequence.departure = waitingForSubmission.remove(event.getRequestId()); + List waiting = waitingForSubmission.remove(event.getRequestId()); + if(waiting != null) { + for (PersonDepartureEvent departure : waiting) { + sequence.getPersonEvents().computeIfAbsent( + departure.getPersonId(), personId -> new EventSequence.PersonEvents() + ).departure = departure; + } + } } } @@ -218,21 +239,23 @@ public void handleEvent(PersonDepartureEvent event) { @Override public void handleEvent(PassengerWaitingEvent event) { if (event.getMode().equals(mode)) { - // must exist, otherwise something is wrong - PersonDepartureEvent departureEvent = Objects.requireNonNull(latestDepartures.remove(event.getPersonId())); - EventSequence sequence = sequences.get(event.getRequestId()); - if (sequence != null) { - // prebooked request, we already have the submission - Verify.verifyNotNull(sequence.submitted); - sequence.departure = departureEvent; - } else { - // immediate request, submission event should come soon - waitingForSubmission.put(event.getRequestId(), departureEvent); + for (Id personId : event.getPersonIds()) { + // must exist, otherwise something is wrong + PersonDepartureEvent departureEvent = Objects.requireNonNull(latestDepartures.remove(personId)); + + if (sequence != null) { + // prebooked request, we already have the submission + Verify.verifyNotNull(sequence.submitted); + sequence.personEvents.computeIfAbsent(personId, p -> new EventSequence.PersonEvents()).departure = departureEvent; + } else { + // immediate request, submission event should come soon + waitingForSubmission.computeIfAbsent(event.getRequestId(), requestId -> new ArrayList<>()).add(departureEvent); + } } } } - + @Override public void handleEvent(PassengerRequestScheduledEvent event) { if (event.getMode().equals(mode)) { @@ -250,14 +273,14 @@ public void handleEvent(PassengerRequestRejectedEvent event) { @Override public void handleEvent(PassengerPickedUpEvent event) { if (event.getMode().equals(mode)) { - sequences.get(event.getRequestId()).pickedUp = event; + sequences.get(event.getRequestId()).personEvents.get(event.getPersonId()).pickedUp = event; } } @Override public void handleEvent(PassengerDroppedOffEvent event) { if (event.getMode().equals(mode)) { - sequences.get(event.getRequestId()).droppedOff = event; + sequences.get(event.getRequestId()).personEvents.get(event.getPersonId()).droppedOff = event; } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZonalWaitTimesAnalyzer.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZonalWaitTimesAnalyzer.java index be03eda92f5..40c885b5531 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZonalWaitTimesAnalyzer.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/analysis/zonal/DrtZonalWaitTimesAnalyzer.java @@ -39,6 +39,8 @@ import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Polygon; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector.EventSequence; import org.matsim.contrib.drt.run.DrtConfigGroup; @@ -138,11 +140,13 @@ private Map createZonalStats() { zoneStats.put(zoneIdForOutsideOfZonalSystem, new DescriptiveStatistics()); for (EventSequence seq : requestAnalyzer.getPerformedRequestSequences().values()) { - if (seq.getPickedUp().isPresent()) { - DrtZone zone = zones.getZoneForLinkId(seq.getSubmitted().getFromLinkId()); - final String zoneStr = zone != null ? zone.getId() : zoneIdForOutsideOfZonalSystem; - double waitTime = seq.getPickedUp().get().getTime() - seq.getSubmitted().getTime(); - zoneStats.get(zoneStr).addValue(waitTime); + for (Map.Entry, EventSequence.PersonEvents> entry : seq.getPersonEvents().entrySet()) { + if(entry.getValue().getPickedUp().isPresent()) { + DrtZone zone = zones.getZoneForLinkId(seq.getSubmitted().getFromLinkId()); + final String zoneStr = zone != null ? zone.getId() : zoneIdForOutsideOfZonalSystem; + double waitTime = entry.getValue().getPickedUp().get() .getTime() - seq.getSubmitted().getTime(); + zoneStats.get(zoneStr).addValue(waitTime); + } } } return zoneStats; diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserter.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserter.java index 34503207f82..0f8443f0141 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserter.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserter.java @@ -64,7 +64,7 @@ public class DefaultUnplannedRequestInserter implements UnplannedRequestInserter private final DrtInsertionSearch insertionSearch; private final DrtRequestInsertionRetryQueue insertionRetryQueue; private final DrtOfferAcceptor drtOfferAcceptor; - private final ForkJoinPool forkJoinPool; + private final ForkJoinPool forkJoinPool; private final PassengerStopDurationProvider stopDurationProvider; public DefaultUnplannedRequestInserter(DrtConfigGroup drtCfg, Fleet fleet, MobsimTimer mobsimTimer, @@ -127,12 +127,12 @@ private void scheduleUnplannedRequest(DrtRequest req, Map, Vehic if (best.isEmpty()) { if (!insertionRetryQueue.tryAddFailedRequest(req, now)) { eventsManager.processEvent( - new PassengerRequestRejectedEvent(now, mode, req.getId(), req.getPassengerId(), + new PassengerRequestRejectedEvent(now, mode, req.getId(), req.getPassengerIds(), NO_INSERTION_FOUND_CAUSE)); log.debug("No insertion found for drt request " + req - + " from passenger id=" - + req.getPassengerId() + + " with passenger ids=" + + req.getPassengerIds().stream().map(Object::toString).collect(Collectors.joining(",")) + " fromLinkId=" + req.getFromLink().getId()); } @@ -153,7 +153,7 @@ private void scheduleUnplannedRequest(DrtRequest req, Map, Vehic } else { vehicleEntries.remove(vehicle.getId()); } - + double expectedPickupTime = pickupDropoffTaskPair.pickupTask.getBeginTime(); expectedPickupTime = Math.max(expectedPickupTime, acceptedRequest.get().getEarliestStartTime()); expectedPickupTime += stopDurationProvider.calcPickupDuration(vehicle, req); @@ -162,7 +162,7 @@ private void scheduleUnplannedRequest(DrtRequest req, Map, Vehic expectedDropoffTime += stopDurationProvider.calcDropoffDuration(vehicle, req); eventsManager.processEvent( - new PassengerRequestScheduledEvent(now, mode, req.getId(), req.getPassengerId(), vehicle.getId(), + new PassengerRequestScheduledEvent(now, mode, req.getId(), req.getPassengerIds(), vehicle.getId(), expectedPickupTime, expectedDropoffTime)); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGenerator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGenerator.java index df990058487..60e27e4bc5a 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGenerator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGenerator.java @@ -20,6 +20,7 @@ package org.matsim.contrib.drt.optimizer.insertion; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.matsim.contrib.drt.optimizer.VehicleEntry; @@ -152,14 +153,20 @@ public InsertionGenerator(StopTimeCalculator stopTimeCalculator, DetourTimeEstim public List generateInsertions(DrtRequest drtRequest, VehicleEntry vEntry) { int stopCount = vEntry.stops.size(); List insertions = new ArrayList<>(); + + if (drtRequest.getPassengerCount() > vEntry.vehicle.getCapacity()) { + //exit early + return Collections.EMPTY_LIST; + } + int occupancy = vEntry.start.occupancy; - + for (int i = 0; i < stopCount; i++) {// insertions up to before last stop Waypoint.Stop nextStop = nextStop(vEntry, i); - + // (1) only not fully loaded arcs - boolean allowed = occupancy < vEntry.vehicle.getCapacity(); - + boolean allowed = occupancy + drtRequest.getPassengerCount() <= vEntry.vehicle.getCapacity(); + // (2) check if the request wants to depart after the departure time of the next // stop. We can early on filter out the current insertion, because we will // neither be able to insert our stop before the next stop nor merge the request @@ -211,7 +218,7 @@ private void generateDropoffInsertions(DrtRequest request, VehicleEntry vEntry, earliestPickupStartTime); //TODO stopDuration not included var pickupDetourInfo = detourTimeCalculator.calcPickupDetourInfo(vEntry, pickupInsertion, toPickupTT, fromPickupTT, true, request); - + if (i == 0 && !checkStartSlack(vEntry, request, pickupDetourInfo)) { // Inserting at schedule start and extending an ongoing stop task further than allowed return; @@ -251,7 +258,7 @@ private void generateDropoffInsertions(DrtRequest request, VehicleEntry vEntry, // i -> pickup -> i+1 && j -> dropoff -> j+1 // check the capacity constraints if i < j (already validated for `i == j`) Waypoint.Stop currentStop = currentStop(vEntry, j); - if (currentStop.outgoingOccupancy == vEntry.vehicle.getCapacity()) { + if (currentStop.outgoingOccupancy + request.getPassengerCount() > vEntry.vehicle.getCapacity()) { if (request.getToLink() == currentStop.task.getLink()) { //special case -- we can insert dropoff exactly at node j addInsertion(insertions, @@ -288,24 +295,24 @@ private Waypoint.Stop currentStop(VehicleEntry entry, int insertionIdx) { private Waypoint.Stop nextStop(VehicleEntry entry, int insertionIdx) { return entry.stops.get(insertionIdx); } - + private boolean checkStartSlack(VehicleEntry vEntry, DrtRequest request, PickupDetourInfo pickupDetourInfo) { if (vEntry.start.task.isEmpty()) { return true; } - + Task startTask = vEntry.start.task.get(); - + if (!DrtTaskBaseType.STOP.isBaseTypeOf(startTask)) { return true; } - + DrtStopTask stopTask = (DrtStopTask) startTask; - + if (stopTask.getLink() != request.getFromLink()) { return true; } - + return vEntry.getStartSlackTime() >= pickupDetourInfo.departureTime - stopTask.getEndTime(); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/NetDepartureReplenishDemandEstimator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/NetDepartureReplenishDemandEstimator.java index 04753cebeb3..2529a02b8d3 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/NetDepartureReplenishDemandEstimator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/rebalancing/demandestimator/NetDepartureReplenishDemandEstimator.java @@ -43,7 +43,7 @@ public NetDepartureReplenishDemandEstimator(DrtZonalSystem zonalSystem, DrtConfi public void handleEvent(PassengerRequestRejectedEvent event) { if (event.getMode().equals(mode)) { // Ignore rejected request. - potentialDrtTripsMap.remove(event.getPersonId()); + potentialDrtTripsMap.remove(event.getRequestId()); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/AcceptedDrtRequest.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/AcceptedDrtRequest.java index 4b3e594020b..81dd831859a 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/AcceptedDrtRequest.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/AcceptedDrtRequest.java @@ -20,12 +20,13 @@ package org.matsim.contrib.drt.passenger; +import com.google.common.base.MoreObjects; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; -import com.google.common.base.MoreObjects; +import java.util.List; /** * @author Michal Maciejewski (michalm) @@ -98,8 +99,8 @@ public Link getToLink() { return request.getToLink(); } - public Id getPassengerId() { - return request.getPassengerId(); + public List> getPassengerIds() { + return request.getPassengerIds(); } public String getMode() { diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequest.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequest.java index 64a64ee58ff..e1c034f2a92 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequest.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequest.java @@ -20,13 +20,15 @@ package org.matsim.contrib.drt.passenger; +import com.google.common.base.MoreObjects; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.passenger.PassengerRequest; -import com.google.common.base.MoreObjects; +import java.util.*; +import java.util.stream.Collectors; /** * @author michalm @@ -38,7 +40,7 @@ public class DrtRequest implements PassengerRequest { private final double latestStartTime; private final double latestArrivalTime; - private final Id passengerId; + private final List> passengerIds = new ArrayList<>(); private final String mode; private final Link fromLink; @@ -50,7 +52,7 @@ private DrtRequest(Builder builder) { earliestStartTime = builder.earliestStartTime; latestStartTime = builder.latestStartTime; latestArrivalTime = builder.latestArrivalTime; - passengerId = builder.passengerId; + passengerIds.addAll(builder.passengerIds); mode = builder.mode; fromLink = builder.fromLink; toLink = builder.toLink; @@ -67,7 +69,7 @@ public static Builder newBuilder(DrtRequest copy) { builder.earliestStartTime = copy.getEarliestStartTime(); builder.latestStartTime = copy.getLatestStartTime(); builder.latestArrivalTime = copy.getLatestArrivalTime(); - builder.passengerId = copy.getPassengerId(); + builder.passengerIds = new ArrayList<>(copy.getPassengerIds()); builder.mode = copy.getMode(); builder.fromLink = copy.getFromLink(); builder.toLink = copy.getToLink(); @@ -109,8 +111,8 @@ public Link getToLink() { } @Override - public Id getPassengerId() { - return passengerId; + public List> getPassengerIds() { + return List.copyOf(passengerIds); } @Override @@ -118,6 +120,11 @@ public String getMode() { return mode; } + @Override + public int getPassengerCount() { + return passengerIds.size(); + } + @Override public String toString() { return MoreObjects.toStringHelper(this) @@ -126,7 +133,7 @@ public String toString() { .add("earliestStartTime", earliestStartTime) .add("latestStartTime", latestStartTime) .add("latestArrivalTime", latestArrivalTime) - .add("passengerId", passengerId) + .add("passengerIds", passengerIds.stream().map(Object::toString).collect(Collectors.joining(","))) .add("mode", mode) .add("fromLink", fromLink) .add("toLink", toLink) @@ -139,7 +146,7 @@ public static final class Builder { private double earliestStartTime; private double latestStartTime; private double latestArrivalTime; - private Id passengerId; + private List> passengerIds = new ArrayList<>(); private String mode; private Link fromLink; private Link toLink; @@ -172,8 +179,8 @@ public Builder latestArrivalTime(double val) { return this; } - public Builder passengerId(Id val) { - passengerId = val; + public Builder passengerIds(List> val) { + passengerIds = new ArrayList<>(val); return this; } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequestCreator.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequestCreator.java index 029bcf08239..8a2cff7227e 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequestCreator.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtRequestCreator.java @@ -31,6 +31,8 @@ import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator; import org.matsim.core.api.experimental.events.EventsManager; +import java.util.List; + /** * @author michalm */ @@ -45,19 +47,19 @@ public DrtRequestCreator(String mode, EventsManager eventsManager) { } @Override - public DrtRequest createRequest(Id id, Id passengerId, Route route, Link fromLink, Link toLink, - double departureTime, double submissionTime) { + public DrtRequest createRequest(Id id, List> passengerIds, Route route, Link fromLink, Link toLink, + double departureTime, double submissionTime) { DrtRoute drtRoute = (DrtRoute)route; double latestDepartureTime = departureTime + drtRoute.getMaxWaitTime(); double latestArrivalTime = departureTime + drtRoute.getTravelTime().seconds(); eventsManager.processEvent( - new DrtRequestSubmittedEvent(submissionTime, mode, id, passengerId, fromLink.getId(), toLink.getId(), + new DrtRequestSubmittedEvent(submissionTime, mode, id, passengerIds, fromLink.getId(), toLink.getId(), drtRoute.getDirectRideTime(), drtRoute.getDistance(), departureTime, latestDepartureTime, latestArrivalTime)); DrtRequest request = DrtRequest.newBuilder() .id(id) - .passengerId(passengerId) + .passengerIds(passengerIds) .mode(mode) .fromLink(fromLink) .toLink(toLink) diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtStopActivity.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtStopActivity.java index e646c761df1..0f1e36e5706 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtStopActivity.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/DrtStopActivity.java @@ -20,15 +20,17 @@ package org.matsim.contrib.drt.passenger; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Supplier; import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Identifiable; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.passenger.PassengerHandler; import org.matsim.contrib.dvrp.passenger.PassengerPickupActivity; -import org.matsim.contrib.dvrp.schedule.StayTask; import org.matsim.contrib.dynagent.DynAgent; import org.matsim.contrib.dynagent.FirstLastSimStepDynActivity; import org.matsim.core.mobsim.framework.MobsimPassengerAgent; @@ -67,7 +69,7 @@ protected boolean isLastStep(double now) { protected void beforeFirstStep(double now) { // TODO probably we should simulate it more accurately (passenger by passenger, not all at once...) for (var request : dropoffRequests.values()) { - passengerHandler.dropOffPassenger(driver, request.getId(), now); + passengerHandler.dropOffPassengers(driver, request.getId(), now); } } @@ -75,7 +77,7 @@ protected void beforeFirstStep(double now) { protected void simStep(double now) { if (now == endTime.get()) { for (var request : pickupRequests.values()) { - if (passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now)) { + if (passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now)) { passengersPickedUp++; } } @@ -83,23 +85,23 @@ protected void simStep(double now) { } @Override - public void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, double now) { + public void notifyPassengersAreReadyForDeparture(List passengers, double now) { if (now < endTime.get()) { return;// pick up only at the end of stop activity } - var request = getRequestForPassenger(passenger.getId()); - if (passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now)) { + var request = getRequestForPassengers(passengers.stream().map(Identifiable::getId).toList()); + if (passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now)) { passengersPickedUp++; } else { throw new IllegalStateException("The passenger is not on the link or not available for departure!"); } } - private AcceptedDrtRequest getRequestForPassenger(Id passengerId) { + private AcceptedDrtRequest getRequestForPassengers(List> passengerIds) { return pickupRequests.values() .stream() - .filter(r -> passengerId.equals(r.getPassengerId())) + .filter(r -> r.getPassengerIds().size() == passengerIds.size() && r.getPassengerIds().containsAll(passengerIds)) .findAny() .orElseThrow(() -> new IllegalArgumentException("I am waiting for different passengers!")); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/events/DrtRequestSubmittedEvent.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/events/DrtRequestSubmittedEvent.java index 7bf6f23b343..09f04e814d9 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/events/DrtRequestSubmittedEvent.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/passenger/events/DrtRequestSubmittedEvent.java @@ -19,9 +19,6 @@ package org.matsim.contrib.drt.passenger.events; -import java.util.Map; -import java.util.Objects; - import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.GenericEvent; import org.matsim.api.core.v01.network.Link; @@ -29,6 +26,8 @@ import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.passenger.PassengerRequestSubmittedEvent; +import java.util.*; + /** * @author michalm */ @@ -49,10 +48,10 @@ public class DrtRequestSubmittedEvent extends PassengerRequestSubmittedEvent { private final double latestPickupTime; private final double latestDropoffTime; - public DrtRequestSubmittedEvent(double time, String mode, Id requestId, Id personId, + public DrtRequestSubmittedEvent(double time, String mode, Id requestId, List> personIds, Id fromLinkId, Id toLinkId, double unsharedRideTime, double unsharedRideDistance, double earliestDepartureTime, double latestPickupTime, double latestDropoffTime) { - super(time, mode, requestId, personId, fromLinkId, toLinkId); + super(time, mode, requestId, personIds, fromLinkId, toLinkId); this.unsharedRideTime = unsharedRideTime; this.unsharedRideDistance = unsharedRideDistance; this.earliestDepartureTime = earliestDepartureTime; @@ -82,7 +81,7 @@ public final double getUnsharedRideDistance() { public final double getEarliestDepartureTime() { return earliestDepartureTime; } - + public final double getLatestPickupTime() { return latestPickupTime; } @@ -107,7 +106,11 @@ public static DrtRequestSubmittedEvent convert(GenericEvent event) { double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); String mode = Objects.requireNonNull(attributes.get(ATTRIBUTE_MODE)); Id requestId = Id.create(attributes.get(ATTRIBUTE_REQUEST), Request.class); - Id personId = Id.createPersonId(attributes.get(ATTRIBUTE_PERSON)); + String[] personIdsAttribute = attributes.get(ATTRIBUTE_PERSON).split(","); + List> personIds = new ArrayList<>(); + for (String person : personIdsAttribute) { + personIds.add(Id.create(person, Person.class)); + } Id fromLinkId = Id.createLinkId(attributes.get(ATTRIBUTE_FROM_LINK)); Id toLinkId = Id.createLinkId(attributes.get(ATTRIBUTE_TO_LINK)); double unsharedRideTime = Double.parseDouble(attributes.get(ATTRIBUTE_UNSHARED_RIDE_TIME)); @@ -115,7 +118,7 @@ public static DrtRequestSubmittedEvent convert(GenericEvent event) { double earliestDepartureTime = Double.parseDouble(attributes.getOrDefault(ATTRIBUTE_EARLIEST_DEPARTURE_TIME, "NaN")); double latestPickupTime = Double.parseDouble(attributes.getOrDefault(ATTRIBUTE_LATEST_PICKUP_TIME, "NaN")); double latestDropoffTime = Double.parseDouble(attributes.getOrDefault(ATTRIBUTE_LATEST_DROPOFF_TIME, "NaN")); - return new DrtRequestSubmittedEvent(time, mode, requestId, personId, fromLinkId, toLinkId, unsharedRideTime, + return new DrtRequestSubmittedEvent(time, mode, requestId, personIds, fromLinkId, toLinkId, unsharedRideTime, unsharedRideDistance, earliestDepartureTime, latestPickupTime, latestDropoffTime); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java index 9b8ce42dd25..e58f8835dfb 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java @@ -1,5 +1,6 @@ package org.matsim.contrib.drt.prebooking; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -16,7 +17,7 @@ public class PassengerRequestBookedEvent extends AbstractPassengerRequestEvent { public static final String EVENT_TYPE = "PassengerRequest booked"; public PassengerRequestBookedEvent(double time, String mode, Id requestId, Id personId) { - super(time, mode, requestId, personId); + super(time, mode, requestId, List.of(personId)); } @Override diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java index f7cc5cedccb..1fc580715f4 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java @@ -52,15 +52,15 @@ * need to pass a person, a leg with the respective DRT mode, the * requested/expected earliest departure time, and the time at which the request * should be submitted / taken into account in the system. - * + * * Preplanned requests can be submitted any time before the planned * departure/submission times. - * + * * Internally, the prebooking manager will create a request identifier and * return the request once the agent actually wants to depart on the planned * leg. The link between a leg and a request is managed by inserting a special * attribute in the leg instance. - * + * * @author Sebastian Hörl (sebhoerl), IRT SystemX */ public class PrebookingManager implements MobsimEngine, MobsimAfterSimStepListener, AdvanceRequestProvider, @@ -137,19 +137,19 @@ public void handleEvent(PersonStuckEvent event) { // Event handling: We don't want to process events in notifyMobsimAfterSimStep, // so we do it at the next time step - private record RejectionItem(Id requestId, Id personId, String cause) { + private record RejectionItem(Id requestId, List> personIds, String cause) { } private final ConcurrentLinkedQueue rejections = new ConcurrentLinkedQueue<>(); private void processRejection(PassengerRequest request, String cause) { - rejections.add(new RejectionItem(request.getId(), request.getPassengerId(), cause)); + rejections.add(new RejectionItem(request.getId(), request.getPassengerIds(), cause)); } private void flushRejections(double now) { for (RejectionItem item : rejections) { eventsManager.processEvent( - new PassengerRequestRejectedEvent(now, mode, item.requestId, item.personId, item.cause)); + new PassengerRequestRejectedEvent(now, mode, item.requestId, item.personIds, item.cause)); } rejections.clear(); @@ -172,7 +172,7 @@ public void prebook(MobsimAgent person, Leg leg, double earliestDepartureTime) { eventsManager.processEvent(new PassengerRequestBookedEvent(now, mode, requestId, person.getId())); - PassengerRequest request = requestCreator.createRequest(requestId, person.getId(), leg.getRoute(), + PassengerRequest request = requestCreator.createRequest(requestId, List.of(person.getId()), leg.getRoute(), getLink(leg.getRoute().getStartLinkId()), getLink(leg.getRoute().getEndLinkId()), earliestDepartureTime, now); @@ -394,10 +394,10 @@ private void processRejections(double now) { // Stuck private void processStuckAgents(double now) { - bookingQueue.removeIf(request -> stuckPersonsIds.contains(request.getPassengerId())); + bookingQueue.removeIf(request -> stuckPersonsIds.containsAll(request.getPassengerIds())); for (RequestItem item : requests.values()) { - if (stuckPersonsIds.contains(item.request.getPassengerId())) { + if (stuckPersonsIds.containsAll(item.request.getPassengerIds())) { cancel(item.request.getId()); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingStopActivity.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingStopActivity.java index 849ca5bde14..3fef9b0c932 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingStopActivity.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingStopActivity.java @@ -1,12 +1,14 @@ package org.matsim.contrib.drt.prebooking; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.function.Supplier; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.IdMap; +import org.matsim.api.core.v01.Identifiable; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.drt.passenger.AcceptedDrtRequest; import org.matsim.contrib.drt.prebooking.abandon.AbandonVoter; @@ -41,7 +43,7 @@ public class PrebookingStopActivity extends FirstLastSimStepDynActivity implemen private final PrebookingManager prebookingManager; private final PassengerHandler passengerHandler; - + private final PassengerStopDurationProvider stopDurationProvider; private final AbandonVoter abandonVoter; @@ -91,7 +93,7 @@ private void processDropoffRequests(double now) { var entry = iterator.next(); if (entry.getValue() <= now) { // Request should leave now - passengerHandler.dropOffPassenger(driver, entry.getKey(), now); + passengerHandler.dropOffPassengers(driver, entry.getKey(), now); prebookingManager.notifyDropoff(entry.getKey()); iterator.remove(); } @@ -108,7 +110,7 @@ private boolean updatePickupRequests(double now) { // this is a new request that has been added after the activity has been created // or that had not arrived yet - if (passengerHandler.notifyWaitForPassenger(this, this.driver, request.getId())) { + if (passengerHandler.notifyWaitForPassengers(this, this.driver, request.getId())) { // agent starts to enter queuePickup(request, now); } else if (now > request.getEarliestStartTime()) { @@ -126,7 +128,7 @@ private boolean updatePickupRequests(double now) { if (entry.getValue() <= now) { // let agent enter now - Verify.verify(passengerHandler.tryPickUpPassenger(this, driver, entry.getKey(), now)); + Verify.verify(passengerHandler.tryPickUpPassengers(this, driver, entry.getKey(), now)); enteredRequests.add(entry.getKey()); enterIterator.remove(); } @@ -147,13 +149,17 @@ protected void simStep(double now) { } @Override - public void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, double now) { - var request = getRequestForPassenger(passenger.getId()); + public void notifyPassengersAreReadyForDeparture(List passengers, double now) { + var request = getRequestForPassengers(passengers.stream().map(Identifiable::getId).toList()); queuePickup(request, now); } - private AcceptedDrtRequest getRequestForPassenger(Id passengerId) { - return pickupRequests.values().stream().filter(r -> passengerId.equals(r.getPassengerId())).findAny() + + private AcceptedDrtRequest getRequestForPassengers(List> passengerIds) { + return pickupRequests.values() + .stream() + .filter(r -> r.getPassengerIds().size() == passengerIds.size() && r.getPassengerIds().containsAll(passengerIds)) + .findAny() .orElseThrow(() -> new IllegalArgumentException("I am waiting for different passengers!")); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisHandler.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisHandler.java index e618476ceb0..a160ac5fce4 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisHandler.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisHandler.java @@ -89,7 +89,7 @@ public List getRecords() { List records = new LinkedList<>(); for (Sequence sequence : sequences) { - records.add(new RequestRecord(sequence.booked.getRequestId(), sequence.booked.getPersonId(), + records.add(new RequestRecord(sequence.booked.getRequestId(), sequence.booked.getPersonIds(), sequence.submitted != null ? sequence.submitted.getTime() : null, sequence.scheduled != null ? sequence.scheduled.getTime() : null, sequence.rejected != null ? sequence.rejected.getTime() : null, @@ -100,7 +100,7 @@ public List getRecords() { return records; } - public record RequestRecord(Id requestId, Id personId, Double submissionTime, Double scheduledTime, + public record RequestRecord(Id requestId, List> personIds, Double submissionTime, Double scheduledTime, Double rejectedTime, Double departureTime, String rejectedReason) { } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisWriter.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisWriter.java index a641eb6e281..5127841f292 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisWriter.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/analysis/PrebookingAnalysisWriter.java @@ -3,6 +3,7 @@ import java.io.BufferedWriter; import java.io.IOException; import java.util.List; +import java.util.stream.Collectors; import org.matsim.core.utils.io.IOUtils; @@ -30,7 +31,7 @@ public void write(List records) { for (var record : records) { writer.write(String.join(",", new String[] { // record.requestId().toString(), // - record.personId().toString(), // + record.personIds().stream().map(Object::toString).collect(Collectors.joining("-")), // record.submissionTime() == null ? "" : String.valueOf(record.submissionTime()), // record.scheduledTime() == null ? "" : String.valueOf(record.scheduledTime()), // record.rejectedTime() == null ? "" : String.valueOf(record.rejectedTime()), // diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtSpeedUp.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtSpeedUp.java index 978ddbc7417..5ad8dd776af 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtSpeedUp.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/speedup/DrtSpeedUp.java @@ -27,8 +27,10 @@ import org.apache.commons.math3.stat.regression.SimpleRegression; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.common.util.DistanceUtils; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; import org.matsim.contrib.drt.passenger.events.DrtRequestSubmittedEvent; @@ -192,18 +194,24 @@ private SimulatedTripStats computeSimulatedTripStats() { continue;//skip incomplete sequences } DrtRequestSubmittedEvent submittedEvent = sequence.getSubmitted(); - Link depLink = network.getLinks().get(submittedEvent.getFromLinkId()); Link arrLink = network.getLinks().get(submittedEvent.getToLinkId()); double beelineDistance = DistanceUtils.calculateDistance(depLink.getToNode(), arrLink.getToNode()); - double pickupTime = sequence.getPickedUp().get().getTime(); - double waitTime = pickupTime - sequence.getSubmitted().getTime(); - double rideTime = sequence.getDroppedOff().get().getTime() - pickupTime; - - //TODO I would map unshared_ride_time to rideTime -- should be more precise - meanInVehicleBeelineSpeed.increment(beelineDistance / rideTime); - meanWaitTime.increment(waitTime); + for (Id personId : submittedEvent.getPersonIds()) { + if(sequence.getPersonEvents().containsKey(personId)) { + DrtEventSequenceCollector.EventSequence.PersonEvents personEvents = sequence.getPersonEvents().get(personId); + if(personEvents.getPickedUp().isPresent() && personEvents.getDroppedOff().isPresent()) { + double pickupTime = personEvents.getPickedUp().get().getTime(); + double waitTime = pickupTime - sequence.getSubmitted().getTime(); + double rideTime = personEvents.getDroppedOff().get().getTime() - pickupTime; + + //TODO I would map unshared_ride_time to rideTime -- should be more precise + meanInVehicleBeelineSpeed.increment(beelineDistance / rideTime); + meanWaitTime.increment(waitTime); + } + } + } } int count = (int)meanWaitTime.getN(); diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/fare/DrtFareHandlerTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/fare/DrtFareHandlerTest.java index 2751caf417e..e586e1a9dda 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/fare/DrtFareHandlerTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/fare/DrtFareHandlerTest.java @@ -31,6 +31,8 @@ import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEvent; import org.matsim.core.events.ParallelEventsManager; +import java.util.List; + /** * @author jbischoff */ @@ -69,7 +71,7 @@ public void reset(int iteration) { var personId = Id.createPersonId("p1"); { var requestId = Id.create(0, Request.class); - events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId, personId, Id.createLinkId("12"), + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId, List.of(personId), Id.createLinkId("12"), Id.createLinkId("23"), 240, 1000, 0.0, 0.0, 0.0)); events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId, personId, null)); events.flush(); @@ -80,7 +82,7 @@ public void reset(int iteration) { { // test minFarePerTrip var requestId = Id.create(1, Request.class); - events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId, personId, Id.createLinkId("45"), + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId, List.of(personId), Id.createLinkId("45"), Id.createLinkId("56"), 24, 100, 0.0, 0.0, 0.0)); events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId, personId, null)); events.finishProcessing(); diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserterTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserterTest.java index ee1e2efc29a..5730c59ea46 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserterTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/DefaultUnplannedRequestInserterTest.java @@ -29,10 +29,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Optional; +import java.util.*; import org.apache.commons.lang3.mutable.MutableInt; import org.junit.Rule; @@ -119,7 +116,7 @@ public void notScheduled_rejected() { PassengerRequestRejectedEvent.class); verify(eventsManager, times(1)).processEvent(captor.capture()); assertThat(captor.getValue()).isEqualToComparingFieldByField( - new PassengerRequestRejectedEvent(now, mode, request1.getId(), request1.getPassengerId(), + new PassengerRequestRejectedEvent(now, mode, request1.getId(), request1.getPassengerIds(), NO_INSERTION_FOUND_CAUSE)); } @@ -248,7 +245,7 @@ public void acceptedRequest() { PassengerRequestScheduledEvent.class); verify(eventsManager, times(1)).processEvent(captor.capture()); assertThat(captor.getValue()).isEqualToComparingFieldByField( - new PassengerRequestScheduledEvent(now, mode, request1.getId(), request1.getPassengerId(), + new PassengerRequestScheduledEvent(now, mode, request1.getId(), request1.getPassengerIds(), vehicle1.getId(), pickupEndTime, dropoffBeginTime)); //vehicle entry was created twice: @@ -276,7 +273,7 @@ private DvrpVehicle vehicle(String vehicleId) { private DrtRequest request(String id, String fromLinkId, String toLinkId) { return DrtRequest.newBuilder() .id(Id.create(id, Request.class)) - .passengerId(Id.createPersonId(id)) + .passengerIds(List.of(Id.createPersonId(id))) .fromLink(link(fromLinkId)) .toLink(link(toLinkId)) .mode(mode) diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGeneratorTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGeneratorTest.java index c433ced72db..18bb57661f2 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGeneratorTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/optimizer/insertion/InsertionGeneratorTest.java @@ -27,6 +27,7 @@ import java.util.Collections; import java.util.List; +import com.google.common.collect.Sets; import org.junit.Test; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; @@ -64,7 +65,22 @@ public class InsertionGeneratorTest { private final Link fromLink = link("from"); private final Link toLink = link("to"); - private final DrtRequest drtRequest = DrtRequest.newBuilder().fromLink(fromLink).toLink(toLink).build(); + private final DrtRequest drtRequest = DrtRequest.newBuilder().fromLink(fromLink).toLink(toLink).passengerIds(List.of(Id.createPersonId("person"))).build(); + + private final DrtRequest drtRequest2Pax = DrtRequest.newBuilder().fromLink(fromLink).toLink(toLink).passengerIds( + List.of( + Id.createPersonId("person1"), + Id.createPersonId("person2") + )).build(); + + private final DrtRequest drtRequest5Pax = DrtRequest.newBuilder().fromLink(fromLink).toLink(toLink).passengerIds( + List.of( + Id.createPersonId("person1"), + Id.createPersonId("person2"), + Id.createPersonId("person3"), + Id.createPersonId("person4"), + Id.createPersonId("person5") + )).build(); private final DrtRequest prebookedRequest = DrtRequest.newBuilder().fromLink(fromLink).toLink(toLink).earliestStartTime(100).build(); private final Link depotLink = link("depot"); @@ -211,9 +227,9 @@ public void startEmpty_twoStops_notFullBetweenStops_tightSlackTimes() { double[] slackTimes = { 0, 0, // impossible insertions: 00, 01, 02 (pickup at 0 is not possible) 500, // additional impossible insertions: 11 (too long total detour); however 12 is possible 1000 }; // 22 is possible - + List precedingStayTimes = Arrays.asList(0.0, 0.0); - + VehicleEntry entry = new VehicleEntry(vehicle, start, ImmutableList.of(stop0, stop1), slackTimes, precedingStayTimes, 0); var insertions = new ArrayList(); @@ -348,7 +364,7 @@ public void noDetourForDropoff_vehicleOutgoingFullAfterDropoff_insertionPossible //pickup after stop 0 new Insertion(drtRequest, entry, 2, 2)); } - + @Test public void startEmpty_prebookedRequest() { Waypoint.Start start = new Waypoint.Start(null, link("start"), 0, 0); @@ -395,6 +411,39 @@ public void startEmpty_prebookedRequest_afterAlreadyPrebookedOtherRequest() { new Insertion(prebookedRequest, entry, 2, 2)); } + + @Test + public void startEmpty_smallGroup() { + Waypoint.Start start = new Waypoint.Start(null, link("start"), 0, 0); //empty + VehicleEntry entry = entry(start); + assertInsertionsOnly(drtRequest2Pax, entry, + //pickup after start + new Insertion(drtRequest2Pax, entry, 0, 0)); + } + + @Test + public void startEmpty_groupExceedsCapacity() { + Waypoint.Start start = new Waypoint.Start(null, link("start"), 0, 0); //empty + VehicleEntry entry = entry(start); + assertInsertionsOnly(drtRequest5Pax, entry + //no insertion possible + ); + } + + @Test + public void startEmpty_twoStops_groupExceedsCapacityAtFirstStop() { + Waypoint.Start start = new Waypoint.Start(null, link("start"), 0, 0); //empty + Waypoint.Stop stop0 = stop(0, toLink, 3);//dropoff 1 pax + Waypoint.Stop stop1 = stop(0, link("stop1"), 0);//dropoff 1 pax + VehicleEntry entry = entry(start, stop0, stop1); + assertInsertionsOnly(drtRequest2Pax, entry, + //pickup after start: + new Insertion(drtRequest2Pax, entry, 0, 1), + //pickup after stop 1 + new Insertion(drtRequest2Pax, entry, 2, 2) + ); + } + private Link link(String id) { return new FakeLink(Id.createLinkId(id)); } diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PersonStuckPrebookingTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PersonStuckPrebookingTest.java index 92ff2b4ebc0..11d3951b632 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PersonStuckPrebookingTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PersonStuckPrebookingTest.java @@ -76,7 +76,7 @@ public void cancelTest() { /* * Agent personA is performing three drt legs during the day. Agent personB does * exactly the same in parallel, both prebook there requests. - * + * * We cancel the first request of personA. We check that the other reservations * are automatically rejected as soon as the person is stuck. */ @@ -198,14 +198,14 @@ public void handleEvent(PassengerDroppedOffEvent event) { @Override public void handleEvent(PassengerRequestSubmittedEvent event) { - if (event.getPersonId().equals(personId)) { + if (event.getPersonIds().contains(personId)) { submittedCount++; } } @Override public void handleEvent(PassengerRequestRejectedEvent event) { - if (event.getPersonId().equals(personId)) { + if (event.getPersonIds().contains(personId)) { rejectedCount++; } } diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTestEnvironment.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTestEnvironment.java index d8e5f1c8624..8e1f6394388 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTestEnvironment.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTestEnvironment.java @@ -4,6 +4,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.Pair; import org.matsim.api.core.v01.Coord; @@ -381,16 +382,17 @@ private class RequestListener implements DrtRequestSubmittedEventHandler, Passen PassengerDroppedOffEventHandler, PassengerRequestRejectedEventHandler { @Override public void handleEvent(DrtRequestSubmittedEvent event) { - requestInfo.computeIfAbsent(event.getPersonId().toString(), id -> new RequestInfo()).submissionTime = event + String ids = event.getPersonIds().stream().map(Object::toString).collect(Collectors.joining("-")); + requestInfo.computeIfAbsent(ids, id -> new RequestInfo()).submissionTime = event .getTime(); - requestInfo.computeIfAbsent(event.getPersonId().toString(), id -> new RequestInfo()).submissionTimes + requestInfo.computeIfAbsent(ids, id -> new RequestInfo()).submissionTimes .add(event.getTime()); } @Override public void handleEvent(PassengerRequestRejectedEvent event) { - requestInfo.computeIfAbsent(event.getPersonId().toString(), id -> new RequestInfo()).rejected = true; + requestInfo.computeIfAbsent(event.getPersonIds().stream().map(Object::toString).collect(Collectors.joining("-")), id -> new RequestInfo()).rejected = true; } @Override diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/speedup/DrtSpeedUpTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/speedup/DrtSpeedUpTest.java index 0cc447f1237..af92306b69e 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/speedup/DrtSpeedUpTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/speedup/DrtSpeedUpTest.java @@ -27,7 +27,9 @@ import static org.mockito.Mockito.when; import java.util.Arrays; +import java.util.Collections; import java.util.List; +import java.util.Map; import org.junit.Test; import org.matsim.api.core.v01.Coord; @@ -38,6 +40,7 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.network.Node; +import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.common.util.DistanceUtils; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector.EventSequence; @@ -270,7 +273,9 @@ private void updateRequestAnalyser(EventSequence... eventSequences) { private EventSequence eventSequence(String id, double submittedTime, double waitTime, double inVehicleSpeed) { var requestId = Id.create(id, Request.class); - var submittedEvent = new DrtRequestSubmittedEvent(submittedTime, MODE, requestId, null, linkAB.getId(), + var personId = Id.create(id, Person.class); + + var submittedEvent = new DrtRequestSubmittedEvent(submittedTime, MODE, requestId, List.of(personId), linkAB.getId(), linkBC.getId(), Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN); var pickupEvent = new PassengerPickedUpEvent(submittedTime + waitTime, MODE, requestId, null, null); double rideTime = DistanceUtils.calculateDistance(linkBC, linkAB) / inVehicleSpeed; @@ -280,7 +285,8 @@ private EventSequence eventSequence(String id, double submittedTime, double wait MODE, requestId.toString()); var departureEvent = mock(PersonDepartureEvent.class); - return new EventSequence(departureEvent, submittedEvent, mock(PassengerRequestScheduledEvent.class), + + return new EventSequence(Id.createPersonId("r1"), departureEvent, submittedEvent, mock(PassengerRequestScheduledEvent.class), pickupEvent, dropoffEvent, List.of(drtFare)); } diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/util/DrtEventsReadersTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/util/DrtEventsReadersTest.java index afc39c2e6de..27fcefc3b52 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/util/DrtEventsReadersTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/util/DrtEventsReadersTest.java @@ -25,6 +25,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.junit.Test; @@ -64,7 +65,7 @@ public class DrtEventsReadersTest { //standard dvrp events are tested in DvrpEventsReadersTest private final List drtEvents = List.of( - new DrtRequestSubmittedEvent(0, mode, request, person, link1, link2, 111, 222, 0.0, 412.0, 512.0),// + new DrtRequestSubmittedEvent(0, mode, request, List.of(person), link1, link2, 111, 222, 0.0, 412.0, 512.0),// taskStarted(10, DrtDriveTask.TYPE, 0, link1),// taskEnded(30, DefaultDrtStopTask.TYPE, 1, link2), // taskStarted(50, DrtStayTask.TYPE, 2, link1),// diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiOptimizer.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiOptimizer.java index fb23bac474d..ee5f38811fe 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiOptimizer.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiOptimizer.java @@ -133,7 +133,7 @@ public void requestSubmitted(Request request) { eventsManager.processEvent( new PassengerRequestScheduledEvent(timer.getTimeOfDay(), TransportMode.taxi, request.getId(), - req.getPassengerId(), vehicle.getId(), t1, t4)); + req.getPassengerIds(), vehicle.getId(), t1, t4)); } @Override diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiRequest.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiRequest.java index a1d86022e2a..de9f9f0936b 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiRequest.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/examples/onetaxi/OneTaxiRequest.java @@ -28,6 +28,8 @@ import org.matsim.contrib.dvrp.passenger.PassengerRequest; import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator; +import java.util.*; + /** * @author michalm */ @@ -36,18 +38,18 @@ public final class OneTaxiRequest implements PassengerRequest { private final double submissionTime; private final double earliestStartTime; - private final Id passengerId; + private final List> passengerIds = new ArrayList<>(); private final String mode; private final Link fromLink; private final Link toLink; - public OneTaxiRequest(Id id, Id passengerId, String mode, Link fromLink, Link toLink, - double departureTime, double submissionTime) { + public OneTaxiRequest(Id id, Collection> passengerIds, String mode, Link fromLink, Link toLink, + double departureTime, double submissionTime) { this.id = id; this.submissionTime = submissionTime; this.earliestStartTime = departureTime; - this.passengerId = passengerId; + this.passengerIds.addAll(passengerIds); this.mode = mode; this.fromLink = fromLink; this.toLink = toLink; @@ -79,8 +81,8 @@ public Link getToLink() { } @Override - public Id getPassengerId() { - return passengerId; + public List> getPassengerIds() { + return List.copyOf(passengerIds); } @Override @@ -88,11 +90,16 @@ public String getMode() { return mode; } + @Override + public int getPassengerCount() { + return passengerIds.size(); + } + public static final class OneTaxiRequestCreator implements PassengerRequestCreator { @Override - public OneTaxiRequest createRequest(Id id, Id passengerId, Route route, Link fromLink, + public OneTaxiRequest createRequest(Id id, List> passengerIds, Route route, Link fromLink, Link toLink, double departureTime, double submissionTime) { - return new OneTaxiRequest(id, passengerId, TransportMode.taxi, fromLink, toLink, departureTime, + return new OneTaxiRequest(id, passengerIds, TransportMode.taxi, fromLink, toLink, departureTime, submissionTime); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/AbstractPassengerRequestEvent.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/AbstractPassengerRequestEvent.java index ca269d5975a..529f2bfc5ba 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/AbstractPassengerRequestEvent.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/AbstractPassengerRequestEvent.java @@ -20,13 +20,15 @@ package org.matsim.contrib.dvrp.passenger; -import java.util.Map; - import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.Event; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; -import org.matsim.api.core.v01.events.HasPersonId; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; /** * This class is designed for inheritance without overriding. @@ -35,19 +37,20 @@ * * @author Michal Maciejewski (michalm) */ -public abstract class AbstractPassengerRequestEvent extends Event implements HasPersonId { +public abstract class AbstractPassengerRequestEvent extends Event { public static final String ATTRIBUTE_MODE = "mode"; public static final String ATTRIBUTE_REQUEST = "request"; + public static final String ATTRIBUTE_PERSON = "person"; private final String mode; private final Id requestId; - private final Id personId; + private final List> personIds; - protected AbstractPassengerRequestEvent(double time, String mode, Id requestId, Id personId) { + protected AbstractPassengerRequestEvent(double time, String mode, Id requestId, List> personIds) { super(time); this.mode = mode; this.requestId = requestId; - this.personId = personId; + this.personIds = personIds; } public final String getMode() { @@ -62,11 +65,10 @@ public final Id getRequestId() { } /** - * @return id of the passenger (person) + * @return ids of the passengers (persons) */ - @Override - public final Id getPersonId() { - return personId; + public final List> getPersonIds() { + return List.copyOf(personIds); } @Override @@ -74,6 +76,7 @@ public Map getAttributes() { Map attr = super.getAttributes(); attr.put(ATTRIBUTE_MODE, mode); attr.put(ATTRIBUTE_REQUEST, requestId + ""); + attr.put(ATTRIBUTE_PERSON, personIds.stream().map(Object::toString).collect(Collectors.joining(","))); return attr; } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java index 8887040be7b..6a19b93d470 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java @@ -19,12 +19,10 @@ package org.matsim.contrib.dvrp.passenger; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Queue; +import java.util.*; import java.util.concurrent.ConcurrentLinkedQueue; +import com.google.common.base.Verify; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; @@ -67,7 +65,7 @@ public final class DefaultPassengerEngine implements PassengerEngine, PassengerR private InternalInterface internalInterface; //accessed in doSimStep() and handleDeparture() (no need to sync) - private final Map, MobsimPassengerAgent> activePassengers = new HashMap<>(); + private final Map, List> activePassengers = new HashMap<>(); // holds vehicle stop activities for requests that have not arrived at departure point yet private final Map, PassengerPickupActivity> waitingForPassenger = new HashMap<>(); @@ -116,12 +114,15 @@ public void doSimStep(double time) { break; } - MobsimPassengerAgent passenger = activePassengers.remove(event.getRequestId()); - if (passenger != null) { + List passengers = activePassengers.remove(event.getRequestId()); + + if (passengers != null) { // not much else can be done for immediate requests // set the passenger agent to abort - the event will be thrown by the QSim - passenger.setStateToAbort(time); - internalInterface.arrangeNextAgentState(passenger); + for (MobsimPassengerAgent passenger: passengers) { + passenger.setStateToAbort(mobsimTimer.getTimeOfDay()); + internalInterface.arrangeNextAgentState(passenger); + } iterator.remove(); } } @@ -148,29 +149,30 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id fromLinkI if (request == null) { // immediate request Route route = ((Leg)((PlanAgent)passenger).getCurrentPlanElement()).getRoute(); - request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), passenger.getId(), route, getLink(fromLinkId), - getLink(toLinkId), now, now); + request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), + List.of(passenger.getId()), route, getLink(fromLinkId), getLink(toLinkId), now, now); // must come before validateAndSubmitRequest (to come before rejection event) - eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), request.getPassengerId())); - activePassengers.put(request.getId(), passenger); + eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), List.of(passenger.getId()))); + activePassengers.put(request.getId(), List.of(passenger)); - validateAndSubmitRequest(passenger, request, now); + validateAndSubmitRequest(List.of(passenger), request, now); } else { // advance request - eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), request.getPassengerId())); - activePassengers.put(request.getId(), passenger); + eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), List.of(passenger.getId()))); + activePassengers.put(request.getId(), List.of(passenger)); PassengerPickupActivity pickupActivity = waitingForPassenger.remove(request.getId()); if (pickupActivity != null) { // the vehicle is already waiting for the request, notify it - pickupActivity.notifyPassengerIsReadyForDeparture(passenger, now); + pickupActivity.notifyPassengersAreReadyForDeparture(List.of(passenger), now); } } return true; } - private void validateAndSubmitRequest(MobsimPassengerAgent passenger, PassengerRequest request, double now) { + private void validateAndSubmitRequest(List passengers, PassengerRequest request, double now) { + activePassengers.put(request.getId(), passengers); if (internalPassengerHandling.validateRequest(request, requestValidator, now)) { //need to synchronise to address cases where requestSubmitted() may: // - be called from outside DepartureHandlers @@ -208,7 +210,7 @@ private Link getLink(Id linkId) { * notified once the agent arrives for departure. */ @Override - public boolean notifyWaitForPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId) { + public boolean notifyWaitForPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId) { if (!activePassengers.containsKey(requestId)) { waitingForPassenger.put(requestId, pickupActivity); return false; @@ -218,13 +220,17 @@ public boolean notifyWaitForPassenger(PassengerPickupActivity pickupActivity, Mo } @Override - public boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, double now) { - return internalPassengerHandling.tryPickUpPassenger(driver, activePassengers.get(requestId), requestId, now); + public boolean tryPickUpPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, + Id requestId, double now) { + boolean pickedUp = internalPassengerHandling.tryPickUpPassengers(driver, activePassengers.get(requestId), + requestId, now); + Verify.verify(pickedUp, "Not possible without prebooking"); + return pickedUp; } @Override - public void dropOffPassenger(MobsimDriverAgent driver, Id requestId, double now) { - internalPassengerHandling.dropOffPassenger(driver, activePassengers.remove(requestId), requestId, now); + public void dropOffPassengers(MobsimDriverAgent driver, Id requestId, double now) { + internalPassengerHandling.dropOffPassengers(driver, activePassengers.remove(requestId), requestId, now); } @Override diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngine.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngine.java new file mode 100644 index 00000000000..a5c2f9e9c84 --- /dev/null +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngine.java @@ -0,0 +1,231 @@ +package org.matsim.contrib.dvrp.passenger; + +import com.google.common.base.Preconditions; +import com.google.common.base.Verify; +import jakarta.inject.Inject; +import jakarta.inject.Provider; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Identifiable; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Route; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; +import org.matsim.contrib.dvrp.run.DvrpModes; +import org.matsim.core.api.experimental.events.EventsManager; +import org.matsim.core.gbl.Gbl; +import org.matsim.core.mobsim.framework.*; +import org.matsim.core.mobsim.qsim.InternalInterface; +import org.matsim.core.modal.ModalProviders; + +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.stream.Collectors; + +public class GroupPassengerEngine implements PassengerEngine, PassengerRequestRejectedEventHandler { + + private final String mode; + private final MobsimTimer mobsimTimer; + + private final EventsManager eventsManager; + + private final PassengerRequestCreator requestCreator; + private final VrpOptimizer optimizer; + private final Network network; + private final PassengerRequestValidator requestValidator; + + private final InternalPassengerHandling internalPassengerHandling; + + private InternalInterface internalInterface; + + + //accessed in doSimStep() and handleDeparture() (no need to sync) + private final Map, List> activePassengers = new HashMap<>(); + + // holds vehicle stop activities for requests that have not arrived at departure point yet + private final Map, PassengerPickupActivity> waitingForPassenger = new HashMap<>(); + + //accessed in doSimStep() and handleEvent() (potential data races) + private final Queue rejectedRequestsEvents = new ConcurrentLinkedQueue<>(); + + private final Set currentDepartures = new LinkedHashSet<>(); + + private final PassengerGroupIdentifier groupIdentifier; + + GroupPassengerEngine(String mode, EventsManager eventsManager, MobsimTimer mobsimTimer, PassengerRequestCreator requestCreator, VrpOptimizer optimizer, Network network, PassengerRequestValidator requestValidator, PassengerGroupIdentifier groupIdentifier) { + this.mode = mode; + this.eventsManager = eventsManager; + this.mobsimTimer = mobsimTimer; + this.requestCreator = requestCreator; + this.optimizer = optimizer; + this.network = network; + this.requestValidator = requestValidator; + this.groupIdentifier = groupIdentifier; + + internalPassengerHandling = new InternalPassengerHandling(mode, eventsManager); + } + + @Override + public void setInternalInterface(InternalInterface internalInterface) { + this.internalInterface = internalInterface; + internalPassengerHandling.setInternalInterface(internalInterface); + } + + @Override + public void onPrepareSim() { + } + + @Override + public void doSimStep(double time) { + handleDepartures(time); + while (!rejectedRequestsEvents.isEmpty()) { + List passengers = activePassengers.remove(rejectedRequestsEvents.poll().getRequestId()); + //not much else can be done for immediate requests + //set the passenger agent to abort - the event will be thrown by the QSim + for (MobsimPassengerAgent passenger : passengers) { + passenger.setStateToAbort(mobsimTimer.getTimeOfDay()); + internalInterface.arrangeNextAgentState(passenger); + } + } + } + + @Override + public void afterSim() { + } + + @Override + public boolean handleDeparture(double now, MobsimAgent agent, Id fromLinkId) { + + if (!agent.getMode().equals(mode)) { + return false; + } + + MobsimPassengerAgent passenger = (MobsimPassengerAgent)agent; + internalInterface.registerAdditionalAgentOnLink(passenger); + currentDepartures.add(passenger); + return true; + } + + private void handleDepartures(double now) { + Map, List> agentGroups = + currentDepartures.stream().collect( + Collectors.groupingBy( + groupIdentifier, + Collectors.collectingAndThen(Collectors.toList(), list -> { + list.sort(Comparator.comparing(MobsimPassengerAgent::getId)); + return list; + }))); + + for (List group : agentGroups.values()) { + + Iterator iterator = group.iterator(); + MobsimAgent firstAgent = iterator.next(); + MobsimPassengerAgent passenger = (MobsimPassengerAgent) firstAgent; + + Id toLinkId = firstAgent.getDestinationLinkId(); + + while (iterator.hasNext()) { + MobsimAgent next = iterator.next(); + Gbl.assertIf(firstAgent.getCurrentLinkId().equals(next.getCurrentLinkId())); + Gbl.assertIf(toLinkId.equals(next.getDestinationLinkId())); + } + + Route route = ((Leg)((PlanAgent)passenger).getCurrentPlanElement()).getRoute(); + + PassengerRequest request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), + group.stream().map(Identifiable::getId).toList(), route, + getLink(firstAgent.getCurrentLinkId()), getLink(toLinkId), + now, now); + + + // must come before validateAndSubmitRequest (to come before rejection event) + eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), group.stream().map(Identifiable::getId).toList())); + + validateAndSubmitRequest(group, request, mobsimTimer.getTimeOfDay()); + } + currentDepartures.clear(); + } + + private void validateAndSubmitRequest(List passengers, PassengerRequest request, double now) { + activePassengers.put(request.getId(), passengers); + if (internalPassengerHandling.validateRequest(request, requestValidator, now)) { + //need to synchronise to address cases where requestSubmitted() may: + // - be called from outside DepartureHandlers + // - interfere with VrpOptimizer.nextTask() + // - impact VrpAgentLogic.computeNextAction() + synchronized (optimizer) { + //optimizer can also reject request if cannot handle it + // (async operation, notification comes via the events channel) + optimizer.requestSubmitted(request); + } + } + } + + private Link getLink(Id linkId) { + return Preconditions.checkNotNull(network.getLinks().get(linkId), "Link id=%s does not exist in network for mode %s. Agent departs from a link that does not belong to that network?", linkId, mode); + } + + /** + * There are two ways of interacting with the PassengerEngine: + *

+ * - (1) The stop activity tries to pick up a passenger and receives whether the + * pickup succeeded or not (see tryPickUpPassenger). In the classic + * implementation, the vehicle only calls tryPickUpPassenger at the time when it + * actually wants to pick up the person (at the end of the activity). It may + * happen that the person is not present yet. In that case, the pickup request + * is saved and notifyPassengerReady is called on the stop activity upen + * departure of the agent. + *

+ * - (2) If pickup and dropoff times are handled more flexibly by the stop + * activity, it might want to detect whether an agent is ready to be picked up, + * then start an "interaction time" and only after perform the actual pickup. + * For that purpose, we have queryPickUpPassenger, which indicates whether the + * agent is already there, and, if not, makes sure that the stop activity is + * notified once the agent arrives for departure. + */ + @Override + public boolean notifyWaitForPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId) { + + if (!activePassengers.containsKey(requestId)) { + waitingForPassenger.put(requestId, pickupActivity); + return false; + } + + return true; + } + + @Override + public boolean tryPickUpPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, double now) { + boolean pickedUp = internalPassengerHandling.tryPickUpPassengers(driver, activePassengers.get(requestId), requestId, now); + Verify.verify(pickedUp, "Not possible without prebooking"); + return pickedUp; + } + + @Override + public void dropOffPassengers(MobsimDriverAgent driver, Id requestId, double now) { + internalPassengerHandling.dropOffPassengers(driver, activePassengers.remove(requestId), requestId, now); + } + + @Override + public void handleEvent(PassengerRequestRejectedEvent event) { + if (event.getMode().equals(mode)) { + rejectedRequestsEvents.add(event); + } + } + + public static Provider createProvider(String mode) { + return new ModalProviders.AbstractProvider<>(mode, DvrpModes::mode) { + @Inject + private EventsManager eventsManager; + + @Inject + private MobsimTimer mobsimTimer; + + @Override + public GroupPassengerEngine get() { + return new GroupPassengerEngine(getMode(), eventsManager, mobsimTimer, getModalInstance(PassengerRequestCreator.class), getModalInstance(VrpOptimizer.class), getModalInstance(Network.class), getModalInstance(PassengerRequestValidator.class), getModalInstance(PassengerGroupIdentifier.class)); + } + }; + } +} diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/InternalPassengerHandling.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/InternalPassengerHandling.java index 67da85c61a7..c36393d16f4 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/InternalPassengerHandling.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/InternalPassengerHandling.java @@ -22,6 +22,7 @@ import static java.lang.String.format; +import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; @@ -70,39 +71,50 @@ boolean validateRequest(PassengerRequest request, PassengerRequestValidator requ LOGGER.warn(format("Request: %s of mode: %s will not be served. Agent will get stuck. Cause: %s", request.getId(), mode, cause)); eventsManager.processEvent( - new PassengerRequestRejectedEvent(now, mode, request.getId(), request.getPassengerId(), cause)); + new PassengerRequestRejectedEvent(now, mode, request.getId(), request.getPassengerIds(), cause)); } return violations.isEmpty(); } - boolean tryPickUpPassenger(MobsimDriverAgent driver, MobsimPassengerAgent passenger, Id requestId, + boolean tryPickUpPassengers(MobsimDriverAgent driver, List passengers, Id requestId, double now) { - if (internalInterface.unregisterAdditionalAgentOnLink(passenger.getId(), driver.getCurrentLinkId()) == null) { - //only possible with prebooking - return false; + + //ensure for every passenger first + for (MobsimPassengerAgent passenger : passengers) { + if (internalInterface.unregisterAdditionalAgentOnLink(passenger.getId(), driver.getCurrentLinkId()) == null) { + //only possible with prebooking + return false; + } } - MobsimVehicle mobVehicle = driver.getVehicle(); - mobVehicle.addPassenger(passenger); - passenger.setVehicle(mobVehicle); + for (MobsimPassengerAgent passenger : passengers) { + + MobsimVehicle mobVehicle = driver.getVehicle(); + mobVehicle.addPassenger(passenger); + passenger.setVehicle(mobVehicle); + + Id vehicleId = Id.create(mobVehicle.getId(), DvrpVehicle.class); + eventsManager.processEvent(new PersonEntersVehicleEvent(now, passenger.getId(), mobVehicle.getId())); + eventsManager.processEvent(new PassengerPickedUpEvent(now, mode, requestId, passenger.getId(), vehicleId)); + } - Id vehicleId = Id.create(mobVehicle.getId(), DvrpVehicle.class); - eventsManager.processEvent(new PersonEntersVehicleEvent(now, passenger.getId(), mobVehicle.getId())); - eventsManager.processEvent(new PassengerPickedUpEvent(now, mode, requestId, passenger.getId(), vehicleId)); return true; } - void dropOffPassenger(MobsimDriverAgent driver, MobsimPassengerAgent passenger, Id requestId, double now) { + void dropOffPassengers(MobsimDriverAgent driver, List passengers, Id requestId, double now) { MobsimVehicle mobVehicle = driver.getVehicle(); - mobVehicle.removePassenger(passenger); - passenger.setVehicle(null); - Id vehicleId = Id.create(mobVehicle.getId(), DvrpVehicle.class); - eventsManager.processEvent(new PassengerDroppedOffEvent(now, mode, requestId, passenger.getId(), vehicleId)); - eventsManager.processEvent(new PersonLeavesVehicleEvent(now, passenger.getId(), mobVehicle.getId())); - passenger.notifyArrivalOnLinkByNonNetworkMode(passenger.getDestinationLinkId()); - passenger.endLegAndComputeNextState(now); - internalInterface.arrangeNextAgentState(passenger); + for (MobsimPassengerAgent passenger : passengers) { + mobVehicle.removePassenger(passenger); + passenger.setVehicle(null); + + eventsManager.processEvent(new PassengerDroppedOffEvent(now, mode, requestId, passenger.getId(), vehicleId)); + eventsManager.processEvent(new PersonLeavesVehicleEvent(now, passenger.getId(), mobVehicle.getId())); + + passenger.notifyArrivalOnLinkByNonNetworkMode(passenger.getDestinationLinkId()); + passenger.endLegAndComputeNextState(now); + internalInterface.arrangeNextAgentState(passenger); + } } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerDropoffActivity.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerDropoffActivity.java index e93a08f840b..7d157e4c798 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerDropoffActivity.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerDropoffActivity.java @@ -54,7 +54,7 @@ protected boolean isLastStep(double now) { protected void afterLastStep(double now) { // dropoff at the end of stop activity for (PassengerRequest request : requests.values()) { - passengerHandler.dropOffPassenger(driver, request.getId(), now); + passengerHandler.dropOffPassengers(driver, request.getId(), now); } } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerPickupActivity.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerPickupActivity.java index 8d7bcb2da96..badc060b37e 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerPickupActivity.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/MultiPassengerPickupActivity.java @@ -19,9 +19,13 @@ package org.matsim.contrib.dvrp.passenger; +import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Identifiable; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.schedule.StayTask; @@ -35,7 +39,7 @@ public class MultiPassengerPickupActivity extends FirstLastSimStepDynActivity im private final Map, ? extends PassengerRequest> requests; private final double expectedEndTime; - private int passengersPickedUp = 0; + private int requestsPickedUp = 0; public MultiPassengerPickupActivity(PassengerHandler passengerHandler, DynAgent driver, StayTask pickupTask, Map, ? extends PassengerRequest> requests, String activityType) { @@ -49,32 +53,32 @@ public MultiPassengerPickupActivity(PassengerHandler passengerHandler, DynAgent @Override protected boolean isLastStep(double now) { - return passengersPickedUp == requests.size() && now >= expectedEndTime; + return requestsPickedUp == requests.size() && now >= expectedEndTime; } @Override protected void beforeFirstStep(double now) { for (PassengerRequest request : requests.values()) { - if (passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now)) { - passengersPickedUp++; + if (passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now)) { + requestsPickedUp++; } } } @Override - public void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, double now) { - PassengerRequest request = getRequestForPassenger(passenger.getId()); - if (passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now)) { - passengersPickedUp++; + public void notifyPassengersAreReadyForDeparture(List passengers, double now) { + PassengerRequest request = getRequestForPassenger(passengers.stream().map(Identifiable::getId).toList()); + if (passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now)) { + requestsPickedUp++; } else { throw new IllegalStateException("The passenger is not on the link or not available for departure!"); } } - private PassengerRequest getRequestForPassenger(Id passengerId) { + private PassengerRequest getRequestForPassenger(List> passengerIds) { return requests.values() .stream() - .filter(r -> passengerId.equals(r.getPassengerId())) + .filter(r -> r.getPassengerIds().size() == passengerIds.size() && r.getPassengerIds().containsAll(passengerIds)) .findAny() .orElseThrow(() -> new IllegalArgumentException("I am waiting for different passengers!")); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineQSimModule.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineQSimModule.java index 6f9517c5153..e9d806af179 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineQSimModule.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineQSimModule.java @@ -10,7 +10,7 @@ */ public class PassengerEngineQSimModule extends AbstractDvrpModeQSimModule { public enum PassengerEngineType { - DEFAULT, WITH_PREBOOKING, TELEPORTING + DEFAULT, WITH_PREBOOKING, TELEPORTING, WITH_GROUPS } private final PassengerEngineType type; @@ -44,6 +44,10 @@ protected void configureQSim() { addModalComponent( PassengerEngine.class, TeleportingPassengerEngine.createProvider( getMode() ) ); return; } + case WITH_GROUPS -> { + addModalComponent( PassengerEngine.class, GroupPassengerEngine.createProvider( getMode() ) ); + return; + } default -> throw new IllegalStateException( "Type: " + type + " is not supported" ); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineWithPrebooking.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineWithPrebooking.java index 354bb788a6f..dc7939b34df 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineWithPrebooking.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineWithPrebooking.java @@ -19,12 +19,7 @@ package org.matsim.contrib.dvrp.passenger; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Queue; +import java.util.*; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.stream.Collectors; @@ -139,11 +134,12 @@ public void bookTrip(MobsimPassengerAgent passenger, TripInfoWithRequiredBooking double now = mobsimTimer.getTimeOfDay(); //TODO have a separate request creator for prebooking (accept TripInfo instead of Route) PassengerRequest request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), - passenger.getId(), tripInfo.getOriginalRequest().getPlannedRoute(), + List.of(passenger.getId()), tripInfo.getOriginalRequest().getPlannedRoute(), getLink(tripInfo.getPickupLocation().getLinkId()), getLink(tripInfo.getDropoffLocation().getLinkId()), tripInfo.getExpectedBoardingTime(), now); validateAndSubmitRequest(passenger, request, tripInfo.getOriginalRequest(), now); - advanceRequests.put(request.getPassengerId(), request); + // hard assumption that with this engine, passenger ids is always a singleton. nkuehnel oct '23 + advanceRequests.put(request.getPassengerIds().stream().findFirst().orElseThrow(), request); } private Link getLink(Id linkId) { return Preconditions.checkNotNull(network.getLinks().get(linkId), @@ -166,14 +162,14 @@ private Link getLink(Id linkId) { //TODO what if it was already rejected while prebooking?? PassengerRequest prebookedRequest = prebookedRequests.get(0); - - eventsManager.processEvent(new PassengerWaitingEvent(now, mode, prebookedRequest.getId(), prebookedRequest.getPassengerId())); - + + eventsManager.processEvent(new PassengerWaitingEvent(now, mode, prebookedRequest.getId(), prebookedRequest.getPassengerIds())); + PassengerPickupActivity awaitingPickup = awaitingPickups.remove(prebookedRequest.getId()); if (awaitingPickup != null) { - awaitingPickup.notifyPassengerIsReadyForDeparture(passenger, now); + awaitingPickup.notifyPassengersAreReadyForDeparture(List.of(passenger), now); } - + return true; } @@ -203,7 +199,7 @@ private void validateAndSubmitRequest(MobsimPassengerAgent passenger, PassengerR // ================ PICKUP / DROPOFF @Override - public boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, + public boolean tryPickUpPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, double now) { Id linkId = driver.getCurrentLinkId(); RequestEntry requestEntry = activeRequests.get(requestId); @@ -216,7 +212,7 @@ public boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, Mobsim return false;// wait for the passenger } - if (!internalPassengerHandling.tryPickUpPassenger(driver, passenger, requestId, now)) { + if (!internalPassengerHandling.tryPickUpPassengers(driver, List.of(passenger), requestId, now)) { // the passenger has already been picked up and is on another taxi trip // seems there have been at least 2 requests made by this passenger for this location awaitingPickups.put(requestId, pickupActivity); @@ -227,8 +223,8 @@ public boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, Mobsim } @Override - public void dropOffPassenger(MobsimDriverAgent driver, Id requestId, double now) { - internalPassengerHandling.dropOffPassenger(driver, activeRequests.remove(requestId).passenger, requestId, now); + public void dropOffPassengers(MobsimDriverAgent driver, Id requestId, double now) { + internalPassengerHandling.dropOffPassengers(driver, List.of(activeRequests.remove(requestId).passenger), requestId, now); } // ================ REJECTED/SCHEDULED EVENTS @@ -307,7 +303,7 @@ public static Provider createProvider(String mode) { * to be tested anywhere. /sebhoerl */ @Override - public boolean notifyWaitForPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId) { + public boolean notifyWaitForPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId) { Id linkId = driver.getCurrentLinkId(); RequestEntry requestEntry = activeRequests.get(requestId); MobsimPassengerAgent passenger = requestEntry.passenger; @@ -318,7 +314,7 @@ public boolean notifyWaitForPassenger(PassengerPickupActivity pickupActivity, Mo awaitingPickups.put(requestId, pickupActivity); return false;// wait for the passenger } - + return true; // passenger present? } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerGroupIdentifier.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerGroupIdentifier.java new file mode 100644 index 00000000000..8dba2bdac52 --- /dev/null +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerGroupIdentifier.java @@ -0,0 +1,21 @@ +package org.matsim.contrib.dvrp.passenger; + +import org.matsim.api.core.v01.Id; +import org.matsim.core.mobsim.framework.MobsimPassengerAgent; + +import java.util.function.Function; + +/** + * Provides a method to identify the passenger group id of an agent. + * @author nkuehnel / MOIA + */ +public interface PassengerGroupIdentifier extends Function> { + + class PassengerGroup { + private PassengerGroup(){} + } + + @Override + Id apply(MobsimPassengerAgent agent); + +} diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerHandler.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerHandler.java index 408f187db77..06912f29ae2 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerHandler.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerHandler.java @@ -30,10 +30,10 @@ * This looks quite general. But as of now is a dvrp thing. kai, apr'23 */ public interface PassengerHandler { - boolean notifyWaitForPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId); + boolean notifyWaitForPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId); - boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, + boolean tryPickUpPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, double now); - void dropOffPassenger(MobsimDriverAgent driver, Id requestId, double now); + void dropOffPassengers(MobsimDriverAgent driver, Id requestId, double now); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerPickupActivity.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerPickupActivity.java index a083ee9ce18..b8aa5cd611e 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerPickupActivity.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerPickupActivity.java @@ -22,6 +22,9 @@ import org.matsim.contrib.dynagent.DynActivity; import org.matsim.core.mobsim.framework.MobsimPassengerAgent; +import java.util.List; +import java.util.Set; + public interface PassengerPickupActivity extends DynActivity { - void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, double now); + void notifyPassengersAreReadyForDeparture(List passengers, double now); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequest.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequest.java index b3378b28f14..e6cbca39641 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequest.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequest.java @@ -24,6 +24,8 @@ import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; +import java.util.List; + public interface PassengerRequest extends Request { /** * @return beginning of the time window (inclusive) - earliest time when the passenger can be picked up @@ -41,7 +43,9 @@ default double getLatestStartTime() { Link getToLink(); - Id getPassengerId(); + List> getPassengerIds(); String getMode(); + + int getPassengerCount(); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestCreator.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestCreator.java index 9423cfd113b..15d4fe0ddde 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestCreator.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestCreator.java @@ -25,6 +25,8 @@ import org.matsim.api.core.v01.population.Route; import org.matsim.contrib.dvrp.optimizer.Request; +import java.util.List; + /** * @author michalm */ @@ -34,7 +36,7 @@ public interface PassengerRequestCreator { * Prefer stateless implementation, otherwise provide other ways to achieve thread-safety. * * @param id request ID - * @param passengerId passenger ID + * @param passengerIds list of unique passenger IDs * @param route planned route (the required route type depends on the optimizer) * @param fromLink start location * @param toLink end location @@ -42,6 +44,6 @@ public interface PassengerRequestCreator { * @param submissionTime time at which request was submitted * @return */ - PassengerRequest createRequest(Id id, Id passengerId, Route route, Link fromLink, Link toLink, - double departureTime, double submissionTime); + PassengerRequest createRequest(Id id, List> passengerIds, Route route, Link fromLink, Link toLink, + double departureTime, double submissionTime); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestRejectedEvent.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestRejectedEvent.java index fbe61390f34..0d17138700d 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestRejectedEvent.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestRejectedEvent.java @@ -20,14 +20,15 @@ package org.matsim.contrib.dvrp.passenger; -import java.util.Map; -import java.util.Objects; +import java.util.*; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.GenericEvent; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; +import static org.matsim.api.core.v01.events.HasPersonId.ATTRIBUTE_PERSON; + /** * @author michalm */ @@ -38,9 +39,9 @@ public class PassengerRequestRejectedEvent extends AbstractPassengerRequestEvent private final String cause; - public PassengerRequestRejectedEvent(double time, String mode, Id requestId, Id personId, + public PassengerRequestRejectedEvent(double time, String mode, Id requestId, List> personIds, String cause) { - super(time, mode, requestId, personId); + super(time, mode, requestId, personIds); this.cause = cause; } @@ -65,8 +66,11 @@ public static PassengerRequestRejectedEvent convert(GenericEvent event) { double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); String mode = Objects.requireNonNull(attributes.get(ATTRIBUTE_MODE)); Id requestId = Id.create(attributes.get(ATTRIBUTE_REQUEST), Request.class); - Id personId = Id.createPersonId(attributes.get(ATTRIBUTE_PERSON)); - String cause = Objects.requireNonNull(attributes.get(ATTRIBUTE_CAUSE)); - return new PassengerRequestRejectedEvent(time, mode, requestId, personId, cause); + String[] personIdsAttribute = attributes.get(ATTRIBUTE_PERSON).split(","); + List> personIds = new ArrayList<>(); + for (String person : personIdsAttribute) { + personIds.add(Id.create(person, Person.class)); + } String cause = Objects.requireNonNull(attributes.get(ATTRIBUTE_CAUSE)); + return new PassengerRequestRejectedEvent(time, mode, requestId, personIds, cause); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestScheduledEvent.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestScheduledEvent.java index 9de0b19b344..413b668db89 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestScheduledEvent.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestScheduledEvent.java @@ -20,8 +20,7 @@ package org.matsim.contrib.dvrp.passenger; -import java.util.Map; -import java.util.Objects; +import java.util.*; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.GenericEvent; @@ -29,6 +28,8 @@ import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.optimizer.Request; +import static org.matsim.api.core.v01.events.HasPersonId.ATTRIBUTE_PERSON; + /** * @author michalm */ @@ -46,9 +47,9 @@ public class PassengerRequestScheduledEvent extends AbstractPassengerRequestEven /** * An event processed upon request submission. */ - public PassengerRequestScheduledEvent(double time, String mode, Id requestId, Id personId, + public PassengerRequestScheduledEvent(double time, String mode, Id requestId, List> personIds, Id vehicleId, double pickupTime, double dropoffTime) { - super(time, mode, requestId, personId); + super(time, mode, requestId, personIds); this.vehicleId = vehicleId; this.pickupTime = pickupTime; this.dropoffTime = dropoffTime; @@ -94,10 +95,14 @@ public static PassengerRequestScheduledEvent convert(GenericEvent event) { double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); String mode = Objects.requireNonNull(attributes.get(ATTRIBUTE_MODE)); Id requestId = Id.create(attributes.get(ATTRIBUTE_REQUEST), Request.class); - Id personId = Id.createPersonId(attributes.get(ATTRIBUTE_PERSON)); + String[] personIdsAttribute = attributes.get(ATTRIBUTE_PERSON).split(","); + List> personIds = new ArrayList<>(); + for (String person : personIdsAttribute) { + personIds.add(Id.create(person, Person.class)); + } Id vehicleId = Id.create(attributes.get(ATTRIBUTE_VEHICLE), DvrpVehicle.class); double pickupTime = Double.parseDouble(attributes.get(ATTRIBUTE_PICKUP_TIME)); double dropoffTime = Double.parseDouble(attributes.get(ATTRIBUTE_DROPOFF_TIME)); - return new PassengerRequestScheduledEvent(time, mode, requestId, personId, vehicleId, pickupTime, dropoffTime); + return new PassengerRequestScheduledEvent(time, mode, requestId, personIds, vehicleId, pickupTime, dropoffTime); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestSubmittedEvent.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestSubmittedEvent.java index 73dd882740c..0b7981bd634 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestSubmittedEvent.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestSubmittedEvent.java @@ -20,8 +20,7 @@ package org.matsim.contrib.dvrp.passenger; -import java.util.Map; -import java.util.Objects; +import java.util.*; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.GenericEvent; @@ -29,6 +28,8 @@ import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; +import static org.matsim.api.core.v01.events.HasPersonId.ATTRIBUTE_PERSON; + /** * @author michalm */ @@ -41,9 +42,9 @@ public class PassengerRequestSubmittedEvent extends AbstractPassengerRequestEven private final Id fromLinkId; private final Id toLinkId; - public PassengerRequestSubmittedEvent(double time, String mode, Id requestId, Id personId, + public PassengerRequestSubmittedEvent(double time, String mode, Id requestId, List> personIds, Id fromLinkId, Id toLinkId) { - super(time, mode, requestId, personId); + super(time, mode, requestId, personIds); this.fromLinkId = fromLinkId; this.toLinkId = toLinkId; } @@ -80,9 +81,14 @@ public static PassengerRequestSubmittedEvent convert(GenericEvent event) { double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); String mode = Objects.requireNonNull(attributes.get(ATTRIBUTE_MODE)); Id requestId = Id.create(attributes.get(ATTRIBUTE_REQUEST), Request.class); - Id personId = Id.createPersonId(attributes.get(ATTRIBUTE_PERSON)); + String[] personIdsAttribute = attributes.get(ATTRIBUTE_PERSON).split(","); + List> personIds = new ArrayList<>(); + for (String person : personIdsAttribute) { + personIds.add(Id.create(person, Person.class)); + } + Id fromLinkId = Id.createLinkId(attributes.get(ATTRIBUTE_FROM_LINK)); Id toLinkId = Id.createLinkId(attributes.get(ATTRIBUTE_TO_LINK)); - return new PassengerRequestSubmittedEvent(time, mode, requestId, personId, fromLinkId, toLinkId); + return new PassengerRequestSubmittedEvent(time, mode, requestId, personIds, fromLinkId, toLinkId); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerWaitingEvent.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerWaitingEvent.java index 6c37f84a9be..db9a477954e 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerWaitingEvent.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerWaitingEvent.java @@ -19,6 +19,8 @@ package org.matsim.contrib.dvrp.passenger; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -33,8 +35,8 @@ public class PassengerWaitingEvent extends AbstractPassengerRequestEvent { public static final String EVENT_TYPE = "passenger waiting"; - public PassengerWaitingEvent(double time, String mode, Id requestId, Id personId) { - super(time, mode, requestId, personId); + public PassengerWaitingEvent(double time, String mode, Id requestId, List> personIds) { + super(time, mode, requestId, personIds); } @Override @@ -47,7 +49,11 @@ public static PassengerWaitingEvent convert(GenericEvent event) { double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); String mode = Objects.requireNonNull(attributes.get(ATTRIBUTE_MODE)); Id requestId = Id.create(attributes.get(ATTRIBUTE_REQUEST), Request.class); - Id personId = Id.createPersonId(attributes.get(ATTRIBUTE_PERSON)); - return new PassengerWaitingEvent(time, mode, requestId, personId); + String[] personIdsAttribute = attributes.get(ATTRIBUTE_PERSON).split(","); + List> personIds = new ArrayList<>(); + for (String person : personIdsAttribute) { + personIds.add(Id.create(person, Person.class)); + } + return new PassengerWaitingEvent(time, mode, requestId, personIds); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerDropoffActivity.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerDropoffActivity.java index 5d74a5ed024..afab824bd96 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerDropoffActivity.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerDropoffActivity.java @@ -48,6 +48,6 @@ protected boolean isLastStep(double now) { @Override protected void afterLastStep(double now) { - passengerHandler.dropOffPassenger(driver, request.getId(), now); + passengerHandler.dropOffPassengers(driver, request.getId(), now); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerPickupActivity.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerPickupActivity.java index 33883acfcd6..3f0ee34bb62 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerPickupActivity.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/SinglePassengerPickupActivity.java @@ -19,18 +19,23 @@ package org.matsim.contrib.dvrp.passenger; +import org.matsim.api.core.v01.Identifiable; import org.matsim.contrib.dvrp.schedule.StayTask; import org.matsim.contrib.dynagent.DynAgent; import org.matsim.contrib.dynagent.FirstLastSimStepDynActivity; import org.matsim.core.mobsim.framework.MobsimPassengerAgent; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + public class SinglePassengerPickupActivity extends FirstLastSimStepDynActivity implements PassengerPickupActivity { private final PassengerHandler passengerHandler; private final DynAgent driver; private final PassengerRequest request; private final double expectedEndTime; - private boolean passengerAboard = false; + private boolean passengersAboard = false; public SinglePassengerPickupActivity(PassengerHandler passengerHandler, DynAgent driver, StayTask pickupTask, PassengerRequest request, String activityType) { @@ -44,22 +49,22 @@ public SinglePassengerPickupActivity(PassengerHandler passengerHandler, DynAgent @Override protected boolean isLastStep(double now) { - return passengerAboard && now >= expectedEndTime; + return passengersAboard && now >= expectedEndTime; } @Override protected void beforeFirstStep(double now) { - passengerAboard = passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now); + passengersAboard = passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now); } @Override - public void notifyPassengerIsReadyForDeparture(MobsimPassengerAgent passenger, double now) { - if (passenger.getId().equals(request.getPassengerId())) { + public void notifyPassengersAreReadyForDeparture(List passengers, double now) { + if (request.getPassengerIds().containsAll(passengers.stream().map(Identifiable::getId).toList())) { throw new IllegalArgumentException("I am waiting for a different passenger!"); } - passengerAboard = passengerHandler.tryPickUpPassenger(this, driver, request.getId(), now); - if (!passengerAboard) { + passengersAboard = passengerHandler.tryPickUpPassengers(this, driver, request.getId(), now); + if (!passengersAboard) { throw new IllegalStateException("The passenger is not on the link or not available for departure!"); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngine.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngine.java index 14374da7168..1f773a77043 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngine.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngine.java @@ -20,10 +20,7 @@ package org.matsim.contrib.dvrp.passenger; -import java.util.Collection; -import java.util.Comparator; -import java.util.PriorityQueue; -import java.util.Queue; +import java.util.*; import jakarta.inject.Inject; import jakarta.inject.Provider; @@ -35,6 +32,7 @@ import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Route; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.run.DvrpModes; @@ -120,8 +118,10 @@ public void doSimStep(double time) { //first process passenger dropoff events while (!teleportedRequests.isEmpty() && teleportedRequests.peek().getLeft() <= time) { PassengerRequest request = teleportedRequests.poll().getRight(); - eventsManager.processEvent( - new PassengerDroppedOffEvent(time, mode, request.getId(), request.getPassengerId(), null)); + for (Id passenger : request.getPassengerIds()) { + eventsManager.processEvent( + new PassengerDroppedOffEvent(time, mode, request.getId(), passenger, null)); + } } //then end teleported rides @@ -143,12 +143,12 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id fromLinkI Id toLinkId = passenger.getDestinationLinkId(); Route route = ((Leg)((PlanAgent)passenger).getCurrentPlanElement()).getRoute(); PassengerRequest request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), - passenger.getId(), route, getLink(fromLinkId), getLink(toLinkId), now, now); - - eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), request.getPassengerId())); + List.of(passenger.getId()), route, getLink(fromLinkId), getLink(toLinkId), now, now); + + eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), request.getPassengerIds())); if (internalPassengerHandling.validateRequest(request, requestValidator, now)) { - Route teleportedRoute = adaptLegRouteForTeleportation(passenger, request, now); + Route teleportedRoute = adaptLegRouteForTeleportation(List.of(passenger), request, now); eventsManager.processEvent(new PassengerPickedUpEvent(now, mode, request.getId(), passenger.getId(), null)); teleportationEngine.handleDeparture(now, passenger, fromLinkId); teleportedRequests.add(ImmutablePair.of(now + teleportedRoute.getTravelTime().seconds(), request)); @@ -158,24 +158,26 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id fromLinkI passenger.setStateToAbort(mobsimTimer.getTimeOfDay()); internalInterface.arrangeNextAgentState(passenger); } - + return true; } - private Route adaptLegRouteForTeleportation(MobsimPassengerAgent passenger, PassengerRequest request, double now) { + private Route adaptLegRouteForTeleportation(List passengers, PassengerRequest request, double now) { Route teleportedRoute = teleportedRouteCalculator.calculateRoute(request); - Leg leg = (Leg)WithinDayAgentUtils.getCurrentPlanElement(passenger);//side effect: makes the plan modifiable - Route originalRoute = leg.getRoute(); - Verify.verify(originalRoute.getStartLinkId().equals(teleportedRoute.getStartLinkId())); - Verify.verify(originalRoute.getEndLinkId().equals(teleportedRoute.getEndLinkId())); - Verify.verify(teleportedRoute.getTravelTime().isDefined()); + for (MobsimPassengerAgent passenger : passengers) { + Leg leg = (Leg)WithinDayAgentUtils.getCurrentPlanElement(passenger);//side effect: makes the plan modifiable + Route originalRoute = leg.getRoute(); + Verify.verify(originalRoute.getStartLinkId().equals(teleportedRoute.getStartLinkId())); + Verify.verify(originalRoute.getEndLinkId().equals(teleportedRoute.getEndLinkId())); + Verify.verify(teleportedRoute.getTravelTime().isDefined()); - leg.getAttributes().putAttribute(ORIGINAL_ROUTE_ATTRIBUTE, originalRoute); - leg.setRoute(teleportedRoute); + leg.getAttributes().putAttribute(ORIGINAL_ROUTE_ATTRIBUTE, originalRoute); + leg.setRoute(teleportedRoute); + } eventsManager.processEvent(new PassengerRequestScheduledEvent(mobsimTimer.getTimeOfDay(), mode, request.getId(), - request.getPassengerId(), null, now, now + teleportedRoute.getTravelTime().seconds())); + request.getPassengerIds(), null, now, now + teleportedRoute.getTravelTime().seconds())); return teleportedRoute; } @@ -186,19 +188,19 @@ private Link getLink(Id linkId) { } @Override - public boolean notifyWaitForPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, + public boolean notifyWaitForPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId) { throw new UnsupportedOperationException("No notifying when teleporting"); } @Override - public boolean tryPickUpPassenger(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, + public boolean tryPickUpPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, double now) { throw new UnsupportedOperationException("No picking-up when teleporting"); } @Override - public void dropOffPassenger(MobsimDriverAgent driver, Id requestId, double now) { + public void dropOffPassengers(MobsimDriverAgent driver, Id requestId, double now) { throw new UnsupportedOperationException("No dropping-off when teleporting"); } diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/examples/onetaxi/RunOneTaxiWithPrebookingExampleIT.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/examples/onetaxi/RunOneTaxiWithPrebookingExampleIT.java index c2b6f6a5753..b6c7e2ed828 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/examples/onetaxi/RunOneTaxiWithPrebookingExampleIT.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/examples/onetaxi/RunOneTaxiWithPrebookingExampleIT.java @@ -142,7 +142,7 @@ public void install() { if (event instanceof AgentWakeupEvent) { wakeupEvents.put(((AgentWakeupEvent)event).getPersonId(), (AgentWakeupEvent)event); } else if (event instanceof PassengerRequestScheduledEvent) { - requestScheduledEvents.put(((PassengerRequestScheduledEvent)event).getPersonId(), + requestScheduledEvents.put(((PassengerRequestScheduledEvent)event).getPersonIds().stream().findFirst().orElseThrow(), (PassengerRequestScheduledEvent)event); } else if (event instanceof ActivityEndEvent && ((ActivityEndEvent)event).getActType() .equals("dummy")) { @@ -239,7 +239,7 @@ private static void assertRequestScheduledEvent(Map, PassengerRequest PassengerRequestScheduledEvent event = events.get(Id.createPersonId(personId)); assertThat(event.getVehicleId().toString()).isEqualTo("taxi_one"); assertThat(event.getPickupTime()).isCloseTo(pickupTime, Offset.offset(0.01)); - assertThat(event.getPersonId().toString()).isEqualTo(personId); + assertThat(event.getPersonIds().get(0).toString()).isEqualTo(personId); assertThat(event.getRequestId().toString()).isEqualTo(requestId); } diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngineTest.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngineTest.java index eb096cf46df..db4efe8d602 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngineTest.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngineTest.java @@ -22,6 +22,8 @@ import static org.matsim.contrib.dvrp.passenger.PassengerEngineTestFixture.*; +import java.util.Collections; +import java.util.List; import java.util.Set; import org.junit.Test; @@ -60,7 +62,6 @@ import com.google.common.collect.ImmutableMap; import com.google.inject.Inject; -import com.google.inject.name.Names; /** * @author Michal Maciejewski (michalm) @@ -81,7 +82,7 @@ public class DefaultPassengerEngineTest { @Test public void test_valid_served() { double departureTime = 0; - fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime); + fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, fixture.PERSON_ID); PassengerRequestValidator requestValidator = request -> Set.of();//valid createQSim(requestValidator, OneTaxiOptimizer.class).run(); @@ -98,10 +99,11 @@ public void test_valid_served() { var requestId = Id.create("taxi_0", Request.class); fixture.assertPassengerEvents( + Collections.singleton(fixture.PERSON_ID), new ActivityEndEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY), new PersonDepartureEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE), - new PassengerWaitingEvent(departureTime, MODE, requestId, fixture.PERSON_ID), - new PassengerRequestScheduledEvent(departureTime, MODE, requestId, fixture.PERSON_ID, VEHICLE_ID, 0, + new PassengerWaitingEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID)), + new PassengerRequestScheduledEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID), VEHICLE_ID, 0, scheduledDropoffTime), new PersonEntersVehicleEvent(pickupStartTime, fixture.PERSON_ID, Id.createVehicleId(VEHICLE_ID)), new PassengerPickedUpEvent(pickupStartTime, MODE, requestId, fixture.PERSON_ID, VEHICLE_ID), @@ -114,32 +116,36 @@ public void test_valid_served() { @Test public void test_invalid_rejected() { double departureTime = 0; - fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime); + fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, fixture.PERSON_ID); PassengerRequestValidator requestValidator = request -> Set.of("invalid"); createQSim(requestValidator, OneTaxiOptimizer.class).run(); var requestId = Id.create("taxi_0", Request.class); - fixture.assertPassengerEvents(new ActivityEndEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY), + fixture.assertPassengerEvents( + Collections.singleton(fixture.PERSON_ID), + new ActivityEndEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY), new PersonDepartureEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE), - new PassengerWaitingEvent(departureTime, MODE, requestId, fixture.PERSON_ID), - new PassengerRequestRejectedEvent(0, MODE, requestId, fixture.PERSON_ID, "invalid"), + new PassengerWaitingEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID)), + new PassengerRequestRejectedEvent(0, MODE, requestId, List.of(fixture.PERSON_ID), "invalid"), new PersonStuckEvent(1, fixture.PERSON_ID, fixture.linkAB.getId(), MODE)); } @Test public void test_valid_rejected() { double departureTime = 0; - fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime); + fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, fixture.PERSON_ID); PassengerRequestValidator requestValidator = request -> Set.of(); createQSim(requestValidator, RejectingOneTaxiOptimizer.class).run(); var requestId = Id.create("taxi_0", Request.class); - fixture.assertPassengerEvents(new ActivityEndEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY), + fixture.assertPassengerEvents( + Collections.singleton(fixture.PERSON_ID), + new ActivityEndEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY), new PersonDepartureEvent(0, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE), - new PassengerWaitingEvent(departureTime, MODE, requestId, fixture.PERSON_ID), - new PassengerRequestRejectedEvent(0, MODE, requestId, fixture.PERSON_ID, "rejecting_all_requests"), + new PassengerWaitingEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID)), + new PassengerRequestRejectedEvent(0, MODE, requestId, List.of(fixture.PERSON_ID), "rejecting_all_requests"), new PersonStuckEvent(1, fixture.PERSON_ID, fixture.linkAB.getId(), MODE)); } @@ -154,7 +160,7 @@ private static class RejectingOneTaxiOptimizer implements VrpOptimizer { public void requestSubmitted(Request request) { PassengerRequest passengerRequest = (PassengerRequest)request; eventsManager.processEvent(new PassengerRequestRejectedEvent(timer.getTimeOfDay(), MODE, request.getId(), - passengerRequest.getPassengerId(), "rejecting_all_requests")); + passengerRequest.getPassengerIds(), "rejecting_all_requests")); } @Override diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngineTest.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngineTest.java new file mode 100644 index 00000000000..2f34014ca2f --- /dev/null +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngineTest.java @@ -0,0 +1,133 @@ +package org.matsim.contrib.dvrp.passenger; + +import com.google.common.collect.ImmutableMap; +import org.apache.commons.compress.utils.Sets; +import org.junit.Test; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.events.*; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.dvrp.examples.onetaxi.OneTaxiActionCreator; +import org.matsim.contrib.dvrp.examples.onetaxi.OneTaxiOptimizer; +import org.matsim.contrib.dvrp.examples.onetaxi.OneTaxiRequest; +import org.matsim.contrib.dvrp.fleet.DvrpVehicle; +import org.matsim.contrib.dvrp.fleet.DvrpVehicleImpl; +import org.matsim.contrib.dvrp.fleet.Fleet; +import org.matsim.contrib.dvrp.fleet.ImmutableDvrpVehicleSpecification; +import org.matsim.contrib.dvrp.optimizer.Request; +import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; +import org.matsim.contrib.dvrp.run.DvrpModes; +import org.matsim.contrib.dvrp.run.MobsimTimerProvider; +import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic; +import org.matsim.contrib.dvrp.vrpagent.VrpAgentSourceQSimModule; +import org.matsim.contrib.dynagent.run.DynActivityEngine; +import org.matsim.core.events.MobsimScopeEventHandlingModule; +import org.matsim.core.mobsim.framework.MobsimTimer; +import org.matsim.core.mobsim.qsim.QSim; +import org.matsim.core.mobsim.qsim.QSimBuilder; +import org.matsim.vehicles.VehicleType; +import org.matsim.vehicles.VehicleUtils; + +import java.util.List; +import java.util.Set; + +import static org.matsim.contrib.dvrp.passenger.PassengerEngineTestFixture.*; + +public class GroupPassengerEngineTest { + + private final PassengerEngineTestFixture fixture = new PassengerEngineTestFixture(); + + private final Id VEHICLE_ID = Id.create("taxi1", DvrpVehicle.class); + private final DvrpVehicle oneTaxi = new DvrpVehicleImpl(ImmutableDvrpVehicleSpecification.newBuilder() + .id(VEHICLE_ID) + .serviceBeginTime(0) + .serviceEndTime(3600) + .startLinkId(fixture.linkAB.getId()) + .capacity(1) + .build(), fixture.linkAB); + private final Fleet fleet = () -> ImmutableMap.of(oneTaxi.getId(), oneTaxi); + + @Test + public void test_group() { + double departureTime = 0; + Id person1 = Id.createPersonId("1"); + Id person2 = Id.createPersonId("2"); + + fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, person1); + fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, person2); + + PassengerRequestValidator requestValidator = request -> Set.of();//valid + createQSim(requestValidator, OneTaxiOptimizer.class).run(); + + double pickupStartTime = 1; + double pickupEndTime = pickupStartTime + OneTaxiOptimizer.PICKUP_DURATION; + double taxiDepartureTime = pickupEndTime + 1; + double taxiEntersLinkBATime = taxiDepartureTime + 1; + double taxiArrivalTime = taxiEntersLinkBATime + (fixture.linkBA.getLength() / fixture.linkBA.getFreespeed()); + double dropoffEndTime = taxiArrivalTime + OneTaxiOptimizer.DROPOFF_DURATION; + + //1 second delay between pickupEndTime and taxiDepartureTime is not considered in schedules + double scheduledDropoffTime = dropoffEndTime - pickupStartTime - 1; + + var requestId = Id.create("taxi_0", Request.class); + fixture.assertPassengerEvents( + List.of(person1, person2), + new ActivityEndEvent(departureTime, person2, fixture.linkAB.getId(), null, START_ACTIVITY), + new PersonDepartureEvent(departureTime, person2, fixture.linkAB.getId(), MODE, MODE), + new ActivityEndEvent(departureTime, person1, fixture.linkAB.getId(), null, START_ACTIVITY), + new PersonDepartureEvent(departureTime, person1, fixture.linkAB.getId(), MODE, MODE), + new PassengerWaitingEvent(departureTime, MODE, requestId, List.of(person1, person2)), + new PassengerRequestScheduledEvent(departureTime, MODE, requestId, List.of(person1, person2), VEHICLE_ID, 0, + scheduledDropoffTime), + new PersonEntersVehicleEvent(pickupStartTime, person1, Id.createVehicleId(VEHICLE_ID)), + new PassengerPickedUpEvent(pickupStartTime, MODE, requestId, person1, VEHICLE_ID), + new PersonEntersVehicleEvent(pickupStartTime, person2, Id.createVehicleId(VEHICLE_ID)), + new PassengerPickedUpEvent(pickupStartTime, MODE, requestId, person2, VEHICLE_ID), + new PassengerDroppedOffEvent(dropoffEndTime, MODE, requestId, person1, VEHICLE_ID), + new PersonLeavesVehicleEvent(dropoffEndTime, person1, Id.createVehicleId(VEHICLE_ID)), + new PersonArrivalEvent(dropoffEndTime, person1, fixture.linkBA.getId(), MODE), + new ActivityStartEvent(dropoffEndTime, person1, fixture.linkBA.getId(), null, END_ACTIVITY), + new PassengerDroppedOffEvent(dropoffEndTime, MODE, requestId, person2, VEHICLE_ID), + new PersonLeavesVehicleEvent(dropoffEndTime, person2, Id.createVehicleId(VEHICLE_ID)), + new PersonArrivalEvent(dropoffEndTime, person2, fixture.linkBA.getId(), MODE), + new ActivityStartEvent(dropoffEndTime, person2, fixture.linkBA.getId(), null, END_ACTIVITY) + ); + } + + + private QSim createQSim(PassengerRequestValidator requestValidator, Class optimizerClass) { + return new QSimBuilder(fixture.config).useDefaults() + .addOverridingModule(new MobsimScopeEventHandlingModule()) + .addQSimModule(new PassengerEngineQSimModule(MODE, PassengerEngineQSimModule.PassengerEngineType.WITH_GROUPS)) + .addQSimModule(new VrpAgentSourceQSimModule(MODE)) + .addQSimModule(new AbstractDvrpModeQSimModule(MODE) { + @Override + protected void configureQSim() { + bindModal(Network.class).toInstance(fixture.network); + bind(MobsimTimer.class).toProvider(MobsimTimerProvider.class).asEagerSingleton(); + + //requests + bindModal(PassengerRequestCreator.class).to(OneTaxiRequest.OneTaxiRequestCreator.class) + .asEagerSingleton(); + bindModal(PassengerRequestValidator.class).toInstance(requestValidator); + + //supply + addQSimComponentBinding(DynActivityEngine.COMPONENT_NAME).to(DynActivityEngine.class); + bindModal(Fleet.class).toInstance(fleet); + bindModal(VehicleType.class).toInstance(VehicleUtils.getDefaultVehicleType()); + bindModal(VrpOptimizer.class).to(optimizerClass).asEagerSingleton(); + bindModal(VrpAgentLogic.DynActionCreator.class).to(OneTaxiActionCreator.class) + .asEagerSingleton(); + + //groups + bindModal(PassengerGroupIdentifier.class).toInstance(agent -> Id.create("group1", PassengerGroupIdentifier.PassengerGroup.class)); + } + }) + .configureQSimComponents(components -> { + components.addComponent(DvrpModes.mode(MODE)); + components.addNamedComponent(DynActivityEngine.COMPONENT_NAME); + }) + .build(fixture.scenario, fixture.eventsManager); + } +} diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/PassengerEngineTestFixture.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/PassengerEngineTestFixture.java index 84ec6202a63..1f83607037f 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/PassengerEngineTestFixture.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/PassengerEngineTestFixture.java @@ -23,6 +23,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import org.matsim.api.core.v01.Coord; @@ -81,7 +82,7 @@ public PassengerEngineTestFixture() { eventsManager.initProcessing(); } - void addPersonWithLeg(Link fromLink, Link toLink, double departureTime) { + void addPersonWithLeg(Link fromLink, Link toLink, double departureTime, Id person_id) { PopulationFactory factory = scenario.getPopulation().getFactory(); Plan plan = factory.createPlan(); @@ -99,15 +100,18 @@ void addPersonWithLeg(Link fromLink, Link toLink, double departureTime) { plan.addActivity(factory.createActivityFromLinkId(END_ACTIVITY, toLink.getId())); - Person person = factory.createPerson(PERSON_ID); + Person person = factory.createPerson(person_id); person.addPlan(plan); scenario.getPopulation().addPerson(person); } - void assertPassengerEvents(Event... events) { + void assertPassengerEvents(Collection> personIds, Event... events) { assertThat(recordedEvents.size()).isGreaterThanOrEqualTo(events.length); var recordedPassengerEvents = recordedEvents.stream() - .filter(e -> e instanceof HasPersonId && ((HasPersonId)e).getPersonId().equals(PERSON_ID)); + .filter(e -> + e instanceof HasPersonId && personIds.contains(((HasPersonId)e).getPersonId()) || + e instanceof AbstractPassengerRequestEvent + ); assertThat(recordedPassengerEvents).usingRecursiveFieldByFieldElementComparator().containsExactly(events); } } diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngineTest.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngineTest.java index b8153c22ccd..6d1c3edd7d1 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngineTest.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/TeleportingPassengerEngineTest.java @@ -20,17 +20,9 @@ package org.matsim.contrib.dvrp.passenger; -import static org.matsim.contrib.dvrp.passenger.PassengerEngineTestFixture.*; - -import java.util.Set; - import org.junit.Test; 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.PersonArrivalEvent; -import org.matsim.api.core.v01.events.PersonDepartureEvent; -import org.matsim.api.core.v01.events.PersonStuckEvent; +import org.matsim.api.core.v01.events.*; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.Route; import org.matsim.contrib.dvrp.optimizer.Request; @@ -45,6 +37,12 @@ import org.matsim.core.mobsim.qsim.QSimBuilder; import org.matsim.core.population.routes.GenericRouteImpl; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import static org.matsim.contrib.dvrp.passenger.PassengerEngineTestFixture.*; + /** * @author Michal Maciejewski (michalm) */ @@ -54,7 +52,7 @@ public class TeleportingPassengerEngineTest { @Test public void test_valid_teleported() { double departureTime = 0; - fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime); + fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, fixture.PERSON_ID); double travelTime = 999; double travelDistance = 555; @@ -70,10 +68,11 @@ public void test_valid_teleported() { double arrivalTime = departureTime + travelTime; var requestId = Id.create("taxi_0", Request.class); fixture.assertPassengerEvents( + Collections.singleton(fixture.PERSON_ID), new ActivityEndEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY), new PersonDepartureEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE), - new PassengerWaitingEvent(departureTime, MODE, requestId, fixture.PERSON_ID), - new PassengerRequestScheduledEvent(departureTime, MODE, requestId, fixture.PERSON_ID, null, departureTime, + new PassengerWaitingEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID)), + new PassengerRequestScheduledEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID), null, departureTime, arrivalTime), new PassengerPickedUpEvent(departureTime, MODE, requestId, fixture.PERSON_ID, null), new PassengerDroppedOffEvent(arrivalTime, MODE, requestId, fixture.PERSON_ID, null), new TeleportationArrivalEvent(arrivalTime, fixture.PERSON_ID, travelDistance, MODE), @@ -84,7 +83,7 @@ public void test_valid_teleported() { @Test public void test_invalid_rejected() { double departureTime = 0; - fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime); + fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, fixture.PERSON_ID); TeleportedRouteCalculator teleportedRouteCalculator = request -> null; // unused PassengerRequestValidator requestValidator = request -> Set.of("invalid"); @@ -92,10 +91,11 @@ public void test_invalid_rejected() { var requestId = Id.create("taxi_0", Request.class); fixture.assertPassengerEvents( + Collections.singleton(fixture.PERSON_ID), new ActivityEndEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), null, START_ACTIVITY), new PersonDepartureEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), MODE, MODE), - new PassengerWaitingEvent(departureTime, MODE, requestId, fixture.PERSON_ID), - new PassengerRequestRejectedEvent(departureTime, MODE, requestId, fixture.PERSON_ID, "invalid"), + new PassengerWaitingEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID)), + new PassengerRequestRejectedEvent(departureTime, MODE, requestId, List.of(fixture.PERSON_ID), "invalid"), new PersonStuckEvent(departureTime, fixture.PERSON_ID, fixture.linkAB.getId(), MODE)); } diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/util/DvrpEventsReadersTest.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/util/DvrpEventsReadersTest.java index 6f661026552..b9452d7c495 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/util/DvrpEventsReadersTest.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/util/DvrpEventsReadersTest.java @@ -26,6 +26,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import org.junit.Test; @@ -71,9 +72,9 @@ private enum TestTaskType implements Task.TaskType { } private final List dvrpEvents = List.of( - new PassengerRequestSubmittedEvent(0, mode, request, person, fromLink, toLink), - new PassengerRequestScheduledEvent(1, mode, request, person, vehicle, 100, 200), - new PassengerRequestRejectedEvent(2, mode, request, person, "cause_1"), + new PassengerRequestSubmittedEvent(0, mode, request, List.of(person), fromLink, toLink), + new PassengerRequestScheduledEvent(1, mode, request, List.of(person), vehicle, 100, 200), + new PassengerRequestRejectedEvent(2, mode, request, List.of(person), "cause_1"), new PassengerPickedUpEvent(111, mode, request, person, vehicle), new PassengerDroppedOffEvent(222, mode, request, person, vehicle), new TaskStartedEvent(300, mode, vehicle, driver, TestTaskType.DRIVE_TASK, 0, fromLink), diff --git a/contribs/taxi/src/main/java/org/matsim/contrib/taxi/scheduler/TaxiScheduler.java b/contribs/taxi/src/main/java/org/matsim/contrib/taxi/scheduler/TaxiScheduler.java index 320420d0c10..eb9df3f0bd1 100644 --- a/contribs/taxi/src/main/java/org/matsim/contrib/taxi/scheduler/TaxiScheduler.java +++ b/contribs/taxi/src/main/java/org/matsim/contrib/taxi/scheduler/TaxiScheduler.java @@ -142,7 +142,7 @@ public void scheduleRequest(DvrpVehicle vehicle, DrtRequest request, VrpPathWith eventsManager.processEvent( new PassengerRequestScheduledEvent(mobsimTimer.getTimeOfDay(), request.getMode(), request.getId(), - request.getPassengerId(), vehicle.getId(), pickupEndTime, dropoffStartTime)); + request.getPassengerIds(), vehicle.getId(), pickupEndTime, dropoffStartTime)); } protected void divertOrAppendDrive(Schedule schedule, VrpPathWithTravelData vrpPath, TaxiTaskType taskType) { diff --git a/contribs/taxi/src/main/java/org/matsim/contrib/taxi/util/TaxiSimulationConsistencyChecker.java b/contribs/taxi/src/main/java/org/matsim/contrib/taxi/util/TaxiSimulationConsistencyChecker.java index dfc34ad205b..d9bf48770c2 100644 --- a/contribs/taxi/src/main/java/org/matsim/contrib/taxi/util/TaxiSimulationConsistencyChecker.java +++ b/contribs/taxi/src/main/java/org/matsim/contrib/taxi/util/TaxiSimulationConsistencyChecker.java @@ -49,7 +49,7 @@ public void addCheckAllRequestsPerformed() { LogManager.getLogger(getClass()) .warn("Taxi request not performed. Request time:\t" + Time.writeTime( seq.getSubmitted().getTime()) + "\tPassenger:\t" + seq.getSubmitted() - .getPersonId()); + .getPersonIds()); } } }