diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/abort/BasicDrtAbortHandler.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/abort/BasicDrtAbortHandler.java
new file mode 100644
index 00000000000..d2ad77c6bfb
--- /dev/null
+++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/abort/BasicDrtAbortHandler.java
@@ -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
+ *
+ * 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 agents = new ArrayList<>();
+
+ List drtModes = new ArrayList<>();
+ Map alphas = new HashMap<>();
+ Map betas = new HashMap<>();
+
+ @Inject
+ BasicDrtAbortHandler(Network network, Map 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 interactionLink = agent.getCurrentLinkId();
+
+ double now = mobsimTimer.getTimeOfDay();
+
+ Leg leg = (Leg) plan.getPlanElements().get(index);
+
+ Id 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);
+ }
+}
diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/abort/DrtRejectionEventHandler.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/abort/DrtRejectionEventHandler.java
new file mode 100644
index 00000000000..31837b623d7
--- /dev/null
+++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/abort/DrtRejectionEventHandler.java
@@ -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 numberOfRejectionsPerTimeBin = new HashMap<>();
+ private final Map numberOfSubmissionsPerTimeBin = new HashMap<>();
+ private final Map 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));
+ }
+
+}
diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/abort/DrtRejectionModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/abort/DrtRejectionModule.java
new file mode 100644
index 00000000000..25592c4d254
--- /dev/null
+++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/optimizer/abort/DrtRejectionModule.java
@@ -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);
+ }
+ });
+ }
+}
diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/examples/RunDrtAbortExample.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/examples/RunDrtAbortExample.java
new file mode 100644
index 00000000000..3869770498a
--- /dev/null
+++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/examples/RunDrtAbortExample.java
@@ -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() );
+ }
+
+}
diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/DrtAbortTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/DrtAbortTest.java
new file mode 100644
index 00000000000..c1c1b3aacf4
--- /dev/null
+++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/DrtAbortTest.java
@@ -0,0 +1,180 @@
+package org.matsim.contrib.drt.run.examples;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.Rule;
+import org.junit.Test;
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.Scenario;
+import org.matsim.api.core.v01.TransportMode;
+import org.matsim.contrib.drt.optimizer.abort.DrtRejectionModule;
+import org.matsim.contrib.drt.run.*;
+import org.matsim.contrib.dvrp.run.DvrpConfigGroup;
+import org.matsim.contrib.dvrp.run.DvrpModule;
+import org.matsim.contrib.dvrp.run.DvrpQSimComponents;
+import org.matsim.core.config.Config;
+import org.matsim.core.config.ConfigUtils;
+import org.matsim.core.config.groups.ReplanningConfigGroup;
+import org.matsim.core.controler.Controler;
+import org.matsim.core.controler.OutputDirectoryHierarchy;
+import org.matsim.core.replanning.strategies.DefaultPlanStrategiesModule;
+import org.matsim.core.router.TripStructureUtils;
+import org.matsim.core.scenario.ScenarioUtils;
+import org.matsim.core.utils.io.IOUtils;
+import org.matsim.examples.ExamplesUtils;
+import org.matsim.testcases.MatsimTestUtils;
+import org.matsim.vis.otfvis.OTFVisConfigGroup;
+
+import java.net.URL;
+
+import static org.matsim.contrib.drt.optimizer.abort.BasicDrtAbortHandler.walkAfterRejectMode;
+import static org.matsim.core.config.groups.ScoringConfigGroup.*;
+
+public class DrtAbortTest{
+ private static final Logger log = LogManager.getLogger(DrtAbortTest.class );
+
+ @Rule
+ public MatsimTestUtils utils = new MatsimTestUtils();
+
+// static final String walkAfterRejectMode = "walkAfterReject";
+
+ enum Variant {simpleTest, iterations, benchmark };
+
+ @Test public void testAbortHandler() {
+ run( Variant.simpleTest);
+ }
+
+ @Test public void testIterations() {
+ run( Variant.iterations );
+ }
+
+ @Test public void testBenchmark() {
+ run( Variant.benchmark );
+ }
+
+ private void run( Variant variant) {
+ Id.resetCaches();
+ URL configUrl = IOUtils.extendUrl( ExamplesUtils.getTestScenarioURL("mielec" ), "mielec_drt_config.xml" );
+ Config config = ConfigUtils.loadConfig(configUrl, new MultiModeDrtConfigGroup(), new DvrpConfigGroup(),
+ new OTFVisConfigGroup() );
+
+ boolean rejectionModule = true;
+ config.controller().setLastIteration(50);
+ config.plans().setInputFile("plans_only_drt_4.0.xml.gz");
+// config.global().setRandomSeed(9999);
+ {
+ ReplanningConfigGroup.StrategySettings settings = new ReplanningConfigGroup.StrategySettings();
+ settings.setStrategyName(DefaultPlanStrategiesModule.DefaultStrategy.ChangeSingleTripMode);
+ settings.setWeight(0.1);
+ config.replanning().addStrategySettings(settings);
+ config.replanning().setFractionOfIterationsToDisableInnovation(0.9);
+ config.replanning().setMaxAgentPlanMemorySize(5);
+ }
+ {
+ config.changeMode().setModes( new String[] { TransportMode.drt, TransportMode.bike });
+ }
+ {
+ ModeParams params = new ModeParams( TransportMode.bike );
+ params.setMarginalUtilityOfTraveling(-6.);
+ config.scoring().addModeParams( params );
+ }
+
+ for ( DrtConfigGroup drtCfg : MultiModeDrtConfigGroup.get( config ).getModalElements()) {
+// drtCfg.vehiclesFile = "vehicles-2-cap-4.xml";
+ drtCfg.vehiclesFile = "vehicles-10-cap-4.xml";
+// drtCfg.vehiclesFile = "vehicles-20-cap-2.xml";
+
+ drtCfg.maxTravelTimeAlpha = 1.5;
+ drtCfg.maxTravelTimeBeta = 600.;
+ drtCfg.maxWaitTime = 300.;
+ drtCfg.stopDuration = 10.;
+ }
+
+ switch ( variant ) {
+ case simpleTest -> {
+ config.controller().setLastIteration(1);
+ config.plans().setInputFile("plans_only_drt_rejection_test.xml");
+ // Chengqi: I have created a special plan for the rejection handler test: 3 requests within 1 time bin (6:45 - 7:00)
+ for ( DrtConfigGroup drtCfg : MultiModeDrtConfigGroup.get( config ).getModalElements()) {
+ drtCfg.vehiclesFile = "vehicles-rejection-test.xml";
+ // Chengqi: I have created a special vehicle file for the rejection handler test: 1 vehicle locates at the departure place of one request
+
+ drtCfg.maxTravelTimeAlpha = 1.2;
+ drtCfg.maxTravelTimeBeta = 100.;
+ drtCfg.maxWaitTime = 10.;
+ drtCfg.stopDuration = 1.;
+ // (Trying to force abort(s); can't say if this is the correct syntax. kai, apr'23)
+ // Chengqi: With this parameter, 2 out of the 3 requests during 6:45-7:00 will be rejected
+ // -> 2/3 probability of being rejected -> 2/3 of penalty to everyone who submit DRT requests
+ // Based on current setup, at iteration 1, we should see person score event for each person
+ // with a negative score of -6: 12 (base penalty) * 2/3 (probability) * 0.75 (learning rate, current) + 0 (previous penalty) * 0.25 (learning rate, previous)
+ // Currently a manual check is performed and passed. Perhaps an integrated test can be implemented here (TODO).
+ }
+ }
+ case benchmark -> rejectionModule = false;
+ case iterations -> {
+ }
+ // What do we want to see?
+
+ // In early iterations, we want many (maybe 20% or 50%) drt_teleported (because of rejections).
+
+ // In late iterations, we want few drt_teleported (because of mode choice).
+
+ // Need to look at the numbers.
+
+ // There should be a certain rejection rate in a given time bin. That should translate into a penalty. The penalty should be reasonable for us.
+
+ // The drt_teleported score should be plausible.
+
+ default -> throw new IllegalStateException("Unexpected value: " + variant);
+ }
+
+ config.controller().setOverwriteFileSetting( OutputDirectoryHierarchy.OverwriteFileSetting.deleteDirectoryIfExists );
+ config.controller().setOutputDirectory(utils.getOutputDirectory());
+
+ config.scoring().addActivityParams( new ActivityParams( TripStructureUtils.createStageActivityType( walkAfterRejectMode ) ).setScoringThisActivityAtAll( false ) );
+ config.scoring().addModeParams( new ModeParams( walkAfterRejectMode ) );
+
+ config.scoring().setWriteExperiencedPlans( true );
+
+// for ( DrtConfigGroup drtCfg : MultiModeDrtConfigGroup.get(config ).getModalElements()) {
+ //relatively high max age to prevent rejections
+// var drtRequestInsertionRetryParams = new DrtRequestInsertionRetryParams();
+// drtRequestInsertionRetryParams.maxRequestAge = 7200;
+// drtCfg.addParameterSet(drtRequestInsertionRetryParams);
+ // I don't know what the above does; might be useful to understand.
+// }
+
+ MultiModeDrtConfigGroup multiModeDrtConfig = MultiModeDrtConfigGroup.get( config );
+ DrtConfigs.adjustMultiModeDrtConfig(multiModeDrtConfig, config.scoring(), config.routing() );
+
+ Scenario scenario = DrtControlerCreator.createScenarioWithDrtRouteFactory( config );
+ ScenarioUtils.loadScenario(scenario );
+
+ Controler controler = new Controler(scenario);
+ controler.addOverridingModule(new DvrpModule() );
+ controler.addOverridingModule(new MultiModeDrtModule() );
+ controler.configureQSimComponents( DvrpQSimComponents.activateAllModes(multiModeDrtConfig ) );
+
+ if (rejectionModule){
+ controler.addOverridingModule( new DrtRejectionModule() );
+ }
+
+ controler.run();
+
+ // yy I cannot say if the expected status is useful here. kai, apr'23
+
+ var expectedStats = RunDrtExampleIT.Stats.newBuilder()
+ .rejectionRate(1.0)
+ .rejections(1)
+ .waitAverage(Double.NaN)
+ .inVehicleTravelTimeMean(Double.NaN)
+ .totalTravelTimeMean(Double.NaN)
+ .build();
+
+ // Chengqi: I commented this line, because NaN cannot be checked (NaN == NaN always false)
+// RunDrtExampleIT.verifyDrtCustomerStatsCloseToExpectedStats(utils.getOutputDirectory(), expectedStats);
+ }
+
+
+}
diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/RunDrtExampleIT.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/RunDrtExampleIT.java
index fe7d10d9b7b..1304ef1f8d3 100644
--- a/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/RunDrtExampleIT.java
+++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/run/examples/RunDrtExampleIT.java
@@ -25,14 +25,22 @@
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
+import com.google.inject.Inject;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.junit.Rule;
import org.junit.Test;
+import org.matsim.api.core.v01.Coord;
import org.matsim.api.core.v01.Id;
+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.optimizer.DrtRequestInsertionRetryParams;
import org.matsim.contrib.drt.optimizer.insertion.repeatedselective.RepeatedSelectiveInsertionSearchParams;
import org.matsim.contrib.drt.optimizer.insertion.selective.SelectiveInsertionSearchParams;
@@ -50,18 +58,36 @@
import org.matsim.core.config.ConfigUtils;
import org.matsim.core.controler.Controler;
import org.matsim.core.controler.OutputDirectoryHierarchy.OverwriteFileSetting;
+import org.matsim.core.gbl.Gbl;
+import org.matsim.core.mobsim.framework.MobsimAgent;
+import org.matsim.core.mobsim.framework.MobsimPassengerAgent;
+import org.matsim.core.mobsim.framework.MobsimTimer;
+import org.matsim.core.mobsim.framework.PassengerAgent;
+import org.matsim.core.mobsim.qsim.AbortHandler;
+import org.matsim.core.mobsim.qsim.AbstractQSimModule;
+import org.matsim.core.mobsim.qsim.InternalInterface;
+import org.matsim.core.mobsim.qsim.agents.WithinDayAgentUtils;
+import org.matsim.core.mobsim.qsim.components.QSimComponentsConfigGroup;
+import org.matsim.core.mobsim.qsim.interfaces.MobsimEngine;
+import org.matsim.core.modal.AbstractModalQSimModule;
+import org.matsim.core.population.PopulationUtils;
+import org.matsim.core.population.routes.GenericRouteImpl;
+import org.matsim.core.router.TripStructureUtils;
import org.matsim.core.utils.io.IOUtils;
import org.matsim.examples.ExamplesUtils;
import org.matsim.testcases.MatsimTestUtils;
import org.matsim.vis.otfvis.OTFVisConfigGroup;
import com.google.common.base.MoreObjects;
+import org.matsim.withinday.utils.EditPlans;
+import org.matsim.withinday.utils.EditTrips;
/**
* @author jbischoff
* @author Sebastian Hörl, IRT SystemX (sebhoerl)
*/
public class RunDrtExampleIT {
+ private static final Logger log = LogManager.getLogger(RunDrtExampleIT.class);
@Rule
public MatsimTestUtils utils = new MatsimTestUtils();
@@ -181,12 +207,12 @@ public void testRunDrtExampleWithRequestRetry() {
RunDrtExample.run(config, false);
var expectedStats = Stats.newBuilder()
- .rejectionRate(0.0)
- .rejections(1)
- .waitAverage(305.97)
- .inVehicleTravelTimeMean(378.18)
- .totalTravelTimeMean(684.16)
- .build();
+ .rejectionRate(0.0)
+ .rejections(1)
+ .waitAverage(305.97)
+ .inVehicleTravelTimeMean(378.18)
+ .totalTravelTimeMean(684.16)
+ .build();
verifyDrtCustomerStatsCloseToExpectedStats(utils.getOutputDirectory(), expectedStats);
}
@@ -314,7 +340,7 @@ public void install() {
* rejectionRate, rejections, waitAverage, inVehicleTravelTimeMean, & totalTravelTimeMean
*/
- private void verifyDrtCustomerStatsCloseToExpectedStats(String outputDirectory, Stats expectedStats) {
+ static void verifyDrtCustomerStatsCloseToExpectedStats( String outputDirectory, Stats expectedStats ) {
String filename = outputDirectory + "/drt_customer_stats_drt.csv";
@@ -345,7 +371,7 @@ private void verifyDrtCustomerStatsCloseToExpectedStats(String outputDirectory,
assertThat(actualStats).usingRecursiveComparison().isEqualTo(expectedStats);
}
- private static class Stats {
+ static class Stats {
private final double rejectionRate;
private final double rejections;
private final double waitAverage;
@@ -415,4 +441,5 @@ public Stats build() {
}
}
}
+
}
diff --git a/contribs/osm/src/main/java/org/matsim/contrib/osm/networkReader/LinkProperties.java b/contribs/osm/src/main/java/org/matsim/contrib/osm/networkReader/LinkProperties.java
index 41d2b302d37..aa14def431b 100644
--- a/contribs/osm/src/main/java/org/matsim/contrib/osm/networkReader/LinkProperties.java
+++ b/contribs/osm/src/main/java/org/matsim/contrib/osm/networkReader/LinkProperties.java
@@ -26,6 +26,7 @@ public class LinkProperties {
* @see #calculateSpeedIfSpeedTag(double)
*/
public static final double DEFAULT_FREESPEED_FACTOR = 0.9;
+ // This was calibrated by CR against OD travel times from here.com. Probably during day excluding rush hour.
/**
* Increase lane capacity for links shorter than this value, assuming they are crossing.
diff --git a/examples/scenarios/mielec/mielec_drt_config.xml b/examples/scenarios/mielec/mielec_drt_config.xml
index db2a8763ade..f6da9df061d 100644
--- a/examples/scenarios/mielec/mielec_drt_config.xml
+++ b/examples/scenarios/mielec/mielec_drt_config.xml
@@ -65,7 +65,9 @@
-
-
+
+
+
+
diff --git a/examples/scenarios/mielec/mielec_edrt_config.xml b/examples/scenarios/mielec/mielec_edrt_config.xml
index fdb3bd3c0f6..9f5b1eef218 100644
--- a/examples/scenarios/mielec/mielec_edrt_config.xml
+++ b/examples/scenarios/mielec/mielec_edrt_config.xml
@@ -82,7 +82,9 @@
-
-
+
+
+
+
diff --git a/examples/scenarios/mielec/mielec_etaxi_benchmark_config.xml b/examples/scenarios/mielec/mielec_etaxi_benchmark_config.xml
index 5102b37d246..38a0004109c 100644
--- a/examples/scenarios/mielec/mielec_etaxi_benchmark_config.xml
+++ b/examples/scenarios/mielec/mielec_etaxi_benchmark_config.xml
@@ -68,7 +68,9 @@
-
-
+
+
+
+
diff --git a/examples/scenarios/mielec/mielec_etaxi_config.xml b/examples/scenarios/mielec/mielec_etaxi_config.xml
index 59bfc20c581..6b11380d02a 100644
--- a/examples/scenarios/mielec/mielec_etaxi_config.xml
+++ b/examples/scenarios/mielec/mielec_etaxi_config.xml
@@ -74,7 +74,9 @@
-
-
+
+
+
+
diff --git a/examples/scenarios/mielec/mielec_etaxi_config_assignment.xml b/examples/scenarios/mielec/mielec_etaxi_config_assignment.xml
index b5df7dc9a9e..745fb5a4933 100644
--- a/examples/scenarios/mielec/mielec_etaxi_config_assignment.xml
+++ b/examples/scenarios/mielec/mielec_etaxi_config_assignment.xml
@@ -81,7 +81,9 @@
-
-
+
+
+
+
diff --git a/examples/scenarios/mielec/mielec_serviceArea_based_drt_config.xml b/examples/scenarios/mielec/mielec_serviceArea_based_drt_config.xml
index d2bdd9305f7..a43bc86595c 100644
--- a/examples/scenarios/mielec/mielec_serviceArea_based_drt_config.xml
+++ b/examples/scenarios/mielec/mielec_serviceArea_based_drt_config.xml
@@ -79,7 +79,9 @@
-
-
+
+
+
+
diff --git a/examples/scenarios/mielec/mielec_stop_based_drt_config.xml b/examples/scenarios/mielec/mielec_stop_based_drt_config.xml
index dd3df82fa60..00c0e17cf50 100644
--- a/examples/scenarios/mielec/mielec_stop_based_drt_config.xml
+++ b/examples/scenarios/mielec/mielec_stop_based_drt_config.xml
@@ -75,7 +75,9 @@
-
-
+
+
+
+
diff --git a/examples/scenarios/mielec/mielec_taxi_benchmark_config.xml b/examples/scenarios/mielec/mielec_taxi_benchmark_config.xml
index f94bca1d15e..a2ca777411d 100644
--- a/examples/scenarios/mielec/mielec_taxi_benchmark_config.xml
+++ b/examples/scenarios/mielec/mielec_taxi_benchmark_config.xml
@@ -58,7 +58,9 @@
-
-
+
+
+
+
diff --git a/examples/scenarios/mielec/mielec_taxi_config.xml b/examples/scenarios/mielec/mielec_taxi_config.xml
index 43389699cba..f37c7d92231 100644
--- a/examples/scenarios/mielec/mielec_taxi_config.xml
+++ b/examples/scenarios/mielec/mielec_taxi_config.xml
@@ -59,7 +59,9 @@
-
-
+
+
+
+
diff --git a/examples/scenarios/mielec/mielec_taxi_mini_benchmark_config.xml b/examples/scenarios/mielec/mielec_taxi_mini_benchmark_config.xml
index 8ab14beb61a..be6c84cd348 100644
--- a/examples/scenarios/mielec/mielec_taxi_mini_benchmark_config.xml
+++ b/examples/scenarios/mielec/mielec_taxi_mini_benchmark_config.xml
@@ -54,7 +54,9 @@
-
-
+
+
+
+
diff --git a/examples/scenarios/mielec/plans_only_drt_rejection_test.xml b/examples/scenarios/mielec/plans_only_drt_rejection_test.xml
new file mode 100644
index 00000000000..d3a7b5cc06c
--- /dev/null
+++ b/examples/scenarios/mielec/plans_only_drt_rejection_test.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/scenarios/mielec/vehicles-2-cap-4.xml b/examples/scenarios/mielec/vehicles-2-cap-4.xml
new file mode 100644
index 00000000000..776b28ba124
--- /dev/null
+++ b/examples/scenarios/mielec/vehicles-2-cap-4.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/examples/scenarios/mielec/vehicles-rejection-test.xml b/examples/scenarios/mielec/vehicles-rejection-test.xml
new file mode 100644
index 00000000000..f5de23d8f05
--- /dev/null
+++ b/examples/scenarios/mielec/vehicles-rejection-test.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/matsim/src/main/java/org/matsim/api/core/v01/population/PopulationFactory.java b/matsim/src/main/java/org/matsim/api/core/v01/population/PopulationFactory.java
index 16e81137b3c..f4648dcddf3 100644
--- a/matsim/src/main/java/org/matsim/api/core/v01/population/PopulationFactory.java
+++ b/matsim/src/main/java/org/matsim/api/core/v01/population/PopulationFactory.java
@@ -44,7 +44,10 @@ public interface PopulationFactory extends MatsimFactory {
* It might in fact make sense to add a creational method that takes coord and link id. kai, aug'10
*/
Activity createActivityFromCoord(String actType, Coord coord);
-
+
+ /**
+ * This presumably works for plans in the agent database, but does not seem to work for within-day replanning. kai, apr'23
+ */
Activity createInteractionActivityFromCoord(String actType, Coord coord);
/**
diff --git a/matsim/src/main/java/org/matsim/core/mobsim/qsim/AbortHandler.java b/matsim/src/main/java/org/matsim/core/mobsim/qsim/AbortHandler.java
new file mode 100644
index 00000000000..52105848b6d
--- /dev/null
+++ b/matsim/src/main/java/org/matsim/core/mobsim/qsim/AbortHandler.java
@@ -0,0 +1,9 @@
+package org.matsim.core.mobsim.qsim;
+
+import org.matsim.core.mobsim.framework.MobsimAgent;
+import org.matsim.core.mobsim.qsim.components.QSimComponent;
+
+public interface AbortHandler extends QSimComponent {
+ boolean handleAbort( MobsimAgent abortHandler );
+}
+
diff --git a/matsim/src/main/java/org/matsim/core/mobsim/qsim/QSim.java b/matsim/src/main/java/org/matsim/core/mobsim/qsim/QSim.java
index 6187c872dd6..489b3eb6b18 100755
--- a/matsim/src/main/java/org/matsim/core/mobsim/qsim/QSim.java
+++ b/matsim/src/main/java/org/matsim/core/mobsim/qsim/QSim.java
@@ -124,6 +124,7 @@ public final class QSim implements VisMobsim, Netsim, ActivityEndRescheduler {
private final MobsimListenerManager listenerManager;
private final Scenario scenario;
private final List activityHandlers = new ArrayList<>();
+ private final List abortHandlers = new ArrayList<>();
private final List departureHandlers = new ArrayList<>();
private final org.matsim.core.mobsim.qsim.AgentCounter agentCounter;
private final Map, MobsimAgent> agents = new LinkedHashMap<>();
@@ -272,8 +273,8 @@ public void run() {
// we want to log this exception thrown during the cleanup, but not to throw it
// since there is another (earlier) exception thrown in the try block
log.warn("exception in finally block - "
- + "this may be a follow-up exception of an exception thrown in the try block.",
- cleanupException);
+ + "this may be a follow-up exception of an exception thrown in the try block.",
+ cleanupException);
} else {
// no exception thrown in the try block, let's re-throw the exception from the cleanup step
throw cleanupException;
@@ -294,8 +295,8 @@ public void run() {
createAgents();
this.initSimTimer();
this.infoTime = Math.floor(this.simTimer.getSimStartTime()
- / INFO_PERIOD)
- * INFO_PERIOD; // infoTime may be < simStartTime, this ensures
+ / INFO_PERIOD)
+ * INFO_PERIOD; // infoTime may be < simStartTime, this ensures
// to print out the info at the very first
// timestep already
@@ -322,7 +323,7 @@ public void addParkedVehicle(MobsimVehicle veh, Id startLinkId) {
} else {
if (wrnCnt2 < 1) {
log.warn( "not able to add parked vehicle since there is no netsim engine. continuing anyway, but it may "
- + "not be clear what this means ...") ;
+ + "not be clear what this means ...") ;
log.warn(Gbl.ONLYONCE);
wrnCnt2++;
}
@@ -456,33 +457,32 @@ public void insertAgentIntoMobsim(final MobsimAgent agent) {
}
private void arrangeNextAgentAction(final MobsimAgent agent) {
- switch( agent.getState() ) {
- case ACTIVITY:
- arrangeAgentActivity(agent);
- break ;
- case LEG:
- this.arrangeAgentDeparture(agent);
- break ;
- case ABORT:
- this.events.processEvent( new PersonStuckEvent(this.simTimer.getTimeOfDay(), agent.getId(), agent.getCurrentLinkId(), agent.getMode()));
-
- // NOTE: in the same way as one can register departure handler or activity handler, we could allow to
- // register abort handlers. If someone ever comes to this place here and needs this. kai, nov'17
-
- this.agents.remove(agent.getId()) ;
- this.agentCounter.decLiving();
- this.agentCounter.incLost();
- break ;
- default:
- throw new RuntimeException("agent with unknown state (possibly null)") ;
- }
- }
-
- private void arrangeAgentActivity(final MobsimAgent agent) {
- for (ActivityHandler activityHandler : this.activityHandlers) {
- if (activityHandler.handleActivity(agent)) {
- return;
+ switch( agent.getState() ){
+ case ACTIVITY -> {
+ for( ActivityHandler activityHandler : this.activityHandlers ){
+ if( activityHandler.handleActivity( agent ) ){
+ break;
+ }
+ }
+ }
+ case LEG -> this.arrangeAgentDeparture( agent );
+ case ABORT -> {
+ for( AbortHandler abortHandler : this.abortHandlers ){
+ if( abortHandler.handleAbort( agent ) ){
+ return;
+ }
+ }
+ this.events.processEvent( new PersonStuckEvent( this.simTimer.getTimeOfDay(), agent.getId(), agent.getCurrentLinkId(), agent.getMode() ) );
+
+ // NOTE: in the same way as one can register departure handler or activity handler, we could allow to
+ // register abort handlers. If someone ever comes to this place here and needs this. kai, nov'17
+ // The above comment has now been satisfied by the introduction of abort handlers. kai, mar'22
+
+ this.agents.remove( agent.getId() );
+ this.agentCounter.decLiving();
+ this.agentCounter.incLost();
}
+ default -> throw new RuntimeException( "agent with unknown state (possibly null)" );
}
}
@@ -561,13 +561,13 @@ private void printSimLog(final double time) {
this.infoTime += INFO_PERIOD;
Date endtime = new Date();
long diffreal = (endtime.getTime() - this.realWorldStarttime
- .getTime()) / 1000;
+ .getTime()) / 1000;
double diffsim = time - this.simTimer.getSimStartTime();
log.info("SIMULATION (NEW QSim) AT " + Time.writeTime(time)
- + " : #Veh=" + this.agentCounter.getLiving() + " lost="
- + this.agentCounter.getLost() + " simT=" + diffsim
- + "s realT=" + (diffreal) + "s; (s/r): "
- + (diffsim / (diffreal + Double.MIN_VALUE)));
+ + " : #Veh=" + this.agentCounter.getLiving() + " lost="
+ + this.agentCounter.getLost() + " simT=" + diffsim
+ + "s realT=" + (diffreal) + "s; (s/r): "
+ + (diffsim / (diffreal + Double.MIN_VALUE)));
}
}
@@ -659,6 +659,11 @@ public void addActivityHandler(ActivityHandler activityHandler) {
}
}
+ public void addAbortHandler(AbortHandler abortHandler ) {
+ Gbl.assertNotNull( abortHandler );
+ this.abortHandlers.add( abortHandler );
+ }
+
/**
* Adds the QueueSimulationListener instance given as parameters as listener
* to this QueueSimulation instance.
@@ -736,7 +741,7 @@ public final void addNetworkChangeEvent( NetworkChangeEvent event ) {
}
if ( !processed ) {
throw new RuntimeException("received a network change event, but did not process it. Maybe " +
- "the network change events engine was not set up for the qsim? Aborting ...") ;
+ "the network change events engine was not set up for the qsim? Aborting ...") ;
}
}
diff --git a/matsim/src/main/java/org/matsim/core/mobsim/qsim/QSimProvider.java b/matsim/src/main/java/org/matsim/core/mobsim/qsim/QSimProvider.java
index 5f11d96de97..15ce8449b83 100644
--- a/matsim/src/main/java/org/matsim/core/mobsim/qsim/QSimProvider.java
+++ b/matsim/src/main/java/org/matsim/core/mobsim/qsim/QSimProvider.java
@@ -155,6 +155,11 @@ protected void configure() {
log.info("Added MobsimListener " + instance.getClass());
}
+ if ( qSimComponent instanceof AbortHandler ) {
+ AbortHandler instance = (AbortHandler) qSimComponent;
+ qSim.addAbortHandler( instance );
+ }
+
}
}
diff --git a/matsim/src/main/java/org/matsim/core/mobsim/qsim/components/QSimComponentsConfigGroup.java b/matsim/src/main/java/org/matsim/core/mobsim/qsim/components/QSimComponentsConfigGroup.java
index 8878ebe4118..79704b119e6 100644
--- a/matsim/src/main/java/org/matsim/core/mobsim/qsim/components/QSimComponentsConfigGroup.java
+++ b/matsim/src/main/java/org/matsim/core/mobsim/qsim/components/QSimComponentsConfigGroup.java
@@ -25,6 +25,7 @@
import java.util.stream.Collectors;
import org.matsim.core.config.ConfigGroup;
+import org.matsim.core.config.ConfigUtils;
import org.matsim.core.config.ReflectiveConfigGroup.StringGetter;
import org.matsim.core.config.ReflectiveConfigGroup.StringSetter;
import org.matsim.core.mobsim.qsim.ActivityEngineModule;
@@ -71,6 +72,7 @@ public void setActiveComponents(List activeComponents) {
this.activeComponents = new ArrayList<>( activeComponentsAsSet ) ;
}
+
public void addActiveComponent( String component ) {
// I need this so often that I am finally adding it here. kai, apr'23
diff --git a/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java b/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java
index 9a572ead6ca..91c920da404 100644
--- a/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java
+++ b/matsim/src/main/java/org/matsim/core/population/PopulationUtils.java
@@ -786,6 +786,9 @@ public static Activity createActivityFromLinkId(String type, Id linkId) {
return getFactory().createActivityFromLinkId(type, linkId) ;
}
+ /**
+ * This presumably works for plans in the agent database, but does not seem to work for within-day replanning. kai, apr'23
+ */
public static Activity createInteractionActivityFromLinkId(String type, Id linkId) {
return getFactory().createInteractionActivityFromLinkId(type, linkId) ;
}
@@ -794,6 +797,9 @@ public static Activity createActivityFromFacilityId(String type, Id facilityId) {
return getFactory().createInteractionActivityFromActivityFacilityId(type, facilityId);
}
@@ -802,6 +808,9 @@ public static Activity createActivityFromCoord(String type, Coord coord) {
return getFactory().createActivityFromCoord(type, coord) ;
}
+ /**
+ * This presumably works for plans in the agent database, but does not seem to work for within-day replanning. kai, apr'23
+ */
public static Activity createInteractionActivityFromCoord(String type, Coord coord) {
return getFactory().createInteractionActivityFromCoord(type, coord) ;
}
@@ -812,6 +821,9 @@ public static Activity createActivityFromCoordAndLinkId(String type, Coord coord
return act ;
}
+ /**
+ * This presumably works for plans in the agent database, but does not seem to work for within-day replanning. kai, apr'23
+ */
public static Activity createInteractionActivityFromCoordAndLinkId(String type, Coord coord, Id linkId) {
Activity act = getFactory().createInteractionActivityFromCoord(type, coord) ;
act.setLinkId(linkId);
diff --git a/matsim/src/main/java/org/matsim/core/router/TripStructureUtils.java b/matsim/src/main/java/org/matsim/core/router/TripStructureUtils.java
index 69ac6c3d0c3..049d3dae53f 100644
--- a/matsim/src/main/java/org/matsim/core/router/TripStructureUtils.java
+++ b/matsim/src/main/java/org/matsim/core/router/TripStructureUtils.java
@@ -385,12 +385,14 @@ public final static class Trip {
private final List trip;
private final List legs;
- Trip( final Activity originActivity,
- final List trip,
+ public Trip( final Activity originActivity,
+ final List planElements,
final Activity destinationActivity) {
+ // (this is needed "public" quite often. Since there is no reason given why it should not be public, I am making it public now. kai, mar'23)
+
this.originActivity = originActivity;
- this.trip = trip;
- this.legs = extractLegs( trip );
+ this.trip = planElements;
+ this.legs = extractLegs( planElements );
this.destinationActivity = destinationActivity;
}
diff --git a/matsim/src/main/java/org/matsim/withinday/utils/EditPlans.java b/matsim/src/main/java/org/matsim/withinday/utils/EditPlans.java
index 9ffc19497e6..614373c521c 100644
--- a/matsim/src/main/java/org/matsim/withinday/utils/EditPlans.java
+++ b/matsim/src/main/java/org/matsim/withinday/utils/EditPlans.java
@@ -232,7 +232,6 @@ public void insertActivity(MobsimAgent agent, int index, Activity activity ) {
String mode = TripStructureUtils.identifyMainMode( EditTrips.findCurrentTrip(agent ).getTripElements() ) ;
insertActivity( agent, index, activity, mode, mode ) ;
}
-
// === internal utility methods: ===
private void checkIfNotStageActivity(Activity origAct) {
if( StageActivityTypeIdentifier.isStageActivity(origAct.getType()) ){
diff --git a/matsim/src/main/java/org/matsim/withinday/utils/EditTrips.java b/matsim/src/main/java/org/matsim/withinday/utils/EditTrips.java
index 36f4776cbee..34d1fffcca3 100644
--- a/matsim/src/main/java/org/matsim/withinday/utils/EditTrips.java
+++ b/matsim/src/main/java/org/matsim/withinday/utils/EditTrips.java
@@ -42,6 +42,7 @@
import org.matsim.core.api.experimental.events.EventsManager;
import org.matsim.core.gbl.Gbl;
import org.matsim.core.mobsim.framework.MobsimAgent;
+import org.matsim.core.mobsim.framework.PassengerAgent;
import org.matsim.core.mobsim.qsim.AgentTracker;
import org.matsim.core.mobsim.qsim.InternalInterface;
import org.matsim.core.mobsim.qsim.agents.WithinDayAgentUtils;
@@ -184,8 +185,10 @@ private void replanCurrentTripFromLeg(Activity newAct, final PlanElement current
} else if ( currentLeg.getRoute() instanceof GenericRouteImpl ) {
// teleported leg
replanCurrentLegWithGenericRoute(newAct, routingMode, currentLeg, now, agent);
+ // yyyy ignores newAct and pulls the next activity from the plan, other than the other parallel methods for other route types. kai, mar'23
} else {
- throw new ReplanningException("not implemented for the route type of the current leg") ;
+ log.warn("replanning not explicitly implemented for the route type of the current leg; falling back on generic method");
+ replanFromCurrentLegWithUnknownRouteType( newAct, routingMode, currentLeg, now, agent, routingAttributes );
}
WithinDayAgentUtils.resetCaches(agent);
}
@@ -222,6 +225,21 @@ private void replanCurrentLegWithNetworkRoute(Activity newAct, String mainMode,
WithinDayAgentUtils.resetCaches(agent);
}
+
+ private void replanFromCurrentLegWithUnknownRouteType( Activity newAct, String mainMode, Leg currentLeg, double now, MobsimAgent agent, Attributes routingAttributes ) {
+ Plan plan = WithinDayAgentUtils.getModifiablePlan(agent) ;
+ List planElements = plan.getPlanElements() ;
+ Person person = plan.getPerson() ;
+ PassengerAgent ptPassengerAgent = (PassengerAgent) agent;
+ MobsimVehicle mobsimVehicle = ptPassengerAgent.getVehicle();
+ if ( mobsimVehicle != null ) {
+ throw new ReplanningException( "agent has already boarded vehicle; don't know what to do." );
+ }
+
+
+ }
+
+
private void replanCurrentLegWithTransitRoute(Activity newAct, String routingMode, Leg currentLeg, double now, MobsimAgent agent, Attributes routingAttributes) {
log.debug("entering replanCurrentLegWithTransitRoute for agentId=" + agent.getId()) ;
@@ -240,38 +258,26 @@ private void replanCurrentLegWithTransitRoute(Activity newAct, String routingMod
TransitPassengerRoute oldPtRoute = (TransitPassengerRoute) currentLeg.getRoute();
/*
- * In AbstractTransitDriverAgent nextStop is moved forward only at departure, so
- * while the vehicle stops "nextStop" is the stop where the vehicle stops at.
- * (see AbstractTransitDriverAgent.depart() and
- * AbstractTransitDriverAgent.processEventVehicleArrives())
- *
- * So if the vehicle stops at a stop facility, driver.getNextTransitStop() will
- * return that stop facility and we can replan the agent from that stop facility.
- * gl may'19
- */
+ * In AbstractTransitDriverAgent nextStop is moved forward only at departure, so while the vehicle stops "nextStop" is the stop where
+ * the vehicle stops at. (see AbstractTransitDriverAgent.depart() and AbstractTransitDriverAgent.processEventVehicleArrives())
+
+ * So if the vehicle stops at a stop facility, driver.getNextTransitStop() will return that stop facility and we can replan the agent
+ * from that stop facility. gl may'19
+
+ * TODO: Routing with the current vehicle as a start: If the agent is currently on a delayed bus, the router won't find that bus when
+ * looking for shortest paths from the next stop, because it assumes the bus has already departed. It will suggest to take another
+ * later bus even if staying on the current (delayed) bus is better. A real-time schedule based router would solve this apart from
+ * transfer times and disutilities (it won't take into account that staying on the same bus is one transfer less than getting off at
+ * the next stop and taking another bus from there). Otherwise one might route from all following stop facilities the delayed bus will
+ * still serve (high computation time, maybe less so with some special kind of many-to-one tree) or move only the corresponding
+ * departure in the schedule (high computation time for preparing the Raptor router's internal data structures, probably worst
+ * option). Not solving this will likely lead to change of routes without a proper reason / some kind of oscillation between two best
+ * or at least similarly good paths.
+
+ * Idea 1: Use scheduled departure time instead of real current time to keep (delayed) bus the passenger is sitting on available from
+ * the router's perspective. Idea 2: Use input schedule file which has real (delayed) departure times as far as known at the time of
+ * withinday-replanning.
- /*
- * TODO: Routing with the current vehicle as a start: If the agent is currently
- * on a delayed bus, the router won't find that bus when looking for shortest
- * paths from the next stop, because it assumes the bus has already departed. It
- * will suggest to take another later bus even if staying on the current
- * (delayed) bus is better. A real-time schedule based router would solve this
- * apart from transfer times and disutilities (it won't take into account that
- * staying on the same bus is one transfer less than getting off at the next
- * stop and taking another bus from there). Otherwise one might route from all
- * following stop facilities the delayed bus will still serve (high computation
- * time, maybe less so with some special kind of many-to-one tree) or move only
- * the corresponding departure in the schedule (high computation time for
- * preparing the Raptor router's internal data structures, probably worst
- * option). Not solving this will likely lead to change of routes without a
- * proper reason / some kind of oscillation between two best or at least
- * similarly good paths.
- *
- * Idea 1: Use scheduled departure time instead of real current time to keep
- * (delayed) bus the passenger is sitting on available from the router's
- * perspective. Idea 2: Use input schedule file which has real (delayed)
- * departure times as far as known at the time of withinday-replanning.
- *
* gl may'19
*/
@@ -281,6 +287,8 @@ private void replanCurrentLegWithTransitRoute(Activity newAct, String routingMod
List extends PlanElement> newTripElements = null;
// (1) get new trip from current position to new activity:
if (mobsimVehicle == null) {
+ // this is (I think) the situation where the agent is on the pt leg, but has not yet boarded a vehicle. kai, mar'23
+
currentOrNextStop = scenario.getTransitSchedule().getFacilities().get(oldPtRoute.getAccessStopId());
log.debug( "agent with ID=" + agent.getId() + " is waiting at a stop=" + currentOrNextStop ) ;
newTripElements = newTripToNewActivity(currentOrNextStop, newAct, routingMode, now, person, routingAttributes );
@@ -469,6 +477,7 @@ private void replanCurrentLegWithTransitRoute(Activity newAct, String routingMod
* hypothetically something else than teleportation.
*/
private void replanCurrentLegWithGenericRoute(Activity newAct, String routingMode, Leg currentLeg, double now, MobsimAgent agent) {
+ // this pulls the next activity from the plan, other than the other parallel methods for other route types. kai, mar'23
Plan plan = WithinDayAgentUtils.getModifiablePlan(agent) ;
@@ -604,12 +613,13 @@ private void replanCurrentTripFromStageActivity(Trip trip, int tripElementsIndex
WithinDayAgentUtils.resetCaches(agent);
}
- public static boolean insertEmptyTrip( Plan plan, Activity fromActivity, Activity toActivity, String mainMode, PopulationFactory pf ) {
+ public static Trip insertEmptyTrip( Plan plan, Activity fromActivity, Activity toActivity, String mainMode, PopulationFactory pf ) {
List list = Collections.singletonList( pf.createLeg( mainMode ) ) ;
- TripRouter.insertTrip(plan, fromActivity, list, toActivity ) ;
- return true ;
+ List planElements = TripRouter.insertTrip( plan, fromActivity, list, toActivity );
+ TripStructureUtils.Trip trip = new Trip( fromActivity, planElements, toActivity );
+ return trip;
}
- public final boolean insertEmptyTrip( Plan plan, Activity fromActivity, Activity toActivity, String mainMode ) {
+ public final Trip insertEmptyTrip( Plan plan, Activity fromActivity, Activity toActivity, String mainMode ) {
return insertEmptyTrip( plan, fromActivity, toActivity, mainMode, this.pf ) ;
}
/**