Commit fb884b9a authored by clivings's avatar clivings
Browse files

Merge branch 'master' of

https://gitlab.ethz.ch/ivt-vpl/astra_2018_002.git

Conflicts:
	src/main/java/ch/ethz/matsim/projects/astra_2018_002/ASTRAConfigurator.java
parents 8f6df1df 18ff9f48
......@@ -24,6 +24,16 @@ Likewise, this option can be set via the command line:
java [...] --config:private_av.usePrivateAVs true
```
# How to create a network with all relevant modes
To create a network that includes prav3, prav4, prav5 (and car_passenger and truck), one needs to call the script `ch.ethz.matsim.projects.astra_2018_002.link_assignment.AVLinkAssignment`:
```
java [...] ch.ethz.matsim.projects.astra_2018_002.link_assignment.AVLinkAssignment --input-path switzerland_network.xml.gz --output-path switzerland_2040_network.xml.gz --cities-shapefile gis/cities/cities.shp
```
Such a network needs to be used in the simulations, otherwise an error will be thrown.
# How to run a simulation with shared AVs
Shared AVs can be activated via the `shared_av` config group:
......
PROJCS["CH1903+_LV95",GEOGCS["GCS_CH1903+",DATUM["D_CH1903+",SPHEROID["Bessel_1841",6377397.155,299.1528128]],PRIMEM["Greenwich",0],UNIT["Degree",0.017453292519943295]],PROJECTION["Hotine_Oblique_Mercator_Azimuth_Center"],PARAMETER["latitude_of_center",46.95240555555556],PARAMETER["longitude_of_center",7.439583333333333],PARAMETER["azimuth",90],PARAMETER["scale_factor",1],PARAMETER["false_easting",2600000],PARAMETER["false_northing",1200000],UNIT["Meter",1]]
\ No newline at end of file
PROJCS["CH1903+ / LV95",GEOGCS["CH1903+",DATUM["CH1903+",SPHEROID["Bessel 1841",6377397.155,299.1528128,AUTHORITY["EPSG","7004"]],TOWGS84[674.374,15.056,405.346,0,0,0,0],AUTHORITY["EPSG","6150"]],PRIMEM["Greenwich",0,AUTHORITY["EPSG","8901"]],UNIT["degree",0.0174532925199433,AUTHORITY["EPSG","9122"]],AUTHORITY["EPSG","4150"]],PROJECTION["Hotine_Oblique_Mercator_Azimuth_Center"],PARAMETER["latitude_of_center",46.95240555555556],PARAMETER["longitude_of_center",7.439583333333333],PARAMETER["azimuth",90],PARAMETER["rectified_grid_angle",90],PARAMETER["scale_factor",1],PARAMETER["false_easting",2600000],PARAMETER["false_northing",1200000],UNIT["metre",1,AUTHORITY["EPSG","9001"]],AXIS["Y",EAST],AXIS["X",NORTH],AUTHORITY["EPSG","2056"]]
package ch.ethz.matsim.projects.astra_2018_002;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.matsim.api.core.v01.Scenario;
import org.matsim.api.core.v01.network.Link;
import org.matsim.core.config.Config;
import org.matsim.core.config.groups.ControlerConfigGroup.RoutingAlgorithmType;
public class ASTRAConfigurator {
static public void adjustNetwork(Scenario scenario, int year) {
for (Link link : scenario.getNetwork().getLinks().values()) {
Set<String> allowedModes = new HashSet<>(link.getAllowedModes());
if (allowedModes.contains("car")) {
allowedModes.addAll(Arrays.asList("av", "prav3", "prav4", "prav5", "car_passenger", "truck", "truckAv"));
static public void configure(Config config) {
config.controler().setRoutingAlgorithmType(RoutingAlgorithmType.FastAStarLandmarks);
}
static public void checkNetwork(Scenario scenario, int year) {
if (year >= 2030) {
for (Link link : scenario.getNetwork().getLinks().values()) {
if (link.getAllowedModes().contains("prav3")) {
return;
}
}
link.setAllowedModes(allowedModes);
throw new IllegalStateException("Cannot find 'prav3' in network modes. Are you using the wrong network?");
}
}
static public void configure(Config config) {
config.controler().setRoutingAlgorithmType(RoutingAlgorithmType.FastAStarLandmarks);
}
}
......@@ -26,13 +26,13 @@ import ch.sbb.matsim.routing.pt.raptor.SwissRailRaptorModule;
public class RunASTRA2018002 {
static public void main(String[] args) throws ConfigurationException {
CommandLine cmd = new CommandLine.Builder(args) //
.requireOptions("config-path") //
.requireOptions("config-path", "year") //
.allowOptions("use-route-choice", "use-only-significant", "utility-parameters", "crossing-penalty",
"model", "year") //
"model") //
.allowPrefixes(SwissDiscreteModeChoiceModule.COMMAND_LINE_PREFIX) //
.build();
int year = cmd.getOption("year").map(Integer::parseInt).orElse(2020);
int year = Integer.parseInt(cmd.getOptionStrict("year"));
// Load config
Config config = ConfigUtils.loadConfig(cmd.getOptionStrict("config-path"), new PrivateAVConfigGroup(),
......@@ -43,7 +43,7 @@ public class RunASTRA2018002 {
SwissModeChoiceConfigurator.configure(config);
SharedAVConfigurator.configure(config);
PrivateAVConfigurator.configure(config, year);
FlowEfficiencyConfigurator.configure(config, year);
FlowEfficiencyConfigurator.configure(config);
ASTRAConfigurator.configure(config);
// Load scenario
......@@ -55,8 +55,8 @@ public class RunASTRA2018002 {
// Adjust scenario
SwissModeChoiceConfigurator.copyHousholdAttributes(scenario);
FlowEfficiencyConfigurator.defineVehicleTypes(scenario, year);
ASTRAConfigurator.adjustNetwork(scenario, year);
PrivateAVConfigurator.assignPrivateAVs(scenario, year);
ASTRAConfigurator.checkNetwork(scenario, year);
// Set up controller
Controler controller = new Controler(scenario);
......@@ -72,7 +72,7 @@ public class RunASTRA2018002 {
boolean useOnlySignificant = cmd.getOption("use-only-significant").map(Boolean::parseBoolean).orElse(false);
controller.addOverridingModule(new DiscreteModeChoiceModule());
controller.addOverridingModule(
new SwissDiscreteModeChoiceModule(utilitySet, useRouteChoice, useOnlySignificant, cmd));
new SwissDiscreteModeChoiceModule(utilitySet, useRouteChoice, useOnlySignificant, cmd, year));
SharedAVConfigurator.configurePricing(controller);
// Run
......
......@@ -5,20 +5,10 @@ import org.matsim.vehicles.Vehicle;
import org.matsim.vehicles.VehicleType;
public class AvFlowEfficiencyCalculator implements FlowEfficiencyCalculator {
private double flowEfficiency;
private int year;
public AvFlowEfficiencyCalculator(int year, double flowEfficiency) {
this.flowEfficiency = flowEfficiency;
this.year = year;
}
public AvFlowEfficiencyCalculator(int year) {
this(year, 1.0);
}
@Override
public double calculateFlowEfficiency(Vehicle vehicle, Link link) {
double flowEfficiency = Double.NaN;
// define the pcu for each vehicle type and link type and if automated operation is allowed.
// first get vehicle type and link type and AV Operation permissions.
......@@ -255,6 +245,10 @@ public class AvFlowEfficiencyCalculator implements FlowEfficiencyCalculator {
// are defining vehicle type by mode, correct?)
// Will AV-taxis at least just refer to the vehicle type "prav5"?
if (Double.isNaN(flowEfficiency)) {
throw new RuntimeException("Something is wrong with the flow efficiency.");
}
return flowEfficiency;
}
}
......@@ -24,7 +24,7 @@ public class FlowEfficiencyConfigurator {
static final public Id<VehicleType> FREIGHT_TRUCK_ID = Id.create("truck", VehicleType.class);
static final public Id<VehicleType> FREIGHT_TRUCK_AV_ID = Id.create("truckAv", VehicleType.class);
static public void configure(Config config, int year) {
static public void configure(Config config) {
// Load vehicles by vehicle type
config.qsim().setVehiclesSource(VehiclesSource.modeVehicleTypesFromVehiclesData);
......@@ -36,7 +36,7 @@ public class FlowEfficiencyConfigurator {
if (privateConfig.getUsePrivateAVs() || sharedConfig.getUseSharedAVs()) {
// With the AvFlowEfficiencyCalculator we can distinguish between HLS
// and other roads in combination with vehicle types
FlowEfficiencyCalculator calculator = new AvFlowEfficiencyCalculator(year);
FlowEfficiencyCalculator calculator = new AvFlowEfficiencyCalculator();
FlowEfficiencyCalculator.INSTANCE.set(calculator);
} else {
// Set the calculator instance (not the best design, but works)
......
package ch.ethz.matsim.projects.astra_2018_002.link_assignment;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import org.apache.log4j.Logger;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.data.simple.SimpleFeatureSource;
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.api.core.v01.population.Person;
import org.matsim.core.network.NetworkUtils;
import org.matsim.core.network.algorithms.NetworkCleaner;
import org.matsim.core.network.algorithms.TransportModeNetworkFilter;
import org.matsim.core.network.io.MatsimNetworkReader;
import org.matsim.core.network.io.NetworkWriter;
import org.matsim.core.router.DijkstraFactory;
import org.matsim.core.router.util.LeastCostPathCalculator;
import org.matsim.core.router.util.LeastCostPathCalculator.Path;
import org.matsim.core.router.util.TravelDisutility;
import org.matsim.core.router.util.TravelTime;
import org.matsim.core.trafficmonitoring.FreeSpeedTravelTime;
import org.matsim.core.utils.collections.QuadTree;
import org.matsim.vehicles.Vehicle;
import org.opengis.feature.simple.SimpleFeature;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import ch.ethz.matsim.baseline_scenario.config.CommandLine;
import ch.ethz.matsim.baseline_scenario.config.CommandLine.ConfigurationException;
public class AVLinkAssignment {
private final Logger logger = Logger.getLogger(AVLinkAssignment.class);
private final GeometryFactory geometryFactory = new GeometryFactory();
private final Network network;
private final Collection<Link> allowedLinks = new HashSet<>();
private final QuadTree<Link> index;
private final List<Coord> centroids = new LinkedList<>();
private Collection<Id<Link>> linkIdsCache = null;
public AVLinkAssignment(Network network) {
this.network = NetworkUtils.createNetwork();
new TransportModeNetworkFilter(network).filter(this.network, Collections.singleton("car"));
new NetworkCleaner().run(this.network);
double[] dimensions = NetworkUtils.getBoundingBox(network.getNodes().values());
this.index = new QuadTree<>(dimensions[0], dimensions[1], dimensions[2], dimensions[3]);
}
public void allowLink(Link link) {
linkIdsCache = null;
if (!allowedLinks.contains(link)) {
index.put(link.getCoord().getX(), link.getCoord().getY(), link);
}
allowedLinks.add(link);
}
public void allowOsmType(String osmType) {
for (Link link : network.getLinks().values()) {
String linkOsmType = (String) link.getAttributes().getAttribute("osm:way:highway");
if (linkOsmType != null && linkOsmType.equals(osmType)) {
allowLink(link);
}
}
}
public void allowAll() {
for (Link link : network.getLinks().values()) {
allowLink(link);
}
}
public void allowIf(Function<Link, Boolean> filter) {
for (Link link : network.getLinks().values()) {
if (filter.apply(link)) {
allowLink(link);
}
}
}
public void allowArea(URL url) {
try {
DataStore dataStore = DataStoreFinder.getDataStore(Collections.singletonMap("url", url));
SimpleFeatureSource featureSource = dataStore.getFeatureSource(dataStore.getTypeNames()[0]);
SimpleFeatureCollection featureCollection = featureSource.getFeatures();
SimpleFeatureIterator featureIterator = featureCollection.features();
while (featureIterator.hasNext()) {
SimpleFeature feature = featureIterator.next();
Geometry geometry = (Geometry) feature.getDefaultGeometry();
if (geometry instanceof MultiPolygon) {
for (Link link : network.getLinks().values()) {
Coord linkCoord = link.getCoord();
Coordinate linkCoordinate = new Coordinate(linkCoord.getX(), linkCoord.getY());
Point linkPoint = geometryFactory.createPoint(linkCoordinate);
if (geometry.contains(linkPoint)) {
allowLink(link);
}
}
} else {
throw new IllegalStateException("Expected a shape file with polygons.");
}
centroids.add(new Coord(geometry.getCentroid().getX(), geometry.getCentroid().getY()));
}
featureIterator.close();
dataStore.dispose();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private boolean hasAllowedUpstream(Link link) {
for (Link inLink : link.getFromNode().getInLinks().values()) {
if (inLink != link && allowedLinks.contains(inLink)) {
return true;
}
}
return false;
}
private boolean hasAllowedDownstream(Link link) {
for (Link outLink : link.getToNode().getOutLinks().values()) {
if (outLink != link && allowedLinks.contains(outLink)) {
return true;
}
}
return false;
}
public Collection<Id<Link>> getLinkIds() {
TravelTime travelTime = new FreeSpeedTravelTime();
TravelDisutility travelDisutility = new TravelDisutility() {
@Override
public double getLinkTravelDisutility(Link link, double time, Person person, Vehicle vehicle) {
return getLinkMinimumTravelDisutility(link) + (allowedLinks.contains(link) ? 1.0 : 1000.0);
}
@Override
public double getLinkMinimumTravelDisutility(Link link) {
return link.getLength() / link.getFreespeed();
}
};
Set<Link> result = new HashSet<>();
result.addAll(allowedLinks);
// Make sure there is a connection between all centroids
LeastCostPathCalculator router = new DijkstraFactory().createPathCalculator(this.network, travelDisutility,
travelTime);
if (centroids.size() > 0) {
for (Coord a : centroids) {
for (Coord b : centroids) {
Link linkA = index.getClosest(a.getX(), a.getY());
Link linkB = index.getClosest(b.getX(), b.getY());
Path path = router.calcLeastCostPath(linkA.getToNode(), linkB.getFromNode(), 0.0, null, null);
for (Link link : path.links) {
result.add(link);
}
}
}
}
// Find islands and connect them
logger.info("Finding islands ...");
List<Link> islands = new LinkedList<>();
Set<Link> remaining = new HashSet<>(result);
while (remaining.size() > 0) {
List<Link> pending = new LinkedList<>();
Link islandLink = remaining.iterator().next();
pending.add(islandLink);
while (pending.size() > 0) {
Link link = pending.remove(0);
remaining.remove(link);
for (Link downstreamLink : link.getToNode().getOutLinks().values()) {
if (remaining.contains(downstreamLink)) {
pending.add(downstreamLink);
}
}
}
pending.add(islandLink);
while (pending.size() > 0) {
Link link = pending.remove(0);
remaining.remove(link);
for (Link upstreamLink : link.getFromNode().getInLinks().values()) {
if (remaining.contains(upstreamLink)) {
pending.add(upstreamLink);
}
}
}
islands.add(islandLink);
}
logger.info(String.format("Found %d islands!", islands.size()));
logger.info(String.format("Routing islands ..."));
// Make sure there is a connection from every island to some reference link
Link referenceLink = null;
for (Link link : allowedLinks) {
if (hasAllowedDownstream(link) && hasAllowedUpstream(link)) {
referenceLink = link;
break;
}
}
long newLinkCount = 0;
long numberOfIslands = islands.size();
long numberOfProcessedIslands = 0;
for (Link link : islands) {
Path path1 = router.calcLeastCostPath(link.getToNode(), referenceLink.getFromNode(), 0.0, null, null);
Path path2 = router.calcLeastCostPath(referenceLink.getToNode(), link.getFromNode(), 0.0, null, null);
for (Link pathLink : path1.links) {
result.add(pathLink);
newLinkCount++;
}
for (Link pathLink : path2.links) {
result.add(pathLink);
newLinkCount++;
}
numberOfProcessedIslands++;
logger.info(String.format("Processing island %d/%d ...", numberOfProcessedIslands, numberOfIslands));
}
logger.info(String.format("Routing done! Added %d new links.", newLinkCount));
// Add tags
logger.info(String.format("Creating cleaned network ..."));
for (Link link : result) {
Set<String> allowedModes = new HashSet<>(link.getAllowedModes());
allowedModes.add("___newmode___");
link.setAllowedModes(allowedModes);
}
Network newNetwork = NetworkUtils.createNetwork();
new TransportModeNetworkFilter(network).filter(newNetwork, Collections.singleton("___newmode___"));
new NetworkCleaner().run(newNetwork);
Set<Id<Link>> remainingIds = new HashSet<>();
for (Link link : newNetwork.getLinks().values()) {
if (link.getAllowedModes().contains("___newmode___")) {
remainingIds.add(link.getId());
}
}
for (Link link : result) {
Set<String> allowedModes = new HashSet<>(link.getAllowedModes());
allowedModes.remove("___newmode___");
link.setAllowedModes(allowedModes);
}
logger.info(String.format("Cleaning network done!"));
return remainingIds;
}
public void apply(Network network) {
if (linkIdsCache == null) {
linkIdsCache = getLinkIds();
}
logger.info(String.format("Setting attribute ..."));
for (Id<Link> linkId : linkIdsCache) {
Link link = network.getLinks().get(linkId);
link.getAttributes().putAttribute("avOperation", true);
}
logger.info(String.format("Setting attribute done!"));
}
static public void main(String[] args) throws MalformedURLException, ConfigurationException {
CommandLine cmd = new CommandLine.Builder(args) //
.requireOptions("input-path", "output-path", "cities-shapefile", "year") //
.build();
Network network = NetworkUtils.createNetwork();
new MatsimNetworkReader(network).readFile(cmd.getOptionStrict("input-path"));
int year = Integer.parseInt(cmd.getOptionStrict("year"));
AVLinkAssignment assignment = new AVLinkAssignment(network);
if (year >= 2030) {
assignment.allowArea(new File(cmd.getOptionStrict("cities-shapefile")).toURI().toURL());
assignment.allowOsmType("motorway");
assignment.allowOsmType("motorway_link");
assignment.allowOsmType("trunk");
assignment.allowOsmType("trunk_link");
}
if (year >= 2040) {
assignment.allowOsmType("primary");
assignment.allowOsmType("primary_link");
assignment.allowIf(link -> link.getFreespeed() <= 13.8);
}
if (year >= 2050) {
assignment.allowAll();
}
// Add attribute
assignment.apply(network);
// Add prav3, car_passenger and truck to every link
for (Link link : network.getLinks().values()) {
if (link.getAllowedModes().contains("car")) {
Set<String> allowedModes = new HashSet<>(link.getAllowedModes());
allowedModes.addAll(Arrays.asList("prav3", "prav4", "prav5", "car_passenger", "truck"));
Boolean avOperation = (Boolean) link.getAttributes().getAttribute("avOperation");
if (avOperation != null && avOperation) {
allowedModes.add("av");
}
link.setAllowedModes(allowedModes);
}
}
new NetworkWriter(network).write(cmd.getOptionStrict("output-path"));
}
}
......@@ -5,6 +5,7 @@ import java.io.IOException;
import org.matsim.api.core.v01.network.Network;
import org.matsim.core.router.TripRouter;
import org.matsim.core.router.util.TravelTime;
import org.matsim.facilities.ActivityFacilities;
import org.matsim.pt.transitSchedule.api.TransitSchedule;
......@@ -42,13 +43,15 @@ public class SwissDiscreteModeChoiceModule extends AbstractDiscreteModeChoiceExt
private final boolean onlySignificant;
private final CommandLine cmd;
private final int year;
public SwissDiscreteModeChoiceModule(UtilitySet utilitySet, boolean useRouteChoice, boolean onlySignificant,
CommandLine cmd) {
CommandLine cmd, int year) {
this.utilitySet = utilitySet;
this.useRouteChoice = useRouteChoice;
this.onlySignificant = onlySignificant;
this.cmd = cmd;
this.year = year;