Skip to content

Commit

Permalink
clean up prebooking logic
Browse files Browse the repository at this point in the history
  • Loading branch information
sebhoerl committed Nov 12, 2023
1 parent ebb1a81 commit 32e7298
Show file tree
Hide file tree
Showing 23 changed files with 651 additions and 351 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,12 @@ public EmptyVehicleRelocator get() {

bindModal(DrtScheduleInquiry.class).to(DrtScheduleInquiry.class).asEagerSingleton();

boolean scheduleWaitBeforeDrive = drtCfg.getPrebookingParams().map(p -> p.scheduleWaitBeforeDrive).orElse(false);
bindModal(RequestInsertionScheduler.class).toProvider(modalProvider(
getter -> new DefaultRequestInsertionScheduler(getter.getModal(Fleet.class),
getter.get(MobsimTimer.class), getter.getModal(TravelTime.class),
getter.getModal(ScheduleTimingUpdater.class), getter.getModal(DrtTaskFactory.class),
getter.getModal(StopTimeCalculator.class), drtCfg.scheduleWaitBeforeDrive)))
getter.getModal(StopTimeCalculator.class), scheduleWaitBeforeDrive)))
.asEagerSingleton();

bindModal(DrtOfferAcceptor.class).toInstance(DrtOfferAcceptor.DEFAULT_ACCEPTOR);
Expand All @@ -192,7 +193,8 @@ public EmptyVehicleRelocator get() {
})).in(Singleton.class);

bindModal(EDrtActionCreator.class).toProvider(modalProvider(getter -> {
VrpAgentLogic.DynActionCreator delegate = drtCfg.prebooking ? getter.getModal(PrebookingActionCreator.class)
VrpAgentLogic.DynActionCreator delegate = drtCfg.getPrebookingParams().isPresent()
? getter.getModal(PrebookingActionCreator.class)
: getter.getModal(DrtActionCreator.class);

// EDrtActionCreator wraps around delegate and initializes consumption trackers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ protected void configureQSim() {
timer);
})).in(Singleton.class);

Preconditions.checkState(!drtCfg.prebooking, "cannot use preplanned schedules with prebooking");
Preconditions.checkState(drtCfg.getPrebookingParams().isEmpty(), "cannot use preplanned schedules with prebooking");
bindModal(VrpAgentLogic.DynActionCreator.class).to(modalKey(DrtActionCreator.class));

bindModal(VrpOptimizer.class).to(modalKey(DrtOptimizer.class));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
import java.net.URL;

import org.junit.Test;
import org.matsim.contrib.drt.prebooking.logic.FixedSharePrebookingLogic;
import org.matsim.contrib.drt.prebooking.PrebookingParams;
import org.matsim.contrib.drt.prebooking.logic.ProbabilityBasedPrebookingLogic;
import org.matsim.contrib.drt.run.DrtConfigGroup;
import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup;
import org.matsim.contrib.dvrp.run.DvrpConfigGroup;
Expand Down Expand Up @@ -51,10 +52,10 @@ public void testWithPrebooking() {
new OTFVisConfigGroup(), new EvConfigGroup());

DrtConfigGroup drtConfig = DrtConfigGroup.getSingleModeDrtConfig(config);
drtConfig.prebooking = true;
drtConfig.addParameterSet(new PrebookingParams());

Controler controller = RunEDrtScenario.createControler(config, false);
FixedSharePrebookingLogic.install(drtConfig.mode, 0.5, 4.0 * 3600.0, controller);
ProbabilityBasedPrebookingLogic.install(controller, drtConfig, 0.5, 4.0 * 3600.0);

controller.run();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,13 @@ public EmptyVehicleRelocator get() {
}).asEagerSingleton();

bindModal(DrtScheduleInquiry.class).to(DrtScheduleInquiry.class).asEagerSingleton();


