Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/freight_eventsReaderTest' into f…
Browse files Browse the repository at this point in the history
…reight_eventsReaderTest
  • Loading branch information
kt86 committed Oct 20, 2023
2 parents 376a7ec + a12f2ec commit 05e2387
Show file tree
Hide file tree
Showing 61 changed files with 2,864 additions and 1,467 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@
import org.matsim.core.network.NetworkUtils;
import org.matsim.core.population.PopulationUtils;
import org.matsim.core.utils.io.IOUtils;
import org.matsim.vehicles.Vehicle;
import org.matsim.vehicles.VehicleType;
import org.matsim.vehicles.VehicleUtils;
import org.matsim.vehicles.Vehicles;

import java.io.BufferedWriter;
import java.io.IOException;
Expand Down Expand Up @@ -169,7 +171,7 @@ private static void writeCSVWithCategoryHeader(HashMap<String, Object2DoubleMap<
writer.close();

} catch (IOException e) {
e.printStackTrace();
log.error("Could not write the csv file with the data distribution data.", e);
}
}

Expand All @@ -186,6 +188,8 @@ static void createPlansBasedOnCarrierPlans(Scenario scenario, String smallScaleC
Map<String, AtomicLong> idCounter = new HashMap<>();

Population populationFromCarrier = (Population) scenario.getScenarioElement("allpersons");
Vehicles allVehicles = VehicleUtils.getOrCreateAllvehicles(scenario);

for (Person person : populationFromCarrier.getPersons().values()) {

Plan plan = popFactory.createPlan();
Expand Down Expand Up @@ -238,11 +242,12 @@ else if (subpopulation.contains("goodsTraffic"))
if (relatedCarrier.getAttributes().getAsMap().containsKey("tourStartArea"))
newPerson.getAttributes().putAttribute("tourStartArea",
relatedCarrier.getAttributes().getAttribute("tourStartArea"));
VehicleUtils.insertVehicleIdsIntoAttributes(newPerson, (new HashMap<>() {
{
put(mode, (Id.createVehicleId(person.getId().toString())));
}
}));

Id<Vehicle> vehicleId = Id.createVehicleId(person.getId().toString());

VehicleUtils.insertVehicleIdsIntoAttributes(newPerson, Map.of(mode, vehicleId));
VehicleUtils.insertVehicleTypesIntoAttributes(newPerson, Map.of(mode, allVehicles.getVehicles().get(vehicleId).getType().getId()));

population.addPerson(newPerson);
}

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ public void install() {

controller.run();

assertEquals(44196, (int) listener.counts.get("pt"));
assertEquals(132316, (int) listener.counts.get("car"));
assertEquals(82140, (int) listener.counts.get("walk"));
// assertEquals(42520, (int) listener.counts.get("pt"));
// assertEquals(132100, (int) listener.counts.get("car"));
// assertEquals(79106, (int) listener.counts.get("walk"));
// ...setConstrainedModes(...) (inside configureAsSubtourModeChoiceReplacement(...)) used to ignore its arguments because of a typo.
// This is now corrected, but results are no longer backwards compatible. kai, jan'23

System.out.println((int) listener.counts.get("pt"));
System.out.println((int) listener.counts.get("car"));
System.out.println(listener.counts.get("walk"));

assertEquals(44195, listener.counts.get("pt"), 2);
assertEquals(132316, listener.counts.get("car"), 2);
assertEquals(82139, listener.counts.get("walk"), 2);

}

Expand Down
7 changes: 7 additions & 0 deletions contribs/drt-extensions/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@
<version>16.0-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.matsim.contrib</groupId>
<artifactId>informed-mode-choice</artifactId>
<version>16.0-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>org.matsim.contrib</groupId>
<artifactId>simwrapper</artifactId>
Expand All @@ -33,6 +39,7 @@
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<!-- Scenario parameters only used in testing -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
package org.matsim.contrib.drt.extension.estimator;

import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
import org.apache.commons.math3.stat.regression.RegressionResults;
import org.apache.commons.math3.stat.regression.SimpleRegression;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.matsim.api.core.v01.events.PersonMoneyEvent;
import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector;
import org.matsim.contrib.drt.extension.estimator.run.DrtEstimatorConfigGroup;
import org.matsim.contrib.drt.fare.DrtFareParams;
import org.matsim.contrib.drt.routing.DrtRoute;
import org.matsim.contrib.drt.run.DrtConfigGroup;
import org.matsim.contrib.drt.speedup.DrtSpeedUp;
import org.matsim.core.controler.events.IterationEndsEvent;
import org.matsim.core.controler.listener.IterationEndsListener;
import org.matsim.core.utils.misc.OptionalTime;

import java.util.SplittableRandom;

/**
* Estimates drt trips based only daily averages. No spatial or temporal differentiation is taken into account for the estimate.
* This estimator is suited for small scenarios with few vehicles and trips and consequently few data points.
*/
public class BasicDrtEstimator implements DrtEstimator, IterationEndsListener {

private static final Logger log = LogManager.getLogger(BasicDrtEstimator.class);

private final DrtEventSequenceCollector collector;
private final DrtEstimatorConfigGroup config;
private final DrtConfigGroup drtConfig;

private final SplittableRandom rnd = new SplittableRandom();
/**
* Currently valid estimates.
*/
private GlobalEstimate currentEst;
private RegressionResults fare;

public BasicDrtEstimator(DrtEventSequenceCollector collector, DrtEstimatorConfigGroup config,
DrtConfigGroup drtConfig) {
//zones = injector.getModal(DrtZonalSystem.class);
this.collector = collector;
this.config = config;
this.drtConfig = drtConfig;
}

@Override
public void notifyIterationEnds(IterationEndsEvent event) {

// Speed-up iteration need to be ignored for the estimates
if (drtConfig.getDrtSpeedUpParams().isPresent() &&
DrtSpeedUp.isTeleportDrtUsers(drtConfig.getDrtSpeedUpParams().get(),
event.getServices().getConfig().controller(), event.getIteration())) {
return;
}

GlobalEstimate est = new GlobalEstimate();

int n = 0;

int nRejections = collector.getRejectedRequestSequences().size();
int nSubmitted = collector.getRequestSubmissions().size();

for (DrtEventSequenceCollector.EventSequence seq : collector.getPerformedRequestSequences().values()) {

if (seq.getPickedUp().isPresent() && seq.getDroppedOff().isPresent()) {

double waitTime = seq.getPickedUp().get().getTime() - seq.getSubmitted().getTime();
est.waitTime.addValue(waitTime);

double unsharedTime = seq.getSubmitted().getUnsharedRideTime();
double travelTime = seq.getDroppedOff().get().getTime() - seq.getPickedUp().get().getTime();

est.detour.addValue(travelTime / unsharedTime);

double fare = seq.getDrtFares().stream().mapToDouble(PersonMoneyEvent::getAmount).sum();
est.fare.addData(seq.getSubmitted().getUnsharedRideDistance(), fare);
n++;
}
}

// At least some data points are required
if (n <= 3)
return;

fare = est.fare.regress();

double rejectionRate = (double) nRejections / nSubmitted;

if (currentEst == null) {
est.meanWait = est.waitTime.getMean();
est.stdWait = est.waitTime.getStandardDeviation();
est.meanDetour = est.detour.getMean();
est.stdDetour = est.detour.getStandardDeviation();
est.rejectionRate = rejectionRate;
} else {
est.meanWait = config.decayFactor * est.waitTime.getMean() + (1 - config.decayFactor) * currentEst.waitTime.getMean();
est.stdWait = config.decayFactor * est.waitTime.getStandardDeviation() + (1 - config.decayFactor) * currentEst.waitTime.getStandardDeviation();
est.meanDetour = config.decayFactor * est.detour.getMean() + (1 - config.decayFactor) * currentEst.detour.getMean();
est.stdDetour = config.decayFactor * est.detour.getStandardDeviation() + (1 - config.decayFactor) * currentEst.detour.getStandardDeviation();
est.rejectionRate = config.decayFactor * rejectionRate + (1 - config.decayFactor) * currentEst.rejectionRate;
}

log.info("Calculated {}", est);
currentEst = est;
}

@Override
public Estimate estimate(DrtRoute route, OptionalTime departureTime) {

if (currentEst == null) {
// If not estimates are present, use travel time alpha as detour
// beta is not used, because estimates are supposed to be minimums and not worst cases
double travelTime = Math.min(route.getDirectRideTime() + drtConfig.maxAbsoluteDetour,
route.getDirectRideTime() * drtConfig.maxTravelTimeAlpha);

double fare = 0;
if (drtConfig.getDrtFareParams().isPresent()) {
DrtFareParams fareParams = drtConfig.getDrtFareParams().get();
fare = fareParams.distanceFare_m * route.getDistance()
+ fareParams.timeFare_h * route.getDirectRideTime() / 3600.0
+ fareParams.baseFare;

fare = Math.max(fare, fareParams.minFarePerTrip);
}

// for distance, also use the max travel time alpha
return new Estimate(route.getDistance() * drtConfig.maxTravelTimeAlpha, travelTime, drtConfig.maxWaitTime, fare, 0);
}

double fare = 0;
if (this.fare != null)
fare = this.fare.getParameterEstimate(0) + this.fare.getParameterEstimate(1) * route.getDistance();

if (drtConfig.getDrtFareParams().isPresent()) {
fare = Math.max(fare, drtConfig.getDrtFareParams().get().minFarePerTrip);
}

double detour = Math.max(1, rnd.nextGaussian(currentEst.meanDetour, config.randomization * currentEst.stdDetour));
double waitTime = Math.max(0, rnd.nextGaussian(currentEst.meanWait, config.randomization * currentEst.stdWait));

return new Estimate(route.getDistance() * detour, route.getDirectRideTime() * detour, waitTime, fare, currentEst.rejectionRate);
}

/**
* Helper class to hold statistics.
*/
private static final class GlobalEstimate {

private final SummaryStatistics waitTime = new SummaryStatistics();
private final SummaryStatistics detour = new SummaryStatistics();
private final SimpleRegression fare = new SimpleRegression(true);

private double meanWait;
private double stdWait;
private double meanDetour;
private double stdDetour;
private double rejectionRate;

@Override
public String toString() {
return "GlobalEstimate{" +
"meanWait=" + meanWait +
", stdWait=" + stdWait +
", meanDetour=" + meanDetour +
", stdDetour=" + stdDetour +
", rejectionRate=" + rejectionRate +
'}';
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package org.matsim.contrib.drt.extension.estimator;

import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVPrinter;
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.matsim.api.core.v01.events.PersonMoneyEvent;
import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector;
import org.matsim.contrib.drt.extension.estimator.run.DrtEstimatorConfigGroup;
import org.matsim.contrib.drt.routing.DrtRoute;
import org.matsim.core.controler.events.AfterMobsimEvent;
import org.matsim.core.controler.events.ShutdownEvent;
import org.matsim.core.controler.events.StartupEvent;
import org.matsim.core.controler.listener.AfterMobsimListener;
import org.matsim.core.controler.listener.ShutdownListener;
import org.matsim.core.controler.listener.StartupListener;
import org.matsim.core.utils.misc.OptionalTime;

import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;

/**
* Analyzes and outputs drt estimates errors metrics based on daily requests.
*/
public final class DrtEstimateAnalyzer implements StartupListener, ShutdownListener, AfterMobsimListener {

private static final Logger log = LogManager.getLogger(DrtEstimateAnalyzer.class);

// Might be useful but not needed currently
//private final DefaultMainLegRouter.RouteCreator creator;
private final DrtEstimator estimator;
private final DrtEventSequenceCollector collector;
private final DrtEstimatorConfigGroup config;

private CSVPrinter csv;

public DrtEstimateAnalyzer(DrtEstimator estimator, DrtEventSequenceCollector collector, DrtEstimatorConfigGroup config) {
this.estimator = estimator;
this.collector = collector;
this.config = config;
}

@Override
public void notifyStartup(StartupEvent event) {

String filename = event.getServices().getControlerIO().getOutputFilename("drt_estimates_" + config.getMode() + ".csv");

try {
csv = new CSVPrinter(Files.newBufferedWriter(Path.of(filename), StandardCharsets.UTF_8), CSVFormat.DEFAULT);
csv.printRecord("iteration",
"wait_time_mae", "wait_time_err_q5", "wait_time_err_q50", "wait_time_err_q95",
"travel_time_mae", "travel_time_err_q5", "travel_time_err_q50", "travel_time_err_q95",
"fare_mae", "fare_err_q5", "fare_err_q50", "fare_err_q95"
);

} catch (IOException e) {
throw new UncheckedIOException("Could not open output file for estimates.", e);
}
}

@Override
public void notifyShutdown(ShutdownEvent event) {
try {
csv.close();
} catch (IOException e) {
log.warn("Could not close drt estimate file", e);
}
}

/**
* Needs to run before any estimators updates.
*/
@Override
public void notifyAfterMobsim(AfterMobsimEvent event) {

try {
csv.printRecord(calcMetrics(event.getIteration()));
csv.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

/**
* Return row of metrics for the csv file.
*/
private Iterable<Number> calcMetrics(int iteration) {

DescriptiveStatistics waitTime = new DescriptiveStatistics();
DescriptiveStatistics travelTime = new DescriptiveStatistics();
DescriptiveStatistics fare = new DescriptiveStatistics();

for (DrtEventSequenceCollector.EventSequence seq : collector.getPerformedRequestSequences().values()) {
if (seq.getPickedUp().isPresent() && seq.getDroppedOff().isPresent()) {

// many attributes are not filled, when using the constructor
DrtRoute route = new DrtRoute(seq.getSubmitted().getFromLinkId(), seq.getSubmitted().getToLinkId());
route.setDirectRideTime(seq.getSubmitted().getUnsharedRideTime());
route.setDistance(seq.getSubmitted().getUnsharedRideDistance());

double valWaitTime = seq.getPickedUp().get().getTime() - seq.getSubmitted().getTime();
double valTravelTime = seq.getDroppedOff().get().getTime() - seq.getPickedUp().get().getTime();
double valFare = seq.getDrtFares().stream().mapToDouble(PersonMoneyEvent::getAmount).sum();

DrtEstimator.Estimate estimate = estimator.estimate(route, OptionalTime.defined(seq.getSubmitted().getTime()));

waitTime.addValue(Math.abs(estimate.waitingTime() - valWaitTime));
travelTime.addValue(Math.abs(estimate.travelTime() - valTravelTime));
fare.addValue(Math.abs(estimate.fare() - valFare));
}
}

return List.of(
iteration,
waitTime.getMean(), waitTime.getPercentile(5), waitTime.getPercentile(50), waitTime.getPercentile(95),
travelTime.getMean(), travelTime.getPercentile(5), travelTime.getPercentile(50), travelTime.getPercentile(95),
fare.getMean(), fare.getPercentile(5), fare.getPercentile(50), fare.getPercentile(95)
);
}

}
Loading

0 comments on commit 05e2387

Please sign in to comment.