Skip to content

Commit

Permalink
Merge branch '6.x' into endlessCircleLine
Browse files Browse the repository at this point in the history
  • Loading branch information
vsp-gleich authored Dec 14, 2024
2 parents 5b0e242 + 5a55ca4 commit d86d9b4
Show file tree
Hide file tree
Showing 8 changed files with 246 additions and 78 deletions.
120 changes: 120 additions & 0 deletions src/main/java/org/matsim/run/policies/OpenBerlinChoiceExperiment.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package org.matsim.run.policies;

import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.Scenario;
import org.matsim.api.core.v01.TransportMode;
import org.matsim.api.core.v01.population.Leg;
import org.matsim.api.core.v01.population.Person;
import org.matsim.api.core.v01.population.Plan;
import org.matsim.application.MATSimApplication;
import org.matsim.core.config.Config;
import org.matsim.core.config.ConfigUtils;
import org.matsim.core.controler.Controler;
import org.matsim.core.router.TripStructureUtils;
import org.matsim.modechoice.InformedModeChoiceConfigGroup;
import org.matsim.modechoice.InformedModeChoiceModule;
import org.matsim.modechoice.ModeOptions;
import org.matsim.modechoice.constraints.RelaxedMassConservationConstraint;
import org.matsim.modechoice.estimators.DefaultActivityEstimator;
import org.matsim.modechoice.estimators.DefaultLegScoreEstimator;
import org.matsim.modechoice.estimators.FixedCostsEstimator;
import org.matsim.run.OpenBerlinScenario;
import org.matsim.run.scoring.AdvancedScoringConfigGroup;
import org.matsim.vehicles.VehicleType;
import picocli.CommandLine;

