Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update prebooking stop activity #3447

Merged
merged 3 commits into from
Sep 4, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -39,9 +39,13 @@ public class PrebookingStopActivity extends FirstLastSimStepDynActivity implemen
private final Map<Id<Request>, ? extends AcceptedDrtRequest> pickupRequests;
private final Map<Id<Request>, ? extends AcceptedDrtRequest> dropoffRequests;

private final IdMap<Request, Double> enterTimes = new IdMap<>(Request.class);
private final Queue<QueuedRequest> enterTimes = new PriorityQueue<>();
private final Queue<QueuedRequest> leaveTimes = new PriorityQueue<>();
private final Set<Id<Request>> enteredRequests = new HashSet<>();

private final IdSet<Request> enteredRequests = new IdSet<>(Request.class);

private final IdSet<Request> registeredPickups = new IdSet<>(Request.class);
private final IdMap<Request, AcceptedDrtRequest> expectedPickups = new IdMap<>(Request.class);

private final PrebookingManager prebookingManager;
private final PassengerHandler passengerHandler;
Expand Down Expand Up @@ -93,7 +97,6 @@ private void initDropoffRequests(double now) {
}

private boolean updateDropoffRequests(double now) {

while (!leaveTimes.isEmpty() && leaveTimes.peek().time <= now) {
Id<Request> requestId = leaveTimes.poll().id;
passengerHandler.dropOffPassengers(driver, requestId, now);
Expand All @@ -105,60 +108,94 @@ private boolean updateDropoffRequests(double now) {
}

private record QueuedRequest(Id<Request> id, double time) implements Comparable<QueuedRequest> {

@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.entrySet().iterator();
var enterIterator = enterTimes.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
Expand All @@ -169,7 +206,10 @@ protected void simStep(double now) {
@Override
public void notifyPassengersAreReadyForDeparture(List<MobsimPassengerAgent> passengers, double now) {
var request = getRequestForPassengers(passengers.stream().map(Identifiable::getId).toList());
queuePickup(request, now);
if(expectedPickups.containsKey(request.getId())) {
queuePickup(request, now);
expectedPickups.remove(request.getId());
}
}

private AcceptedDrtRequest getRequestForPassengers(List<Id<Person>> passengerIds) {
Expand Down
Loading