Skip to content

Commit

Permalink
get rid of drsRider teleportation router
Browse files Browse the repository at this point in the history
also: write routed distance/traveltime into drsRider legs
delete conflicting plans
  • Loading branch information
markusstraub committed Nov 22, 2024
1 parent 2f93067 commit cd84ca4
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 94 deletions.
43 changes: 4 additions & 39 deletions data/floridsdorf/config_drs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,48 +33,13 @@
<param name="mainMode" value="car,drsDriver" />
</module>

<!-- DRS-NOTE: drsRider must be added as teleported mode.
Since adding one or more modes clears predefined modes
we must re-add all default teleported modes -->
<module name="routing">
<!-- DRS NOTE: drsDriver mode must be added as a networkMode -->
<param name="networkModes" value="car,drsDriver" />
<param name="clearDefaultTeleportedModeParams" value="true" />

<parameterset type="teleportedModeParameters">
<param name="beelineDistanceFactor" value="1.3" />
<param name="mode" value="bike" />
<param name="teleportedModeSpeed" value="4.166666666666667" />
</parameterset>
<parameterset type="teleportedModeParameters">
<param name="beelineDistanceFactor" value="1.3" />
<param name="mode" value="walk" />
<param name="teleportedModeSpeed" value="0.8333333333333333" />
</parameterset>
<parameterset type="teleportedModeParameters">
<param name="beelineDistanceFactor" value="1.3" />
<param name="mode" value="non_network_walk" />
<param name="teleportedModeSpeed" value="0.8333333333333333" />
</parameterset>
<parameterset type="teleportedModeParameters">
<param name="beelineDistanceFactor" value="1.3" />
<param name="mode" value="ride" />
<param name="teleportedModeFreespeedFactor" value="1.0" />
</parameterset>
<parameterset type="teleportedModeParameters">
<param name="beelineDistanceFactor" value="1.3" />
<param name="mode" value="pt" />
<param name="teleportedModeFreespeedFactor" value="2.0" />
</parameterset>

<!-- DRS NOTE: The routing config for drsRider here is ONLY relevant for UNMATCHED riders
TODO: since we don't allow unmatched riders anymore
can we remove the requirement of adding this teleportation config? -->
<parameterset type="teleportedModeParameters">
<param name="beelineDistanceFactor" value="2" />
<param name="mode" value="drsRider" />
<param name="teleportedModeSpeed" value="8.3" />
</parameterset>
<!-- DRS NOTE: drsRider mode does not need a routing configuration.
Internally drsDriver mode is used to calculate routes,
and omitting a teleportation configuration helps with quickly identifying
cases where drsRiders would be teleported (which should never happen) -->
</module>

<module name="scoring">
Expand Down
15 changes: 13 additions & 2 deletions data/floridsdorf/population_drs_ridersLateForPickup.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</attributes>

<!-- Example for temporal pickup variation (rider on time, late, very late) and geographic stability (exactly the same trip coordinates) -->
<!-- The walk and bike leg are pre-routed so that PersonPreparForSim doesn't trigger (and fails at drsRider mode)-->

