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 { ...@@ -66,7 +66,7 @@ public class RunASTRA2018002 {
CommandLine cmd = new CommandLine.Builder(args) // CommandLine cmd = new CommandLine.Builder(args) //
.requireOptions("config-path", "year", "ASTRA_Scenario") // .requireOptions("config-path", "year", "ASTRA_Scenario") //
.allowOptions("use-route-choice", "use-only-significant", "crossing-penalty", "model", .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") // .allowPrefixes(SwissDiscreteModeChoiceModule.COMMAND_LINE_PREFIX, "utility-parameters") //
.build(); .build();
...@@ -119,75 +119,80 @@ public class RunASTRA2018002 { ...@@ -119,75 +119,80 @@ public class RunASTRA2018002 {
SharedAVConfigurator.configureController(controller); SharedAVConfigurator.configureController(controller);
// Start set up Amodeus // Start set up Amodeus
SharedAVConfigGroup sharedConfig = (SharedAVConfigGroup) config.getModules().get(SharedAVConfigGroup.GROUP_NAME); boolean useAmodeus = cmd.getOption("use-amodeus").map(Boolean::parseBoolean).orElse(true);
File workingDirectory = MultiFileTools.getDefaultWorkingDirectory(); if (useAmodeus) {
ScenarioOptions scenarioOptions = new ScenarioOptions(workingDirectory, ScenarioOptionsBase.getDefault()); SharedAVConfigGroup sharedConfig = (SharedAVConfigGroup) config.getModules()
.get(SharedAVConfigGroup.GROUP_NAME);
scenarioOptions.setProperty("shapeFile", sharedConfig.getVirtualNetworkShapeFile());
File workingDirectory = MultiFileTools.getDefaultWorkingDirectory();
controller.addOverridingModule(new AmodeusModule()); ScenarioOptions scenarioOptions = new ScenarioOptions(workingDirectory, ScenarioOptionsBase.getDefault());
controller.addOverridingModule(new AmodeusDispatcherModule());
controller.addOverridingModule(new AmodeusVehicleGeneratorModule()); scenarioOptions.setProperty("shapeFile", sharedConfig.getVirtualNetworkShapeFile());
controller.addOverridingModule(new AmodeusVehicleToVSGeneratorModule());
controller.addOverridingModule(new DatabaseModule()); controller.addOverridingModule(new AmodeusModule());
controller.addOverridingModule(new AmodeusDispatcherModule());
controller.addOverridingModule(new AbstractModule() { controller.addOverridingModule(new AmodeusVehicleGeneratorModule());
@Override controller.addOverridingModule(new AmodeusVehicleToVSGeneratorModule());
public void install() { controller.addOverridingModule(new DatabaseModule());
}
controller.addOverridingModule(new AbstractModule() {
@Provides @Override
@Singleton public void install() {
public VirtualNetwork<Link> provideVirtualNetwork(SharedAVConfigGroup config,
@Named("av") Network network) {
int numberOfVehicles = config.getTaxiFleetSize() + config.getPooledFleetSize();
return VirtualNetworkCreators.SHAPEFILENETWORK.create(network, scenario.getPopulation(),
scenarioOptions, numberOfVehicles, 30 * 3600);
}
@Provides
@Singleton
public TravelData provideTravelData(SharedAVConfigGroup config, VirtualNetwork<Link> virtualNetwork,
@Named("av") Network avNetwork) throws Exception {
int numberOfVehicles = config.getTaxiFleetSize() + config.getPooledFleetSize();
return StaticTravelDataCreator.create(workingDirectory, virtualNetwork, avNetwork,
scenario.getPopulation(), 30 * 3600, numberOfVehicles, 30 * 3600);
}
@Provides
@Singleton
public MatsimAmodeusDatabase provideDatabase(@Named("av") Network network) {
return MatsimAmodeusDatabase.initialize(network, new ZurichReferenceFrame());
}
@Provides
@Singleton
public Collection<AbstractQSimPlugin> provideQSimPlugins(Config config) {
final Collection<AbstractQSimPlugin> plugins = new ArrayList<>();
plugins.add(new MessageQueuePlugin(config));
plugins.add(new DynActivityEnginePlugin(config));
plugins.add(new QNetsimEnginePlugin(config));
if (config.network().isTimeVariantNetwork()) {
plugins.add(new NetworkChangeEventsPlugin(config));
} }
if (config.transit().isUseTransit()) { @Provides
plugins.add(new BaselineTransitPlugin(config)); @Singleton
public VirtualNetwork<Link> provideVirtualNetwork(SharedAVConfigGroup config,
@Named("av") Network network) {
int numberOfVehicles = config.getTaxiFleetSize() + config.getPooledFleetSize();
return VirtualNetworkCreators.SHAPEFILENETWORK.create(network, scenario.getPopulation(),
scenarioOptions, numberOfVehicles, 30 * 3600);
} }
plugins.add(new TeleportationPlugin(config)); @Provides
plugins.add(new PopulationPlugin(config)); @Singleton
plugins.add(new AmodeusQSimPlugin(config)); public TravelData provideTravelData(SharedAVConfigGroup config, VirtualNetwork<Link> virtualNetwork,
@Named("av") Network avNetwork) throws Exception {
int numberOfVehicles = config.getTaxiFleetSize() + config.getPooledFleetSize();
return StaticTravelDataCreator.create(workingDirectory, virtualNetwork, avNetwork,
scenario.getPopulation(), 30 * 3600, numberOfVehicles, 30 * 3600);
}
return plugins; @Provides
} @Singleton
}); public MatsimAmodeusDatabase provideDatabase(@Named("av") Network network) {
return MatsimAmodeusDatabase.initialize(network, new ZurichReferenceFrame());
}
@Provides
@Singleton
public Collection<AbstractQSimPlugin> provideQSimPlugins(Config config) {
final Collection<AbstractQSimPlugin> plugins = new ArrayList<>();
plugins.add(new MessageQueuePlugin(config));
plugins.add(new DynActivityEnginePlugin(config));
plugins.add(new QNetsimEnginePlugin(config));
if (config.network().isTimeVariantNetwork()) {
plugins.add(new NetworkChangeEventsPlugin(config));
}
if (config.transit().isUseTransit()) {
plugins.add(new BaselineTransitPlugin(config));
}
plugins.add(new TeleportationPlugin(config));
plugins.add(new PopulationPlugin(config));
plugins.add(new AmodeusQSimPlugin(config));
return plugins;
}
});
}
// End set up Amodeus // End set up Amodeus
......
...@@ -46,8 +46,8 @@ public class SharedAVConfigGroup extends ReflectiveConfigGroup { ...@@ -46,8 +46,8 @@ public class SharedAVConfigGroup extends ReflectiveConfigGroup {
private int pooledFleetSize = 0; private int pooledFleetSize = 0;
private double minimumTripDistance_km = 0.0; private double minimumTripDistance_km = 0.0;
private double poolingMaximumAggregationRadius_m = 100000.0; private double poolingMaximumAggregationRadius_m = 500.0;
private double poolingMaximumAggregationTime_min = 60.0; private double poolingMaximumAggregationTime_min = 10.0;
private String serviceAreaAttribute = "SAREA"; private String serviceAreaAttribute = "SAREA";
private String activeServiceArea = "Active service area not set"; private String activeServiceArea = "Active service area not set";
......
...@@ -39,7 +39,7 @@ import ch.ethz.matsim.av.routing.AVRouteFactory; ...@@ -39,7 +39,7 @@ import ch.ethz.matsim.av.routing.AVRouteFactory;
import ch.ethz.matsim.discrete_mode_choice.modules.AbstractDiscreteModeChoiceExtension; 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.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.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.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.ServiceArea;
import ch.ethz.matsim.projects.astra_2018_002.shared_av.service_area.ServiceAreaConstraint; import ch.ethz.matsim.projects.astra_2018_002.shared_av.service_area.ServiceAreaConstraint;
...@@ -82,7 +82,7 @@ public class SharedAVModule extends AbstractDiscreteModeChoiceExtension { ...@@ -82,7 +82,7 @@ public class SharedAVModule extends AbstractDiscreteModeChoiceExtension {
addControlerListenerBinding().to(WaitingTimeListener.class); addControlerListenerBinding().to(WaitingTimeListener.class);
AVUtils.registerDispatcherFactory(binder(), "ASTRADispatcher", ASTRASharingDispatcher.Factory.class); AVUtils.registerDispatcherFactory(binder(), "ASTRA", SharingDispatcher.Factory.class);
} }
@Provides @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;