Skip to content

Commit

Permalink
Work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
luchengqi7 committed Apr 1, 2024
1 parent dac0ab2 commit 9410823
Show file tree
Hide file tree
Showing 5 changed files with 512 additions and 1 deletion.
202 changes: 202 additions & 0 deletions src/main/java/org/matsim/run/drt_post_simulation/DetourAnalysis.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package org.matsim.run.drt_post_simulation;

import org.apache.commons.collections.ArrayStack;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
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.contrib.drt.util.DrtEventsReaders;
import org.matsim.contrib.dvrp.passenger.*;
import org.matsim.contrib.dvrp.path.VrpPaths;
import org.matsim.contrib.dvrp.router.TimeAsTravelDisutility;
import org.matsim.contrib.dvrp.trafficmonitoring.QSimFreeSpeedTravelTime;
import org.matsim.core.api.experimental.events.EventsManager;
import org.matsim.core.events.EventsUtils;
import org.matsim.core.events.MatsimEventsReader;
import org.matsim.core.network.NetworkUtils;
import org.matsim.core.router.speedy.SpeedyALTFactory;
import org.matsim.core.router.util.LeastCostPathCalculator;
import org.matsim.core.router.util.TravelTime;
import org.matsim.core.utils.geometry.CoordUtils;

import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;

import static org.matsim.application.ApplicationUtils.globFile;