<person id="carPerson1">
<attributes>
Expand Down Expand Up @@ -66,7 +67,12 @@
<plan selected="yes">
<!-- short walk from around the corner but starting late so the driver must wait a little bit (<5 minutes) -->
<activity type="home" link="28" x="5991.161" y="345598.0287" end_time="07:20:00" />
<leg mode="walk" />
<leg mode="walk" dep_time="07:20:00" trav_time="00:04:22">
<attributes>
<attribute name="routingMode" class="java.lang.String">walk</attribute>
</attributes>
<route type="generic" start_link="28" end_link="112" trav_time="00:04:22" distance="218.85512479489233"></route>
</leg>
<activity type="home" link="112" x="5826.522" y="345633.182" start_time="07:20:00" end_time="07:20:00" />
<leg mode="drsRider" />
<activity type="work" link="152" x="3360.657" y="344813.230" start_time="07:40:00" end_time="17:30:00" />
Expand All @@ -81,7 +87,12 @@
<plan selected="yes">
<!-- short bike ride from around the corner but starting late so the driver must wait a little bit (<1 minute) -->
<activity type="home" link="28" x="5991.161" y="345598.0287" end_time="07:20:00" />
<leg mode="bike" />
<leg mode="bike" dep_time="07:20:00" trav_time="00:00:52">
<attributes>
<attribute name="routingMode" class="java.lang.String">bike</attribute>
</attributes>
<route type="generic" start_link="28" end_link="112" trav_time="00:00:52" distance="218.85512479489233"></route>
</leg>
<activity type="home" link="112" x="5826.522" y="345633.182" start_time="07:20:00" end_time="07:20:00" />
<leg mode="drsRider" />
<activity type="work" link="152" x="3360.657" y="344813.230" start_time="07:40:00" end_time="17:30:00" />
Expand Down
2 changes: 0 additions & 2 deletions data/vienna/config_drs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,6 @@
<module name="drs">
<param name="driverProfitPerKm" value="0.8" />
<param name="carAndDrsDailyMonetaryConstant" value="-12.34" />
<!-- DRS NOTE: cost per UNMATCHED rider trips (used to avoid too many riders that can not be served by the drivers)-->
<param name="riderMobilityGuaranteeMonetaryConstant" value="-10" />
<param name="pickupWaitingSeconds" value="180" />
</module>
</config>
68 changes: 28 additions & 40 deletions src/main/java/at/ac/ait/matsim/drs/engine/PlanModifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,23 @@
import org.matsim.api.core.v01.network.Network;
import org.matsim.api.core.v01.population.Activity;
import org.matsim.api.core.v01.population.Leg;
import org.matsim.api.core.v01.population.Person;
import org.matsim.api.core.v01.population.PlanElement;
import org.matsim.api.core.v01.population.Population;
import org.matsim.api.core.v01.population.PopulationFactory;
import org.matsim.api.core.v01.population.Route;
import org.matsim.core.config.groups.GlobalConfigGroup;
import org.matsim.core.controler.OutputDirectoryHierarchy;
import org.matsim.core.controler.events.IterationStartsEvent;
import org.matsim.core.controler.events.ReplanningEvent;
import org.matsim.core.controler.listener.IterationStartsListener;
import org.matsim.core.controler.listener.ReplanningListener;
import org.matsim.core.gbl.MatsimRandom;
import org.matsim.core.population.routes.GenericRouteImpl;
import org.matsim.core.replanning.conflicts.ConflictManager;
import org.matsim.core.replanning.conflicts.ConflictResolver;
import org.matsim.core.replanning.conflicts.ConflictWriter;
import org.matsim.core.router.RoutingModule;
import org.matsim.core.router.TripRouter;
import org.matsim.core.router.TripStructureUtils;
import org.matsim.facilities.FacilitiesUtils;
import org.matsim.pt2matsim.tools.NetworkTools;

import com.google.common.collect.ImmutableSet;
Expand All @@ -54,9 +53,10 @@ public class PlanModifier implements ReplanningListener, IterationStartsListener
private final Network drsNetwork;
private final GlobalConfigGroup globalConfig;
private final DrsConfigGroup drsConfig;
private final RoutingModule driverRouter, riderRouter;
private final RoutingModule driverRouter;
private final OutputDirectoryHierarchy outputDirectoryHierarchy;
private final ConflictManager conflictManager;
private final UnmatchedRiderConflictResolver unmatchedRiderConflictResolver;

