Skip to content

Commit

Permalink
Merge pull request #2991 from moia-oss/prebookingGroupBookingConsolid…
Browse files Browse the repository at this point in the history
…ation

Draft: DRT consolidate prebookings and group bookings
  • Loading branch information
nkuehnel authored Dec 20, 2023
2 parents c0622eb + bf5b0a0 commit dbb186e
Show file tree
Hide file tree
Showing 16 changed files with 306 additions and 400 deletions.
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
package org.matsim.contrib.drt.prebooking;

import java.util.List;
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.population.Person;
import org.matsim.contrib.dvrp.optimizer.Request;
import org.matsim.contrib.dvrp.passenger.AbstractPassengerRequestEvent;

import java.util.*;

/**
* @author Sebastian Hörl (sebhoerl), IRT SystemX
*/
public class PassengerRequestBookedEvent extends AbstractPassengerRequestEvent {
public static final String EVENT_TYPE = "PassengerRequest booked";

public PassengerRequestBookedEvent(double time, String mode, Id<Request> requestId, Id<Person> personId) {
super(time, mode, requestId, List.of(personId));
public PassengerRequestBookedEvent(double time, String mode, Id<Request> requestId, List<Id<Person>> personIds) {
super(time, mode, requestId, personIds);
}

@Override
Expand All @@ -30,7 +28,8 @@ public static PassengerRequestBookedEvent convert(GenericEvent event) {
double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME));
String mode = Objects.requireNonNull(attributes.get(ATTRIBUTE_MODE));
Id<Request> requestId = Id.create(attributes.get(ATTRIBUTE_REQUEST), Request.class);
Id<Person> personId = Id.createPersonId(attributes.get(ATTRIBUTE_PERSON));
return new PassengerRequestBookedEvent(time, mode, requestId, personId);
String[] personIdsAttribute = attributes.get(ATTRIBUTE_PERSON).split(",");
List<Id<Person>> personIds = Arrays.stream(personIdsAttribute).map(Id::createPersonId).toList();
return new PassengerRequestBookedEvent(time, mode, requestId, personIds);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,7 @@
package org.matsim.contrib.drt.prebooking;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.Nullable;

import com.google.common.base.Preconditions;
import com.google.common.base.Verify;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.IdMap;
import org.matsim.api.core.v01.IdSet;
Expand All @@ -25,14 +17,7 @@
import org.matsim.contrib.dvrp.fleet.DvrpVehicle;
import org.matsim.contrib.dvrp.optimizer.Request;
import org.matsim.contrib.dvrp.optimizer.VrpOptimizer;
import org.matsim.contrib.dvrp.passenger.AdvanceRequestProvider;
import org.matsim.contrib.dvrp.passenger.PassengerRequest;
import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator;
import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent;
import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEventHandler;
import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEvent;
import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEventHandler;
import org.matsim.contrib.dvrp.passenger.PassengerRequestValidator;
import org.matsim.contrib.dvrp.passenger.*;
import org.matsim.core.api.experimental.events.EventsManager;
import org.matsim.core.mobsim.framework.MobsimAgent;
import org.matsim.core.mobsim.framework.MobsimAgent.State;
Expand All @@ -43,8 +28,10 @@
import org.matsim.core.mobsim.qsim.agents.WithinDayAgentUtils;
import org.matsim.core.mobsim.qsim.interfaces.MobsimEngine;

import com.google.common.base.Preconditions;
import com.google.common.base.Verify;
import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
* This class manages prebooked requests. One instance of PrebookingManager
Expand Down Expand Up @@ -140,6 +127,8 @@ public void handleEvent(PersonStuckEvent event) {
private record RejectionItem(Id<Request> requestId, List<Id<Person>> personIds, String cause) {
}

public record PersonLeg(MobsimAgent agent, Leg leg){}

private final ConcurrentLinkedQueue<RejectionItem> rejections = new ConcurrentLinkedQueue<>();

private void processRejection(PassengerRequest request, String cause) {
Expand All @@ -163,35 +152,47 @@ private void flushRejections(double now) {
// collects new bookings that need to be submitted
private final ConcurrentLinkedQueue<PassengerRequest> bookingQueue = new ConcurrentLinkedQueue<>();

public void prebook(MobsimAgent person, Leg leg, double earliestDepartureTime) {
Preconditions.checkArgument(leg.getMode().equals(mode), "Invalid mode for this prebooking manager");
Preconditions.checkState(!person.getState().equals(State.ABORT), "Cannot prebook aborted agent");
public void prebook(MobsimAgent agent, Leg leg, double earliestDepartureTime) {
prebook(List.of(new PersonLeg(agent, leg)), earliestDepartureTime);
}

public void prebook(List<PersonLeg> personsLegs, double earliestDepartureTime) {
for (PersonLeg personLeg : personsLegs) {
Preconditions.checkArgument(personLeg.leg().getMode().equals(mode), "Invalid mode for this prebooking manager");
Preconditions.checkState(!personLeg.agent().getState().equals(State.ABORT), "Cannot prebook aborted agent");
}

Id<Request> requestId = createRequestId();
double now = mobsimTimer.getTimeOfDay();

eventsManager.processEvent(new PassengerRequestBookedEvent(now, mode, requestId, person.getId()));
List<Id<Person>> personIds = personsLegs.stream().map(p -> p.agent().getId()).toList();
eventsManager.processEvent(new PassengerRequestBookedEvent(now, mode, requestId, personIds));

PassengerRequest request = requestCreator.createRequest(requestId, List.of(person.getId()), leg.getRoute(),
getLink(leg.getRoute().getStartLinkId()), getLink(leg.getRoute().getEndLinkId()), earliestDepartureTime,
Leg representativeLeg = personsLegs.get(0).leg();
PassengerRequest request = requestCreator.createRequest(requestId, personIds, representativeLeg.getRoute(),
getLink(representativeLeg.getRoute().getStartLinkId()), getLink(representativeLeg.getRoute().getEndLinkId()), earliestDepartureTime,
now);

Set<String> violations = requestValidator.validateRequest(request);

Plan plan = WithinDayAgentUtils.getModifiablePlan(person);
int currentLegIndex = WithinDayAgentUtils.getCurrentPlanElementIndex(person);
int prebookingLegIndex = plan.getPlanElements().indexOf(leg);
for (PersonLeg personLeg : personsLegs) {
Plan plan = WithinDayAgentUtils.getModifiablePlan(personLeg.agent());
int currentLegIndex = WithinDayAgentUtils.getCurrentPlanElementIndex(personLeg.agent());
int prebookingLegIndex = plan.getPlanElements().indexOf(personLeg.leg());

if (prebookingLegIndex <= currentLegIndex) {
violations = new HashSet<>(violations);
violations.add("past leg"); // the leg for which the booking was made has already happened
if (prebookingLegIndex <= currentLegIndex) {
violations = new HashSet<>(violations);
violations.add("past leg"); // the leg for which the booking was made has already happened
}
}

if (!violations.isEmpty()) {
String cause = String.join(", ", violations);
processRejection(request, cause);
} else {
leg.getAttributes().putAttribute(requestAttribute, request.getId().toString());
for (PersonLeg personLeg : personsLegs) {
personLeg.leg().getAttributes().putAttribute(requestAttribute, request.getId().toString());
}
bookingQueue.add(request);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.matsim.contrib.drt.prebooking;

import com.google.inject.Singleton;
import org.matsim.api.core.v01.network.Network;
import org.matsim.api.core.v01.population.Population;
import org.matsim.contrib.drt.optimizer.VehicleEntry;
Expand All @@ -15,10 +16,7 @@
import org.matsim.contrib.drt.vrpagent.DrtActionCreator;
import org.matsim.contrib.dvrp.fleet.DvrpVehicleLookup;
import org.matsim.contrib.dvrp.optimizer.VrpOptimizer;
import org.matsim.contrib.dvrp.passenger.PassengerEngine;
import org.matsim.contrib.dvrp.passenger.PassengerHandler;
import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator;
import org.matsim.contrib.dvrp.passenger.PassengerRequestValidator;
import org.matsim.contrib.dvrp.passenger.*;
import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule;
import org.matsim.contrib.dvrp.schedule.ScheduleTimingUpdater;
import org.matsim.core.api.experimental.events.EventsManager;
Expand All @@ -27,8 +25,6 @@
import org.matsim.core.router.util.LeastCostPathCalculator;
import org.matsim.core.router.util.TravelTime;

import com.google.inject.Singleton;

public class PrebookingModeQSimModule extends AbstractDvrpModeQSimModule {
private final PrebookingParams prebookingParams;

Expand Down Expand Up @@ -65,7 +61,7 @@ protected void configureQSim() {
addModalQSimComponentBinding().to(modalKey(PrebookingManager.class));

bindModal(PrebookingQueue.class).toProvider(modalProvider(getter -> {
return new PrebookingQueue(getter.getModal(PrebookingManager.class));
return new PrebookingQueue(getter.getModal(PrebookingManager.class), getter.getModal(PassengerGroupIdentifier.class));
})).in(Singleton.class);
addModalQSimComponentBinding().to(modalKey(PrebookingQueue.class));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
package org.matsim.contrib.drt.prebooking.logic.helpers;

import java.util.PriorityQueue;

import com.google.common.base.Preconditions;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.population.Leg;
import org.matsim.contrib.drt.prebooking.PrebookingManager;
import org.matsim.contrib.dvrp.passenger.PassengerGroupIdentifier;
import org.matsim.core.mobsim.framework.MobsimAgent;
import org.matsim.core.mobsim.framework.MobsimPassengerAgent;
import org.matsim.core.mobsim.framework.events.MobsimBeforeSimStepEvent;
import org.matsim.core.mobsim.framework.listeners.MobsimBeforeSimStepListener;

import com.google.common.base.Preconditions;
import java.util.*;

/**
* This service helps to define a PrebookingLogic where at some point in time
* (usually at the beginning of the simulaton), it is known in advance that a
* request will happen at a specific time. Once we know that the request will be
* sent, we can use the present class to put it in a queue, making sure the
* request will be *booked* the the time we want.
*
*
* @author Sebastian Hörl (sebhoerl), IRT SystemX
*/
public class PrebookingQueue implements MobsimBeforeSimStepListener {
Expand All @@ -27,8 +29,11 @@ public class PrebookingQueue implements MobsimBeforeSimStepListener {

private double currentTime = Double.NEGATIVE_INFINITY;

public PrebookingQueue(PrebookingManager prebookingManager) {
private final PassengerGroupIdentifier groupIdentifier;

public PrebookingQueue(PrebookingManager prebookingManager, PassengerGroupIdentifier groupIdentifier) {
this.prebookingManager = prebookingManager;
this.groupIdentifier = groupIdentifier;
}

@Override
Expand All @@ -39,9 +44,19 @@ public void notifyMobsimBeforeSimStep(@SuppressWarnings("rawtypes") MobsimBefore
private void performSubmissions(double time) {
currentTime = time;

Map<Id<PassengerGroupIdentifier.PassengerGroup>, List<ScheduledSubmission>> groups = new LinkedHashMap<>();
while (!queue.isEmpty() && queue.peek().submissionTime <= time) {
var item = queue.poll();
prebookingManager.prebook(item.agent(), item.leg(), item.departuretime());
Optional<Id<PassengerGroupIdentifier.PassengerGroup>> groupId = groupIdentifier.getGroupId((MobsimPassengerAgent) item.agent);
if(groupId.isEmpty()) {
prebookingManager.prebook(item.agent(), item.leg(), item.departureTime());
} else {
groups.computeIfAbsent(groupId.get(), k -> new ArrayList<>()).add(item);
}
}
for (List<ScheduledSubmission> group : groups.values()) {
List<PrebookingManager.PersonLeg> personsLegs = group.stream().map(entry -> new PrebookingManager.PersonLeg(entry.agent, entry.leg)).toList();
prebookingManager.prebook(personsLegs, group.get(0).departureTime);
}
}

Expand All @@ -62,7 +77,7 @@ public void schedule(double submissionTime, MobsimAgent agent, Leg leg, double d
}
}

private record ScheduledSubmission(double submissionTime, MobsimAgent agent, Leg leg, double departuretime,
private record ScheduledSubmission(double submissionTime, MobsimAgent agent, Leg leg, double departureTime,
int sequence) implements Comparable<ScheduledSubmission> {
@Override
public int compareTo(ScheduledSubmission o) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,16 @@

package org.matsim.contrib.drt.run;

import com.google.inject.Key;
import com.google.inject.Singleton;
import com.google.inject.name.Names;
import org.matsim.api.core.v01.network.Network;
import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector;
import org.matsim.contrib.drt.fare.DrtFareHandler;
import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingModule;
import org.matsim.contrib.drt.prebooking.analysis.PrebookingModeAnalysisModule;
import org.matsim.contrib.drt.speedup.DrtSpeedUp;
import org.matsim.contrib.drt.stops.DefaultStopTimeCalculator;
import org.matsim.contrib.drt.stops.MinimumStopDurationAdapter;
import org.matsim.contrib.drt.stops.PassengerStopDurationProvider;
import org.matsim.contrib.drt.stops.PrebookingStopTimeCalculator;
import org.matsim.contrib.drt.stops.StaticPassengerStopDurationProvider;
import org.matsim.contrib.drt.stops.StopTimeCalculator;
import org.matsim.contrib.drt.stops.*;
import org.matsim.contrib.dvrp.fleet.FleetModule;
import org.matsim.contrib.dvrp.fleet.FleetSpecification;
import org.matsim.contrib.dvrp.router.DvrpModeRoutingNetworkModule;
Expand All @@ -44,10 +42,6 @@
import org.matsim.core.router.costcalculators.TravelDisutilityFactory;
import org.matsim.core.router.util.TravelTime;

import com.google.inject.Key;
import com.google.inject.Singleton;
import com.google.inject.name.Names;

/**
* @author michalm (Michal Maciejewski)
*/
Expand Down Expand Up @@ -87,15 +81,15 @@ public void install() {
getter.getModal(DrtEventSequenceCollector.class)))).asEagerSingleton();
addControlerListenerBinding().to(modalKey(DrtSpeedUp.class));
});

bindModal(PassengerStopDurationProvider.class).toProvider(modalProvider(getter -> {
return StaticPassengerStopDurationProvider.of(drtCfg.stopDuration, 0.0);
}));

bindModal(DefaultStopTimeCalculator.class).toProvider(modalProvider(getter -> {
return new DefaultStopTimeCalculator(drtCfg.stopDuration);
})).in(Singleton.class);

if (drtCfg.getPrebookingParams().isEmpty()) {
bindModal(StopTimeCalculator.class).toProvider(modalProvider(getter -> {
return new DefaultStopTimeCalculator(drtCfg.stopDuration);
Expand All @@ -105,7 +99,7 @@ public void install() {
PassengerStopDurationProvider provider = getter.getModal(PassengerStopDurationProvider.class);
return new MinimumStopDurationAdapter(new PrebookingStopTimeCalculator(provider), drtCfg.stopDuration);
}));

install(new PrebookingModeAnalysisModule(getMode()));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public SharingMetricsModule(DrtConfigGroup drtConfigGroup) {
@Override
public void install() {
bindModal(SharingMetricsTracker.class).toProvider(modalProvider(getter ->
new SharingMetricsTracker(personId -> true))).asEagerSingleton();
new SharingMetricsTracker())).asEagerSingleton();
addEventHandlerBinding().to(modalKey(SharingMetricsTracker.class));
bindModal(SharingMetricsControlerListener.class).toProvider(modalProvider(getter ->
new SharingMetricsControlerListener(getConfig(), drtConfigGroup,
Expand Down
Loading

0 comments on commit dbb186e

Please sign in to comment.