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

abort handler #2417

Draft
wants to merge 24 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f76178f
steps towards an abort handler
kainagel Feb 16, 2023
e865493
abort handler more steps
kainagel Mar 13, 2023
a83acbd
1st plausible prototype
kainagel Mar 20, 2023
c10b52b
Merge branch 'master' into abortHandler
vsp-gleich Apr 5, 2023
bc58fd9
changes around drt abort
kainagel Apr 20, 2023
8443eb3
I think that this is actually working (look at output_experienced_pla…
kainagel Apr 21, 2023
9c6aede
add javadoc that the specific interaction activities do not work in w…
kainagel Apr 21, 2023
db50708
modernize config file syntax
kainagel Apr 21, 2023
f6853a1
maintenance
kainagel Apr 21, 2023
367e06b
The first running version for complete DRT rejection handling (#2527)
luchengqi7 Apr 24, 2023
b187344
Add iterations test and relevant files
luchengqi7 May 8, 2023
63514ef
add an addComponent
kainagel Jun 1, 2023
6c401a1
some refactoring; still needs to be debugged and carried further (e.g…
kainagel Jun 1, 2023
0623a9f
Merge branch 'master' into abortHandler
luchengqi7 Jun 11, 2023
3e9ccad
Revert to previous version of Qsim
luchengqi7 Jun 11, 2023
8c97c03
Update QSim.java
luchengqi7 Jun 12, 2023
7fcf1b5
use bigger test case to avoid small demand small drt fleet artifacts
vsp-gleich Jul 6, 2023
18567f8
Set agent memory size to 5
luchengqi7 Jul 25, 2023
436671c
set up drtAbortTest scenario shown in presentation
vsp-gleich Jul 25, 2023
095faa7
Merge remote-tracking branch 'origin/abortHandler' into abortHandler
vsp-gleich Jul 25, 2023
814c31e
Refactor the abort handler
luchengqi7 Oct 19, 2023
831fc49
Merge branch 'master' into abortHandler
luchengqi7 Oct 20, 2023
c49df7d
Update DrtAbortTest.java
luchengqi7 Oct 20, 2023
30115d5
minor update
luchengqi7 Oct 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package org.matsim.contrib.drt.optimizer.abort;

import com.google.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.matsim.api.core.v01.Coord;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.TransportMode;
import org.matsim.api.core.v01.network.Link;
import org.matsim.api.core.v01.network.Network;
import org.matsim.api.core.v01.population.*;
import org.matsim.contrib.drt.run.DrtConfigGroup;
import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup;
import org.matsim.contrib.dvrp.path.VrpPaths;
import org.matsim.core.config.Config;
import org.matsim.core.mobsim.framework.MobsimAgent;
import org.matsim.core.mobsim.framework.MobsimTimer;
import org.matsim.core.mobsim.qsim.AbortHandler;
import org.matsim.core.mobsim.qsim.InternalInterface;
import org.matsim.core.mobsim.qsim.agents.WithinDayAgentUtils;
import org.matsim.core.mobsim.qsim.interfaces.MobsimEngine;
import org.matsim.core.population.routes.GenericRouteImpl;
import org.matsim.core.router.TripStructureUtils;
import org.matsim.core.router.costcalculators.OnlyTimeDependentTravelDisutility;
import org.matsim.core.router.speedy.SpeedyALTFactory;
import org.matsim.core.router.util.LeastCostPathCalculator;
import org.matsim.core.router.util.TravelTime;

import java.util.*;

/**
* Rejected DRT requests will be teleported to the destination based on max travel time
* <p>
* Author: kai
* */
public class BasicDrtAbortHandler implements AbortHandler, MobsimEngine {
public static final String COMPONENT_NAME = "DrtAbortHandler";
public static final String walkAfterRejectMode = "walkAfterReject";
private static final String delimiter = "============";
@Inject
Network network;
@Inject
Population population;
@Inject
MobsimTimer mobsimTimer;

TravelTime travelTime;
LeastCostPathCalculator router;
private static final Logger log = LogManager.getLogger(BasicDrtAbortHandler.class );
private InternalInterface internalInterface;
private final List<MobsimAgent> agents = new ArrayList<>();

List<String> drtModes = new ArrayList<>();
Map<String, Double> alphas = new HashMap<>();
Map<String, Double> betas = new HashMap<>();

@Inject
BasicDrtAbortHandler(Network network, Map<String, TravelTime> travelTimeMap, Config config) {
travelTime = travelTimeMap.get(TransportMode.car);
router = new SpeedyALTFactory().createPathCalculator(network, new OnlyTimeDependentTravelDisutility(travelTime), travelTime);

for (DrtConfigGroup modalElement : MultiModeDrtConfigGroup.get(config).getModalElements()) {
drtModes.add(modalElement.mode);
alphas.put(modalElement.mode, modalElement.maxTravelTimeAlpha);
betas.put(modalElement.mode, modalElement.maxTravelTimeBeta);
}
}

@Override
public boolean handleAbort(MobsimAgent agent) {
log.warn("need to handle abort of agent=" + agent);
PopulationFactory pf = population.getFactory();

if (drtModes.contains(agent.getMode())) {
Plan plan = WithinDayAgentUtils.getModifiablePlan(agent);

printPlan("\n current plan=", plan);

int index = WithinDayAgentUtils.getCurrentPlanElementIndex(agent);

Id<Link> interactionLink = agent.getCurrentLinkId();

double now = mobsimTimer.getTimeOfDay();

Leg leg = (Leg) plan.getPlanElements().get(index);

Id<Link> originallyPlannedDestinationLink = leg.getRoute().getEndLinkId();

// (1) The current leg needs to be modified so that it ends at the current location. And one should somehow tag this
// as a failed drt trip. (There is presumably already a drt rejected event, so this info could also be reconstructed.)
{
leg.setDepartureTime(now);
leg.setTravelTime(0);
leg.setRoute(pf.getRouteFactories().createRoute(GenericRouteImpl.class, interactionLink, interactionLink));
// (startLinkId and endLinkId are _only_ in the route)
}

// (2) An interaction activity needs to be inserted.
{
Coord interactionCoord = network.getLinks().get(interactionLink).getCoord();
// Activity activity = PopulationUtils.createStageActivityFromCoordLinkIdAndModePrefix( interactionCoord, interactionLink, walkAfterRejectMode );
Activity activity = pf.createActivityFromCoord(TripStructureUtils.createStageActivityType(walkAfterRejectMode), interactionCoord);
activity.setMaximumDuration(1.);
plan.getPlanElements().add(index + 1, activity);
// (inserts at given position; pushes everything else forward)
}

// (3) There needs to be a new teleportation leg from here to there.
{
// Leg secondLeg = pf.createLeg( walkAfterRejectMode+"_"+agent.getMode() );
Leg secondLeg = pf.createLeg(walkAfterRejectMode);
secondLeg.setDepartureTime(now);

double directTravelTime = VrpPaths.calcAndCreatePath
(network.getLinks().get(interactionLink), network.getLinks().get(originallyPlannedDestinationLink), now, router, travelTime).getTravelTime();
double estimatedTravelTime = alphas.get(agent.getMode()) * directTravelTime + betas.get(agent.getMode());
secondLeg.setTravelTime(estimatedTravelTime);
secondLeg.setRoute(pf.getRouteFactories().createRoute(GenericRouteImpl.class, interactionLink, originallyPlannedDestinationLink));
plan.getPlanElements().add(index + 2, secondLeg);

}

// (4) reset the agent caches:
WithinDayAgentUtils.resetCaches(agent);

// (5) add the agent to an internal list, which is processed during doSimStep, which formally ends the current
// (aborted) leg, and moves the agent forward in its state machine.
agents.add(agent);

printPlan("plan after splicing=", plan);
}
return true;
}

@Override
public void doSimStep(double time) {
for (MobsimAgent agent : agents) {
agent.endLegAndComputeNextState(time);
// (we haven't actually thrown an abort event, and are planning not to do this here. We probably have thrown a drt
// rejected event. The "endLeg..." method will throw a person arrival event.)

this.internalInterface.arrangeNextAgentState(agent);
}
agents.clear();
}

@Override
public void onPrepareSim() {
}

@Override
public void afterSim() {
}

@Override
public void setInternalInterface(InternalInterface internalInterface) {
this.internalInterface = internalInterface;
}

private static void printPlan(String x, Plan plan) {
log.warn(delimiter);
log.warn(x + plan);
for (PlanElement planElement : plan.getPlanElements()) {
log.warn(planElement);
}
log.warn(delimiter);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package org.matsim.contrib.drt.optimizer.abort;

import com.google.inject.Inject;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.matsim.api.core.v01.TransportMode;
import org.matsim.api.core.v01.events.PersonScoreEvent;
import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent;
import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEventHandler;
import org.matsim.contrib.dvrp.passenger.PassengerRequestSubmittedEvent;
import org.matsim.contrib.dvrp.passenger.PassengerRequestSubmittedEventHandler;
import org.matsim.core.api.experimental.events.EventsManager;
import org.matsim.core.controler.events.IterationEndsEvent;
import org.matsim.core.controler.listener.IterationEndsListener;

import java.util.HashMap;
import java.util.Map;

public class DrtRejectionEventHandler implements PassengerRequestRejectedEventHandler,
PassengerRequestSubmittedEventHandler, IterationEndsListener {
private static final Logger log = LogManager.getLogger(DrtRejectionEventHandler.class );
private final Map<Integer, MutableInt> numberOfRejectionsPerTimeBin = new HashMap<>();
private final Map<Integer, MutableInt> numberOfSubmissionsPerTimeBin = new HashMap<>();
private final Map<Integer, Double> probabilityOfRejectionPerTimeBin = new HashMap<>();

// Key parameters
// TODO: consider make them configurable
private final double timeBinSize = 900;
// Time bin to analyze the probability of being rejected
private final double rejectionCost = 6;
// 6 -> 1 hour of default performing score
private final double learningRate = 0.25;
// (1 - alpha) * old probability + alpha * new probability (0 < alpha <= 1)

@Inject
private EventsManager events;

@Override
public void handleEvent(PassengerRequestRejectedEvent event) {
// Currently, we assume there is only 1 DRT operator with standard DRT mode ("drt")
// Because it is a little tricky to get DRT Config Group here (which can only be acquired via DvrpQSimModule),
// we just use the simple way. For multi-operator, a map can be introduced to store the data for different DRT modes
if (event.getMode().equals(TransportMode.drt)) {
int timeBin = getTimeBin(event.getTime());
numberOfRejectionsPerTimeBin.computeIfAbsent(timeBin, c -> new MutableInt()).increment();
}
}

@Override
public void reset(int iteration) {
PassengerRequestRejectedEventHandler.super.reset(iteration);
if (iteration != 0) {
log.debug(probabilityOfRejectionPerTimeBin.values());
numberOfSubmissionsPerTimeBin.clear();
numberOfRejectionsPerTimeBin.clear();
}
}

@Override
public void notifyIterationEnds(IterationEndsEvent event) {
// Calculate the probability of being rejected at each time bin
for (Integer timeBin : numberOfSubmissionsPerTimeBin.keySet()) {
double probability = numberOfRejectionsPerTimeBin.getOrDefault(timeBin, new MutableInt()).doubleValue() /
numberOfSubmissionsPerTimeBin.get(timeBin).doubleValue();
// Apply exponential discount
probability = learningRate * probability + (1 - learningRate) * probabilityOfRejectionPerTimeBin.getOrDefault(timeBin, 0.);
probabilityOfRejectionPerTimeBin.put(timeBin, probability);
}
}

@Override
public void handleEvent(PassengerRequestSubmittedEvent event) {
if (event.getMode().equals(TransportMode.drt)) {
int timeBin = getTimeBin(event.getTime());
numberOfSubmissionsPerTimeBin.computeIfAbsent(timeBin, c -> new MutableInt()).increment();

// Add a cost for potential rejection
double extraScore = (-1) * rejectionCost * probabilityOfRejectionPerTimeBin.getOrDefault(getTimeBin(event.getTime()), 0.);
events.processEvent(new PersonScoreEvent(event.getTime(), event.getPersonId(), extraScore, "Potential_of_being_rejected"));
}
}

private int getTimeBin(double time) {
return (int) (Math.floor(time / timeBinSize));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.matsim.contrib.drt.optimizer.abort;

import com.google.inject.Singleton;
import org.matsim.core.config.ConfigUtils;
import org.matsim.core.controler.AbstractModule;
import org.matsim.core.mobsim.qsim.AbstractQSimModule;
import org.matsim.core.mobsim.qsim.components.QSimComponentsConfigGroup;

public class DrtRejectionModule extends AbstractModule {
@Override
public void install() {
ConfigUtils.addOrGetModule(this.getConfig(), QSimComponentsConfigGroup.class).addActiveComponent(BasicDrtAbortHandler.COMPONENT_NAME);

bind(DrtRejectionEventHandler.class).in(Singleton.class);
addEventHandlerBinding().to(DrtRejectionEventHandler.class);
addControlerListenerBinding().to(DrtRejectionEventHandler.class);

this.installQSimModule(new AbstractQSimModule() {
@Override
protected void configureQSim() {
this.addQSimComponentBinding(BasicDrtAbortHandler.COMPONENT_NAME).to(BasicDrtAbortHandler.class);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.matsim.contrib.drt.run.examples;

import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup;
import org.matsim.contrib.dvrp.run.DvrpConfigGroup;
import org.matsim.core.config.Config;
import org.matsim.core.config.ConfigUtils;
import org.matsim.vis.otfvis.OTFVisConfigGroup;

public class RunDrtAbortExample {
public static void main(String[] args) {
String configUrl = args[0];
Config config = ConfigUtils.loadConfig(configUrl, new MultiModeDrtConfigGroup(), new DvrpConfigGroup(),
new OTFVisConfigGroup() );
}

}