@Inject
public PlanModifier(Scenario scenario, TripRouter tripRouter, OutputDirectoryHierarchy outputDirectoryHierarchy) {
Expand All @@ -66,18 +66,20 @@ public PlanModifier(Scenario scenario, TripRouter tripRouter, OutputDirectoryHie
this.globalConfig = scenario.getConfig().global();
this.drsConfig = Drs.addOrGetConfigGroup(scenario);
driverRouter = tripRouter.getRoutingModule(Drs.DRIVER_MODE);
riderRouter = tripRouter.getRoutingModule(Drs.RIDER_MODE);
this.outputDirectoryHierarchy = outputDirectoryHierarchy;

// Create a custom ConflictManager with an actual resolver for our conflicts.
// Must not be bound to the "official" conflict resolver because
// PlansReplanningImpl
// runs the that directly after replanning,
// which is before our PlanModifier can assign riders to drivers.
Set<ConflictResolver> resolvers = Set.of(new UnmatchedRiderConflictResolver());
this.unmatchedRiderConflictResolver = new UnmatchedRiderConflictResolver();
ConflictWriter drsConflictWriter = new ConflictWriter(new File(
outputDirectoryHierarchy.getOutputFilename("drs_conflicts.csv")));
this.conflictManager = new ConflictManager(resolvers, drsConflictWriter, MatsimRandom.getRandom());
this.conflictManager = new ConflictManager(
Set.of(unmatchedRiderConflictResolver),
drsConflictWriter,
MatsimRandom.getRandom());
}

/** before iteration 0 */
Expand All @@ -98,6 +100,7 @@ public void notifyReplanning(ReplanningEvent event) {
conflictManager.initializeReplanning(scenario.getPopulation());
preplanDay(event.isLastIteration());
conflictManager.run(scenario.getPopulation(), event.getIteration());
unmatchedRiderConflictResolver.deleteInvalidPlans(scenario.getPopulation());
LOGGER.info("plan modifier used {} route calculations.", DrsUtil.routeCalculations.get());
}

Expand All @@ -118,14 +121,15 @@ private void preplanDay(boolean isLastIteration) {
for (DrsMatch match : result.matches()) {
modifyPlans(match, populationFactory);
}
addRoutingModeAndRouteForRiders();
// addRoutingModeAndRouteForRiders();
LOGGER.info("Modifying drs agents plans finished.");
}

private void modifyPlans(DrsMatch match, PopulationFactory factory) {
DrsUtil.setAssignedDriver(match.getRider().getLeg(), match.getDriver().getPerson().getId().toString());
double pickupTime = match.getDriver().getDepartureTime() + match.getToPickup().getTravelTime().seconds();
addNewActivitiesToDriverPlan(match, pickupTime, factory);
addRiderRoute(match.getRider());
adjustRiderDepartureTime(match.getRider(), pickupTime);
}

Expand Down Expand Up @@ -156,6 +160,22 @@ private void addNewActivitiesToDriverPlan(DrsMatch match, double pickupTime, Pop
+ match.getDriver().getPerson().getSelectedPlan().getPlanElements().size() + " plan elements.");
}

/**
* For rider legs it is important to set the routing mode and a generic route
* so that PersonPrepareForSim does not reroute our legs (and thereby discards
* our leg attributes where we store if the person found a match or not)
*/
private void addRiderRoute(DrsRequest riderRequest) {
Route genericRoute = new GenericRouteImpl(riderRequest.getFromLink().getId(),
riderRequest.getToLink().getId());
genericRoute.setDistance(riderRequest.getNetworkRouteDistance());
genericRoute.setTravelTime(riderRequest.getNetworkRouteTravelTime().seconds());

Leg leg = riderRequest.getLeg();
leg.setRoute(genericRoute);
TripStructureUtils.setRoutingMode(leg, Drs.RIDER_MODE);
}

/**
* Note: only adjusts one activity!
* It may happen that later activities start before the end of the adjusted
Expand Down Expand Up @@ -189,36 +209,4 @@ static void adjustRiderDepartureTime(DrsRequest riderRequest, double pickupTime)
}
}

/**
* TODO this should be integrated into the optimizer/matchmaker process
*
* for all rider legs: add a routing mode and a generic route (that is provided
* through the drs rider mode configured as teleporting mode)
* so that PersonPrepareForSim does not reroute our legs (and thereby discards
* our leg attributes where we store if the person found a match or not)
*/
private void addRoutingModeAndRouteForRiders() {
for (Person person : scenario.getPopulation().getPersons().values()) {
List<PlanElement> planElements = person.getSelectedPlan().getPlanElements();
for (int i = 0; i < planElements.size(); i++) {
if (planElements.get(i) instanceof Leg) {
Leg leg = (Leg) planElements.get(i);
if (!leg.getMode().equals(Drs.RIDER_MODE)) {
continue;
}
TripStructureUtils.setRoutingMode(leg, Drs.RIDER_MODE);

Activity prevActivity = (Activity) planElements.get(i - 1);
Activity nextActivity = (Activity) planElements.get(i + 1);
Leg calculatedLeg = DrsUtil.calculateLeg(riderRouter,
FacilitiesUtils.wrapActivity(prevActivity),
FacilitiesUtils.wrapActivity(nextActivity),
leg.getDepartureTime().orElse(0),
person);
leg.setRoute(calculatedLeg.getRoute());
}
}
}
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package at.ac.ait.matsim.drs.engine;

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

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.IdSet;
import org.matsim.api.core.v01.population.Leg;
import org.matsim.api.core.v01.population.Person;
Expand All @@ -19,6 +24,8 @@ public class UnmatchedRiderConflictResolver extends UnmatchedRiderConflictIdenti

private static final Logger LOGGER = LogManager.getLogger();

private Map<Id<Person>, Plan> invalidPlans = new HashMap<>();

@Override
public IdSet<Person> resolve(Population population, int iteration) {
IdSet<Person> conflictingPersonIds = new IdSet<>(Person.class);
Expand All @@ -31,6 +38,7 @@ public IdSet<Person> resolve(Population population, int iteration) {
"Unmatched rider '{}': conflict module tries to select a non-conflicting plan",
person.getId());
conflictingPersonIds.add(person.getId());
invalidPlans.put(person.getId(), plan);
break;
}
}
Expand All @@ -39,4 +47,15 @@ public IdSet<Person> resolve(Population population, int iteration) {
return conflictingPersonIds;
}