public class DetourAnalysis {
public static void main(String[] args) throws IOException {
Path outputDirectory = Path.of(args[0]);
DetourAnalysisEventsHandler detourAnalysis = new DetourAnalysisEventsHandler(outputDirectory);
detourAnalysis.readEvents();
detourAnalysis.writeAnalysis();
}

public static class DetourAnalysisEventsHandler implements PassengerRequestSubmittedEventHandler,
PassengerRequestScheduledEventHandler, PassengerRequestRejectedEventHandler, PassengerPickedUpEventHandler,
PassengerDroppedOffEventHandler {
private final Path outputDirectory;
private final Network network;
private final TravelTime travelTime;
private final LeastCostPathCalculator router;
private final Map<Id<Person>, Double> submissionTimeMap = new LinkedHashMap<>();
private final Map<Id<Person>, Double> directTripTimeMap = new HashMap<>();
private final Map<Id<Person>, Double> scheduledPickupTimeMap = new HashMap<>();
private final Map<Id<Person>, Double> actualPickupTimeMap = new HashMap<>();
private final Map<Id<Person>, Double> arrivalTimeMap = new HashMap<>();
private final List<Id<Person>> rejectedPersons = new ArrayList<>();
private final Map<Id<Person>, Double> waitTimeMap = new HashMap<>();
private final Map<Id<Person>, Double> euclideanDistanceMap = new HashMap<>();

public DetourAnalysisEventsHandler(Path outputDirectory) {
this.outputDirectory = outputDirectory;
Path networkPath = globFile(outputDirectory, "*output_network.xml.gz*");
this.network = NetworkUtils.readNetwork(networkPath.toString());
this.travelTime = new QSimFreeSpeedTravelTime(1);
this.router = new SpeedyALTFactory().createPathCalculator(network, new TimeAsTravelDisutility(travelTime), travelTime);
}

@Override
public void handleEvent(PassengerRequestSubmittedEvent event) {
double submissionTime = event.getTime();
Link fromLink = network.getLinks().get(event.getFromLinkId());
Link toLink = network.getLinks().get(event.getToLinkId());
double directTripTime = VrpPaths.calcAndCreatePath
(fromLink, toLink, submissionTime, router, travelTime).getTravelTime();
double euclideanDistance = CoordUtils.calcEuclideanDistance(fromLink.getToNode().getCoord(), toLink.getToNode().getCoord());
submissionTimeMap.put(event.getPersonIds().get(0), submissionTime);
directTripTimeMap.put(event.getPersonIds().get(0), directTripTime);
euclideanDistanceMap.put(event.getPersonIds().get(0), euclideanDistance);
}

@Override
public void handleEvent(PassengerRequestScheduledEvent event) {
double scheduledPickupTime = Math.ceil(event.getPickupTime());
scheduledPickupTimeMap.put(event.getPersonIds().get(0), scheduledPickupTime);
}

@Override
public void handleEvent(PassengerRequestRejectedEvent event) {
rejectedPersons.add(event.getPersonIds().get(0));
}

@Override
public void handleEvent(PassengerPickedUpEvent event) {
double actualPickupTime = event.getTime();
actualPickupTimeMap.put(event.getPersonId(), actualPickupTime);
double waitTime = actualPickupTime - submissionTimeMap.get(event.getPersonId());
waitTimeMap.put(event.getPersonId(), waitTime);
}

@Override
public void handleEvent(PassengerDroppedOffEvent event) {
double arrivalTime = event.getTime();
arrivalTimeMap.put(event.getPersonId(), arrivalTime);
}

@Override
public void reset(int iteration) {
PassengerRequestScheduledEventHandler.super.reset(iteration);
}

public void writeAnalysis() throws IOException {
// Write information about simulated trips. (In the lists below, we only store completed trips)
List<Double> directTripDurations = new ArrayList<>();
List<Double> actualRideDurations = new ArrayList<>();
List<Double> waitTimes = new ArrayList<>();
List<Double> euclideanDistances = new ArrayList<>();

{
String detourAnalysisOutput = outputDirectory.toString() + "/simulated-drt-trips.tsv";
CSVPrinter csvPrinter = new CSVPrinter(new FileWriter(detourAnalysisOutput), CSVFormat.TDF);
csvPrinter.printRecord(Arrays.asList("person_id", "submission", "scheduled_pickup", "actual_pickup",
"arrival", "direct_trip_duration", "total_wait_time", "delay_since_scheduled_pickup",
"actual_ride_duration", "total_travel_time", "euclidean_distance"));

for (Id<Person> personId : submissionTimeMap.keySet()) {
if (rejectedPersons.contains(personId)) {
continue;
}

double submissionTime = submissionTimeMap.get(personId);
double scheduledPickupTime = scheduledPickupTimeMap.get(personId);
double actualPickupTime = actualPickupTimeMap.get(personId);
double arrivalTime = arrivalTimeMap.get(personId);
double directTripDuration = directTripTimeMap.get(personId);
double euclideanDistance = euclideanDistanceMap.get(personId);

double waitTime = actualPickupTime - submissionTime;
double delay = actualPickupTime - scheduledPickupTime;
double actualRideDuration = arrivalTime - actualPickupTime;
double actualTotalTravelTime = arrivalTime - submissionTime;

csvPrinter.printRecord(Arrays.asList(
personId.toString(),
Double.toString(submissionTime),
Double.toString(scheduledPickupTime),
Double.toString(actualPickupTime),
Double.toString(arrivalTime),
Double.toString(directTripDuration),
Double.toString(waitTime),
Double.toString(delay),
Double.toString(actualRideDuration),
Double.toString(actualTotalTravelTime),
Double.toString(euclideanDistance)
));

actualRideDurations.add(actualRideDuration);
directTripDurations.add(directTripDuration);
waitTimes.add(waitTime);
euclideanDistances.add(euclideanDistance);
}
csvPrinter.close();
}

// Write out summary data
{
String tripsSummary = outputDirectory + "/simulated-drt-summary.tsv";
CSVPrinter csvPrinter = new CSVPrinter(new FileWriter(tripsSummary), CSVFormat.TDF);
csvPrinter.printRecord(Arrays.asList("direct_trip_duration", "actual_ride_duration", "wait_time",
"total_travel_time", "euclidean_distance"));

double meanDirectTripDuration = directTripDurations.stream().mapToDouble(v -> v).average().orElseThrow();
double meanActualRideDuration = actualRideDurations.stream().mapToDouble(v -> v).average().orElseThrow();
double meanWaitTime = waitTimes.stream().mapToDouble(v -> v).average().orElseThrow();
double meanTotalTravelTime = meanActualRideDuration + meanWaitTime;
double meanEuclideanDistance = euclideanDistances.stream().mapToDouble(v -> v).average().orElseThrow();

csvPrinter.printRecord(Arrays.asList(
Double.toString(meanDirectTripDuration),
Double.toString(meanActualRideDuration),
Double.toString(meanWaitTime),
Double.toString(meanTotalTravelTime),
Double.toString(meanEuclideanDistance)
));
csvPrinter.close();
}
}

public List<Id<Person>> getRejectedPersons() {
return rejectedPersons;
}

public void readEvents() {
Path outputEventsPath = globFile(outputDirectory, "*output_events.xml.gz*");
EventsManager eventManager = EventsUtils.createEventsManager();
eventManager.addHandler(this);
eventManager.initProcessing();
MatsimEventsReader matsimEventsReader = DrtEventsReaders.createEventsReader(eventManager);
matsimEventsReader.readFile(outputEventsPath.toString());
}

public double get95pctWaitTime() {
List<Double> waitingTimes = waitTimeMap.values().stream().sorted().toList();
int idx = (int) Math.min(Math.ceil(waitingTimes.size() * 0.95), waitingTimes.size() - 1);
return waitingTimes.get(idx);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.matsim.run.drt_post_simulation;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVParser;
import org.apache.commons.csv.CSVRecord;
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.population.*;
import org.matsim.application.MATSimAppCommand;
import org.matsim.core.config.ConfigUtils;
import org.matsim.core.population.PopulationUtils;
import picocli.CommandLine;

import java.nio.file.Files;
import java.nio.file.Path;

import static org.matsim.application.ApplicationUtils.globFile;

public class ExtractDrtTripsForPostSimulation implements MATSimAppCommand {
@CommandLine.Option(names = "--directory", description = "path to the directory of the simulation output", required = true)
private Path directory;

@CommandLine.Option(names = "--drt-operators", description = "path to the directory of the simulation output", arity = "1..*", defaultValue = "drt")
private String[] drtOperators;

public static void main(String[] args) {
new ExtractDrtTripsForPostSimulation().execute(args);
}

@Override
public Integer call() throws Exception {
Population outputDrtPlans = PopulationUtils.createPopulation(ConfigUtils.createConfig());
PopulationFactory populationFactory = outputDrtPlans.getFactory();
int counter = 0;

for (String drtOperator : drtOperators) {
Path drtLegsFile = globFile(directory, "*output_drt_legs_" + drtOperator + ".csv*");
try (CSVParser parser = new CSVParser(Files.newBufferedReader(drtLegsFile),
CSVFormat.DEFAULT.withDelimiter(';').withFirstRecordAsHeader())) {
for (CSVRecord row : parser.getRecords()) {
double departureTime = Double.parseDouble(row.get("departureTime"));
Id<Link> fromLinkId = Id.createLinkId(row.get("fromLinkId"));
Id<Link> toLinkId = Id.createLinkId(row.get("toLinkId"));

Activity fromAct = populationFactory.createActivityFromLinkId("dummy", fromLinkId);
fromAct.setEndTime(departureTime);
Activity toAct = populationFactory.createActivityFromLinkId("dummy", toLinkId);
Leg leg = populationFactory.createLeg(drtOperator);
leg.setMode(TransportMode.drt);

Plan plan = populationFactory.createPlan();
Person person = populationFactory.createPerson(Id.createPersonId("dummy_person_" + counter));
plan.addActivity(fromAct);
plan.addLeg(leg);
plan.addActivity(toAct);
person.addPlan(plan);
outputDrtPlans.addPerson(person);
counter++;
}
}
}

new PopulationWriter(outputDrtPlans).write(directory.toString() + "/extracted-drt-plans.xml.gz");
return 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.matsim.run.drt_post_simulation;

import org.matsim.api.core.v01.Scenario;
import org.matsim.application.MATSimApplication;
import org.matsim.application.options.ShpOptions;
import org.matsim.contrib.drt.routing.DrtRoute;
import org.matsim.contrib.drt.routing.DrtRouteFactory;
import org.matsim.contrib.drt.run.DrtConfigs;
import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup;
import org.matsim.contrib.drt.run.MultiModeDrtModule;
import org.matsim.contrib.dvrp.run.DvrpConfigGroup;
import org.matsim.contrib.dvrp.run.DvrpModule;
import org.matsim.contrib.dvrp.run.DvrpQSimComponents;
import org.matsim.core.config.Config;
import org.matsim.core.config.ConfigUtils;
import org.matsim.core.controler.Controler;
import org.matsim.run.prepare.PrepareNetwork;
import picocli.CommandLine;

import javax.annotation.Nullable;
import java.nio.file.Path;

@CommandLine.Command(header = ":: Run DRT post-simulation ::", version = RunDrtPostSimulation.VERSION)
public class RunDrtPostSimulation extends MATSimApplication {
@CommandLine.Option(names = "--drt-area", description = "Path to SHP file specifying where DRT mode is allowed")
private Path drtArea;

static final String VERSION = "1.0";

public static void main(String[] args) {
MATSimApplication.run(RunDrtPostSimulation.class, args);
}

@Nullable
@Override
protected Config prepareConfig(Config config) {
ConfigUtils.addOrGetModule(config, DvrpConfigGroup.class);
MultiModeDrtConfigGroup multiModeDrtConfig = ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class);
DrtConfigs.adjustMultiModeDrtConfig(multiModeDrtConfig, config.scoring(), config.routing());
return config;
}

@Override
protected void prepareScenario(Scenario scenario) {
scenario.getPopulation()
.getFactory()
.getRouteFactories()
.setRouteFactory(DrtRoute.class, new DrtRouteFactory());
// Prepare network by adding DRT mode to service area
PrepareNetwork.prepareDRT(scenario.getNetwork(), new ShpOptions(drtArea, null, null));

}

@Override
protected void prepareControler(Controler controler) {
Config config = controler.getConfig();
MultiModeDrtConfigGroup multiModeDrtConfig = ConfigUtils.addOrGetModule(config, MultiModeDrtConfigGroup.class);
controler.addOverridingModule(new DvrpModule());
controler.addOverridingModule(new MultiModeDrtModule());
controler.configureQSimComponents(DvrpQSimComponents.activateAllModes(multiModeDrtConfig));
}
}
Loading

0 comments on commit 9410823

Please sign in to comment.