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

Add av restrictions

parent 3a041e8f
......@@ -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:
......
package ch.ethz.matsim.projects.astra_2018_002;
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;
......@@ -7,4 +9,16 @@ public class ASTRAConfigurator {
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;
}
}
throw new IllegalStateException("Cannot find 'prav3' in network modes. Are you using the wrong network?");
}
}
}
......@@ -14,7 +14,6 @@ import ch.ethz.matsim.baseline_scenario.transit.routing.DefaultEnrichedTransitRo
import ch.ethz.matsim.baseline_scenario.transit.routing.DefaultEnrichedTransitRouteFactory;
import ch.ethz.matsim.discrete_mode_choice.modules.DiscreteModeChoiceModule;
import ch.ethz.matsim.projects.astra_2018_002.flow_efficiency.FlowEfficiencyConfigurator;
import ch.ethz.matsim.projects.astra_2018_002.link_assignment.LinkAssignmentConfigurator;
import ch.ethz.matsim.projects.astra_2018_002.mode_choice.SwissDiscreteModeChoiceModule;
import ch.ethz.matsim.projects.astra_2018_002.mode_choice.SwissModeChoiceConfigurator;
import ch.ethz.matsim.projects.astra_2018_002.mode_choice.SwissUtilityParameters.UtilitySet;
......@@ -27,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(),
......@@ -56,8 +55,8 @@ public class RunASTRA2018002 {
// Adjust scenario
SwissModeChoiceConfigurator.copyHousholdAttributes(scenario);
FlowEfficiencyConfigurator.defineVehicleTypes(scenario, year);
LinkAssignmentConfigurator.configureNetwork(config, scenario, year);
PrivateAVConfigurator.assignPrivateAVs(scenario, year);
ASTRAConfigurator.checkNetwork(scenario, year);
// Set up controller
Controler controller = new Controler(scenario);
......
......@@ -4,7 +4,7 @@ import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
......@@ -13,6 +13,7 @@ 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;
......@@ -22,12 +23,12 @@ 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.network.NetworkWriter;
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;
......@@ -44,7 +45,12 @@ 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;
......@@ -53,6 +59,8 @@ public class AVLinkAssignment {
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();
......@@ -64,6 +72,8 @@ public class AVLinkAssignment {
}
public void allowLink(Link link) {
linkIdsCache = null;
if (!allowedLinks.contains(link)) {
index.put(link.getCoord().getX(), link.getCoord().getY(), link);
}
......@@ -131,18 +141,38 @@ public class AVLinkAssignment {
}
}
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 allowedLinks.contains(link) ? 1.0 : 1000.0;
return getLinkMinimumTravelDisutility(link) + (allowedLinks.contains(link) ? 1.0 : 1000.0);
}
@Override
public double getLinkMinimumTravelDisutility(Link link) {
return 1.0;
return link.getLength() / link.getFreespeed();
}
};
......@@ -168,42 +198,86 @@ public class AVLinkAssignment {
}
}
// Make sure there is a connection from every link to some reference link
Link referenceLink = allowedLinks.iterator().next();
// Find islands and connect them
logger.info("Finding islands ...");
for (Link link : new ArrayList<>(allowedLinks)) {
boolean hasAllowedUpstream = false;
boolean hasAllowedDownstream = false;
List<Link> islands = new LinkedList<>();
Set<Link> remaining = new HashSet<>(result);
for (Link inLink : link.getFromNode().getInLinks().values()) {
if (inLink != link && allowedLinks.contains(inLink)) {
hasAllowedUpstream = true;
break;
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);
}
}
}
for (Link inLink : link.getToNode().getOutLinks().values()) {
if (inLink != link && allowedLinks.contains(inLink)) {
hasAllowedDownstream = true;
break;
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);
}
}
}
if (!hasAllowedUpstream || !hasAllowedDownstream) {
Path path1 = router.calcLeastCostPath(link.getToNode(), referenceLink.getFromNode(), 0.0, null, null);
Path path2 = router.calcLeastCostPath(referenceLink.getToNode(), link.getFromNode(), 0.0, null, null);
islands.add(islandLink);
}
for (Link pathLink : path1.links) {
result.add(pathLink);
}
logger.info(String.format("Found %d islands!", islands.size()));
logger.info(String.format("Routing islands ..."));
for (Link pathLink : path2.links) {
result.add(pathLink);
}
// 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___");
......@@ -228,29 +302,73 @@ public class AVLinkAssignment {
link.setAllowedModes(allowedModes);
}
logger.info(String.format("Cleaning network done!"));
return remainingIds;
}
public void apply(Network network, String mode) {
for (Id<Link> linkId : getLinkIds()) {
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);
Set<String> allowedModes = new HashSet<>(link.getAllowedModes());
allowedModes.add(mode);
link.setAllowedModes(allowedModes);
link.getAttributes().putAttribute("avOperation", true);
}
logger.info(String.format("Setting attribute done!"));
}
static public void main(String[] args) throws MalformedURLException {
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(args[0]);
new MatsimNetworkReader(network).readFile(cmd.getOptionStrict("input-path"));
int year = Integer.parseInt(cmd.getOptionStrict("year"));
AVLinkAssignment assignment = new AVLinkAssignment(network);
assignment.allowOsmType("motorway");
assignment.allowArea(new File("allowed.shp").toURI().toURL());
if (year >= 2030) {
assignment.allowArea(new File(cmd.getOptionStrict("cities-shapefile")).toURI().toURL());
assignment.apply(network, "av");
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("assigned_network.xml.gz");
new NetworkWriter(network).write(cmd.getOptionStrict("output-path"));
}
}
package ch.ethz.matsim.projects.astra_2018_002.link_assignment;
import java.net.URL;
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.ConfigGroup;
import ch.ethz.matsim.projects.astra_2018_002.private_av.PrivateAVConfigGroup;
public class LinkAssignmentConfigurator {
static public void configureNetwork(Config config, Scenario scenario, int year) {
PrivateAVConfigGroup pravConfig = (PrivateAVConfigGroup) config.getModules()
.get(PrivateAVConfigGroup.GROUP_NAME);
AVLinkAssignment assignment = new AVLinkAssignment(scenario.getNetwork());
if (year >= 2030) {
if (pravConfig.getCityShapefile() == null) {
throw new IllegalStateException("No city shape file is given for years >= 2030 !");
}
URL citiesURL = ConfigGroup.getInputFileURL(config.getContext(), pravConfig.getCityShapefile());
assignment.allowArea(citiesURL);
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 prav4, prav5, and av (shared) for the criteria set above
assignment.apply(scenario.getNetwork(), "prav4");
assignment.apply(scenario.getNetwork(), "prav5");
assignment.apply(scenario.getNetwork(), "av");
// Add prav3, car_passenger and truck to every link
for (Link link : scenario.getNetwork().getLinks().values()) {
if (link.getAllowedModes().contains("car")) {
Set<String> allowedModes = new HashSet<>(link.getAllowedModes());
allowedModes.addAll(Arrays.asList("prav3", "car_passenger", "truck"));
link.setAllowedModes(allowedModes);
}
}
}
}
......@@ -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;
......@@ -64,8 +65,9 @@ public class SwissDiscreteModeChoiceModule extends AbstractDiscreteModeChoiceExt
@Provides
public SwissUtilityEstimator provideSwissUtilityEstimator(TripRouter tripRouter, Network network,
ActivityFacilities facilities, CostModel costModel, TransitSchedule transitSchedule,
SwissUtilityParameters parameters) {
return new SwissUtilityEstimator(tripRouter, network, facilities, costModel, transitSchedule, parameters);
SwissUtilityParameters parameters, @Named("car") TravelTime travelTime) {
return new SwissUtilityEstimator(tripRouter, network, facilities, costModel, transitSchedule, parameters,
travelTime);
}
@Provides
......
......@@ -41,7 +41,7 @@ public class SwissModeChoiceConfigurator {
dmcConfig.getActivityTourFinderConfigGroup().setActivityType("home");
dmcConfig.setModeAvailability(SwissDiscreteModeChoiceModule.SWISS_MODE_AVAILABILITY_NAME);
dmcConfig.getCarModeAvailabilityConfig().setAvailableModes(Arrays.asList("car", "bike", "pt", "walk", "truck"));
dmcConfig.getCarModeAvailabilityConfig().setAvailableModes(Arrays.asList("car", "bike", "pt", "walk"));
dmcConfig.setTourConstraints(
Arrays.asList(ConstraintModule.VEHICLE_CONTINUITY, ConstraintModule.FROM_TRIP_BASED));
......@@ -69,6 +69,7 @@ public class SwissModeChoiceConfigurator {
scoringConfig.setMarginalUtlOfWaitingPt_utils_hr(0.0);
scoringConfig.getOrCreateModeParams("car_passenger");
scoringConfig.getOrCreateModeParams("truck");
// Some additional settings
config.controler().setRoutingAlgorithmType(RoutingAlgorithmType.FastAStarLandmarks);
......
......@@ -2,6 +2,7 @@ package ch.ethz.matsim.projects.astra_2018_002.mode_choice;
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;
......@@ -27,8 +28,9 @@ public class SwissUtilityEstimator extends BaseUtilityEstimator {
private final SwissUtilityParameters parameters;
public SwissUtilityEstimator(TripRouter tripRouter, Network network, ActivityFacilities facilities,
CostModel costModel, TransitSchedule transitSchedule, SwissUtilityParameters parameters) {
super(tripRouter, network, facilities, costModel, transitSchedule);
CostModel costModel, TransitSchedule transitSchedule, SwissUtilityParameters parameters,
TravelTime travelTime) {
super(tripRouter, network, facilities, costModel, transitSchedule, travelTime);
this.parameters = parameters;
}
......@@ -230,13 +232,12 @@ public class SwissUtilityEstimator extends BaseUtilityEstimator {
switch (tripVariables.automationLevel) {
case LEVEL3:
betaTravelTime = parameters.prav.betaTravelTimeLevel3;
betaTravelTime = parameters.prav.betaTravelTimeConventional;
break;
case LEVEL4:
betaTravelTime = parameters.prav.betaTravelTimeLevel4;
break;
case LEVEL5:
betaTravelTime = parameters.prav.betaTravelTimeLevel5;
betaTravelTime = tripVariables.automatedShare * parameters.prav.betaTravelTimeAutomated
+ (1.0 - tripVariables.automatedShare) * parameters.prav.betaTravelTimeConventional;
break;
default:
throw new IllegalStateException();
......
......@@ -169,9 +169,8 @@ public class SwissUtilityParameters {
public class PrivateAVParameters {
public double alpha = 0.0;
public double betaTravelTimeLevel3 = 0.0;
public double betaTravelTimeLevel4 = 0.0;
public double betaTravelTimeLevel5 = 0.0;
public double betaTravelTimeConventional = 0.0;
public double betaTravelTimeAutomated = 0.0;
public double betaDelay = 0.0;
......@@ -753,9 +752,8 @@ public class SwissUtilityParameters {
// Private AV
parameters.prav.alpha = 0.762;
parameters.prav.betaTravelTimeLevel3 = -0.036;
parameters.prav.betaTravelTimeLevel4 = -0.036;
parameters.prav.betaTravelTimeLevel5 = -0.036;
parameters.prav.betaTravelTimeConventional = parameters.car.betaTravelTime;
parameters.prav.betaTravelTimeAutomated = -0.036;
parameters.prav.constantAccessEgressWalkTime_min = 4;
return parameters;
......
......@@ -3,13 +3,17 @@ package ch.ethz.matsim.projects.astra_2018_002.mode_choice.base;
import java.util.List;
import org.matsim.api.core.v01.Coord;
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.Activity;
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.core.population.routes.NetworkRoute;
import org.matsim.core.router.TripRouter;
import org.matsim.core.router.util.TravelTime;
import org.matsim.core.utils.geometry.CoordUtils;
import org.matsim.core.utils.misc.Time;
import org.matsim.facilities.ActivityFacilities;
......@@ -36,13 +40,17 @@ import ch.ethz.matsim.projects.astra_2018_002.shared_av.routing.ExtendedAVRoute;
public abstract class BaseUtilityEstimator extends AbstractTripRouterEstimator {
private final TransitSchedule transitSchedule;
private final CostModel costModel;
private final Network network;
private final TravelTime travelTime;
public BaseUtilityEstimator(TripRouter tripRouter, Network network, ActivityFacilities facilities,
CostModel costModel, TransitSchedule transitSchedule) {
CostModel costModel, TransitSchedule transitSchedule, TravelTime travelTime) {
super(tripRouter, facilities);
this.costModel = costModel;
this.transitSchedule = transitSchedule;