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);
+
+
+ }
+}