To receive notifications about scheduled maintenance, please subscribe to the mailing-list gitlab-operations@sympa.ethz.ch. You can subscribe to the mailing-list at https://sympa.ethz.ch

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

Multiple AV operators

parent 45c0076c
import pandas as pd
import geopandas as gpd
import numpy as np
import shapely.geometry as geo
hexagon_radius = 1000
operating_areas_path = "/run/media/sebastian/shoerl_data/astra1802/gis/operating_areas.shp"
output_path = "/run/media/sebastian/shoerl_data/astra1802/gis/waiting_time_areas.shp"
df_operating_areas = gpd.read_file(operating_areas_path)
corners = [
np.array([np.cos(30.0 * np.pi / 180.0), np.sin(30.0 * np.pi / 180.0)]) * hexagon_radius,
np.array([np.cos(90.0 * np.pi / 180.0), np.sin(90.0 * np.pi / 180.0)]) * hexagon_radius,
np.array([np.cos(150.0 * np.pi / 180.0), np.sin(150.0 * np.pi / 180.0)]) * hexagon_radius,
np.array([np.cos(210.0 * np.pi / 180.0), np.sin(210.0 * np.pi / 180.0)]) * hexagon_radius,
np.array([np.cos(270.0 * np.pi / 180.0), np.sin(270.0 * np.pi / 180.0)]) * hexagon_radius,
np.array([np.cos(330.0 * np.pi / 180.0), np.sin(330.0 * np.pi / 180.0)]) * hexagon_radius
]
offset_x = 2.0 * hexagon_radius * np.cos(30.0 * np.pi / 180.0)
t = 2.0 * hexagon_radius * np.cos(30.0 * np.pi / 180.0) / np.sqrt(3.0)
offset_y = hexagon_radius + 0.5 * t
geometry = []
for area_name, area_geometry in zip(df_operating_areas["OAREA"], df_operating_areas["geometry"]):
min_x, min_y, max_x, max_y = area_geometry.bounds
max_i = int(np.ceil((max_x - min_x) / offset_x)) + 1
max_j = int(np.ceil((max_y - min_y) / offset_y)) + 1
index = 0
for i in range(max_i):
for j in range(max_j):
centroid = np.array([
min_x + offset_x * i + (0.5 * offset_x if j % 2 == 0 else 0.0),
min_y + offset_y * j
])
hexagon = geo.Polygon(corners + centroid)
if hexagon.intersects(area_geometry):
geometry.append((area_name, index, hexagon))
index += 1
df = pd.DataFrame.from_records(geometry, columns = ["OAREA", "IDX", "geometry"])
df = gpd.GeoDataFrame(df, crs = {"init": "EPSG:2056"})
df.to_file(output_path)
package ch.ethz.matsim.projects.astra_2018_002;
import java.util.ArrayList;
import java.util.Collection;
import org.matsim.contrib.dynagent.run.DynActivityEnginePlugin;
import org.matsim.core.config.Config;
import org.matsim.core.controler.AbstractModule;
import org.matsim.core.mobsim.qsim.AbstractQSimPlugin;
import org.matsim.core.mobsim.qsim.PopulationPlugin;
import org.matsim.core.mobsim.qsim.TeleportationPlugin;
import org.matsim.core.mobsim.qsim.changeeventsengine.NetworkChangeEventsPlugin;
import org.matsim.core.mobsim.qsim.messagequeueengine.MessageQueuePlugin;
import org.matsim.core.mobsim.qsim.qnetsimengine.QNetsimEnginePlugin;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import ch.ethz.matsim.av.framework.AVQSimPlugin;
import ch.ethz.matsim.baseline_scenario.transit.simulation.BaselineTransitPlugin;
import ch.ethz.matsim.projects.astra_2018_002.av.AVNetworkValidator;
public class ASTRAQSimModule extends AbstractModule {
@Override
public void install() {
addControlerListenerBinding().to(AVNetworkValidator.class);
}
@Provides
@Singleton
public Collection<AbstractQSimPlugin> provideAbstractQSimPlugins(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 AVQSimPlugin(config));
return plugins;
}
}
......@@ -62,7 +62,7 @@ public class RunASTRA2018002 {
controler.addOverridingModule(new OperatingAreaModule());
controler.addOverridingModule(new OperatorModule(cmd.getOption("operator-path").map(File::new)));
controler.addOverridingModule(new BaselineTransitModule());
// TODO: Consolidate QSim plugins
controler.addOverridingModule(new ASTRAQSimModule());
UtilitySet utilitySet = Enum.valueOf(UtilitySet.class, cmd.getOptionStrict("model"));
boolean useRouteChoice = cmd.getOption("use-route-choice").map(Boolean::parseBoolean).orElse(true);
......
package ch.ethz.matsim.projects.astra_2018_002.av;
import java.util.HashSet;
import java.util.Set;
import org.matsim.core.config.Config;
import org.matsim.core.config.groups.PlanCalcScoreConfigGroup.ModeParams;
import ch.ethz.matsim.av.framework.AVModule;
import ch.ethz.matsim.discrete_mode_choice.modules.config.DiscreteModeChoiceConfigGroup;
public class AVConfigurator {
static public void configure(Config config) {
ModeParams avParams = new ModeParams(AVModule.AV_MODE);
config.planCalcScore().addModeParams(avParams);
DiscreteModeChoiceConfigGroup dmcConfig = (DiscreteModeChoiceConfigGroup) config.getModules()
.get(DiscreteModeChoiceConfigGroup.GROUP_NAME);
Set<String> availableModes = new HashSet<>(dmcConfig.getCarModeAvailabilityConfig().getAvailableModes());
availableModes.add(AVModule.AV_MODE);
dmcConfig.getCarModeAvailabilityConfig().setAvailableModes(availableModes);
}
}
package ch.ethz.matsim.projects.astra_2018_002.av;
import java.util.Collection;
import org.apache.log4j.Logger;
import org.matsim.api.core.v01.network.Network;
import org.matsim.api.core.v01.network.NetworkWriter;
import org.matsim.core.controler.OutputDirectoryHierarchy;
import org.matsim.core.controler.events.StartupEvent;
import org.matsim.core.controler.listener.StartupListener;
import com.google.inject.Inject;
import com.google.inject.name.Named;
import ch.ethz.matsim.av.framework.AVModule;
import ch.ethz.matsim.projects.astra_2018_002.av.operating_area.OperatingAreaRegistry;
import ch.ethz.matsim.projects.astra_2018_002.av.operator.Operator;
public class AVNetworkValidator implements StartupListener {
private final Logger logger = Logger.getLogger(AVNetworkValidator.class);
private final OutputDirectoryHierarchy hierarchy;
private final Network network;
private final OperatingAreaRegistry registry;
private final Collection<Operator> operators;
@Inject
public AVNetworkValidator(OutputDirectoryHierarchy hierarchy, @Named(AVModule.AV_MODE) Network network,
OperatingAreaRegistry registry, Collection<Operator> operators) {
this.hierarchy = hierarchy;
this.network = network;
this.registry = registry;
this.operators = operators;
}
@Override
public void notifyStartup(StartupEvent event) {
new NetworkWriter(network).write(hierarchy.getOutputFilename("av_network.xml.gz"));
/*
* TravelTime travelTime = new FreeSpeedTravelTime(); LeastCostPathCalculator
* router = new FastAStarLandmarksFactory().createPathCalculator(network, new
* OnlyTimeDependentTravelDisutility(travelTime), travelTime);
*
* boolean valid = true;
*
* for (Operator operator : operators) {
* logger.info(String.format("Validating connectivity for operator %s...",
* operator.getName()));
*
* Collection<Link> links = registry.getLinks(operator.getName()); Link
* referenceLink = links.iterator().next();
*
* for (Link link : links) { Path path =
* router.calcLeastCostPath(link.getToNode(), referenceLink.getFromNode(), 0.0,
* null, null);
*
* if (path == null) {
* logger.error(String.format("No connection from %s to %s for operator %s!",
* link.getId().toString(), referenceLink.getId().toString(),
* operator.getName())); valid = false; } } }
*
* if (!valid) { throw new
* IllegalStateException("There have been gaps in the "); }
*/
}
}
package ch.ethz.matsim.projects.astra_2018_002.av.routing;
import org.matsim.core.utils.misc.Time;
import ch.ethz.matsim.av.routing.AVRoute;
public class ExtendedAVRoute extends AVRoute {
private double waitingTime = Time.getUndefinedTime();
public ExtendedAVRoute(AVRoute route) {
super(route);
}
public double getWaitingTime() {
return waitingTime;
}
public void setWaitingTime(double waitingTime) {
this.waitingTime = waitingTime;
}
public double getJourneyTime() {
return getTravelTime() + waitingTime;
}
}
package ch.ethz.matsim.projects.astra_2018_002.av.routing;
import java.util.LinkedList;
import java.util.List;
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.Leg;
import org.matsim.api.core.v01.population.Person;
import org.matsim.api.core.v01.population.PlanElement;
import org.matsim.api.core.v01.population.PopulationFactory;
import org.matsim.contrib.dvrp.path.VrpPathWithTravelData;
import org.matsim.contrib.dvrp.path.VrpPaths;
import org.matsim.core.network.NetworkUtils;
import org.matsim.core.router.LinkWrapperFacility;
import org.matsim.core.router.RoutingModule;
import org.matsim.core.router.StageActivityTypes;
import org.matsim.core.router.StageActivityTypesImpl;
import org.matsim.core.router.util.LeastCostPathCalculator;
import org.matsim.core.router.util.TravelTime;
import org.matsim.facilities.Facility;
import ch.ethz.matsim.av.data.AVOperator;
import ch.ethz.matsim.av.framework.AVModule;
import ch.ethz.matsim.av.routing.AVRoute;
import ch.ethz.matsim.av.routing.AVRouteFactory;
import ch.ethz.matsim.projects.astra_2018_002.av.operating_area.OperatingAreaRegistry;
import ch.ethz.matsim.projects.astra_2018_002.av.waiting_time.AVWaitingTime;
class ExtendedAVRoutingModule implements RoutingModule {
public static final String AV_INTERACTION_ACTIVITY_TYPE = "av interaction";
private final PopulationFactory populationFactory;
private final AVRouteFactory routeFactory;
private final RoutingModule walkRoutingModule;
private final LeastCostPathCalculator router;
private final Network network;
private final TravelTime travelTime;
private final AVWaitingTime waitingTime;
private final OperatingAreaRegistry areaRegistry;
public ExtendedAVRoutingModule(PopulationFactory populationFactory, AVRouteFactory routeFactory,
RoutingModule walkRoutingModule, LeastCostPathCalculator router, Network network, TravelTime travelTime,
AVWaitingTime waitingTime, OperatingAreaRegistry areaRegistry) {
this.populationFactory = populationFactory;
this.routeFactory = routeFactory;
this.walkRoutingModule = walkRoutingModule;
this.router = router;
this.network = network;
this.travelTime = travelTime;
this.waitingTime = waitingTime;
this.areaRegistry = areaRegistry;
}
@Override
public List<? extends PlanElement> calcRoute(Facility<?> originFacility, Facility<?> destinationFacility,
double departureTime, Person person) {
List<PlanElement> elements = new LinkedList<>();
Link pickupLink = network.getLinks().get(originFacility.getLinkId());
Link dropoffLink = network.getLinks().get(destinationFacility.getLinkId());
Facility<?> pickupFacility = originFacility;
Facility<?> dropoffFacility = destinationFacility;
if (pickupLink == null) {
pickupLink = NetworkUtils.getNearestLink(network, originFacility.getCoord());
pickupFacility = new LinkWrapperFacility(pickupLink);
}
if (dropoffLink == null) {
dropoffLink = NetworkUtils.getNearestLink(network, destinationFacility.getCoord());
dropoffFacility = new LinkWrapperFacility(dropoffLink);
}
String operatingArea = areaRegistry.getOperatingArea(pickupLink.getId(), dropoffLink.getId());
Id<AVOperator> operatorId = Id.create(operatingArea, AVOperator.class);
if (originFacility != pickupFacility) {
List<? extends PlanElement> accessElements = walkRoutingModule.calcRoute(originFacility, pickupFacility,
departureTime, person);
Leg accessLeg = (Leg) accessElements.get(0);
accessLeg.setMode(TransportMode.access_walk);
elements.add(accessLeg);
departureTime += accessLeg.getTravelTime();
}
double pickupWaitingTime = waitingTime.getWaitingTime(pickupFacility, departureTime);
departureTime += pickupWaitingTime;
VrpPathWithTravelData path = VrpPaths.calcAndCreatePath(pickupLink, dropoffLink,
departureTime + pickupWaitingTime, router, travelTime);
AVRoute avRoute = routeFactory.createRoute(pickupLink.getId(), dropoffLink.getId(), operatorId);
avRoute.setDistance(VrpPaths.calcDistance(path));
avRoute.setTravelTime(path.getTravelTime());
ExtendedAVRoute extendedAvRoute = new ExtendedAVRoute(avRoute);
extendedAvRoute.setWaitingTime(pickupWaitingTime);
Leg avLeg = populationFactory.createLeg(AVModule.AV_MODE);
avLeg.setRoute(extendedAvRoute);
avLeg.setTravelTime(extendedAvRoute.getJourneyTime());
avLeg.setDepartureTime(departureTime);
departureTime += extendedAvRoute.getJourneyTime();
if (destinationFacility != dropoffFacility) {
List<? extends PlanElement> egressElements = walkRoutingModule.calcRoute(dropoffFacility,
destinationFacility, departureTime, person);
Leg egressLeg = (Leg) egressElements.get(0);
egressLeg.setMode(TransportMode.egress_walk);
elements.add(egressLeg);
}
return elements;
}
@Override
public StageActivityTypes getStageActivityTypes() {
return new StageActivityTypesImpl(AV_INTERACTION_ACTIVITY_TYPE);
}
}
package ch.ethz.matsim.projects.astra_2018_002.av.waiting_time;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.events.PersonDepartureEvent;
import org.matsim.api.core.v01.events.PersonEntersVehicleEvent;
import org.matsim.api.core.v01.events.handler.PersonDepartureEventHandler;
import org.matsim.api.core.v01.events.handler.PersonEntersVehicleEventHandler;
import org.matsim.api.core.v01.network.Link;
import org.matsim.api.core.v01.network.Network;
import org.matsim.api.core.v01.population.Person;
import org.matsim.facilities.Facility;
import ch.ethz.matsim.av.framework.AVModule;
public class AVWaitingTime implements PersonDepartureEventHandler, PersonEntersVehicleEventHandler {
private final Logger logger = Logger.getLogger(AVWaitingTime.class);
private final double estimationStartTime;
private final double estimationEndTime;
private final double estimationInterval;
private final int numberOfEstimationBins;
private final Map<Id<Link>, Integer> groupIndices;
private final IndexEstimator estimator;
private final String linkAttribute;
public AVWaitingTime(double estimationStartTime, double estimationEndTime, double estimationInterval, int horizon,
double defaultWaitingTime, Network network, String linkAttribute) {
this.estimationStartTime = estimationStartTime;
this.estimationEndTime = estimationEndTime;
this.estimationInterval = estimationInterval;
this.numberOfEstimationBins = (int) Math.ceil((estimationEndTime - estimationStartTime) / estimationInterval);
this.groupIndices = createGroupIndices(network);
this.linkAttribute = linkAttribute;
this.estimator = new MovingAverageIndexEstimator(groupIndices.size(), numberOfEstimationBins, horizon,
defaultWaitingTime);
}
private Map<Id<Link>, Integer> createGroupIndices(Network network) {
List<String> names = new LinkedList<>();
List<Integer> counts = new LinkedList<>();
Map<Id<Link>, Integer> indices = new HashMap<>();
for (Link link : network.getLinks().values()) {
String groupName = (String) link.getAttributes().getAttribute(linkAttribute);
if (groupName == null) {
groupName = "__default__";
}
if (!names.contains(groupName)) {
names.add(groupName);
counts.add(0);
}
int index = names.indexOf(groupName);
indices.put(link.getId(), index);
counts.set(index, counts.get(index) + 1);
}
logger.info(String.format("Found %d estimation groups:", names.size()));
for (int k = 0; k < names.size(); k++) {
logger.info(String.format("%s: %d obs", names.get(k), counts.get(k)));
}
return indices;
}
public double getWaitingTime(Facility<?> facility, double time) {
return estimator.estimate(getGroupIndex(facility.getLinkId()), getTimeIndex(time));
}
private int getGroupIndex(Id<Link> linkId) {
return groupIndices.get(linkId);
}
private int getTimeIndex(double time) {
if (time < estimationStartTime) {
return 0;
} else if (time >= estimationEndTime) {
return numberOfEstimationBins - 1;
} else {
return (int) Math.floor((time - estimationStartTime) / estimationInterval);
}
}
private final Map<Id<Person>, PersonDepartureEvent> departureEvents = new HashMap<>();
private void recordPickupTime(double pickupTime, double time, Id<Link> linkId) {
estimator.record(getGroupIndex(linkId), getTimeIndex(time), pickupTime);
}
@Override
public void handleEvent(PersonDepartureEvent event) {
if (event.getLegMode().equals(AVModule.AV_MODE)) {
departureEvents.put(event.getPersonId(), event);
}
}
@Override
public void handleEvent(PersonEntersVehicleEvent event) {
PersonDepartureEvent departureEvent = departureEvents.remove(event.getPersonId());
if (departureEvent != null) {
recordPickupTime(event.getTime() - departureEvent.getTime(), departureEvent.getTime(),
departureEvent.getLinkId());
}
}
@Override
public void reset(int iteration) {
departureEvents.clear();
estimator.finish();
}
}
package ch.ethz.matsim.projects.astra_2018_002.av.waiting_time;
public interface IndexEstimator {
void record(int itemIndex, int timeIndex, double value);
double estimate(int itemIndex, int timeIndex);
void finish();
}
package ch.ethz.matsim.projects.astra_2018_002.av.waiting_time;
public class MovingAverageIndexEstimator implements IndexEstimator {
private final int numberOfItemIndices;
private final int numberOfTimeIndices;
private final double defaultValue;
private final int horizon;
private double[][][] cumulativeValues;
private int[][][] observationCounts;
private double[][] estimates;
private int currentHorizonIndex = 0;
public MovingAverageIndexEstimator(int numberOfItemIndices, int numberOfTimeIndices, int horizon,
double defaultValue) {
this.numberOfItemIndices = numberOfItemIndices;
this.numberOfTimeIndices = numberOfTimeIndices;
this.defaultValue = defaultValue;
this.horizon = horizon;
this.cumulativeValues = new double[horizon][numberOfItemIndices][numberOfTimeIndices];
this.observationCounts = new int[horizon][numberOfItemIndices][numberOfTimeIndices];
this.estimates = new double[numberOfItemIndices][numberOfTimeIndices];
for (int i = 0; i < numberOfItemIndices; i++) {
for (int t = 0; t < numberOfTimeIndices; t++) {
estimates[i][t] = defaultValue;
}
}
}
@Override
public void record(int itemIndex, int timeIndex, double value) {
cumulativeValues[currentHorizonIndex][itemIndex][timeIndex] += value;
observationCounts[currentHorizonIndex][itemIndex][timeIndex] += 1;
}
@Override
public double estimate(int itemIndex, int timeIndex) {
return estimates[itemIndex][timeIndex];
}
@Override
public void finish() {
for (int i = 0; i < numberOfItemIndices; i++) {
for (int t = 0; t < numberOfTimeIndices; t++) {
estimates[i][t] = 0.0;
int count = 0;
for (int h = 0; h < horizon; h++) {
estimates[i][t] += cumulativeValues[h][i][t];
count += observationCounts[h][i][t];
}
if (count == 0) {
estimates[i][t] = defaultValue;
} else {
estimates[i][t] /= count;
}
}