From 6bd4b3618f53a88a7cfc5be4110cb1d8751e76e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20H=C3=B6rl?= Date: Fri, 30 Aug 2024 10:47:57 +0200 Subject: [PATCH 1/2] potentially more efficient version of PrebookingStopActivity --- .../prebooking/PrebookingStopActivity.java | 96 +++++++++++++------ 1 file changed, 67 insertions(+), 29 deletions(-) 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 fd81c1745f3..3c04ba985a3 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,15 +1,15 @@ package org.matsim.contrib.drt.prebooking; -import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.PriorityQueue; import java.util.Queue; -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.IdSet; import org.matsim.api.core.v01.Identifiable; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.drt.passenger.AcceptedDrtRequest; @@ -39,9 +39,13 @@ public class PrebookingStopActivity extends FirstLastSimStepDynActivity implemen private final Map, ? extends AcceptedDrtRequest> pickupRequests; private final Map, ? extends AcceptedDrtRequest> dropoffRequests; - private final IdMap enterTimes = new IdMap<>(Request.class); + private final Queue enterTimes = new PriorityQueue<>(); private final Queue leaveTimes = new PriorityQueue<>(); - private final Set> enteredRequests = new HashSet<>(); + + private final IdSet enteredRequests = new IdSet<>(Request.class); + + private final IdSet registeredPickups = new IdSet<>(Request.class); + private final IdMap expectedPickups = new IdMap<>(Request.class); private final PrebookingManager prebookingManager; private final PassengerHandler passengerHandler; @@ -93,7 +97,6 @@ private void initDropoffRequests(double now) { } private boolean updateDropoffRequests(double now) { - while (!leaveTimes.isEmpty() && leaveTimes.peek().time <= now) { Id requestId = leaveTimes.poll().id; passengerHandler.dropOffPassengers(driver, requestId, now); @@ -105,60 +108,94 @@ private boolean updateDropoffRequests(double now) { } private record QueuedRequest(Id id, double time) implements Comparable { - @Override public int compareTo(QueuedRequest o) { return Double.compare(this.time, o.time); } } + private int cachedPickupRequestsHash = -1; + private boolean updatePickupRequests(double now, boolean isFirstStep) { - var pickupIterator = pickupRequests.values().iterator(); - - while (pickupIterator.hasNext()) { - var request = pickupIterator.next(); - - if (!enteredRequests.contains(request.getId()) && !enterTimes.containsKey(request.getId())) { - // this is a new request that has been added after the activity has been created - // or that had not arrived yet - - if (passengerHandler.notifyWaitForPassengers(this, this.driver, request.getId())) { - // agent starts to enter - queuePickup(request, now); - } else if (now > request.getEarliestStartTime() && !isFirstStep) { - if (abandonVoter.abandonRequest(now, vehicle, request)) { - prebookingManager.abandon(request.getId()); - } + int pickupRequestsHash = pickupRequests.hashCode(); + + // part 1: check if the pickup list has been updated dynamically + + if (isFirstStep || pickupRequestsHash != cachedPickupRequestsHash) { + cachedPickupRequestsHash = pickupRequestsHash; + + // added requests + for (AcceptedDrtRequest request : pickupRequests.values()) { + if (!registeredPickups.contains(request.getId())) { + // in the first step, this is a standard pickup request, later this is a request that has been added after the activity has been created + expectedPickups.put(request.getId(), request); + registeredPickups.add(request.getId()); + } + } + + // removed requests (for instance via cancellation) + var expectedIterator = expectedPickups.iterator(); + while (expectedIterator.hasNext()) { + if (!pickupRequests.containsKey(expectedIterator.next().getId())) { + // a request has been removed from the list of expected pickups + expectedIterator.remove(); } } } + + // part 2: handle the requests that we expect but which have not arrived yet + + var expectedIterator = expectedPickups.values().iterator(); + while (expectedIterator.hasNext()) { + AcceptedDrtRequest request = expectedIterator.next(); + + if (passengerHandler.notifyWaitForPassengers(this, this.driver, request.getId())) { + // agent starts to enter + queuePickup(request, now); + expectedIterator.remove(); + } else if (now > request.getEarliestStartTime() && !isFirstStep) { + if (abandonVoter.abandonRequest(now, vehicle, request)) { + // abandon the request, but not in the first time step for the sake of event timing + prebookingManager.abandon(request.getId()); + expectedIterator.remove(); + } + } + } + + // part 3: handle the requests that are currently entering the vehicle + + var enterIterator = enterTimes.iterator(); - var enterIterator = enterTimes.entrySet().iterator(); + // logic is as follows: + // - let people enter in the order at which they arrived + their interaction time + // - but in case there is no capacity (others still disembarking) they need to wait while (enterIterator.hasNext()) { var entry = enterIterator.next(); int availableCapacity = vehicle.getCapacity() - onboard; - if (entry.getValue() <= now) { - int requiredCapacity = pickupRequests.get(entry.getKey()).getPassengerCount(); + if (entry.time <= now) { + int requiredCapacity = pickupRequests.get(entry.id).getPassengerCount(); if (requiredCapacity <= availableCapacity) { // let agent enter now - Verify.verify(passengerHandler.tryPickUpPassengers(this, driver, entry.getKey(), now)); - enteredRequests.add(entry.getKey()); + Verify.verify(passengerHandler.tryPickUpPassengers(this, driver, entry.id, now)); + enteredRequests.add(entry.id); onboard += requiredCapacity; enterIterator.remove(); } + } else { + break; } } - return enterTimes.size() == 0 && pickupRequests.size() == enteredRequests.size(); + return expectedPickups.size() == 0 && pickupRequests.size() == enteredRequests.size(); } private void queuePickup(AcceptedDrtRequest request, double now) { prebookingManager.notifyPickup(now, request); double enterTime = now + stopDurationProvider.calcPickupDuration(vehicle, request.getRequest()); - enterTimes.put(request.getId(), enterTime); + enterTimes.add(new QueuedRequest(request.getId(), enterTime)); } @Override @@ -170,6 +207,7 @@ protected void simStep(double now) { public void notifyPassengersAreReadyForDeparture(List passengers, double now) { var request = getRequestForPassengers(passengers.stream().map(Identifiable::getId).toList()); queuePickup(request, now); + expectedPickups.remove(request.getId()); } private AcceptedDrtRequest getRequestForPassengers(List> passengerIds) { From 358f5bca718c3675635a04ef3b46636b5e3023d7 Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Tue, 3 Sep 2024 18:50:44 +0200 Subject: [PATCH 2/2] prebooking stop: check for expected pickups to prevent pickup and abandonment in the same second --- .../contrib/drt/prebooking/PrebookingStopActivity.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 3c04ba985a3..e55af9e76a5 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 @@ -206,8 +206,10 @@ protected void simStep(double now) { @Override public void notifyPassengersAreReadyForDeparture(List passengers, double now) { var request = getRequestForPassengers(passengers.stream().map(Identifiable::getId).toList()); - queuePickup(request, now); - expectedPickups.remove(request.getId()); + if(expectedPickups.containsKey(request.getId())) { + queuePickup(request, now); + expectedPickups.remove(request.getId()); + } } private AcceptedDrtRequest getRequestForPassengers(List> passengerIds) {