/**
* This class can be used to run some synthetic mode choice experiments on the OpenBerlin scenario.
*/
public class OpenBerlinChoiceExperiment extends OpenBerlinScenario {

@CommandLine.Option(names = "--bike-speed-offset", description = "Offset the default bike speed in km/h", defaultValue = "0")
private double bikeSpeedOffset;

@CommandLine.Option(names = "--imc", description = "Enable informed-mode-choice functionality")
private boolean imc;

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

@Override
protected Config prepareConfig(Config config) {

config = super.prepareConfig(config);

if (imc) {

InformedModeChoiceConfigGroup imcConfig = ConfigUtils.addOrGetModule(config, InformedModeChoiceConfigGroup.class);

imcConfig.setConstraintCheck(InformedModeChoiceConfigGroup.ConstraintCheck.repair);

// TODO: enable pruning

// TODO: replace strategy

// TODO: start imc runs
// from uncalibrated population with baseline calibration

// next, with new mode scoring

// different number iterations x pruning thresholds/top k

}

return config;
}

@Override
protected void prepareScenario(Scenario scenario) {

super.prepareScenario(scenario);

// If bike speed is adjusted, we need to remove all bike routes and travel times
// These times will be recalculated by the router
if (bikeSpeedOffset != 0) {

VehicleType bike = scenario.getVehicles().getVehicleTypes().get(Id.create(TransportMode.bike, VehicleType.class));
bike.setMaximumVelocity(bike.getMaximumVelocity() + bikeSpeedOffset / 3.6);

for (Person person : scenario.getPopulation().getPersons().values()) {
for (Plan plan : person.getPlans()) {
for (Leg leg : TripStructureUtils.getLegs(plan)) {
if (leg.getMode().equals(TransportMode.bike)) {
leg.setRoute(null);
leg.setTravelTimeUndefined();
}
}
}
}
}
}


@Override
protected void prepareControler(Controler controler) {
super.prepareControler(controler);

if (imc) {

InformedModeChoiceModule.Builder builder = InformedModeChoiceModule.newBuilder()
.withActivityEstimator(DefaultActivityEstimator.class)
.withFixedCosts(FixedCostsEstimator.DailyConstant.class, "car", "pt")
.withLegEstimator(DefaultLegScoreEstimator.class, ModeOptions.ConsiderIfCarAvailable.class, "car")
.withLegEstimator(DefaultLegScoreEstimator.class, ModeOptions.AlwaysAvailable.class, "pt", "walk", "bike", "ride")
.withConstraint(RelaxedMassConservationConstraint.class);

if (ConfigUtils.hasModule(controler.getConfig(), AdvancedScoringConfigGroup.class)) {

// TODO: add pseudo random errors to estimator
// Implement pseudo trip scoring into informed mode choice

// TODO: option for pruning

}

controler.addOverridingModule(builder.build());
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,25 @@ public static final class ModeParams extends ReflectiveConfigGroup {
public VariationType varDailyConstant = VariationType.fixed;

@Parameter
@Comment("total delta utility per dist group.")
@Comment("Total delta utility per dist group.")
public List<Double> deltaPerDistGroup;

/*
Unused options:
@Parameter
@Comment("Marginal utility of distance calculated as beta_dist * (dist/ref_dist)^exp_dist.")
public double betaDist = 0;
@Parameter
@Comment("Reference mean distance.")
public double refDist;
@Parameter
@Comment("Exponent controlling non-linearity of distance utility.")
public double expDist = 0;
*/

public ModeParams() {
super(GROUP_NAME);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import com.google.inject.Inject;
import org.matsim.api.core.v01.population.Person;
import org.matsim.core.config.Config;
import org.matsim.core.config.ConfigUtils;
import org.matsim.core.router.AnalysisMainModeIdentifier;
import org.matsim.core.scoring.ScoringFunction;
import org.matsim.core.scoring.ScoringFunctionFactory;
import org.matsim.core.scoring.SumScoringFunction;
Expand All @@ -13,23 +15,35 @@
*/
public class AdvancedScoringFunctionFactory implements ScoringFunctionFactory {

@Inject
private Config config;

@Inject
private ScoringParametersForPerson params;
private final Config config;
private final AdvancedScoringConfigGroup scoring;
private final AnalysisMainModeIdentifier mmi;
private final ScoringParametersForPerson params;
private final PseudoRandomScorer pseudoRNG;

@Inject
private PseudoRandomScorer pseudoRNG;
public AdvancedScoringFunctionFactory(Config config, AnalysisMainModeIdentifier mmi,
ScoringParametersForPerson params, PseudoRandomScorer pseudoRNG) {
this.config = config;
this.scoring = ConfigUtils.addOrGetModule(config, AdvancedScoringConfigGroup.class);
this.mmi = mmi;
this.params = params;
this.pseudoRNG = pseudoRNG;
}

@Override
public ScoringFunction createNewScoringFunction(Person person) {
final ScoringParameters parameters = params.getScoringParameters(person);

SumScoringFunction sumScoringFunction = new SumScoringFunction();
sumScoringFunction.addScoringFunction(new CharyparNagelActivityScoring(parameters));

if (scoring.pseudoRamdomScale > 0) {
sumScoringFunction.addScoringFunction(new PseudoRandomTripScoring(person.getId(), mmi, pseudoRNG));
}

// replaced original leg scoring
sumScoringFunction.addScoringFunction(new PiecewiseLinearlLegScoring(parameters, person.getId(), config.transit().getTransitModes(), pseudoRNG));
sumScoringFunction.addScoringFunction(new PiecewiseLinearlLegScoring(parameters, config.transit().getTransitModes()));
sumScoringFunction.addScoringFunction(new CharyparNagelMoneyScoring(parameters));
sumScoringFunction.addScoringFunction(new CharyparNagelAgentStuckScoring(parameters));
sumScoringFunction.addScoringFunction(new ScoreEventScoring());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@

import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.population.Person;
import org.matsim.core.router.TripStructureUtils;

/**
* Computes a random seed based on person id, previous activity and routing mode.
*/
public final class DefaultPseudoRandomTripError implements PseudoRandomTripError {

@Override
public long getSeed(Id<Person> personId, String routingMode, String prevActivityType) {
public long getSeed(Id<Person> personId, String mainMode, TripStructureUtils.Trip trip) {

int personHash = personId.toString().hashCode();

int modeHash = routingMode.hashCode();
int modeAndActHash = 31 * modeHash + prevActivityType.hashCode();
int modeHash = mainMode.hashCode();
int modeAndActHash = 31 * modeHash + trip.getOriginActivity().getType().hashCode();

// Combine two integers to long
return (long) personHash << 32 | modeAndActHash & 0xFFFFFFFFL;
return ((long) personHash << 32) | (modeAndActHash & 0xFFFFFFFFL);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,19 @@

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.TransportMode;
import org.matsim.api.core.v01.events.ActivityEndEvent;
import org.matsim.api.core.v01.events.Event;
import org.matsim.api.core.v01.events.PersonDepartureEvent;
import org.matsim.api.core.v01.events.PersonEntersVehicleEvent;
import org.matsim.api.core.v01.population.Leg;
import org.matsim.api.core.v01.population.Person;
import org.matsim.api.core.v01.population.Route;
import org.matsim.core.gbl.Gbl;
import org.matsim.core.router.TripStructureUtils;
import org.matsim.core.scoring.ScoringFunction;
import org.matsim.core.scoring.functions.ModeUtilityParameters;
import org.matsim.core.scoring.functions.ScoringParameters;
import org.matsim.pt.PtConstants;

import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

/**
Expand All @@ -57,31 +51,20 @@ public final class PiecewiseLinearlLegScoring implements org.matsim.core.scoring
* The parameters used for scoring.
*/
private final ScoringParameters params;
private final Id<Person> personId;
private final Set<String> ptModes;
private final PseudoRandomScorer pseudoRNG;
private final double marginalUtilityOfMoney;
private final Set<String> modesAlreadyConsideredForDailyConstants;
private double score;
private boolean nextEnterVehicleIsFirstOfTrip = true;
private boolean nextStartPtLegIsFirstOfTrip = true;
private boolean currentLegIsPtLeg = false;
private double lastActivityEndTime = Double.NaN;
private String lastActivityType = null;
/**
* The number of legs since the last activity.
*/
private int legIndex = 0;
private final List<Score> legScores;

public PiecewiseLinearlLegScoring(final ScoringParameters params, Id<Person> personId, Set<String> ptModes, PseudoRandomScorer pseudoRNG) {
public PiecewiseLinearlLegScoring(final ScoringParameters params, Set<String> ptModes) {
this.params = params;
this.personId = personId;
this.ptModes = ptModes;
this.pseudoRNG = pseudoRNG;
this.modesAlreadyConsideredForDailyConstants = new HashSet<>();
this.marginalUtilityOfMoney = this.params.marginalUtilityOfMoney;
this.legScores = new LinkedList<>();
}

@Override
Expand All @@ -94,36 +77,15 @@ public double getScore() {
return this.score;
}

@Override
public void explainScore(StringBuilder out) {
out.append("legs_util=").append(score);

// Store for each leg
if (!legScores.isEmpty()) {
for (int i = 0; i < legScores.size(); i++) {
out.append(ScoringFunction.SCORE_DELIMITER).append("leg_").append(i).append("_total=").append(legScores.get(i).total);
out.append(ScoringFunction.SCORE_DELIMITER).append("leg_").append(i).append("_randomComponent=").append(legScores.get(i).randomComponent);
out.append(ScoringFunction.SCORE_DELIMITER).append("leg_").append(i).append("_constant=").append(legScores.get(i).constant);
}
}
}

/**
* Calculate the score for a leg.
*/
private Score calcLegScore(final double departureTime, final double arrivalTime, final Leg leg) {
private double calcLegScore(final double departureTime, final double arrivalTime, final Leg leg) {
double tmpScore = 0.0;
// travel time in seconds
double travelTime = arrivalTime - departureTime;
ModeUtilityParameters modeParams = this.params.modeParams.get(leg.getMode());

// The first leg of a trip incurs trip specific random utility
double randomComponent = 0.0;
if (legIndex == 0) {
randomComponent = pseudoRNG.scoreTrip(personId, leg.getRoutingMode(), lastActivityType);
tmpScore += randomComponent;
}

if (modeParams == null) {
if (leg.getMode().equals(TransportMode.transit_walk) || leg.getMode().equals(TransportMode.non_network_walk)) {
modeParams = this.params.modeParams.get(TransportMode.walk);
Expand Down Expand Up @@ -201,24 +163,18 @@ private Score calcLegScore(final double departureTime, final double arrivalTime,
// yyyy the above will cause problems if we ever decide to differentiate pt mode into bus, tram, train, ...
// Might have to move the MainModeIdentifier then. kai, sep'18

return new Score(tmpScore, randomComponent, modeParams.constant);
return tmpScore;
}

@Override
public void handleEvent(Event event) {
if (event instanceof ActivityEndEvent a) {
if (event instanceof ActivityEndEvent) {
// When there is a "real" activity, flags are reset:
if (!PtConstants.TRANSIT_ACTIVITY_TYPE.equals(a.getActType())) {
if (!PtConstants.TRANSIT_ACTIVITY_TYPE.equals(((ActivityEndEvent) event).getActType())) {
this.nextEnterVehicleIsFirstOfTrip = true;
this.nextStartPtLegIsFirstOfTrip = true;
}
this.lastActivityEndTime = event.getTime();

// Trip occurs when non stating activity ends
if (!TripStructureUtils.isStageActivityType(a.getActType())) {
this.legIndex = 0;
this.lastActivityType = ((ActivityEndEvent) event).getActType();
}
}

if (event instanceof PersonEntersVehicleEvent && currentLegIsPtLeg) {
Expand Down Expand Up @@ -251,24 +207,14 @@ public void handleLeg(Leg leg) {
Gbl.assertIf(leg.getDepartureTime().isDefined());
Gbl.assertIf(leg.getTravelTime().isDefined());

Score legScore = calcLegScore(
double legScore = calcLegScore(
leg.getDepartureTime().seconds(), leg.getDepartureTime().seconds() + leg.getTravelTime()
.seconds(), leg);

// Increase leg index after scoring
legIndex++;

if (Double.isNaN(legScore.total)) {
if (Double.isNaN(legScore)) {
log.error("dpTime=" + leg.getDepartureTime().seconds()
+ "; ttime=" + leg.getTravelTime().seconds() + "; leg=" + leg);
throw new RuntimeException("score is NaN");
}

this.score += legScore.total;
this.legScores.add(legScore);
}

private record Score(double total, double randomComponent, double constant) {
this.score += legScore;
}

}
Loading

0 comments on commit d86d9b4

Please sign in to comment.