From a9cf7de7bf41c012af474a4763850ea1e6f58580 Mon Sep 17 00:00:00 2001 From: rakow Date: Mon, 8 Jan 2024 12:40:24 +0100 Subject: [PATCH] new scoring based on distance groups --- Makefile | 4 +- input/v6.1/berlin-v6.1.config.xml | 20 +- .../prepare/RunOpenBerlinCalibration.java | 2 + .../org/matsim/run/RunOpenBerlinScenario.java | 3 + .../DetailedPersonScoringParameters.java | 72 ++++++ .../DistanceGroupModeUtilityParameters.java | 88 +++++++ .../scoring/PiecewiseLinearlLegScoring.java | 231 ++++++++++++++++++ .../run/scoring/VspScoringConfigGroup.java | 108 ++++++++ .../scoring/VspScoringFunctionFactory.java | 40 +++ .../matsim/run/scoring/VspScoringModule.java | 22 ++ src/main/python/calibrate_new.py | 46 ++++ ...istanceGroupModeUtilityParametersTest.java | 66 +++++ 12 files changed, 699 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/matsim/run/scoring/DetailedPersonScoringParameters.java create mode 100644 src/main/java/org/matsim/run/scoring/DistanceGroupModeUtilityParameters.java create mode 100644 src/main/java/org/matsim/run/scoring/PiecewiseLinearlLegScoring.java create mode 100644 src/main/java/org/matsim/run/scoring/VspScoringConfigGroup.java create mode 100644 src/main/java/org/matsim/run/scoring/VspScoringFunctionFactory.java create mode 100644 src/main/java/org/matsim/run/scoring/VspScoringModule.java create mode 100755 src/main/python/calibrate_new.py create mode 100644 src/test/java/org/matsim/run/scoring/DistanceGroupModeUtilityParametersTest.java diff --git a/Makefile b/Makefile index df8ed81c..1c6111a4 100644 --- a/Makefile +++ b/Makefile @@ -187,7 +187,7 @@ $p/berlin-initial-$V-25pct.plans.xml.gz: $p/berlin-activities-$V-25pct.plans.xml # For debugging and visualization $(sc) prepare downsample-population $@\ --sample-size 0.25\ - --samples 0.1 0.01\ + --samples 0.1 0.03 0.01\ $p/berlin-longHaulFreight-$V-25pct.plans.xml.gz: $p/berlin-$V-network.xml.gz @@ -275,7 +275,7 @@ $p/berlin-$V-25pct.plans-initial.xml.gz: $p/berlin-$V-facilities.xml.gz $p/berli $(sc) prepare downsample-population $@\ --sample-size 0.25\ - --samples 0.1 0.01 0.001\ + --samples 0.1 0.03 0.01 0.001\ $p/berlin-$V-25pct.plans.xml.gz: $(sc) prepare clean-population\ diff --git a/input/v6.1/berlin-v6.1.config.xml b/input/v6.1/berlin-v6.1.config.xml index a2f75b5e..489b939a 100644 --- a/input/v6.1/berlin-v6.1.config.xml +++ b/input/v6.1/berlin-v6.1.config.xml @@ -104,7 +104,7 @@ - + @@ -185,6 +185,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/main/java/org/matsim/prepare/RunOpenBerlinCalibration.java b/src/main/java/org/matsim/prepare/RunOpenBerlinCalibration.java index 255eaf12..a1922977 100644 --- a/src/main/java/org/matsim/prepare/RunOpenBerlinCalibration.java +++ b/src/main/java/org/matsim/prepare/RunOpenBerlinCalibration.java @@ -55,6 +55,7 @@ import org.matsim.prepare.population.*; import org.matsim.run.Activities; import org.matsim.run.RunOpenBerlinScenario; +import org.matsim.run.scoring.VspScoringModule; import org.matsim.simwrapper.SimWrapperConfigGroup; import org.matsim.simwrapper.SimWrapperModule; import org.matsim.smallScaleCommercialTrafficGeneration.GenerateSmallScaleCommercialTrafficDemand; @@ -398,6 +399,7 @@ public void install() { }); controler.addOverridingModule(new RunOpenBerlinScenario.TravelTimeBinding()); + controler.addOverridingModule(new VspScoringModule()); controler.addOverridingModule(new SimWrapperModule()); } diff --git a/src/main/java/org/matsim/run/RunOpenBerlinScenario.java b/src/main/java/org/matsim/run/RunOpenBerlinScenario.java index 20a982cd..a4481f43 100644 --- a/src/main/java/org/matsim/run/RunOpenBerlinScenario.java +++ b/src/main/java/org/matsim/run/RunOpenBerlinScenario.java @@ -16,6 +16,7 @@ import org.matsim.core.router.costcalculators.TravelDisutilityFactory; import org.matsim.core.router.util.TravelTime; import org.matsim.prepare.RunOpenBerlinCalibration; +import org.matsim.run.scoring.VspScoringModule; import org.matsim.simwrapper.SimWrapperConfigGroup; import org.matsim.simwrapper.SimWrapperModule; import picocli.CommandLine; @@ -103,6 +104,8 @@ protected void prepareControler(Controler controler) { controler.addOverridingModule(new TravelTimeBinding()); + controler.addOverridingModule(new VspScoringModule()); + } /** diff --git a/src/main/java/org/matsim/run/scoring/DetailedPersonScoringParameters.java b/src/main/java/org/matsim/run/scoring/DetailedPersonScoringParameters.java new file mode 100644 index 00000000..43b312f8 --- /dev/null +++ b/src/main/java/org/matsim/run/scoring/DetailedPersonScoringParameters.java @@ -0,0 +1,72 @@ +package org.matsim.run.scoring; + +import com.google.inject.Inject; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.population.Person; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.config.groups.ScoringConfigGroup; +import org.matsim.core.population.PopulationUtils; +import org.matsim.core.scoring.functions.ActivityUtilityParameters; +import org.matsim.core.scoring.functions.ModeUtilityParameters; +import org.matsim.core.scoring.functions.ScoringParameters; +import org.matsim.core.scoring.functions.ScoringParametersForPerson; + +import java.util.Map; +import java.util.TreeMap; + +public class DetailedPersonScoringParameters implements ScoringParametersForPerson { + + /** + * Cache instances of {@link ActivityUtilityParameters} for each activity type. + * All params are the same for each person. + */ + private final Map utilParams = new TreeMap<>(); + + /** + * Cache instances of {@link ModeUtilityParameters} for each mode. + */ + private final Map modeParams = new TreeMap<>(); + + @Inject + private Scenario scenario; + + @Override + public ScoringParameters getScoringParameters(Person person) { + + ScoringConfigGroup scoring = scenario.getConfig().scoring(); + String subpopulation = PopulationUtils.getSubpopulation(person); + + ScoringConfigGroup.ScoringParameterSet scoringParameters = scoring.getScoringParameters(subpopulation); + + Map personParams = new TreeMap<>(); + + for (ScoringConfigGroup.ActivityParams params : scoringParameters.getActivityParams()) { + ActivityUtilityParameters p = utilParams.computeIfAbsent(params.getActivityType(), k -> { + ActivityUtilityParameters.Builder factory = new ActivityUtilityParameters.Builder(params); + return factory.build(); + }); + + personParams.put(params.getActivityType(), p); + } + + ScoringParameters.Builder builder = new ScoringParameters.Builder(scoring, scoringParameters, personParams, + scenario.getConfig().scenario()); + + // TODO: not configurable at the moment + if (subpopulation.equals("person")) { + + VspScoringConfigGroup vspScoring = ConfigUtils.addOrGetModule(scenario.getConfig(), VspScoringConfigGroup.class); + + for (Map.Entry e : vspScoring.getModeParams().entrySet()) { + + ModeUtilityParameters params = builder.getModeParameters(e.getKey()); + DistanceGroupModeUtilityParameters p = modeParams.computeIfAbsent(e.getKey(), + k -> new DistanceGroupModeUtilityParameters(params, vspScoring.getDistGroups(), e.getValue())); + + builder.setModeParameters(e.getKey(), p); + } + } + + return builder.build(); + } +} diff --git a/src/main/java/org/matsim/run/scoring/DistanceGroupModeUtilityParameters.java b/src/main/java/org/matsim/run/scoring/DistanceGroupModeUtilityParameters.java new file mode 100644 index 00000000..bcaa13ee --- /dev/null +++ b/src/main/java/org/matsim/run/scoring/DistanceGroupModeUtilityParameters.java @@ -0,0 +1,88 @@ +package org.matsim.run.scoring; + +import org.matsim.core.scoring.functions.ModeUtilityParameters; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Mode utility with separate marginalUtilityOfDistance_m per distance group. + */ +public class DistanceGroupModeUtilityParameters extends ModeUtilityParameters { + + private final List groups = new ArrayList<>(); + + /** + * Constructor which copies the base params from given modeParams. + */ + public DistanceGroupModeUtilityParameters(ModeUtilityParameters modeParams, + List dists, VspScoringConfigGroup.ModeParams params) { + super(modeParams.marginalUtilityOfTraveling_s, modeParams.marginalUtilityOfDistance_m, modeParams.monetaryDistanceCostRate, + modeParams.constant, modeParams.dailyMoneyConstant, modeParams.dailyUtilityConstant); + + // Nothing to do if no distance groups are defined. + if (dists.isEmpty()) { + return; + } + + List copy = new ArrayList<>(dists); + + if (copy.get(0) != 0) + copy.add(0, 0); + + // Effectively no distance groups present + if (copy.size() <= 1) + return; + + for (int i = 0; i < copy.size() - 1; i++) { + + int dist = copy.get(i); + double util = params.getDistUtil(dist).orElseThrow(); + + double constant; + if (i == 0) + constant = 0; + else { + DistanceGroup prev = groups.get(groups.size() - 1); + constant = prev.constant + prev.util_m * (dist - prev.dist); + } + + groups.add(new DistanceGroup(dist, constant, util)); + } + } + + + /** + * Calculate the utility for given distance. + */ + public double calcDistUtility(double dist) { + + if (groups.isEmpty()) + return marginalUtilityOfDistance_m * dist; + + DistanceGroup group = groups.get(0); + for (int i = 1; i < groups.size(); i++) { + if (groups.get(i).dist > dist) + break; + + group = groups.get(i); + } + + return group.constant + group.util_m * (dist - group.dist); + } + + /** + * Store distance group + * @param dist lower bound for distance group + * @param constant added constant + * @param util_m utility per meter, i.e. slope of linear function + */ + record DistanceGroup(double dist, double constant, double util_m) implements Comparable { + @Override + public int compareTo(DistanceGroup o) { + return Double.compare(dist, o.dist); + } + } + +} diff --git a/src/main/java/org/matsim/run/scoring/PiecewiseLinearlLegScoring.java b/src/main/java/org/matsim/run/scoring/PiecewiseLinearlLegScoring.java new file mode 100644 index 00000000..72b0a5d7 --- /dev/null +++ b/src/main/java/org/matsim/run/scoring/PiecewiseLinearlLegScoring.java @@ -0,0 +1,231 @@ +/* *********************************************************************** * + * project: org.matsim.* + * CharyparNagelOpenTimesScoringFunctionFactory.java + * * + * *********************************************************************** * + * * + * copyright : (C) 2007 by the members listed in the COPYING, * + * LICENSE and WARRANTY file. * + * email : info at matsim dot org * + * * + * *********************************************************************** * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * See also COPYING, LICENSE and WARRANTY file * + * * + * *********************************************************************** */ + +package org.matsim.run.scoring; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +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.network.Network; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Route; +import org.matsim.core.gbl.Gbl; +import org.matsim.core.scoring.functions.ModeUtilityParameters; +import org.matsim.core.scoring.functions.ScoringParameters; +import org.matsim.pt.PtConstants; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * This is a copy of {@link org.matsim.core.scoring.functions.CharyparNagelLegScoring}. + * Distance utilities are scored with different linear functions per distance group. + */ +public class PiecewiseLinearlLegScoring implements org.matsim.core.scoring.SumScoringFunction.LegScoring, org.matsim.core.scoring.SumScoringFunction.ArbitraryEventScoring { + // yyyy URL in above javadoc is broken. kai, feb'17 + + private static final Logger log = LogManager.getLogger(PiecewiseLinearlLegScoring.class); + private static int ccc = 0; + /** + * The parameters used for scoring + */ + protected final ScoringParameters params; + private final Set ptModes; + private final double marginalUtilityOfMoney; + protected double score; + protected Network network; + private boolean nextEnterVehicleIsFirstOfTrip = true; + private boolean nextStartPtLegIsFirstOfTrip = true; + private boolean currentLegIsPtLeg = false; + private double lastActivityEndTime = Double.NaN; + private final Set modesAlreadyConsideredForDailyConstants; + + public PiecewiseLinearlLegScoring(final ScoringParameters params, Network network, Set ptModes) { + this.params = params; + this.network = network; + this.ptModes = ptModes; + this.modesAlreadyConsideredForDailyConstants = new HashSet<>(); + this.marginalUtilityOfMoney = this.params.marginalUtilityOfMoney; + } + + /** + * Scoring with person-specific marginal utility of money + */ + public PiecewiseLinearlLegScoring(final ScoringParameters params, double marginalUtilityOfMoney, Network network, Set ptModes) { + this.params = params; + this.network = network; + this.ptModes = ptModes; + this.modesAlreadyConsideredForDailyConstants = new HashSet<>(); + this.marginalUtilityOfMoney = marginalUtilityOfMoney; + } + + /** + * Scoring with pt modes set to 'pt' + */ + public PiecewiseLinearlLegScoring(final ScoringParameters params, Network network) { + this(params, network, new HashSet<>(Collections.singletonList("pt"))); + } + + @Override + public void finish() { + + } + + @Override + public double getScore() { + return this.score; + } + + protected double calcLegScore(final double departureTime, final double arrivalTime, final Leg leg) { + double tmpScore = 0.0; + double travelTime = arrivalTime - departureTime; // travel time in seconds + ModeUtilityParameters modeParams = this.params.modeParams.get(leg.getMode()); + + if (modeParams == null) { + if (leg.getMode().equals(TransportMode.transit_walk) || leg.getMode().equals(TransportMode.non_network_walk) + || leg.getMode().equals(TransportMode.non_network_walk)) { + modeParams = this.params.modeParams.get(TransportMode.walk); + } else { +// modeParams = this.params.modeParams.get(TransportMode.other); + throw new RuntimeException("just encountered mode for which no scoring parameters are defined: " + leg.getMode()); + } + } + + tmpScore += travelTime * modeParams.marginalUtilityOfTraveling_s; + + if (modeParams instanceof DistanceGroupModeUtilityParameters distParams) { + + if (modeParams.monetaryDistanceCostRate != 0.0) { + Route route = leg.getRoute(); + double dist = route.getDistance(); // distance in meters + if (Double.isNaN(dist)) { + if (ccc < 10) { + ccc++; + LogManager.getLogger(this.getClass()).warn("distance is NaN. Will make score of this plan NaN. Possible reason: Simulation does not report " + + "a distance for this trip. Possible reason for that: mode is teleported and router does not " + + "write distance into plan. Needs to be fixed or these plans will die out."); + if (ccc == 10) { + LogManager.getLogger(this.getClass()).warn(Gbl.FUTURE_SUPPRESSED); + } + } + } + tmpScore += modeParams.monetaryDistanceCostRate * this.marginalUtilityOfMoney * dist; + } + + Route route = leg.getRoute(); + double dist = route.getDistance(); + + tmpScore += distParams.calcDistUtility(dist); + + } else { + + // standard leg scoring + if (modeParams.marginalUtilityOfDistance_m != 0.0 + || modeParams.monetaryDistanceCostRate != 0.0) { + Route route = leg.getRoute(); + double dist = route.getDistance(); // distance in meters + if ( Double.isNaN(dist) ) { + if ( ccc<10 ) { + ccc++ ; + LogManager.getLogger(this.getClass()).warn("distance is NaN. Will make score of this plan NaN. Possible reason: Simulation does not report " + + "a distance for this trip. Possible reason for that: mode is teleported and router does not " + + "write distance into plan. Needs to be fixed or these plans will die out.") ; + if ( ccc==10 ) { + LogManager.getLogger(this.getClass()).warn(Gbl.FUTURE_SUPPRESSED) ; + } + } + } + tmpScore += modeParams.marginalUtilityOfDistance_m * dist; + tmpScore += modeParams.monetaryDistanceCostRate * this.marginalUtilityOfMoney * dist; + } + } + + tmpScore += modeParams.constant; + // (yyyy once we have multiple legs without "real" activities in between, this will produce wrong results. kai, dec'12) + // (yy NOTE: the constant is added for _every_ pt leg. This is not how such models are estimated. kai, nov'12) + + // account for the daily constants + if (!modesAlreadyConsideredForDailyConstants.contains(leg.getMode())) { + tmpScore += modeParams.dailyUtilityConstant + modeParams.dailyMoneyConstant * this.marginalUtilityOfMoney; + modesAlreadyConsideredForDailyConstants.add(leg.getMode()); + } + // 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 tmpScore; + } + + @Override + public void handleEvent(Event event) { + if (event instanceof ActivityEndEvent) { + // When there is a "real" activity, flags are reset: + if (!PtConstants.TRANSIT_ACTIVITY_TYPE.equals(((ActivityEndEvent) event).getActType())) { + this.nextEnterVehicleIsFirstOfTrip = true; + this.nextStartPtLegIsFirstOfTrip = true; + } + this.lastActivityEndTime = event.getTime(); + } + + if (event instanceof PersonEntersVehicleEvent && currentLegIsPtLeg) { + if (!this.nextEnterVehicleIsFirstOfTrip) { + // all vehicle entering after the first triggers the disutility of line switch: + this.score += params.utilityOfLineSwitch; + } + this.nextEnterVehicleIsFirstOfTrip = false; + // add score of waiting, _minus_ score of travelling (since it is added in the legscoring above): + this.score += (event.getTime() - this.lastActivityEndTime) * (this.params.marginalUtilityOfWaitingPt_s - this.params.modeParams.get(TransportMode.pt).marginalUtilityOfTraveling_s); + } + + if (event instanceof PersonDepartureEvent) { + String mode = ((PersonDepartureEvent) event).getLegMode(); + + this.currentLegIsPtLeg = this.ptModes.contains(mode); + if (currentLegIsPtLeg) { + if (!this.nextStartPtLegIsFirstOfTrip) { + this.score -= params.modeParams.get(mode).constant; + // (yyyy deducting this again, since is it wrongly added above. should be consolidated; this is so the code + // modification is minimally invasive. kai, dec'12) + } + this.nextStartPtLegIsFirstOfTrip = false; + } + } + } + + @Override + public void handleLeg(Leg leg) { + Gbl.assertIf(leg.getDepartureTime().isDefined()); + Gbl.assertIf(leg.getTravelTime().isDefined()); + + double legScore = calcLegScore( + leg.getDepartureTime().seconds(), leg.getDepartureTime().seconds() + leg.getTravelTime() + .seconds(), leg); + 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; + } +} diff --git a/src/main/java/org/matsim/run/scoring/VspScoringConfigGroup.java b/src/main/java/org/matsim/run/scoring/VspScoringConfigGroup.java new file mode 100644 index 00000000..bf0d3e78 --- /dev/null +++ b/src/main/java/org/matsim/run/scoring/VspScoringConfigGroup.java @@ -0,0 +1,108 @@ +package org.matsim.run.scoring; + +import org.matsim.core.config.ConfigGroup; +import org.matsim.core.config.ReflectiveConfigGroup; + +import java.util.*; + +/** + * Stores vsp specific scoring parameters. + */ +public final class VspScoringConfigGroup extends ReflectiveConfigGroup { + + private static final String GROUP_NAME = "vspScoring"; + + private final Map modeParams = new HashMap<>(); + + @Parameter + @Comment("The distance groups for which the marginal utility of distance is defined.") + private String distGroups; + + public VspScoringConfigGroup() { + super(GROUP_NAME); + } + + + public List getDistGroups() { + return distGroups == null ? List.of() : Arrays.stream(distGroups.split(",")).map(Double::parseDouble).map(Double::intValue).toList(); + } + + /** + * Configured mode parameters. + */ + public Map getModeParams() { + return modeParams; + } + + /** + * Retrieve mode parameters. + */ + public ModeParams getModeParams(String mode) { + if (!modeParams.containsKey(mode)) { + ModeParams p = new ModeParams(); + p.mode = mode; + + addParameterSet(p); + return p; + } + + return modeParams.get(mode); + } + + @Override + public ModeParams createParameterSet(String type) { + if (type.equals(ModeParams.GROUP_NAME)) { + return new ModeParams(); + } else { + throw new IllegalArgumentException("Unsupported parameter set type: " + type); + } + } + + @Override + public void addParameterSet(ConfigGroup set) { + if (set instanceof ModeParams p) { + super.addParameterSet(set); + modeParams.put(p.mode, p); + } else { + throw new IllegalArgumentException("Unsupported parameter set class: " + set); + } + } + + /** + * Stores context specific parameters. + */ + public static final class ModeParams extends ReflectiveConfigGroup { + + private static final String GROUP_NAME = "modeParams"; + + @Parameter + @Comment("The mode for which the parameters are defined.") + public String mode; + + + public ModeParams() { + super(GROUP_NAME, true); + } + + public ModeParams setDistUtil(int dist, double util) { + Map p = getParams(); + p.put(Double.toString(dist), Double.toString(util)); + return this; + } + + /** + * Get the utility for given distance group. + */ + public OptionalDouble getDistUtil(int distGroup) { + + Map p = getParams(); + + String key = Integer.toString(distGroup); + if (p.containsKey(key)) { + return OptionalDouble.of(Double.parseDouble(p.get(key))); + } + + return OptionalDouble.empty(); + } + } +} diff --git a/src/main/java/org/matsim/run/scoring/VspScoringFunctionFactory.java b/src/main/java/org/matsim/run/scoring/VspScoringFunctionFactory.java new file mode 100644 index 00000000..604a39f7 --- /dev/null +++ b/src/main/java/org/matsim/run/scoring/VspScoringFunctionFactory.java @@ -0,0 +1,40 @@ +package org.matsim.run.scoring; + +import com.google.inject.Inject; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.Person; +import org.matsim.core.config.Config; +import org.matsim.core.scoring.ScoringFunction; +import org.matsim.core.scoring.ScoringFunctionFactory; +import org.matsim.core.scoring.SumScoringFunction; +import org.matsim.core.scoring.functions.*; + +/** + * Same as {@link org.matsim.core.scoring.functions.CharyparNagelScoringFunctionFactory} but with {@link PiecewiseLinearlLegScoring}. + */ +public class VspScoringFunctionFactory implements ScoringFunctionFactory { + + @Inject + private Config config; + + @Inject + private ScoringParametersForPerson params; + + @Inject + private Network network; + + @Override + public ScoringFunction createNewScoringFunction(Person person) { + final ScoringParameters parameters = params.getScoringParameters(person); + + SumScoringFunction sumScoringFunction = new SumScoringFunction(); + sumScoringFunction.addScoringFunction(new CharyparNagelActivityScoring(parameters)); + // replaced original leg scoring + sumScoringFunction.addScoringFunction(new PiecewiseLinearlLegScoring(parameters, this.network, config.transit().getTransitModes())); + sumScoringFunction.addScoringFunction(new CharyparNagelMoneyScoring(parameters)); + sumScoringFunction.addScoringFunction(new CharyparNagelAgentStuckScoring(parameters)); + sumScoringFunction.addScoringFunction(new ScoreEventScoring()); + return sumScoringFunction; + } + +} diff --git a/src/main/java/org/matsim/run/scoring/VspScoringModule.java b/src/main/java/org/matsim/run/scoring/VspScoringModule.java new file mode 100644 index 00000000..778f0e77 --- /dev/null +++ b/src/main/java/org/matsim/run/scoring/VspScoringModule.java @@ -0,0 +1,22 @@ +package org.matsim.run.scoring; + +import jakarta.inject.Singleton; +import org.matsim.core.config.ConfigUtils; +import org.matsim.core.controler.AbstractModule; +import org.matsim.core.scoring.functions.ScoringParametersForPerson; + +/** + * Module to bind components needed for VSP scoring. + */ +public class VspScoringModule extends AbstractModule { + + @Override + public void install() { + + ConfigUtils.addOrGetModule(getConfig(), VspScoringConfigGroup.class); + + bind(ScoringParametersForPerson.class).to(DetailedPersonScoringParameters.class).in(Singleton.class); + + bindScoringFunctionFactory().to(VspScoringFunctionFactory.class).in(Singleton.class); + } +} diff --git a/src/main/python/calibrate_new.py b/src/main/python/calibrate_new.py new file mode 100755 index 00000000..446cbd54 --- /dev/null +++ b/src/main/python/calibrate_new.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import pandas as pd + +from matsim.calibration import create_calibration, ASCDistCalibrator, utils + +# %% + +modes = ["walk", "car", "pt", "bike", "ride"] +fixed_mode = "walk" +initial = { + "bike": -2.14, + "pt": -0.39, + "car": -0.30, + "ride": -1.20 +} + +target = pd.read_csv("mode_share_ref.csv") + + +def filter_persons(persons): + df = persons[persons.person.str.startswith("berlin")] + print("Filtered %s persons" % len(df)) + return df + + +def filter_modes(df): + return df[df.main_mode.isin(modes)] + + +study, obj = create_calibration( + "calib", + ASCDistCalibrator(modes, initial, target, lr=utils.linear_scheduler(start=0.3, interval=15)), + "matsim-berlin-6.1-SNAPSHOT.jar", + "../../../input/v6.1/berlin-v6.1.config.xml", + args="--1pct --iterations 0", + jvm_args="-Xmx20G -Xms20G -XX:+AlwaysPreTouch -XX:+UseParallelGC", + transform_persons=filter_persons, + transform_trips=filter_modes, + chain_runs=utils.default_chain_scheduler, debug=True +) + +# %% + +study.optimize(obj, 10) diff --git a/src/test/java/org/matsim/run/scoring/DistanceGroupModeUtilityParametersTest.java b/src/test/java/org/matsim/run/scoring/DistanceGroupModeUtilityParametersTest.java new file mode 100644 index 00000000..92452cd6 --- /dev/null +++ b/src/test/java/org/matsim/run/scoring/DistanceGroupModeUtilityParametersTest.java @@ -0,0 +1,66 @@ +package org.matsim.run.scoring; + +import org.junit.jupiter.api.Test; +import org.matsim.core.scoring.functions.ModeUtilityParameters; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +class DistanceGroupModeUtilityParametersTest { + + private final ModeUtilityParameters base = new ModeUtilityParameters( + 0, -1, 0, + 0, 0, 0 + ); + + private static VspScoringConfigGroup.ModeParams params(List dists, List utils) { + VspScoringConfigGroup.ModeParams p = new VspScoringConfigGroup.ModeParams(); + + p.setDistUtil(0, utils.get(0)); + + for (int i = 0; i < utils.size() - 1; i++) { + p.setDistUtil(dists.get(i), utils.get(i + 1)); + } + + return p; + } + + @Test + void empty() { + + DistanceGroupModeUtilityParameters m = new DistanceGroupModeUtilityParameters(base, List.of(), new VspScoringConfigGroup.ModeParams()); + + assertThat(m.calcDistUtility(1000)).isEqualTo(-1000); + assertThat(m.calcDistUtility(0)).isEqualTo(0); + assertThat(m.calcDistUtility(5000)).isEqualTo(-5000); + + } + + @Test + void groups() { + + List dists = List.of(1000, 5000, 10000); + DistanceGroupModeUtilityParameters m = new DistanceGroupModeUtilityParameters( + base, + dists, + params(dists, List.of(-1d, -0.5d, -0.1d, -0.001d)) + ); + + + assertThat(m.calcDistUtility(0)).isEqualTo(0); + assertThat(m.calcDistUtility(500)).isEqualTo(-500); + assertThat(m.calcDistUtility(1000)).isEqualTo(-1000); + + assertThat(m.calcDistUtility(1500)).isEqualTo(-1250d); + assertThat(m.calcDistUtility(2000)).isEqualTo(-1500d); + + assertThat(m.calcDistUtility(5000)).isEqualTo(-3000d); + assertThat(m.calcDistUtility(8000)).isEqualTo(-3300d); + + assertThat(m.calcDistUtility(10000)).isEqualTo(-3500d); + assertThat(m.calcDistUtility(15000)).isEqualTo(-3505d); + + + } +}