Commit 8bd7cb35 authored by Sebastian Hörl's avatar Sebastian Hörl
Browse files

Rewrite of sharing dispatcher

parent 6b9f66ad
......@@ -66,7 +66,7 @@ public class RunASTRA2018002 {
CommandLine cmd = new CommandLine.Builder(args) //
.requireOptions("config-path", "year", "ASTRA_Scenario") //
.allowOptions("use-route-choice", "use-only-significant", "crossing-penalty", "model",
"use-pt-cost-reduction", "flow-efficiency-path") //
"use-pt-cost-reduction", "flow-efficiency-path", "use-amodeus") //
.allowPrefixes(SwissDiscreteModeChoiceModule.COMMAND_LINE_PREFIX, "utility-parameters") //
.build();
......@@ -120,7 +120,11 @@ public class RunASTRA2018002 {
// Start set up Amodeus
SharedAVConfigGroup sharedConfig = (SharedAVConfigGroup) config.getModules().get(SharedAVConfigGroup.GROUP_NAME);
boolean useAmodeus = cmd.getOption("use-amodeus").map(Boolean::parseBoolean).orElse(true);
if (useAmodeus) {
SharedAVConfigGroup sharedConfig = (SharedAVConfigGroup) config.getModules()
.get(SharedAVConfigGroup.GROUP_NAME);
File workingDirectory = MultiFileTools.getDefaultWorkingDirectory();
ScenarioOptions scenarioOptions = new ScenarioOptions(workingDirectory, ScenarioOptionsBase.getDefault());
......@@ -188,6 +192,7 @@ public class RunASTRA2018002 {
return plugins;
}
});
}
// End set up Amodeus
......
......@@ -46,8 +46,8 @@ public class SharedAVConfigGroup extends ReflectiveConfigGroup {
private int pooledFleetSize = 0;
private double minimumTripDistance_km = 0.0;
private double poolingMaximumAggregationRadius_m = 100000.0;
private double poolingMaximumAggregationTime_min = 60.0;
private double poolingMaximumAggregationRadius_m = 500.0;
private double poolingMaximumAggregationTime_min = 10.0;
private String serviceAreaAttribute = "SAREA";
private String activeServiceArea = "Active service area not set";
......
......@@ -39,7 +39,7 @@ import ch.ethz.matsim.av.routing.AVRouteFactory;
import ch.ethz.matsim.discrete_mode_choice.modules.AbstractDiscreteModeChoiceExtension;
import ch.ethz.matsim.projects.astra_2018_002.flow_efficiency.FlowEfficiencyConfigurator;
import ch.ethz.matsim.projects.astra_2018_002.shared_av.analysis.WaitingTimeListener;
import ch.ethz.matsim.projects.astra_2018_002.shared_av.dispatching.ASTRASharingDispatcher;
import ch.ethz.matsim.projects.astra_2018_002.shared_av.dispatching.SharingDispatcher;
import ch.ethz.matsim.projects.astra_2018_002.shared_av.routing.ASTRAAVRoutingModule;
import ch.ethz.matsim.projects.astra_2018_002.shared_av.service_area.ServiceArea;
import ch.ethz.matsim.projects.astra_2018_002.shared_av.service_area.ServiceAreaConstraint;
......@@ -82,7 +82,7 @@ public class SharedAVModule extends AbstractDiscreteModeChoiceExtension {
addControlerListenerBinding().to(WaitingTimeListener.class);
AVUtils.registerDispatcherFactory(binder(), "ASTRADispatcher", ASTRASharingDispatcher.Factory.class);
AVUtils.registerDispatcherFactory(binder(), "ASTRA", SharingDispatcher.Factory.class);
}
@Provides
......
package ch.ethz.matsim.projects.astra_2018_002.shared_av.dispatching;
import java.util.Collection;
import java.util.LinkedList;
import org.matsim.core.utils.geometry.CoordUtils;
import ch.ethz.matsim.av.data.AVVehicle;
import ch.ethz.matsim.av.passenger.AVRequest;
public class ASTRAAggregatedRequest {
final private AVRequest master;
final Collection<AVRequest> slaves = new LinkedList<>();
final long occupancyThreshold;
final double distanceThreshold_m;;
private final double latestDepartureTime;
private AVVehicle vehicle = null;
public ASTRAAggregatedRequest(AVRequest master, long occupancyThreshold, double distanceThreshold_m,
double latestDepartureTime) {
this.master = master;
this.distanceThreshold_m = distanceThreshold_m;
this.occupancyThreshold = occupancyThreshold;
this.latestDepartureTime = latestDepartureTime;
}
public AVRequest getMasterRequest() {
return master;
}
public void addSlaveRequest(AVRequest slave) {
slaves.add(slave);
}
public Collection<AVRequest> getSlaveRequests() {
return slaves;
}
public Double accept(AVRequest candidate) {
if (slaves.size() + 1 >= occupancyThreshold) {
return null;
}
double destinationDistance_m = CoordUtils.calcEuclideanDistance(candidate.getToLink().getCoord(),
master.getToLink().getCoord());
double originDistance_m = CoordUtils.calcEuclideanDistance(candidate.getFromLink().getCoord(),
master.getFromLink().getCoord());
if (originDistance_m <= distanceThreshold_m && destinationDistance_m <= distanceThreshold_m) {
return originDistance_m + destinationDistance_m;
}
return null;
}
public void setVehicle(AVVehicle vehicle) {
this.vehicle = vehicle;
}
public AVVehicle getVehicle() {
return vehicle;
}
public boolean isReady(double now) {
return hasVehicleArrived && (now >= latestDepartureTime || (slaves.size() + 1) == occupancyThreshold);
}
private boolean hasVehicleArrived = false;
public void notifyVehicleHasArrived() {
hasVehicleArrived = true;
}
}
\ No newline at end of file
package ch.ethz.matsim.projects.astra_2018_002.shared_av.dispatching;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.core.api.experimental.events.EventsManager;
import org.matsim.core.network.NetworkUtils;
import org.matsim.core.router.util.LeastCostPathCalculator;
import org.matsim.core.router.util.TravelTime;
import org.matsim.core.utils.collections.QuadTree;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import ch.ethz.matsim.av.config.AVDispatcherConfig;
import ch.ethz.matsim.av.data.AVOperator;
import ch.ethz.matsim.av.data.AVVehicle;
import ch.ethz.matsim.av.dispatcher.AVDispatcher;
import ch.ethz.matsim.av.dispatcher.AVVehicleAssignmentEvent;
import ch.ethz.matsim.av.dispatcher.multi_od_heuristic.AggregateRideAppender;
import ch.ethz.matsim.av.dispatcher.multi_od_heuristic.FactorTravelTimeEstimator;
import ch.ethz.matsim.av.dispatcher.multi_od_heuristic.ParallelAggregateRideAppender;
import ch.ethz.matsim.av.dispatcher.multi_od_heuristic.aggregation.AggregatedRequest;
import ch.ethz.matsim.av.dispatcher.multi_od_heuristic.aggregation.AggregationEvent;
import ch.ethz.matsim.av.dispatcher.single_heuristic.ModeChangeEvent;
import ch.ethz.matsim.av.dispatcher.single_heuristic.SingleHeuristicDispatcher;
import ch.ethz.matsim.av.framework.AVModule;
import ch.ethz.matsim.av.passenger.AVRequest;
import ch.ethz.matsim.av.router.AVRouter;
import ch.ethz.matsim.av.schedule.AVStayTask;
import ch.ethz.matsim.av.schedule.AVTask;
public class ASTRASharingDispatcher implements AVDispatcher {
private boolean reoptimize = true;
private double nextReplanningTimestamp = 0.0;
final private Id<AVOperator> operatorId;
final private EventsManager eventsManager;
final private double replanningInterval;
final private long numberOfSeats;
final private List<AVVehicle> availableVehicles = new LinkedList<>();
final private List<ASTRAAggregatedRequest> pendingRequests = new LinkedList<>();
final private List<ASTRAAggregatedRequest> assignableRequests = new LinkedList<>();
final private QuadTree<AVVehicle> availableVehiclesTree;
final private QuadTree<ASTRAAggregatedRequest> pendingRequestsTree;
final private Map<AVVehicle, Link> vehicleLinks = new HashMap<>();
final private Map<ASTRAAggregatedRequest, Link> requestLinks = new HashMap<>();
private final Set<AVVehicle> arrivingVehicles = new HashSet<>();
final private Map<AVVehicle, ASTRAAggregatedRequest> vehicle2Request = new HashMap<>();
private SingleHeuristicDispatcher.HeuristicMode mode = SingleHeuristicDispatcher.HeuristicMode.OVERSUPPLY;
final private AggregateRideAppender rideAppender;
final private PickupAppender pickupAppender;
private final double maximumAggregationRadius_m;
private final double maximumAggregationTime_min;
private double now = 0.0;
public ASTRASharingDispatcher(Id<AVOperator> operatorId, EventsManager eventsManager, Network network,
AggregateRideAppender rideAppender, PickupAppender pickupAppender, FactorTravelTimeEstimator estimator,
double replanningInterval, long numberOfSeats, double maximumAggregationRadius_m,
double maximumAggregationTime_min) {
this.operatorId = operatorId;
this.eventsManager = eventsManager;
this.rideAppender = rideAppender;
this.replanningInterval = replanningInterval;
this.numberOfSeats = numberOfSeats;
this.pickupAppender = pickupAppender;
double[] bounds = NetworkUtils.getBoundingBox(network.getNodes().values()); // minx, miny, maxx, maxy
availableVehiclesTree = new QuadTree<>(bounds[0], bounds[1], bounds[2], bounds[3]);
pendingRequestsTree = new QuadTree<>(bounds[0], bounds[1], bounds[2], bounds[3]);
this.maximumAggregationRadius_m = maximumAggregationRadius_m;
this.maximumAggregationTime_min = maximumAggregationTime_min;
}
@Override
public void onRequestSubmitted(AVRequest request) {
addRequest(request, request.getFromLink());
}
@Override
public void onNextTaskStarted(AVVehicle vehicle) {
AVTask task = (AVTask) vehicle.getSchedule().getCurrentTask();
//if (task.getAVTaskType() == AVTask.AVTaskType.PICKUP) {
// assignableRequests.remove(vehicle2Request.remove(vehicle));
//}
if (task.getAVTaskType() == AVTask.AVTaskType.STAY) {
if (arrivingVehicles.remove(vehicle)) {
ASTRAAggregatedRequest request = vehicle2Request.get(vehicle);
request.notifyVehicleHasArrived();
} else {
addVehicle(vehicle, ((AVStayTask) task).getLink());
}
}
}
private void reoptimize(double now) {
SingleHeuristicDispatcher.HeuristicMode updatedMode = availableVehicles.size() > pendingRequests.size()
? SingleHeuristicDispatcher.HeuristicMode.OVERSUPPLY
: SingleHeuristicDispatcher.HeuristicMode.UNDERSUPPLY;
if (!updatedMode.equals(mode)) {
mode = updatedMode;
eventsManager.processEvent(new ModeChangeEvent(mode, operatorId, now));
}
while (pendingRequests.size() > 0 && availableVehicles.size() > 0) {
ASTRAAggregatedRequest request = null;
AVVehicle vehicle = null;
switch (mode) {
case OVERSUPPLY:
request = findRequest();
vehicle = findClosestVehicle(request.getMasterRequest().getFromLink());
break;
case UNDERSUPPLY:
vehicle = findVehicle();
request = findClosestRequest(vehicleLinks.get(vehicle));
break;
}
removeRequest(request);
removeVehicle(vehicle);
vehicle2Request.put(vehicle, request);
request.setVehicle(vehicle);
pickupAppender.schedule(vehicle, request, now);
arrivingVehicles.add(vehicle);
}
}
@Override
public void onNextTimestep(double now) {
this.now = now;
for (Map.Entry<AVRequest, AVRequest> pair : aggregationMap.entrySet()) {
eventsManager.processEvent(new AggregationEvent(pair.getValue(), pair.getKey(), now));
}
aggregationMap.clear();
rideAppender.update();
pickupAppender.update(now);
if (now >= nextReplanningTimestamp) {
reoptimize = true;
nextReplanningTimestamp = now + replanningInterval;
}
if (reoptimize) {
reoptimize(now);
reoptimize = false;
}
Iterator<ASTRAAggregatedRequest> iterator = assignableRequests.iterator();
while (iterator.hasNext()) {
ASTRAAggregatedRequest request = iterator.next();
if (request.isReady(now)) {
iterator.remove();
AggregatedRequest wrap = new AggregatedRequest(request.getMasterRequest(), new FactorTravelTimeEstimator(0.0), 1000);
request.getSlaveRequests().forEach(wrap::addSlaveRequest);
rideAppender.schedule(wrap, request.getVehicle(), now);
}
}
}
final private Map<AVRequest, AVRequest> aggregationMap = new HashMap<>();
private void addRequest(AVRequest request, Link link) {
ASTRAAggregatedRequest aggregate = findAggregateRequest(request);
if (aggregate != null) {
aggregate.addSlaveRequest(request);
aggregationMap.put(request, aggregate.getMasterRequest());
} else {
double latestDepartureTime = now + maximumAggregationTime_min * 60.0;
aggregate = new ASTRAAggregatedRequest(request, numberOfSeats, maximumAggregationRadius_m,
latestDepartureTime);
pendingRequests.add(aggregate);
assignableRequests.add(aggregate);
requestLinks.put(aggregate, link);
pendingRequestsTree.put(link.getCoord().getX(), link.getCoord().getY(), aggregate);
// reoptimize = true;
}
}
private ASTRAAggregatedRequest findAggregateRequest(AVRequest request) {
ASTRAAggregatedRequest bestAggregate = null;
double bestCost = Double.POSITIVE_INFINITY;
for (ASTRAAggregatedRequest candidate : assignableRequests) {
if (candidate == null)
throw new IllegalStateException();
Double cost = candidate.accept(request);
if (cost != null && cost < bestCost) {
bestCost = cost;
bestAggregate = candidate;
}
}
return bestAggregate;
}
private ASTRAAggregatedRequest findRequest() {
return pendingRequests.get(0);
}
private AVVehicle findVehicle() {
return availableVehicles.get(0);
}
private AVVehicle findClosestVehicle(Link link) {
Coord coord = link.getCoord();
return availableVehiclesTree.getClosest(coord.getX(), coord.getY());
}
private ASTRAAggregatedRequest findClosestRequest(Link link) {
Coord coord = link.getCoord();
return pendingRequestsTree.getClosest(coord.getX(), coord.getY());
}
@Override
public void addVehicle(AVVehicle vehicle) {
addVehicle(vehicle, vehicle.getStartLink());
eventsManager.processEvent(new AVVehicleAssignmentEvent(vehicle, 0));
}
private void addVehicle(AVVehicle vehicle, Link link) {
availableVehicles.add(vehicle);
availableVehiclesTree.put(link.getCoord().getX(), link.getCoord().getY(), vehicle);
vehicleLinks.put(vehicle, link);
// reoptimize = true;
}
private void removeVehicle(AVVehicle vehicle) {
availableVehicles.remove(vehicle);
Coord coord = vehicleLinks.remove(vehicle).getCoord();
availableVehiclesTree.remove(coord.getX(), coord.getY(), vehicle);
}
private void removeRequest(ASTRAAggregatedRequest request) {
pendingRequests.remove(request);
Coord coord = requestLinks.remove(request).getCoord();
pendingRequestsTree.remove(coord.getX(), coord.getY(), request);
}
static public class Factory implements AVDispatcherFactory {
@Inject
@Named(AVModule.AV_MODE)
private Network network;
@Inject
private EventsManager eventsManager;
@Inject
@Named(AVModule.AV_MODE)
private LeastCostPathCalculator router;
@Inject
@Named(AVModule.AV_MODE)
private TravelTime travelTime;
@Override
public AVDispatcher createDispatcher(AVDispatcherConfig config, AVRouter parallelRouter) {
double replanningInterval = Double
.parseDouble(config.getParams().getOrDefault("replanningInterval", "10.0"));
long numberOfSeats = Long
.parseLong(config.getParent().getGeneratorConfig().getParams().getOrDefault("numberOfSeats", "4"));
double maximumAggregationRadius_m = Double
.parseDouble(config.getParams().getOrDefault("maximumAggregationRadius_m", "200.0"));
double maximumAggregationTime_min = Double
.parseDouble(config.getParams().getOrDefault("maximumAggregationTime_min", "5.0"));
FactorTravelTimeEstimator estimator = new FactorTravelTimeEstimator(maximumAggregationTime_min * 60.0);
return new ASTRASharingDispatcher(config.getParent().getId(), eventsManager, network,
new ParallelAggregateRideAppender(config, parallelRouter, travelTime, estimator),
new PickupAppender(travelTime, parallelRouter), estimator, replanningInterval, numberOfSeats,
maximumAggregationRadius_m, maximumAggregationTime_min);
}
}
}
......@@ -30,16 +30,15 @@ public class PickupAppender {
this.travelTime = travelTime;
}
public void schedule(AVVehicle vehicle, ASTRAAggregatedRequest request, double now) {
public void schedule(AVVehicle vehicle, Link pickupLink, double now) {
Link fromLink = ((StayTask) vehicle.getSchedule().getCurrentTask()).getLink();
Task task = new Task();
task.vehicle = vehicle;
task.request = request;
task.pickupLink = pickupLink;
Link fromLink = ((StayTask) vehicle.getSchedule().getCurrentTask()).getLink();
Link toLink = request.getMasterRequest().getFromLink();
task.path = router.calcLeastCostPath(fromLink.getToNode(), toLink.getFromNode(), now, null, null);
task.path = router.calcLeastCostPath(fromLink.getToNode(), pickupLink.getFromNode(), now, null, null);
tasks.add(task);
}
......@@ -62,8 +61,8 @@ public class PickupAppender {
Link currentLink = stayTask.getLink();
double currentTime = startTime;
VrpPathWithTravelData path = VrpPaths.createPath(currentLink, task.request.getMasterRequest().getFromLink(),
currentTime, task.path.get(), travelTime);
VrpPathWithTravelData path = VrpPaths.createPath(currentLink, task.pickupLink, currentTime, task.path.get(),
travelTime);
AVDriveTask driveTask = new AVDriveTask(path);
schedule.addTask(driveTask);
......@@ -88,7 +87,8 @@ public class PickupAppender {
private class Task {
AVVehicle vehicle;
ASTRAAggregatedRequest request;
Link pickupLink;
Future<Path> path;
}
}
package ch.ethz.matsim.projects.astra_2018_002.shared_av.dispatching;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.matsim.api.core.v01.network.Link;
import org.matsim.contrib.dvrp.path.VrpPath;
import org.matsim.contrib.dvrp.path.VrpPathWithTravelData;
import org.matsim.contrib.dvrp.path.VrpPaths;
import org.matsim.contrib.dvrp.schedule.Schedule;
import org.matsim.contrib.dvrp.schedule.Schedules;
import org.matsim.contrib.dvrp.schedule.Task;
import org.matsim.core.router.util.LeastCostPathCalculator.Path;
import org.matsim.core.router.util.TravelTime;
import ch.ethz.matsim.av.config.AVDispatcherConfig;
import ch.ethz.matsim.av.config.AVTimingParameters;
import ch.ethz.matsim.av.data.AVVehicle;
import ch.ethz.matsim.av.passenger.AVRequest;
import ch.ethz.matsim.av.plcpc.ParallelLeastCostPathCalculator;
import ch.ethz.matsim.av.schedule.AVDriveTask;
import ch.ethz.matsim.av.schedule.AVDropoffTask;
import ch.ethz.matsim.av.schedule.AVPickupTask;
import ch.ethz.matsim.av.schedule.AVStayTask;
import ch.ethz.matsim.av.schedule.AVTask;
public class ServiceAppender {
final private ParallelLeastCostPathCalculator router;
final private TravelTime travelTime;
final private AVTimingParameters timing;
private final List<AppendTask> tasks = new LinkedList<>();
public ServiceAppender(AVDispatcherConfig config, ParallelLeastCostPathCalculator router, TravelTime travelTime) {
this.router = router;
this.travelTime = travelTime;
this.timing = config.getParent().getTimingParameters();
}
private class AppendTask {
public AVVehicle vehicle;
public List<AVRequest> pickupOrder = new LinkedList<>();
public List<AVRequest> dropoffOrder = new LinkedList<>();
public List<Future<Path>> pickupPaths = new LinkedList<>();
public List<Future<Path>> dropoffPaths = new LinkedList<>();
}
public void schedule(AVVehicle vehicle, List<AVRequest> pickupOrder, List<AVRequest> dropoffOrder, double now) {
if (pickupOrder.size() != dropoffOrder.size()) {
throw new RuntimeException(
String.format("Pickups don't equal dropoffs %d != %d", pickupOrder.size(), dropoffOrder.size()));
}
AppendTask appendTask = new AppendTask();
appendTask.vehicle = vehicle;
appendTask.pickupOrder.addAll(pickupOrder);
appendTask.dropoffOrder.addAll(dropoffOrder);