boolean scheduleWaitBeforeDrive = drtCfg.getPrebookingParams().map(p -> p.scheduleWaitBeforeDrive).orElse(false);
bindModal(RequestInsertionScheduler.class).toProvider(modalProvider(
getter -> new DefaultRequestInsertionScheduler(getter.getModal(Fleet.class),
getter.get(MobsimTimer.class), getter.getModal(TravelTime.class),
getter.getModal(ScheduleTimingUpdater.class), getter.getModal(DrtTaskFactory.class),
getter.getModal(StopTimeCalculator.class), drtCfg.scheduleWaitBeforeDrive)))
getter.getModal(StopTimeCalculator.class), scheduleWaitBeforeDrive)))
.asEagerSingleton();

bindModal(DrtOfferAcceptor.class).toInstance(DrtOfferAcceptor.DEFAULT_ACCEPTOR);
Expand All @@ -160,7 +161,7 @@ public EmptyVehicleRelocator get() {
timer);
})).in(Singleton.class);

if (!drtCfg.prebooking) {
if (drtCfg.getPrebookingParams().isEmpty()) {
bindModal(VrpAgentLogic.DynActionCreator.class).to(modalKey(DrtActionCreator.class));
} else {
bindModal(VrpAgentLogic.DynActionCreator.class).to(modalKey(PrebookingActionCreator.class));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ private void processQueue(double now) {

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

if (!violations.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package org.matsim.contrib.drt.prebooking;

import org.matsim.api.core.v01.network.Network;
import org.matsim.api.core.v01.population.Population;
import org.matsim.contrib.drt.prebooking.logic.helpers.PopulationIterator.PopulationIteratorFactory;
import org.matsim.contrib.drt.prebooking.logic.helpers.PrebookingQueue;
import org.matsim.contrib.drt.stops.PassengerStopDurationProvider;
import org.matsim.contrib.drt.vrpagent.DrtActionCreator;
import org.matsim.contrib.dvrp.optimizer.VrpOptimizer;
Expand All @@ -10,6 +13,7 @@
import org.matsim.contrib.dvrp.passenger.PassengerRequestValidator;
import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule;
import org.matsim.core.api.experimental.events.EventsManager;
import org.matsim.core.mobsim.qsim.QSim;

import com.google.inject.Singleton;

Expand Down Expand Up @@ -39,5 +43,14 @@ protected void configureQSim() {
eventsManager);
})).in(Singleton.class);
addModalQSimComponentBinding().to(modalKey(PrebookingManager.class));

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

bindModal(PopulationIteratorFactory.class).toProvider(modalProvider(getter -> {
return new PopulationIteratorFactory(getter.get(Population.class), getter.get(QSim.class));
}));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.matsim.contrib.drt.prebooking;

import org.matsim.core.config.ReflectiveConfigGroup;

public class PrebookingParams extends ReflectiveConfigGroup {
public static final String SET_NAME = "prebooking";

public PrebookingParams() {
super(SET_NAME);
}

@Parameter
@Comment("Defines whether vehicles drive immediately to the next"
+ "(prebooked) future task and wait for the planned stop to begin, or wait at the current"
+ "position and depart to arrive on time at the following stop. The latter behavior (not"
+ "the default) may lead to larger ucnertainty in highly congested scenarios.")
public boolean scheduleWaitBeforeDrive = false; // in the future, this could also become a double value indicating
// how many minutes before the next stop the vehicle should plan to
// be there
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package org.matsim.contrib.drt.prebooking.logic;

import org.matsim.api.core.v01.events.ActivityStartEvent;
import org.matsim.api.core.v01.events.handler.ActivityStartEventHandler;
import org.matsim.api.core.v01.population.Activity;
import org.matsim.api.core.v01.population.Leg;
import org.matsim.api.core.v01.population.Plan;
import org.matsim.api.core.v01.population.PlanElement;
import org.matsim.contrib.drt.prebooking.PrebookingManager;
import org.matsim.contrib.drt.prebooking.logic.helpers.PrebookingQueue;
import org.matsim.contrib.drt.run.DrtConfigGroup;
import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule;
import org.matsim.core.controler.Controler;
import org.matsim.core.events.MobsimScopeEventHandler;
import org.matsim.core.mobsim.framework.MobsimAgent;
import org.matsim.core.mobsim.qsim.QSim;
import org.matsim.core.mobsim.qsim.agents.WithinDayAgentUtils;
import org.matsim.core.router.TripStructureUtils;
import org.matsim.core.utils.timing.TimeInterpretation;
import org.matsim.core.utils.timing.TimeTracker;

import com.google.common.base.Preconditions;

/**
* This is a prebooking logic that, whenever an agent starts an activity, checks
* whether there is a DRT leg coming up before the next main (non-stage)
* activity. If so, the upcoming leg is prebooked in advance with the
* submissionSlack parameter defining how much in advance.
*
* @author Sebastian Hörl (sebhoerl), IRT SystemX
*/
public class AdaptivePrebookingLogic implements PrebookingLogic, ActivityStartEventHandler, MobsimScopeEventHandler {
private final QSim qsim;
private final PrebookingManager prebookingManager;
private final PrebookingQueue prebookingQueue;
private final TimeInterpretation timeInterpretation;

private final String mode;

private final double submissionSlack;

private AdaptivePrebookingLogic(String mode, QSim qsim, PrebookingManager prebookingManager,
PrebookingQueue prebookingQueue, TimeInterpretation timeInterpretation, double submissionSlack) {
this.prebookingManager = prebookingManager;
this.prebookingQueue = prebookingQueue;
this.qsim = qsim;
this.mode = mode;
this.timeInterpretation = timeInterpretation;
this.submissionSlack = submissionSlack;
}

@Override
public void handleEvent(ActivityStartEvent event) {
MobsimAgent agent = qsim.getAgents().get(event.getPersonId());

Plan plan = WithinDayAgentUtils.getModifiablePlan(agent);
int planElementIndex = WithinDayAgentUtils.getCurrentPlanElementIndex(agent);

TimeTracker timeTracker = new TimeTracker(timeInterpretation);
timeTracker.setTime(event.getTime());

/*
* Now we starting to traverse the remaining plan to see if we find a DRT leg
* that comes after the currently started activity. If so, we prebook it with
* the configured submission slack. We start searching one we reach the next
* non-stage activity type.
*/

for (int i = planElementIndex + 1; i < plan.getPlanElements().size(); i++) {
PlanElement element = plan.getPlanElements().get(i);

if (element instanceof Activity) {
Activity activity = (Activity) element;

if (!TripStructureUtils.isStageActivityType(activity.getType())) {
break; // only consider legs coming directly after the ongoing activity
}
} else if (element instanceof Leg) {
Leg leg = (Leg) element;

if (mode.equals(leg.getMode())) {
double departureTime = timeTracker.getTime().seconds();

if (prebookingManager.getRequestId(leg) == null) {
// only book legs that are not already prebooked

double submissionTime = Math.max(event.getTime(), departureTime - submissionSlack);
if (submissionTime > departureTime) {
prebookingQueue.schedule(submissionTime, agent, leg, departureTime);
}
}
}
}

timeTracker.addElement(element);
}
}

static public AbstractDvrpModeQSimModule createModule(DrtConfigGroup drtConfig, double subissionSlack) {
return new AbstractDvrpModeQSimModule(drtConfig.getMode()) {
@Override
protected void configureQSim() {
bindModal(AdaptivePrebookingLogic.class).toProvider(modalProvider(getter -> {
Preconditions.checkState(drtConfig.getPrebookingParams().isPresent());

return new AdaptivePrebookingLogic(drtConfig.getMode(), getter.get(QSim.class),
getter.getModal(PrebookingManager.class), getter.getModal(PrebookingQueue.class),
getter.get(TimeInterpretation.class), subissionSlack);
}));
addMobsimScopeEventHandlerBinding().to(modalKey(AdaptivePrebookingLogic.class));
}
};
}

static public void install(Controler controller, DrtConfigGroup drtConfig, double subissionSlack) {
controller.addOverridingQSimModule(createModule(drtConfig, subissionSlack));
}
}
Loading

0 comments on commit 32e7298

Please sign in to comment.