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

ASTRA Pooling dispatcher

parent 2865e733
......@@ -12,7 +12,8 @@ public class SharedAVConfigGroup extends ReflectiveConfigGroup {
private final static String POOLED_FLEET_SIZE = "pooledFleetSize";
private final static String MINIMUM_TRIP_DISTANCE_KM = "minimumTripDistance_km";
private final static String POOLING_THRESHOLD_MIN = "poolingThreshold_min";
private final static String POOLING_MAXIMUM_AGGREGATION_RADIUS_M = "poolingMaximumAggregationRadius_m";
private final static String POOLING_MAXIMUM_AGGREGATION_TIME_MIN = "poolingMaximumAggregationTime_min";
private final static String SERVICE_AREA_ATTRIBUTE = "serviceAreaAttribute";
private final static String ACTIVE_SERVICE_AREA = "activeServiceArea";
......@@ -42,7 +43,8 @@ public class SharedAVConfigGroup extends ReflectiveConfigGroup {
private int pooledFleetSize = 0;
private double minimumTripDistance_km = 0.0;
private double poolingThreshold_min = 4.0;
private double poolingMaximumAggregationRadius_m = 100000.0;
private double poolingMaximumAggregationTime_min = 60.0;
private String serviceAreaAttribute = "SAREA";
private String activeServiceArea = "Active service area not set";
......@@ -99,14 +101,24 @@ public class SharedAVConfigGroup extends ReflectiveConfigGroup {
this.minimumTripDistance_km = minimumTripDistance;
}
@StringGetter(POOLING_THRESHOLD_MIN)
public double getPoolingThreshold_min() {
return poolingThreshold_min;
@StringGetter(POOLING_MAXIMUM_AGGREGATION_RADIUS_M)
public double getPoolingMaximumAggregationRadius_m() {
return poolingMaximumAggregationRadius_m;
}
@StringSetter(POOLING_THRESHOLD_MIN)
public void setPoolingThreshold_min(double poolingThreshold_min) {
this.poolingThreshold_min = poolingThreshold_min;
@StringSetter(POOLING_MAXIMUM_AGGREGATION_RADIUS_M)
public void setPoolingMaximumAggregationRadius_m(double poolingMaximumAggregationRadius_m) {
this.poolingMaximumAggregationRadius_m = poolingMaximumAggregationRadius_m;
}
@StringGetter(POOLING_MAXIMUM_AGGREGATION_TIME_MIN)
public double getPoolingMaximumAggregationTime_min() {
return poolingMaximumAggregationTime_min;
}
@StringSetter(POOLING_MAXIMUM_AGGREGATION_TIME_MIN)
public void setPoolingMaximumAggregationTime_min(double poolingMaximumAggregationTime_min) {
this.poolingMaximumAggregationTime_min = poolingMaximumAggregationTime_min;
}
@StringGetter(TAXI_FLEET_SIZE)
......
......@@ -39,6 +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.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;
......@@ -59,7 +60,7 @@ public class SharedAVModule extends AbstractDiscreteModeChoiceExtension {
public static final String WAITING_TIME_GROUP_ATTRIBUTE = "waitingTimeGroup";
public static final long POOLED_NUMBER_OF_SEATS = 4;
public static final String TAXI_CC_TYPE = "Solo";
public static final String POOLED_CC_TYPE = "Midsize";
......@@ -80,23 +81,26 @@ public class SharedAVModule extends AbstractDiscreteModeChoiceExtension {
addControlerListenerBinding().to(ZonalWaitingTime.class);
addControlerListenerBinding().to(WaitingTimeListener.class);
AVUtils.registerDispatcherFactory(binder(), "ASTRADispatcher", ASTRASharingDispatcher.Factory.class);
}
@Provides
@Singleton
@Named("service_area")
public QuadTree<Link> provideServiceAreaQuadTree(ServiceArea serviceArea, @Named(AVModule.AV_MODE) Network network) {
public QuadTree<Link> provideServiceAreaQuadTree(ServiceArea serviceArea,
@Named(AVModule.AV_MODE) Network network) {
double[] bounds = NetworkUtils.getBoundingBox(network.getNodes().values());
QuadTree<Link> index = new QuadTree<>(bounds[0], bounds[1], bounds[2], bounds[3]);
for (Id<Link> linkId : serviceArea.getServedLinkIds()) {
Link link = network.getLinks().get(linkId);
if (link != null) {
index.put(link.getCoord().getX(), link.getCoord().getY(), link);
}
}
return index;
}
......@@ -104,7 +108,8 @@ public class SharedAVModule extends AbstractDiscreteModeChoiceExtension {
@Named(AV_TAXI)
public RoutingModule provideAvTaxiRoutingModule(PopulationFactory populationFactory, AVRouteFactory routeFactory,
@Named(TransportMode.walk) RoutingModule walkRoutingModule, @Named(AVModule.AV_MODE) Network network,
AVWaitingTime waitingTime, @Named(AV_TAXI) TravelTime travelTime, @Named("service_area") QuadTree<Link> serviceAreaIndex) {
AVWaitingTime waitingTime, @Named(AV_TAXI) TravelTime travelTime,
@Named("service_area") QuadTree<Link> serviceAreaIndex) {
LeastCostPathCalculator router = new DijkstraFactory().createPathCalculator(network,
new OnlyTimeDependentTravelDisutility(travelTime), travelTime);
return new ASTRAAVRoutingModule(AV_TAXI, populationFactory, routeFactory, walkRoutingModule, router, network,
......@@ -115,7 +120,8 @@ public class SharedAVModule extends AbstractDiscreteModeChoiceExtension {
@Named(AV_POOL)
public RoutingModule provideAvPoolRoutingModule(PopulationFactory populationFactory, AVRouteFactory routeFactory,
@Named(TransportMode.walk) RoutingModule walkRoutingModule, @Named(AVModule.AV_MODE) Network network,
AVWaitingTime waitingTime, @Named(AV_POOL) TravelTime travelTime, @Named("service_area") QuadTree<Link> serviceAreaIndex) {
AVWaitingTime waitingTime, @Named(AV_POOL) TravelTime travelTime,
@Named("service_area") QuadTree<Link> serviceAreaIndex) {
LeastCostPathCalculator router = new DijkstraFactory().createPathCalculator(network,
new OnlyTimeDependentTravelDisutility(travelTime), travelTime);
return new ASTRAAVRoutingModule(AV_POOL, populationFactory, routeFactory, walkRoutingModule, router, network,
......@@ -155,11 +161,18 @@ public class SharedAVModule extends AbstractDiscreteModeChoiceExtension {
AVGeneratorConfig poolGeneratorConfig = poolConfig.createGeneratorConfig(SharedAVModule.GENERATOR_NAME);
poolGeneratorConfig.setNumberOfVehicles(astraConfig.getPooledFleetSize());
AVDispatcherConfig poolDispatcherConfig = poolConfig.createDispatcherConfig("MultiOD");
AVDispatcherConfig poolDispatcherConfig = poolConfig.createDispatcherConfig("ASTRADispatcher");
poolConfig.createPriceStructureConfig();
double poolingThreshold_min = astraConfig.getPoolingThreshold_min();
poolDispatcherConfig.addParam("maximumTimeRadius", String.valueOf(60.0 * poolingThreshold_min));
// double poolingThreshold_min = astraConfig.getPoolingThreshold_min();
// poolDispatcherConfig.addParam("maximumTimeRadius", String.valueOf(60.0 *
// poolingThreshold_min));
double poolingAggregationRadius_m = astraConfig.getPoolingMaximumAggregationRadius_m();
double poolingAggregationTime_min = astraConfig.getPoolingMaximumAggregationTime_min();
poolDispatcherConfig.addParam("maximumAggregationRadius_m", String.valueOf(poolingAggregationRadius_m));
poolDispatcherConfig.addParam("maximumAggregationTime_min", String.valueOf(poolingAggregationTime_min));
return avConfig;
}
......
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);
}
}
}
package ch.ethz.matsim.projects.astra_2018_002.shared_av.dispatching;
import java.util.Collection;
import java.util.LinkedList;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import org.matsim.api.core.v01.network.Link;
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.StayTask;
import org.matsim.contrib.dvrp.schedule.Task.TaskStatus;
import org.matsim.core.router.util.LeastCostPathCalculator.Path;
import org.matsim.core.router.util.TravelTime;
import ch.ethz.matsim.av.data.AVVehicle;
import ch.ethz.matsim.av.router.AVRouter;
import ch.ethz.matsim.av.schedule.AVDriveTask;
import ch.ethz.matsim.av.schedule.AVStayTask;
public class PickupAppender {
private final TravelTime travelTime;
private final Collection<Task> tasks = new LinkedList<>();
private final AVRouter router;
public PickupAppender(TravelTime travelTime, AVRouter router) {
this.router = router;
this.travelTime = travelTime;
}