/**
* ConflictManager does not delete invalid plans as of MATSim 2024.0, so let's
* take care of that ourselves
*/
public void deleteInvalidPlans(Population population) {
for (Entry<Id<Person>, Plan> e : invalidPlans.entrySet()) {
Person person = population.getPersons().get(e.getKey());
person.removePlan(e.getValue());
}
}

}
10 changes: 10 additions & 0 deletions src/main/java/at/ac/ait/matsim/drs/optimizer/DrsRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.matsim.contrib.dvrp.optimizer.Request;
import org.matsim.core.population.routes.NetworkRoute;
import org.matsim.core.router.TripStructureUtils.Trip;
import org.matsim.core.utils.misc.OptionalTime;

/**
* Request for DRS trip. The given trip must be part of the person's selected
Expand Down Expand Up @@ -129,6 +130,15 @@ public double getNetworkRouteDistance() {
return Double.NEGATIVE_INFINITY;
}

public OptionalTime getNetworkRouteTravelTime() {
if (legWithRoute != null) {
if (legWithRoute.getRoute() instanceof NetworkRoute) {
return legWithRoute.getRoute().getTravelTime();
}
}
return OptionalTime.undefined();
}

public boolean isMatched() {
return matchedRequest != null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package at.ac.ait.matsim.drs.run;

import org.matsim.core.config.Config;
import org.matsim.core.config.groups.ReplanningConfigGroup.StrategySettings;

/**
* Minimal example demonstrating what happens when rider agents are late for
Expand All @@ -18,12 +17,8 @@ public void adjustConfig(Config config) {
config.controller().setLastIteration(0);
config.controller().setOutputDirectory("output-floridsdorf-ridersLateForPickup");
config.plans().setInputFile("population_drs_ridersLateForPickup.xml");
// disable replaning
config.replanning().clearStrategySettings();
config.replanning().addStrategySettings(new StrategySettings().setStrategyName("SelectExpBeta").setWeight(1));

DrsConfigGroup drs = (DrsConfigGroup) config.getModules().get("drs");
drs.setCellSize(200);
drs.setPickupWaitingSeconds(120);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public void run(boolean assignDrs, Path outputDirOverride) {
new CarLinkAssigner(scenario.getNetwork()).run(scenario.getPopulation());
DrsUtil.addMissingCoordsToPlanElementsFromLinks(scenario.getPopulation(), scenario.getNetwork());
DrsUtil.addNewAllowedModeToCarLinks(scenario.getNetwork(), Drs.DRIVER_MODE);
DrsUtil.addFakeGenericRouteToDrsDriverLegs(scenario.getPopulation());

if (assignDrs) {
// kick-start all ride agents as riders
Expand Down
7 changes: 1 addition & 6 deletions src/main/java/at/ac/ait/matsim/drs/run/RunViennaExample.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,8 @@ public static void main(String[] args) {
DrsUtil.addMissingCoordsToPlanElementsFromLinks(scenario.getPopulation(), scenario.getNetwork());
DrsUtil.addNewAllowedModeToCarLinks(scenario.getNetwork(), Drs.DRIVER_MODE);

// kick-start all ride agents as riders
int count = DrsUtil.addDrsPlanForEligiblePlans(scenario.getPopulation(), scenario.getConfig(),
Drs.RIDER_MODE, Set.of(TransportMode.ride));
LOGGER.info("added initial drs rider plan to {} agent(s)", count);

// necessary to kick-start the drs driver pool
count = DrsUtil.addDrsDriverPlans(scenario.getPopulation(), scenario.getConfig());
int count = DrsUtil.addDrsDriverPlans(scenario.getPopulation(), scenario.getConfig());
LOGGER.info("added initial drs driver plan to {} agent(s)", count);

Controler controller = new Controler(scenario);
Expand Down
Loading

0 comments on commit cd84ca4

Please sign in to comment.