diff --git a/.gitignore b/.gitignore
index f12897823..8b2072c1b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ dependency-reduced-pom.xml
*.iml
*.idea
simulation_output
+.vscode
diff --git a/core/src/main/java/org/eqasim/core/components/raptor/EqasimRaptorConfigGroup.java b/core/src/main/java/org/eqasim/core/components/raptor/EqasimRaptorConfigGroup.java
index d0ee550f9..213eee283 100644
--- a/core/src/main/java/org/eqasim/core/components/raptor/EqasimRaptorConfigGroup.java
+++ b/core/src/main/java/org/eqasim/core/components/raptor/EqasimRaptorConfigGroup.java
@@ -10,26 +10,26 @@ public EqasimRaptorConfigGroup() {
}
@Parameter
- public double travelTimeRail_u_h = -7.0;
+ public double travelTimeRail_u_h = -0.08317394412379128;
@Parameter
- public double travelTimeSubway_u_h = -7.0;
+ public double travelTimeSubway_u_h = -1.0;
@Parameter
- public double travelTimeBus_u_h = -7.0;
+ public double travelTimeBus_u_h = -2.8470557962683523;
@Parameter
- public double travelTimeTram_u_h = -7.0;
+ public double travelTimeTram_u_h = -4.849609430935352;
@Parameter
- public double travelTimeOther_u_h = -7.0;
+ public double travelTimeOther_u_h = -2.8470557962683523;
@Parameter
- public double perTransfer_u = -1.0;
+ public double perTransfer_u = -0.47539778048347203;
@Parameter
- public double waitTime_u_h = -6.0;
+ public double waitTime_u_h = -17.935075050105493;
@Parameter
- public double walkTime_u_h = -7.0;
+ public double walkTime_u_h = -4.198783720934392;
}
diff --git a/core/src/main/java/org/eqasim/core/scenario/freeflow/FreeflowConfiguration.java b/core/src/main/java/org/eqasim/core/scenario/freeflow/FreeflowConfiguration.java
index 9457e33bd..7dcf77eb3 100644
--- a/core/src/main/java/org/eqasim/core/scenario/freeflow/FreeflowConfiguration.java
+++ b/core/src/main/java/org/eqasim/core/scenario/freeflow/FreeflowConfiguration.java
@@ -34,6 +34,9 @@ static public class Area {
@JsonProperty("factor")
double factor = 1.0;
+ @JsonProperty("crossing_penalty_s")
+ public double crossingPenalty_s = 0.0;
+
@JsonProperty("name")
String name = "";
}
diff --git a/core/src/main/java/org/eqasim/core/scenario/freeflow/FreeflowConfigurator.java b/core/src/main/java/org/eqasim/core/scenario/freeflow/FreeflowConfigurator.java
index 87014231d..d7a6e0bb4 100644
--- a/core/src/main/java/org/eqasim/core/scenario/freeflow/FreeflowConfigurator.java
+++ b/core/src/main/java/org/eqasim/core/scenario/freeflow/FreeflowConfigurator.java
@@ -49,7 +49,7 @@ private FreeflowConfigurator(IdMap records, Network network) {
this.network = network;
}
- private record AreaRecord(Geometry geometry, double factor) {
+ private record AreaRecord(Geometry geometry, double factor, double crossingPenalty_s) {
}
private record Container(IdMap delays, IdMap factors) {
@@ -63,7 +63,7 @@ private Container buildContainer(FreeflowConfiguration configuration) {
for (FreeflowConfiguration.Area area : configuration.areas) {
try {
Geometry geometry = new WKTReader().read(area.wkt);
- areas.add(new AreaRecord(geometry, area.factor));
+ areas.add(new AreaRecord(geometry, area.factor, area.crossingPenalty_s));
} catch (ParseException e) {
throw new IllegalStateException(e);
}
@@ -95,6 +95,7 @@ private Container buildContainer(FreeflowConfiguration configuration) {
for (AreaRecord area : areas) {
if (area.geometry.covers(point)) {
roadFactor *= area.factor;
+ delay += area.crossingPenalty_s;
}
}
}
@@ -119,8 +120,8 @@ public TravelTime buildTravelTime(FreeflowConfiguration configuration) {
Id linkId = entry.getKey();
double travelTime = entry.getValue().networkTravelTime;
- travelTime *= container.factors.get(linkId);
- travelTime += container.delays.get(linkId);
+ travelTime *= container.factors.getOrDefault(linkId, 1.0);
+ travelTime += container.delays.getOrDefault(linkId, 0.0);
travelTimes.put(linkId, travelTime);
}
diff --git a/core/src/main/java/org/eqasim/core/scenario/preparation/AdjustFreespeed.java b/core/src/main/java/org/eqasim/core/scenario/preparation/AdjustFreespeed.java
new file mode 100644
index 000000000..e9a21bf6c
--- /dev/null
+++ b/core/src/main/java/org/eqasim/core/scenario/preparation/AdjustFreespeed.java
@@ -0,0 +1,40 @@
+package org.eqasim.core.scenario.preparation;
+
+import org.matsim.api.core.v01.Scenario;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.core.config.CommandLine;
+import org.matsim.core.config.CommandLine.ConfigurationException;
+
+public class AdjustFreespeed {
+ static public final String INITIAL_FREESPEED = "eqasim:initialFreespeed";
+
+ static public void main(String[] args) throws ConfigurationException {
+ CommandLine commandLine = new CommandLine.Builder(args) //
+ .requireOptions("input-path", "output-path") //
+ .allowPrefixes("freespeed") //
+ .build();
+
+
+
+ }
+
+ static public void run(Network network) {
+
+ }
+
+ static public void validate(Scenario scenario) {
+ boolean valid = false;
+
+ for (Link link : scenario.getNetwork().getLinks().values()) {
+ if (link.getAttributes().getAttribute(INITIAL_FREESPEED) != null) {
+ valid = true;
+ break;
+ }
+ }
+
+ if (!valid) {
+ throw new IllegalStateException("Did not find initial freespeed. Did you call AdjustFreespeed?");
+ }
+ }
+}
diff --git a/core/src/main/java/org/eqasim/core/scenario/spatial/ImputeSpatialAttribute.java b/core/src/main/java/org/eqasim/core/scenario/spatial/ImputeSpatialAttribute.java
index 3c948a4c9..8069f8c56 100644
--- a/core/src/main/java/org/eqasim/core/scenario/spatial/ImputeSpatialAttribute.java
+++ b/core/src/main/java/org/eqasim/core/scenario/spatial/ImputeSpatialAttribute.java
@@ -13,6 +13,8 @@
import org.matsim.api.core.v01.population.Population;
import org.matsim.core.router.TripStructureUtils;
import org.matsim.core.router.TripStructureUtils.StageActivityHandling;
+import org.matsim.pt.transitSchedule.api.TransitSchedule;
+import org.matsim.pt.transitSchedule.api.TransitStopFacility;
public class ImputeSpatialAttribute {
private final Geometry geometry;
@@ -30,12 +32,17 @@ public void run(Population population) throws InterruptedException {
for (Person person : population.getPersons().values()) {
for (Plan plan : person.getPlans()) {
- for (Activity activity : TripStructureUtils.getActivities(plan, StageActivityHandling.ExcludeStageActivities)) {
+ for (Activity activity : TripStructureUtils.getActivities(plan,
+ StageActivityHandling.ExcludeStageActivities)) {
Point point = factory
.createPoint(new Coordinate(activity.getCoord().getX(), activity.getCoord().getY()));
if (geometry.contains(point)) {
activity.getAttributes().putAttribute(attribute, true);
+
+ if (activity.getType().equals("home")) {
+ person.getAttributes().putAttribute(attribute, true);
+ }
}
}
}
@@ -62,4 +69,21 @@ public void run(Network network) throws InterruptedException {
progress.close();
}
+
+ public void run(TransitSchedule schedule) throws InterruptedException {
+ ParallelProgress progress = new ParallelProgress("Imputing spatial schedule attributes ...",
+ schedule.getFacilities().size());
+
+ for (TransitStopFacility facility : schedule.getFacilities().values()) {
+ Point point = factory.createPoint(new Coordinate(facility.getCoord().getX(), facility.getCoord().getY()));
+
+ if (geometry.covers(point)) {
+ facility.getAttributes().putAttribute(attribute, true);
+ }
+
+ progress.update();
+ }
+
+ progress.close();
+ }
}
diff --git a/core/src/main/java/org/eqasim/core/scenario/spatial/RunImputeSpatialAttribute.java b/core/src/main/java/org/eqasim/core/scenario/spatial/RunImputeSpatialAttribute.java
index a92a6c6a8..f181e92c7 100644
--- a/core/src/main/java/org/eqasim/core/scenario/spatial/RunImputeSpatialAttribute.java
+++ b/core/src/main/java/org/eqasim/core/scenario/spatial/RunImputeSpatialAttribute.java
@@ -18,14 +18,17 @@
import org.matsim.core.population.io.PopulationReader;
import org.matsim.core.population.io.PopulationWriter;
import org.matsim.core.scenario.ScenarioUtils;
+import org.matsim.pt.transitSchedule.api.TransitScheduleReader;
+import org.matsim.pt.transitSchedule.api.TransitScheduleWriter;
public class RunImputeSpatialAttribute {
static public void main(String[] args)
throws ConfigurationException, MalformedURLException, IOException, InterruptedException {
CommandLine cmd = new CommandLine.Builder(args) //
.allowOptions("input-population-path", "input-network-path", "output-population-path",
- "output-network-path") //
- .requireOptions("shape-path", "shape-attribute", "shape-value", "attribute", EqasimConfigurator.CONFIGURATOR) //
+ "output-network-path", "input-schedule-path", "output-schedule-path",
+ EqasimConfigurator.CONFIGURATOR) //
+ .requireOptions("shape-path", "shape-attribute", "shape-value", "attribute") //
.build();
if (cmd.hasOption("input-population-path") ^ cmd.hasOption("output-population-path")) {
@@ -36,6 +39,10 @@ static public void main(String[] args)
throw new IllegalStateException("Both input and output path must be given for the network.");
}
+ if (cmd.hasOption("input-schedule-path") ^ cmd.hasOption("output-schedule-path")) {
+ throw new IllegalStateException("Both input and output path must be given for the schedule.");
+ }
+
// Load shape
String shapeAttribute = cmd.getOptionStrict("shape-attribute");
String shapeValue = cmd.getOptionStrict("shape-value");
@@ -68,5 +75,14 @@ static public void main(String[] args)
algorithm.run(scenario.getPopulation());
new PopulationWriter(scenario.getPopulation()).write(cmd.getOptionStrict("output-population-path"));
}
+
+ // Load schedule
+ if (cmd.hasOption("input-schedule-path")) {
+ File populationPath = new File(cmd.getOptionStrict("input-schedule-path"));
+ new TransitScheduleReader(scenario).readFile(populationPath.toString());
+ algorithm.run(scenario.getTransitSchedule());
+ new TransitScheduleWriter(scenario.getTransitSchedule())
+ .writeFile(cmd.getOptionStrict("output-schedule-path"));
+ }
}
}
diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/cost/AbstractCostModelWithPreviousTrips.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/cost/AbstractCostModelWithPreviousTrips.java
new file mode 100644
index 000000000..c4b5d8ae7
--- /dev/null
+++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/cost/AbstractCostModelWithPreviousTrips.java
@@ -0,0 +1,8 @@
+package org.eqasim.core.simulation.mode_choice.cost;
+
+public abstract class AbstractCostModelWithPreviousTrips extends AbstractCostModel
+ implements CostModelWithPreviousTrips {
+ protected AbstractCostModelWithPreviousTrips(String mode) {
+ super(mode);
+ }
+}
diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/cost/CostModelWithPreviousTrips.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/cost/CostModelWithPreviousTrips.java
new file mode 100644
index 000000000..bdb5daec7
--- /dev/null
+++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/cost/CostModelWithPreviousTrips.java
@@ -0,0 +1,13 @@
+package org.eqasim.core.simulation.mode_choice.cost;
+
+import java.util.List;
+
+import org.matsim.api.core.v01.population.Person;
+import org.matsim.api.core.v01.population.PlanElement;
+import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate;
+
+public interface CostModelWithPreviousTrips extends CostModel {
+ double calculateCost_MU(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements,
+ List previousTrips);
+}
diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/epsilon/AdaptConfigForEpsilon.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/epsilon/AdaptConfigForEpsilon.java
index bf12e883a..2e33824d4 100644
--- a/core/src/main/java/org/eqasim/core/simulation/mode_choice/epsilon/AdaptConfigForEpsilon.java
+++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/epsilon/AdaptConfigForEpsilon.java
@@ -15,12 +15,16 @@ public static void main(String[] args) throws CommandLine.ConfigurationException
Config config = ConfigUtils.loadConfig(commandLine.getOptionStrict("input-config-path"), new EqasimConfigGroup(), new DiscreteModeChoiceConfigGroup());
commandLine.applyConfiguration(config);
+ run(config);
+
+ ConfigUtils.writeConfig(config, commandLine.getOptionStrict("output-config-path"));
+ }
+
+ static public void run(Config config) {
DiscreteModeChoiceConfigGroup discreteModeChoiceConfigGroup = (DiscreteModeChoiceConfigGroup) config.getModules().get(DiscreteModeChoiceConfigGroup.GROUP_NAME);
discreteModeChoiceConfigGroup.setSelector(SelectorModule.MAXIMUM);
EqasimConfigGroup eqasimConfigGroup = (EqasimConfigGroup) config.getModules().get(EqasimConfigGroup.GROUP_NAME);
eqasimConfigGroup.setUsePseudoRandomErrors(true);
-
- ConfigUtils.writeConfig(config, commandLine.getOptionStrict("output-config-path"));
}
}
diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/EqasimUtilityEstimator.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/EqasimUtilityEstimator.java
index 56918edf2..daf860c96 100644
--- a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/EqasimUtilityEstimator.java
+++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/EqasimUtilityEstimator.java
@@ -37,7 +37,14 @@ protected double estimateTrip(Person person, String mode, DiscreteModeChoiceTrip
if (estimator == null) {
throw new IllegalStateException(String.format("No estimator registered for mode '%s'", mode));
} else {
- double utility = estimator.estimateUtility(person, trip, elements);
+ double utility = 0.0;
+
+ if (estimator instanceof UtilityEstimatorWithPreviousTrips withPreviousTrips) {
+ utility += withPreviousTrips.estimateUtility(person, trip, elements, previousTrips);
+ } else {
+ utility += estimator.estimateUtility(person, trip, elements);
+ }
+
utility += epsilonProvider.getEpsilon(person.getId(), trip.getIndex(), mode);
utility -= utilityPenalty.calculatePenalty(mode, person, trip, elements);
return utility;
diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/UtilityEstimatorWithPreviousTrips.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/UtilityEstimatorWithPreviousTrips.java
new file mode 100644
index 000000000..2b5d06163
--- /dev/null
+++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/UtilityEstimatorWithPreviousTrips.java
@@ -0,0 +1,13 @@
+package org.eqasim.core.simulation.mode_choice.utilities;
+
+import java.util.List;
+
+import org.matsim.api.core.v01.population.Person;
+import org.matsim.api.core.v01.population.PlanElement;
+import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate;
+
+public interface UtilityEstimatorWithPreviousTrips extends UtilityEstimator {
+ double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements,
+ List previousTrips);
+}
diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/estimators/CarUtilityEstimator.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/estimators/CarUtilityEstimator.java
index b0628fa2f..55511f924 100644
--- a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/estimators/CarUtilityEstimator.java
+++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/estimators/CarUtilityEstimator.java
@@ -3,16 +3,17 @@
import java.util.List;
import org.eqasim.core.simulation.mode_choice.parameters.ModeParameters;
-import org.eqasim.core.simulation.mode_choice.utilities.UtilityEstimator;
+import org.eqasim.core.simulation.mode_choice.utilities.UtilityEstimatorWithPreviousTrips;
import org.eqasim.core.simulation.mode_choice.utilities.predictors.CarPredictor;
import org.eqasim.core.simulation.mode_choice.utilities.variables.CarVariables;
import org.matsim.api.core.v01.population.Person;
import org.matsim.api.core.v01.population.PlanElement;
import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate;
import com.google.inject.Inject;
-public class CarUtilityEstimator implements UtilityEstimator {
+public class CarUtilityEstimator implements UtilityEstimatorWithPreviousTrips {
private final ModeParameters parameters;
private final CarPredictor predictor;
@@ -40,8 +41,9 @@ protected double estimateMonetaryCostUtility(CarVariables variables) {
}
@Override
- public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
- CarVariables variables = predictor.predictVariables(person, trip, elements);
+ public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements,
+ List previousTrips) {
+ CarVariables variables = predictor.predictVariables(person, trip, elements, previousTrips);
double utility = 0.0;
@@ -52,4 +54,9 @@ public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List
return utility;
}
+
+ @Override
+ public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
+ throw new IllegalStateException("Need to provide previous trips");
+ }
}
diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/CachedVariablePredictorWithPreviousTrips.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/CachedVariablePredictorWithPreviousTrips.java
new file mode 100644
index 000000000..986aa92c6
--- /dev/null
+++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/CachedVariablePredictorWithPreviousTrips.java
@@ -0,0 +1,29 @@
+package org.eqasim.core.simulation.mode_choice.utilities.predictors;
+
+import java.util.List;
+
+import org.eqasim.core.simulation.mode_choice.utilities.variables.BaseVariables;
+import org.matsim.api.core.v01.population.Person;
+import org.matsim.api.core.v01.population.PlanElement;
+import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate;
+
+public abstract class CachedVariablePredictorWithPreviousTrips
+ implements VariablePredictorWithPreviousTrips {
+ private DiscreteModeChoiceTrip cacheKey = null;
+ private T cacheValue = null;
+
+ @Override
+ public T predictVariables(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements,
+ List previousTrips) {
+ if (trip != cacheKey) {
+ cacheKey = trip;
+ cacheValue = predict(person, trip, elements, previousTrips);
+ }
+
+ return cacheValue;
+ }
+
+ protected abstract T predict(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements,
+ List previousTrips);
+}
diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/CarPredictor.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/CarPredictor.java
index df6ce4600..063ebbb5b 100644
--- a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/CarPredictor.java
+++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/CarPredictor.java
@@ -3,6 +3,7 @@
import java.util.List;
import org.eqasim.core.simulation.mode_choice.cost.CostModel;
+import org.eqasim.core.simulation.mode_choice.cost.CostModelWithPreviousTrips;
import org.eqasim.core.simulation.mode_choice.parameters.ModeParameters;
import org.eqasim.core.simulation.mode_choice.utilities.variables.CarVariables;
import org.matsim.api.core.v01.TransportMode;
@@ -10,13 +11,14 @@
import org.matsim.api.core.v01.population.Person;
import org.matsim.api.core.v01.population.PlanElement;
import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate;
import org.matsim.core.router.TripStructureUtils;
import com.google.common.base.Verify;
import com.google.inject.Inject;
import com.google.inject.name.Named;
-public class CarPredictor extends CachedVariablePredictor {
+public class CarPredictor extends CachedVariablePredictorWithPreviousTrips {
private final CostModel costModel;
private final ModeParameters parameters;
@@ -27,7 +29,8 @@ public CarPredictor(ModeParameters parameters, @Named("car") CostModel costModel
}
@Override
- public CarVariables predict(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
+ public CarVariables predict(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements,
+ List previousTrips) {
double carTravelTime_min = parameters.car.constantParkingSearchPenalty_min;
double accessEgressTime_min = parameters.car.additionalAccessEgressWalkTime_min;
@@ -44,7 +47,13 @@ public CarVariables predict(Person person, DiscreteModeChoiceTrip trip, List e
}
}
- double cost_MU = costModel.calculateCost_MU(person, trip, elements);
+ final double cost_MU;
+ if (costModel instanceof CostModelWithPreviousTrips) {
+ cost_MU = ((CostModelWithPreviousTrips) costModel).calculateCost_MU(person, trip, elements, previousTrips);
+ } else {
+ cost_MU = costModel.calculateCost_MU(person, trip, elements);
+ }
+
double euclideanDistance_km = PredictorUtils.calculateEuclideanDistance_km(trip);
return new CarVariables(carTravelTime_min, cost_MU, euclideanDistance_km, accessEgressTime_min);
diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/VariablePredictorWithPreviousTrips.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/VariablePredictorWithPreviousTrips.java
new file mode 100644
index 000000000..d4085845b
--- /dev/null
+++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/VariablePredictorWithPreviousTrips.java
@@ -0,0 +1,14 @@
+package org.eqasim.core.simulation.mode_choice.utilities.predictors;
+
+import java.util.List;
+
+import org.eqasim.core.simulation.mode_choice.utilities.variables.BaseVariables;
+import org.matsim.api.core.v01.population.Person;
+import org.matsim.api.core.v01.population.PlanElement;
+import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate;
+
+public interface VariablePredictorWithPreviousTrips {
+ T predictVariables(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements,
+ List previousTrips);
+}
diff --git a/core/src/main/java/org/eqasim/core/simulation/vdf/VDFModule.java b/core/src/main/java/org/eqasim/core/simulation/vdf/VDFModule.java
index 84f3872d9..24fa1fc47 100644
--- a/core/src/main/java/org/eqasim/core/simulation/vdf/VDFModule.java
+++ b/core/src/main/java/org/eqasim/core/simulation/vdf/VDFModule.java
@@ -88,6 +88,7 @@ public VDFTravelTime provideVDFTravelTime(VDFConfigGroup config, VDFScope scope,
: new ShapeScenarioExtent.Builder(new File(ConfigGroup
.getInputFileURL(getConfig().getContext(), config.getUpdateAreaShapefile()).getPath()),
Optional.empty(), Optional.empty()).build();
+
VDFTravelTime vdfTravelTime = new VDFTravelTime(scope, config.getMinimumSpeed(), config.getCapacityFactor(),
eqasimConfig.getSampleSize(), network, vdf, crossingPenalty, updateExtent);
if (config.getInputTravelTimesFile() != null) {
diff --git a/core/src/test/java/org/eqasim/TestEmissions.java b/core/src/test/java/org/eqasim/TestEmissions.java
index 012d17bd4..866dc36f6 100644
--- a/core/src/test/java/org/eqasim/TestEmissions.java
+++ b/core/src/test/java/org/eqasim/TestEmissions.java
@@ -140,11 +140,11 @@ private void runModifyNetwork() {
private void runMelunEmissions() throws CommandLine.ConfigurationException, IOException {
Map counts = countLegs("melun_test/output/output_events.xml.gz");
- Assert.assertEquals(3297, (long) counts.get("car"));
- Assert.assertEquals(1560, (long) counts.get("car_passenger"));
- Assert.assertEquals(9348, (long) counts.get("walk"));
- Assert.assertEquals(3412, (long) counts.getOrDefault("bike", 0L));
- Assert.assertEquals(2108, (long) counts.get("pt"));
+ Assert.assertEquals(2793, (long) counts.get("car"));
+ Assert.assertEquals(1559, (long) counts.get("car_passenger"));
+ Assert.assertEquals(11642, (long) counts.get("walk"));
+ Assert.assertEquals(2861, (long) counts.getOrDefault("bike", 0L));
+ Assert.assertEquals(3347, (long) counts.get("pt"));
SafeOsmHbefaMapping.defaultType = "URB/Loca/50";
@@ -177,6 +177,8 @@ private void runMelunEmissions() throws CommandLine.ConfigurationException, IOEx
double expectedCo = 0.627553969527029;
double expectedNox = 0.810111846744523;
double expectedUnknown = Double.NaN;
+
+
assertEquals(expectedPm, feature.getAttribute("PM"));
assertEquals(expectedCo, feature.getAttribute("CO"));
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/IDFConfigurator.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/IDFConfigurator.java
index 5fecd6ba5..9b5bdea53 100644
--- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/IDFConfigurator.java
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/IDFConfigurator.java
@@ -2,6 +2,9 @@
import org.eqasim.core.simulation.EqasimConfigurator;
import org.eqasim.ile_de_france.mode_choice.IDFModeChoiceModule;
+import org.eqasim.ile_de_france.parking.IDFParkingModule;
+import org.eqasim.ile_de_france.probing.ProbeConfigGroup;
+import org.eqasim.ile_de_france.probing.ProbeModule;
import org.matsim.core.config.CommandLine;
public class IDFConfigurator extends EqasimConfigurator {
@@ -9,5 +12,9 @@ public IDFConfigurator(CommandLine cmd) {
super(cmd);
registerModule(new IDFModeChoiceModule(cmd));
+ registerModule(new IDFParkingModule());
+
+ registerConfigGroup(new ProbeConfigGroup(), false);
+ registerModule(new ProbeModule());
}
}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/RunSimulation.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/RunSimulation.java
index d2c0a75a7..95fc31688 100644
--- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/RunSimulation.java
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/RunSimulation.java
@@ -1,25 +1,62 @@
package org.eqasim.ile_de_france;
+import java.util.Collections;
+import java.util.Set;
+
import org.eqasim.core.scenario.validation.VehiclesValidator;
+import org.eqasim.core.simulation.vdf.VDFConfigGroup;
+import org.eqasim.core.simulation.vdf.engine.VDFEngineConfigGroup;
+import org.eqasim.core.simulation.vdf.travel_time.VDFTravelTime;
import org.matsim.api.core.v01.Scenario;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.population.Person;
import org.matsim.core.config.CommandLine;
import org.matsim.core.config.CommandLine.ConfigurationException;
+import org.matsim.core.config.groups.QSimConfigGroup.NodeTransition;
import org.matsim.core.config.Config;
import org.matsim.core.config.ConfigUtils;
+import org.matsim.core.controler.AbstractModule;
import org.matsim.core.controler.Controler;
+import org.matsim.core.router.util.TravelTime;
import org.matsim.core.scenario.ScenarioUtils;
+import org.matsim.vehicles.Vehicle;
+
+import com.google.inject.Inject;
+import com.google.inject.Provider;
public class RunSimulation {
static public void main(String[] args) throws ConfigurationException {
CommandLine cmd = new CommandLine.Builder(args) //
.requireOptions("config-path") //
- .allowPrefixes("mode-choice-parameter", "cost-parameter") //
+ .allowPrefixes("mode-choice-parameter", "cost-parameter", "use-vdf", "use-vdf-engine") //
+ .allowOptions("passenger-speed-factor") //
.build();
IDFConfigurator configurator = new IDFConfigurator(cmd);
Config config = ConfigUtils.loadConfig(cmd.getOptionStrict("config-path"));
configurator.updateConfig(config);
+ if (cmd.getOption("use-vdf").map(Boolean::parseBoolean).orElse(false)) {
+ VDFConfigGroup vdfConfig = new VDFConfigGroup();
+ config.addModule(vdfConfig);
+
+ vdfConfig.setCapacityFactor(1.0);
+ vdfConfig.setModes(Set.of("car", "car_passenger"));
+
+ config.qsim().setFlowCapFactor(1e9);
+ config.qsim().setStorageCapFactor(1e9);
+ config.qsim().setStuckTime(3600.0);
+
+ if (cmd.getOption("use-vdf-engine").map(Boolean::parseBoolean).orElse(false)) {
+ VDFEngineConfigGroup engineConfig = new VDFEngineConfigGroup();
+ engineConfig.setModes(Set.of("car", "car_passenger"));
+ engineConfig.setGenerateNetworkEvents(false);
+ config.addModule(engineConfig);
+
+ config.qsim().setMainModes(Collections.emptySet());
+ }
+ }
+
cmd.applyConfiguration(config);
VehiclesValidator.validate(config);
@@ -30,6 +67,35 @@ static public void main(String[] args) throws ConfigurationException {
Controler controller = new Controler(scenario);
configurator.configureController(controller);
+
+ double passengerSpeedFactor = cmd.getOption("passenger-speed-factor").map(Double::parseDouble).orElse(1.0);
+
+ if (cmd.hasOption("use-vdf")) {
+ controller.addOverridingModule(new AbstractModule() {
+ @Override
+ public void install() {
+ addTravelTimeBinding("car_passenger").toProvider(new Provider<>() {
+ @Inject
+ VDFTravelTime delegate;
+
+ @Override
+ public TravelTime get() {
+ return new TravelTime() {
+ @Override
+ public double getLinkTravelTime(Link link, double time, Person person,
+ Vehicle vehicle) {
+ double travelTime = delegate.getLinkTravelTime(link, time, person, vehicle);
+ travelTime /= passengerSpeedFactor;
+ double linkTravelTime = Math.floor(travelTime);
+ return linkTravelTime + 1.0;
+ }
+ };
+ }
+ });
+ }
+ });
+ }
+
controller.run();
}
}
\ No newline at end of file
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/RunTemporaryModelModification.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/RunTemporaryModelModification.java
new file mode 100644
index 000000000..99563feca
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/RunTemporaryModelModification.java
@@ -0,0 +1,150 @@
+package org.eqasim.ile_de_france;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.eqasim.core.misc.ParallelProgress;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFPredictorUtils;
+import org.eqasim.ile_de_france.parking.ParkingPressure;
+import org.eqasim.ile_de_france.parking.ParkingTariff;
+import org.geotools.api.data.SimpleFeatureReader;
+import org.geotools.api.feature.simple.SimpleFeature;
+import org.geotools.geopkg.FeatureEntry;
+import org.geotools.geopkg.GeoPackage;
+import org.locationtech.jts.geom.Coordinate;
+import org.locationtech.jts.geom.Geometry;
+import org.locationtech.jts.geom.GeometryFactory;
+import org.locationtech.jts.geom.Point;
+import org.matsim.api.core.v01.Scenario;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.population.Activity;
+import org.matsim.api.core.v01.population.Person;
+import org.matsim.api.core.v01.population.Plan;
+import org.matsim.core.config.CommandLine;
+import org.matsim.core.config.CommandLine.ConfigurationException;
+import org.matsim.core.config.Config;
+import org.matsim.core.config.ConfigUtils;
+import org.matsim.core.network.io.MatsimNetworkReader;
+import org.matsim.core.network.io.NetworkWriter;
+import org.matsim.core.population.io.PopulationReader;
+import org.matsim.core.population.io.PopulationWriter;
+import org.matsim.core.router.TripStructureUtils;
+import org.matsim.core.router.TripStructureUtils.StageActivityHandling;
+import org.matsim.core.scenario.ScenarioUtils;
+
+import com.google.common.base.Preconditions;
+
+public class RunTemporaryModelModification {
+ private final static Logger logger = LogManager.getLogger(RunTemporaryModelModification.class);
+ private final static GeometryFactory geometryFactory = new GeometryFactory();
+
+ static public void main(String[] args) throws ConfigurationException, IOException, InterruptedException {
+ CommandLine cmd = new CommandLine.Builder(args) //
+ .requireOptions("config-path", "data-path", "output-population-path", "output-network-path") //
+ .build();
+
+ IDFConfigurator configurator = new IDFConfigurator(cmd);
+ Config config = ConfigUtils.loadConfig(cmd.getOptionStrict("config-path"));
+ configurator.updateConfig(config);
+ cmd.applyConfiguration(config);
+
+ logger.info("Reading zone information ...");
+ List- items = new LinkedList<>();
+ try (GeoPackage gpkg = new GeoPackage(new File(cmd.getOptionStrict("data-path")))) {
+ FeatureEntry featureEntry = gpkg.feature("data");
+ SimpleFeatureReader reader = gpkg.reader(featureEntry, null, null);
+
+ while (reader.hasNext()) {
+ SimpleFeature feature = reader.next();
+
+ String municipalityId = (String) feature.getAttribute("municipality_id");
+ Preconditions.checkNotNull(municipalityId);
+
+ Double parkingPressure = (Double) feature.getAttribute("parking_pressure");
+ Preconditions.checkNotNull(parkingPressure);
+
+ Double parkingTariff = (Double) feature.getAttribute("parking_tariff");
+ Preconditions.checkNotNull(parkingTariff);
+
+ items.add(new Item(municipalityId, parkingPressure, parkingTariff,
+ (Geometry) feature.getDefaultGeometry()));
+ }
+ }
+
+ Scenario scenario = ScenarioUtils.createScenario(config);
+ configurator.adjustScenario(scenario);
+
+ new PopulationReader(scenario)
+ .readURL(config.plans().getInputFileURL(config.getContext()));
+
+ new MatsimNetworkReader(scenario.getNetwork()).readURL(config.network().getInputFileURL(config.getContext()));
+
+ ParallelProgress progress = new ParallelProgress("Processing population ...",
+ scenario.getPopulation().getPersons().size());
+ progress.start();
+
+ for (Person person : scenario.getPopulation().getPersons().values()) {
+ for (Plan plan : person.getPlans()) {
+ Activity homeActivity = null;
+
+ for (Activity activity : TripStructureUtils.getActivities(plan,
+ StageActivityHandling.ExcludeStageActivities)) {
+ if (homeActivity == null && activity.getType().equals("home")) {
+ homeActivity = activity;
+ }
+
+ Coordinate coordinate = new Coordinate(activity.getCoord().getX(), activity.getCoord().getY());
+ Point pointGeometry = geometryFactory.createPoint(coordinate);
+
+ for (Item item : items) {
+ if (item.geometry.covers(pointGeometry)) {
+ activity.getAttributes().putAttribute(IDFPredictorUtils.ACTIVITY_MUNICIPALITY_ID,
+ item.municipalityId);
+ break;
+ }
+ }
+ }
+
+ if (homeActivity != null) {
+ person.getAttributes().putAttribute(IDFPredictorUtils.RESIDENCE_MUNICIPALITY_ID,
+ homeActivity.getAttributes().getAttribute(IDFPredictorUtils.ACTIVITY_MUNICIPALITY_ID));
+ }
+ }
+
+ progress.update();
+ }
+
+ progress.close();
+
+ progress = new ParallelProgress("Processing network ...",
+ scenario.getNetwork().getLinks().size());
+ progress.start();
+
+ for (Link link : scenario.getNetwork().getLinks().values()) {
+ Coordinate coordinate = new Coordinate(link.getCoord().getX(), link.getCoord().getY());
+ Point pointGeometry = geometryFactory.createPoint(coordinate);
+
+ for (Item item : items) {
+ if (item.geometry.covers(pointGeometry)) {
+ link.getAttributes().putAttribute(ParkingPressure.LINK_ATTRIBUTE, item.parkingPressure);
+ link.getAttributes().putAttribute(ParkingTariff.LINK_ATTRIBUTE, item.parkingTariff);
+ break;
+ }
+ }
+
+ progress.update();
+ }
+
+ progress.close();
+
+ new PopulationWriter(scenario.getPopulation()).write(cmd.getOptionStrict("output-population-path"));
+ new NetworkWriter(scenario.getNetwork()).write(cmd.getOptionStrict("output-network-path"));
+ }
+
+ private record Item(String municipalityId, double parkingPressure, double parkingTariff, Geometry geometry) {
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/IDFModeAvailability.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/IDFModeAvailability.java
index e4dd523a3..73cb246bd 100644
--- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/IDFModeAvailability.java
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/IDFModeAvailability.java
@@ -5,11 +5,11 @@
import java.util.List;
import java.util.Set;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFPredictorUtils;
import org.matsim.api.core.v01.TransportMode;
import org.matsim.api.core.v01.population.Person;
import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
import org.matsim.contribs.discrete_mode_choice.model.mode_availability.ModeAvailability;
-import org.matsim.core.population.PersonUtils;
public class IDFModeAvailability implements ModeAvailability {
private final Set additionalModes;
@@ -27,45 +27,24 @@ public Collection getAvailableModes(Person person, List elements,
+ List previousTrips) {
+ double parkingCost_EUR = calculateParkingCost_EUR(person, trip, elements, previousTrips);
+
+ return costParameters.carCost_EUR_km * getInVehicleDistance_km(elements)
+ + parkingCost_EUR;
+ }
+
+ public double calculateParkingCost_EUR(Person person, DiscreteModeChoiceTrip trip,
+ List extends PlanElement> tripElements, List previousTrips) {
+ IDFSpatialVariables spatialVariables = spatialPredictor.predictVariables(person, trip, tripElements);
+ IDFParkingVariables parkingVariables = parkingPredictor.predictVariables(person, trip, tripElements);
+ IDFPersonVariables personVariables = personPredictor.predictVariables(person, trip, tripElements);
+
+ // Origin parking costs
+ double originParkingCost_EUR = 0.0;
+
+ boolean isOriginWork = trip.getOriginActivity().getType().equals("work");
+ boolean isOriginResident = spatialVariables.originMunicipalityId
+ .equals(personVariables.residenceMunicipalityId);
+
+ if (!isOriginWork && !isOriginResident) {
+ double originDuration = getOriginDuration(person, trip, previousTrips);
+ originParkingCost_EUR = Math.ceil(originDuration / 3600.0)
+ * parkingVariables.originParkingTariff_EUR_h;
+ }
+
+ // Destination parking costs
+ double destinationParkingCost_EUR = 0.0;
+
+ boolean isDestinationWork = trip.getDestinationActivity().getType().equals("work");
+ boolean isDestinationResident = spatialVariables.destinationMunicipalityId
+ .equals(personVariables.residenceMunicipalityId);
+
+ if (!isDestinationWork && !isDestinationResident) {
+ double destinationDuration = getDestinationDuration(person, trip, tripElements);
+ destinationParkingCost_EUR = Math.ceil(destinationDuration / 3600.0)
+ * parkingVariables.destinationParkingTariff_EUR_h;
+ }
+
+ return originParkingCost_EUR + destinationParkingCost_EUR;
+ }
+
+ private final double FIRST_LAST_DURATION = 8.0 * 3600.0;
+
+ private final static String PRECEDING_PARKING_DURATION = "precedingParkingDuration";
+ private final static String FOLLOWING_PARKING_DURATION = "followingParkingDuration";
+
+ private double getOriginDuration(Person person, DiscreteModeChoiceTrip trip, List previousTrips) {
+ Double precedingParkingDuration = (Double) trip.getTripAttributes().getAttribute(PRECEDING_PARKING_DURATION);
+ if (precedingParkingDuration != null) {
+ return precedingParkingDuration;
+ }
+
+ if (previousTrips.size() == 0) {
+ return FIRST_LAST_DURATION;
+ } else {
+ TimeTracker timeTracker = new TimeTracker(timeInterpretation);
+
+ DefaultRoutedTripCandidate previousTrip = (DefaultRoutedTripCandidate) previousTrips
+ .get(previousTrips.size() - 1);
+ List extends PlanElement> precedingElements = previousTrip.getRoutedPlanElements();
+
+ double startTime = ((Leg) precedingElements.get(0)).getDepartureTime().seconds();
+ timeTracker.setTime(startTime);
+ timeTracker.addElements(precedingElements);
+
+ double arrivalTime = timeTracker.getTime().seconds();
+ timeTracker.addActivity(trip.getOriginActivity());
+ double departureTime = timeTracker.getTime().seconds();
+
+ Preconditions.checkState(arrivalTime <= departureTime);
+ return departureTime - arrivalTime;
+ }
+ }
+
+ private double getDestinationDuration(Person person, DiscreteModeChoiceTrip trip,
+ List extends PlanElement> tripElements) {
+ Double followingParkingDuration = (Double) trip.getTripAttributes().getAttribute(FOLLOWING_PARKING_DURATION);
+ if (followingParkingDuration != null) {
+ return followingParkingDuration;
+ }
+
+ List extends PlanElement> planElements = person.getSelectedPlan().getPlanElements();
+ if (planElements.get(planElements.size() - 1) == trip.getDestinationActivity()) {
+ return FIRST_LAST_DURATION;
+ } else {
+ TimeTracker timeTracker = new TimeTracker(timeInterpretation);
+ timeTracker.setTime(trip.getDepartureTime());
+ timeTracker.addElements(tripElements);
+
+ double arrivalTime = timeTracker.getTime().seconds();
+ timeTracker.addActivity(trip.getDestinationActivity());
+ double departureTime = timeTracker.getTime().seconds();
+
+ Preconditions.checkState(arrivalTime <= departureTime);
+ return departureTime - arrivalTime;
+ }
}
@Override
public double calculateCost_MU(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
- return costParameters.carCost_EUR_km * getInVehicleDistance_km(elements);
+ throw new IllegalStateException("Use cost calculator with previous costs");
}
}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/costs/IDFPtCostModel.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/costs/IDFPtCostModel.java
index 37ac35dce..969b56156 100644
--- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/costs/IDFPtCostModel.java
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/costs/IDFPtCostModel.java
@@ -4,100 +4,80 @@
import org.eqasim.core.simulation.mode_choice.cost.CostModel;
import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFPersonPredictor;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFPtPredictor;
import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFSpatialPredictor;
import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFPersonVariables;
+import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFPtVariables;
import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFSpatialVariables;
import org.matsim.api.core.v01.Coord;
-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.PlanElement;
import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
import org.matsim.core.utils.geometry.CoordUtils;
-import org.matsim.pt.routes.TransitPassengerRoute;
-import org.matsim.pt.transitSchedule.api.TransitSchedule;
import com.google.inject.Inject;
public class IDFPtCostModel implements CostModel {
+ private static final double UNIT_PRICE = 1.8;
+
private final IDFPersonPredictor personPredictor;
+ private final IDFPtPredictor ptPredictor;
private final IDFSpatialPredictor spatialPredictor;
- // TODO: This should be hidden by some custom predictor
- private final TransitSchedule transitSchedule;
-
@Inject
- public IDFPtCostModel(IDFPersonPredictor personPredictor, IDFSpatialPredictor spatialPredictor,
- TransitSchedule transitSchedule) {
+ public IDFPtCostModel(IDFPersonPredictor personPredictor, IDFPtPredictor ptPredictor,
+ IDFSpatialPredictor spatialPredictor) {
this.personPredictor = personPredictor;
+ this.ptPredictor = ptPredictor;
this.spatialPredictor = spatialPredictor;
- this.transitSchedule = transitSchedule;
- }
-
- private boolean isOnlyMetroOrBus(List extends PlanElement> elements) {
- for (PlanElement element : elements) {
- if (element instanceof Leg) {
- Leg leg = (Leg) element;
-
- if (leg.getMode().equals(TransportMode.pt)) {
- TransitPassengerRoute route = (TransitPassengerRoute) leg.getRoute();
-
- String transportMode = transitSchedule.getTransitLines().get(route.getLineId()).getRoutes()
- .get(route.getRouteId()).getTransportMode();
-
- if (!transportMode.equals("bus") && !transportMode.equals("subway")) {
- return false;
- }
- }
- }
- }
-
- return true;
}
private final static Coord CENTER = new Coord(651726, 6862287);
- private double calculateBasisDistance_km(DiscreteModeChoiceTrip trip) {
- return 1e-3 * (CoordUtils.calcEuclideanDistance(CENTER, trip.getOriginActivity().getCoord())
- + CoordUtils.calcEuclideanDistance(CENTER, trip.getDestinationActivity().getCoord()));
- }
+ private final static double regressionA = 0.098;
+ private final static double regressionB = 0.006;
+ private final static double regressionC = 0.006;
+ private final static double regressionD = -0.77;
+ private final static double basePrice = 5.5;
@Override
public double calculateCost_MU(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
// I) If the person has a subscription, the price is zero!
-
IDFPersonVariables personVariables = personPredictor.predictVariables(person, trip, elements);
if (personVariables.hasSubscription) {
return 0.0;
}
- // II) If the trip is entirely inside of Paris, or it only consists of metro and
- // bus, the price is 1.80 EUR
+ // II) Special case: Within Paris or only metro and bus
+ IDFPtVariables ptVariables = ptPredictor.predictVariables(person, trip, elements);
- IDFSpatialVariables spatialVariables = spatialPredictor.predictVariables(person, trip, elements);
- boolean isWithinParis = spatialVariables.hasUrbanOrigin && spatialVariables.hasUrbanDestination;
+ if (ptVariables.isWithoutRail) {
+ return UNIT_PRICE;
+ }
- boolean isOnlyMetroOrBus = isOnlyMetroOrBus(elements);
+ IDFSpatialVariables spatialVariables = spatialPredictor.predictVariables(person, trip, elements);
- if (isOnlyMetroOrBus || isWithinParis) {
- return 1.8;
+ if (spatialVariables.isInsideParisBoundary) {
+ return UNIT_PRICE;
}
- /*-
- * III) Otherwise, we calculate as follows:
- *
- * 1) Determine the Euclidean distance from the origin station to the center of Paris.
- * 2) Determine the Euclidean distance from the destination station to the center of Paris.
- * 3) Add up the two distances to arrive at the distance D as the basis for price calculation.
- * 4) Calculate 0.25 EUR/km * D to arrive at a rough price estimate.
- *
- * This assumes that trips in Île-de-France usually must cross through Paris (and otherwise
- * they would usually be a bus). And some brief experimentation with the route planner of
- * RATP showed that the prices are roghly constructed by the total ride distance with
- * a price per distance. TODO: A more detailed analysis would be good to have!
- */
-
- return 0.25 * calculateBasisDistance_km(trip);
+ // III) Otherwise, use regression by Abdelkader DIB
+ double directDistance_km = 1e-3 * CoordUtils.calcEuclideanDistance(trip.getOriginActivity().getCoord(),
+ trip.getDestinationActivity().getCoord());
+
+ double originCenterDistance_km = 1e-3
+ * CoordUtils.calcEuclideanDistance(CENTER, trip.getOriginActivity().getCoord());
+
+ double destinationCenterDistance_km = 1e-3
+ * CoordUtils.calcEuclideanDistance(CENTER, trip.getDestinationActivity().getCoord());
+
+ return Math.max(UNIT_PRICE,
+ basePrice * sigmoid(regressionA * directDistance_km + regressionB * originCenterDistance_km
+ + regressionC * destinationCenterDistance_km + regressionD));
+ }
+
+ private double sigmoid(double x) {
+ return 1.0 / (1.0 + Math.exp(x));
}
}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/parameters/IDFCostParameters.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/parameters/IDFCostParameters.java
index 4e2563840..eb36cab60 100644
--- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/parameters/IDFCostParameters.java
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/parameters/IDFCostParameters.java
@@ -4,11 +4,13 @@
public class IDFCostParameters implements ParameterDefinition {
public double carCost_EUR_km = 0.0;
+ // public double parisParkingCost_EUR_h = 0.0;
public static IDFCostParameters buildDefault() {
IDFCostParameters parameters = new IDFCostParameters();
- parameters.carCost_EUR_km = 0.15;
+ parameters.carCost_EUR_km = 0.12;
+ // parameters.parisParkingCost_EUR_h = 3.0;
return parameters;
}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/parameters/IDFModeParameters.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/parameters/IDFModeParameters.java
index e145acd47..aae97068f 100644
--- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/parameters/IDFModeParameters.java
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/parameters/IDFModeParameters.java
@@ -4,52 +4,81 @@
public class IDFModeParameters extends ModeParameters {
public class IDFCarParameters {
- public double betaInsideUrbanArea;
- public double betaCrossingUrbanArea;
+ public double betaParkingPressure_u;
}
- public class IDFBikeParameters {
- public double betaInsideUrbanArea;
+ public final IDFCarParameters idfCar = new IDFCarParameters();
+
+ public class IDFCarPassengerParameters {
+ public double alpha_u;
+ public double betaInVehicleTravelTime_u_min;
+ public double betaDrivingPermit_u;
}
- public final IDFCarParameters idfCar = new IDFCarParameters();
- public final IDFBikeParameters idfBike = new IDFBikeParameters();
+ public final IDFCarPassengerParameters idfCarPassenger = new IDFCarPassengerParameters();
+
+ public class IDFPtParameters {
+ public double betaDrivingPermit_u;
+ public double betaOnlyBus_u;
+ public double betaCrossingParisBorder_u;
+ }
+
+ public final IDFPtParameters idfPt = new IDFPtParameters();
+
+ public double betaAccessTime_u_min;
+
+ public double referenceIncomePerCU_EUR;
+ public double lambdaCostIncome;
+
+ public double betaRoadInsideParis_u;
public static IDFModeParameters buildDefault() {
IDFModeParameters parameters = new IDFModeParameters();
+ // Access
+ parameters.betaAccessTime_u_min = -0.021105;
+
// Cost
- parameters.betaCost_u_MU = -0.206;
- parameters.lambdaCostEuclideanDistance = -0.4;
- parameters.referenceEuclideanDistance_km = 40.0;
+ parameters.betaCost_u_MU = -0.169591;
+
+ parameters.lambdaCostEuclideanDistance = 0.174056;
+ parameters.referenceEuclideanDistance_km = 4.329534430285437;
+
+ parameters.lambdaCostIncome = -0.131802;
+ parameters.referenceIncomePerCU_EUR = 1842.3492427549477;
// Car
- parameters.car.alpha_u = 1.35;
- parameters.car.betaTravelTime_u_min = -0.06;
+ parameters.car.alpha_u = 1.164972;
+ parameters.car.betaTravelTime_u_min = -0.042989;
- parameters.car.additionalAccessEgressWalkTime_min = 4.0;
- parameters.car.constantParkingSearchPenalty_min = 4.0;
+ parameters.idfCar.betaParkingPressure_u = -1.274770;
- parameters.idfCar.betaInsideUrbanArea = -0.5;
- parameters.idfCar.betaCrossingUrbanArea = -1.0;
+ // Car passenger
+ parameters.idfCarPassenger.alpha_u = -0.340312;
+ parameters.idfCarPassenger.betaDrivingPermit_u = -1.206877;
+ parameters.idfCarPassenger.betaInVehicleTravelTime_u_min = -0.070463;
+
+ // Road
+ parameters.betaRoadInsideParis_u = -1.513682;
// PT
parameters.pt.alpha_u = 0.0;
- parameters.pt.betaLineSwitch_u = -0.17;
- parameters.pt.betaInVehicleTime_u_min = -0.017;
- parameters.pt.betaWaitingTime_u_min = -0.0484;
- parameters.pt.betaAccessEgressTime_u_min = -0.0804;
+ parameters.pt.betaLineSwitch_u = -0.263965;
+ parameters.pt.betaInVehicleTime_u_min = -0.007223;
+ parameters.pt.betaWaitingTime_u_min = -0.034504;
- // Bike
- parameters.bike.alpha_u = -2.0;
- parameters.bike.betaTravelTime_u_min = -0.05;
- parameters.bike.betaAgeOver18_u_a = -0.0496;
+ parameters.idfPt.betaDrivingPermit_u = -0.955961;
+ parameters.idfPt.betaOnlyBus_u = -0.748072;
- parameters.idfBike.betaInsideUrbanArea = 1.5;
+ parameters.idfPt.betaCrossingParisBorder_u = 0.934523;
+
+ // Bike
+ parameters.bike.alpha_u = -2.283258;
+ parameters.bike.betaTravelTime_u_min = -0.050672;
// Walk
- parameters.walk.alpha_u = 1.43;
- parameters.walk.betaTravelTime_u_min = -0.15;
+ parameters.walk.alpha_u = 2.369931;
+ parameters.walk.betaTravelTime_u_min = -0.114553;
return parameters;
}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFBicycleUtilityEstimator.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFBicycleUtilityEstimator.java
new file mode 100644
index 000000000..806dc6d80
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFBicycleUtilityEstimator.java
@@ -0,0 +1,30 @@
+package org.eqasim.ile_de_france.mode_choice.utilities.estimators;
+
+import java.util.List;
+
+import org.eqasim.core.simulation.mode_choice.utilities.estimators.BikeUtilityEstimator;
+import org.eqasim.core.simulation.mode_choice.utilities.predictors.BikePredictor;
+import org.eqasim.core.simulation.mode_choice.utilities.predictors.PersonPredictor;
+import org.eqasim.ile_de_france.mode_choice.parameters.IDFModeParameters;
+import org.matsim.api.core.v01.population.Person;
+import org.matsim.api.core.v01.population.PlanElement;
+import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+
+import com.google.inject.Inject;
+
+public class IDFBicycleUtilityEstimator extends BikeUtilityEstimator {
+ @Inject
+ public IDFBicycleUtilityEstimator(IDFModeParameters parameters, PersonPredictor personPredictor,
+ BikePredictor predictor) {
+ super(parameters, personPredictor, predictor);
+ }
+
+ @Override
+ public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
+ double utility = 0.0;
+
+ utility += super.estimateUtility(person, trip, elements);
+
+ return utility;
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFBikeUtilityEstimator.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFBikeUtilityEstimator.java
deleted file mode 100644
index 4c77d18bc..000000000
--- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFBikeUtilityEstimator.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package org.eqasim.ile_de_france.mode_choice.utilities.estimators;
-
-import java.util.List;
-
-import org.eqasim.core.simulation.mode_choice.utilities.estimators.BikeUtilityEstimator;
-import org.eqasim.core.simulation.mode_choice.utilities.predictors.BikePredictor;
-import org.eqasim.core.simulation.mode_choice.utilities.predictors.PersonPredictor;
-import org.eqasim.ile_de_france.mode_choice.parameters.IDFModeParameters;
-import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFSpatialPredictor;
-import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFSpatialVariables;
-import org.matsim.api.core.v01.population.Person;
-import org.matsim.api.core.v01.population.PlanElement;
-import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
-
-import com.google.inject.Inject;
-
-public class IDFBikeUtilityEstimator extends BikeUtilityEstimator {
- private final IDFModeParameters parameters;
- private final IDFSpatialPredictor spatialPredictor;
-
- @Inject
- public IDFBikeUtilityEstimator(IDFModeParameters parameters, IDFSpatialPredictor spatialPredictor,
- PersonPredictor personPredictor, BikePredictor bikePredictor) {
- super(parameters, personPredictor, bikePredictor);
-
- this.parameters = parameters;
- this.spatialPredictor = spatialPredictor;
- }
-
- protected double estimateUrbanUtility(IDFSpatialVariables variables) {
- double utility = 0.0;
-
- if (variables.hasUrbanOrigin && variables.hasUrbanDestination) {
- utility += parameters.idfBike.betaInsideUrbanArea;
- }
-
- return utility;
- }
-
- @Override
- public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
- IDFSpatialVariables variables = spatialPredictor.predictVariables(person, trip, elements);
-
- double utility = 0.0;
-
- utility += super.estimateUtility(person, trip, elements);
- utility += estimateUrbanUtility(variables);
-
- return utility;
- }
-}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFCarPassengerUtilityEstimator.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFCarPassengerUtilityEstimator.java
new file mode 100644
index 000000000..1ba848963
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFCarPassengerUtilityEstimator.java
@@ -0,0 +1,70 @@
+package org.eqasim.ile_de_france.mode_choice.utilities.estimators;
+
+import java.util.List;
+
+import org.eqasim.core.simulation.mode_choice.utilities.UtilityEstimator;
+import org.eqasim.ile_de_france.mode_choice.parameters.IDFModeParameters;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFCarPassengerPredictor;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFPersonPredictor;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFSpatialPredictor;
+import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFCarPassengerVariables;
+import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFPersonVariables;
+import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFSpatialVariables;
+import org.matsim.api.core.v01.population.Person;
+import org.matsim.api.core.v01.population.PlanElement;
+import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+
+import com.google.inject.Inject;
+
+public class IDFCarPassengerUtilityEstimator implements UtilityEstimator {
+ private final IDFModeParameters parameters;
+ private final IDFCarPassengerPredictor predictor;
+ private final IDFPersonPredictor personPredictor;
+ private final IDFSpatialPredictor spatialPredictor;
+
+ @Inject
+ public IDFCarPassengerUtilityEstimator(IDFModeParameters parameters, IDFCarPassengerPredictor predictor,
+ IDFPersonPredictor personPredictor, IDFSpatialPredictor spatialPredictor) {
+ this.parameters = parameters;
+ this.predictor = predictor;
+ this.personPredictor = personPredictor;
+ this.spatialPredictor = spatialPredictor;
+ }
+
+ protected double estimateConstantUtility() {
+ return parameters.idfCarPassenger.alpha_u;
+ }
+
+ protected double estimateTravelTimeUtility(IDFCarPassengerVariables variables) {
+ return parameters.idfCarPassenger.betaInVehicleTravelTime_u_min * variables.travelTime_min;
+ }
+
+ protected double estimateAccessEgressTimeUtility(IDFCarPassengerVariables variables) {
+ return parameters.betaAccessTime_u_min * variables.accessEgressTime_min;
+ }
+
+ protected double estimateDrivingPermit(IDFPersonVariables variables) {
+ return variables.hasDrivingPermit ? parameters.idfCarPassenger.betaDrivingPermit_u : 0.0;
+ }
+
+ protected double estimateInsideParisUtility(IDFSpatialVariables spatialVariables) {
+ return spatialVariables.isInsideParisBoundary ? parameters.betaRoadInsideParis_u : 0.0;
+ }
+
+ @Override
+ public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
+ IDFCarPassengerVariables variables = predictor.predictVariables(person, trip, elements);
+ IDFPersonVariables personVariables = personPredictor.predictVariables(person, trip, elements);
+ IDFSpatialVariables spatialVariables = spatialPredictor.predictVariables(person, trip, elements);
+
+ double utility = 0.0;
+
+ utility += estimateConstantUtility();
+ utility += estimateTravelTimeUtility(variables);
+ utility += estimateAccessEgressTimeUtility(variables);
+ utility += estimateDrivingPermit(personVariables);
+ utility += estimateInsideParisUtility(spatialVariables);
+
+ return utility;
+ }
+}
\ No newline at end of file
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFCarUtilityEstimator.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFCarUtilityEstimator.java
index ca318a373..cc4360589 100644
--- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFCarUtilityEstimator.java
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFCarUtilityEstimator.java
@@ -3,51 +3,78 @@
import java.util.List;
import org.eqasim.core.simulation.mode_choice.utilities.estimators.CarUtilityEstimator;
+import org.eqasim.core.simulation.mode_choice.utilities.estimators.EstimatorUtils;
import org.eqasim.core.simulation.mode_choice.utilities.predictors.CarPredictor;
+import org.eqasim.core.simulation.mode_choice.utilities.variables.CarVariables;
import org.eqasim.ile_de_france.mode_choice.parameters.IDFModeParameters;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFParkingPredictor;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFPersonPredictor;
import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFSpatialPredictor;
+import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFParkingVariables;
+import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFPersonVariables;
import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFSpatialVariables;
import org.matsim.api.core.v01.population.Person;
import org.matsim.api.core.v01.population.PlanElement;
import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate;
import com.google.inject.Inject;
public class IDFCarUtilityEstimator extends CarUtilityEstimator {
private final IDFModeParameters parameters;
+ private final IDFPersonPredictor personPredictor;
private final IDFSpatialPredictor spatialPredictor;
+ private final IDFParkingPredictor parkingPredictor;
+ private final CarPredictor predictor;
@Inject
- public IDFCarUtilityEstimator(IDFModeParameters parameters, IDFSpatialPredictor spatialPredictor,
- CarPredictor carPredictor) {
- super(parameters, carPredictor);
+ public IDFCarUtilityEstimator(IDFModeParameters parameters, CarPredictor predictor,
+ IDFPersonPredictor personPredictor, IDFSpatialPredictor spatialPredictor,
+ IDFParkingPredictor parkingPredictor) {
+ super(parameters, predictor);
this.parameters = parameters;
+ this.predictor = predictor;
+ this.personPredictor = personPredictor;
this.spatialPredictor = spatialPredictor;
+ this.parkingPredictor = parkingPredictor;
}
- protected double estimateUrbanUtility(IDFSpatialVariables variables) {
- double utility = 0.0;
+ protected double estimateAccessEgressTimeUtility(CarVariables variables) {
+ return parameters.betaAccessTime_u_min * variables.accessEgressTime_min;
+ }
+
+ protected double estimateMonetaryCostUtility(CarVariables carVariables, IDFPersonVariables personVariables) {
+ double baseValue = super.estimateMonetaryCostUtility(carVariables);
- if (variables.hasUrbanOrigin && variables.hasUrbanDestination) {
- utility += parameters.idfCar.betaInsideUrbanArea;
- }
+ return baseValue * EstimatorUtils.interaction(personVariables.householdIncomePerCU_EUR,
+ parameters.referenceIncomePerCU_EUR, parameters.lambdaCostIncome);
+ }
- if (variables.hasUrbanOrigin || variables.hasUrbanDestination) {
- utility += parameters.idfCar.betaCrossingUrbanArea;
- }
+ protected double estimateParkingPressureUtility(IDFParkingVariables parkingVariables) {
+ return parameters.idfCar.betaParkingPressure_u * parkingVariables.parkingPressure;
+ }
- return utility;
+ protected double estimateInsideParisUtility(IDFSpatialVariables spatialVariables) {
+ return spatialVariables.isInsideParisBoundary ? parameters.betaRoadInsideParis_u : 0.0;
}
@Override
- public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
- IDFSpatialVariables variables = spatialPredictor.predictVariables(person, trip, elements);
+ public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements,
+ List previousTrips) {
+ IDFPersonVariables personVariables = personPredictor.predictVariables(person, trip, elements);
+ IDFSpatialVariables spatialVariables = spatialPredictor.predictVariables(person, trip, elements);
+ IDFParkingVariables parkingVariables = parkingPredictor.predictVariables(person, trip, elements);
+ CarVariables variables = predictor.predictVariables(person, trip, elements, previousTrips);
double utility = 0.0;
- utility += super.estimateUtility(person, trip, elements);
- utility += estimateUrbanUtility(variables);
+ utility += estimateConstantUtility();
+ utility += estimateTravelTimeUtility(variables);
+ utility += estimateAccessEgressTimeUtility(variables);
+ utility += estimateMonetaryCostUtility(variables, personVariables);
+ utility += estimateParkingPressureUtility(parkingVariables);
+ utility += estimateInsideParisUtility(spatialVariables);
return utility;
}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFPtUtilityEstimator.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFPtUtilityEstimator.java
new file mode 100644
index 000000000..a0dd57aac
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFPtUtilityEstimator.java
@@ -0,0 +1,106 @@
+package org.eqasim.ile_de_france.mode_choice.utilities.estimators;
+
+import java.util.List;
+
+import org.eqasim.core.simulation.mode_choice.cost.CostModel;
+import org.eqasim.core.simulation.mode_choice.utilities.UtilityEstimator;
+import org.eqasim.core.simulation.mode_choice.utilities.estimators.EstimatorUtils;
+import org.eqasim.ile_de_france.mode_choice.parameters.IDFModeParameters;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFPersonPredictor;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFPtPredictor;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFSpatialPredictor;
+import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFPersonVariables;
+import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFPtVariables;
+import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFSpatialVariables;
+import org.matsim.api.core.v01.population.Person;
+import org.matsim.api.core.v01.population.PlanElement;
+import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+
+import com.google.inject.Inject;
+import com.google.inject.name.Named;
+
+public class IDFPtUtilityEstimator implements UtilityEstimator {
+ private final IDFModeParameters parameters;
+ private final IDFPersonPredictor personPredictor;
+ private final IDFPtPredictor idfPtPredictor;
+ private final IDFSpatialPredictor spatialPredictor;
+ private final CostModel costModel;
+
+ @Inject
+ public IDFPtUtilityEstimator(IDFModeParameters parameters, IDFPtPredictor idfPtPredictor,
+ IDFPersonPredictor personPredictor, IDFSpatialPredictor spatialPredictor,
+ @Named("pt") CostModel costModel) {
+ this.personPredictor = personPredictor;
+ this.idfPtPredictor = idfPtPredictor;
+ this.spatialPredictor = spatialPredictor;
+ this.parameters = parameters;
+ this.costModel = costModel;
+ }
+
+ protected double estimateConstantUtility() {
+ return parameters.pt.alpha_u;
+ }
+
+ protected double estimateAccessEgressTimeUtility(IDFPtVariables variables) {
+ return parameters.betaAccessTime_u_min * variables.accessEgressTime_min;
+ }
+
+ protected double estimateLineSwitchUtility(IDFPtVariables variables) {
+ return parameters.pt.betaLineSwitch_u * variables.numberOfLineSwitches;
+ }
+
+ protected double estimateWaitingTimeUtility(IDFPtVariables variables) {
+ return parameters.pt.betaWaitingTime_u_min * variables.waitingTime_min;
+ }
+
+ protected double estimateMonetaryCostUtility(IDFPtVariables variables, IDFPersonVariables personVariables,
+ double cost_EUR) {
+ return parameters.betaCost_u_MU * //
+ EstimatorUtils.interaction(variables.euclideanDistance_km,
+ parameters.referenceEuclideanDistance_km, parameters.lambdaCostEuclideanDistance) //
+ * EstimatorUtils.interaction(personVariables.householdIncomePerCU_EUR,
+ parameters.referenceIncomePerCU_EUR, parameters.lambdaCostIncome) //
+ * cost_EUR;
+ }
+
+ protected double estimateInVehicleTimeUtility(IDFPtVariables variables) {
+ return parameters.pt.betaInVehicleTime_u_min * variables.inVehicleTime_min;
+ }
+
+ protected double estimateDrivingPermitUtility(IDFPersonVariables variables) {
+ return variables.hasDrivingPermit ? parameters.idfPt.betaDrivingPermit_u : 0.0;
+ }
+
+ protected double estimateOnlyBusUtility(IDFPtVariables variables) {
+ return variables.isOnlyBus ? parameters.idfPt.betaOnlyBus_u : 0.0;
+ }
+
+ protected double estimateCrossingParisBoundaryUtility(IDFSpatialVariables variables) {
+ return variables.isCrossingParisBoundary ? parameters.idfPt.betaCrossingParisBorder_u : 0.0;
+ }
+
+ @Override
+ public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
+ IDFPersonVariables personVariables = personPredictor.predictVariables(person, trip, elements);
+ IDFPtVariables ptVariables = idfPtPredictor.predictVariables(person, trip, elements);
+ IDFSpatialVariables spatialVariables = spatialPredictor.predictVariables(person, trip, elements);
+
+ double cost_EUR = costModel.calculateCost_MU(person, trip, elements);
+
+ double utility = 0.0;
+
+ utility += estimateConstantUtility();
+ utility += estimateAccessEgressTimeUtility(ptVariables);
+ utility += estimateLineSwitchUtility(ptVariables);
+ utility += estimateWaitingTimeUtility(ptVariables);
+ utility += estimateMonetaryCostUtility(ptVariables, personVariables, cost_EUR);
+ utility += estimateInVehicleTimeUtility(ptVariables);
+
+ utility += estimateOnlyBusUtility(ptVariables);
+ utility += estimateDrivingPermitUtility(personVariables);
+
+ utility += estimateCrossingParisBoundaryUtility(spatialVariables);
+
+ return utility;
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFCarPassengerPredictor.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFCarPassengerPredictor.java
new file mode 100644
index 000000000..5d3cd491c
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFCarPassengerPredictor.java
@@ -0,0 +1,42 @@
+package org.eqasim.ile_de_france.mode_choice.utilities.predictors;
+
+import java.util.List;
+
+import org.eqasim.core.simulation.mode_choice.utilities.predictors.CachedVariablePredictor;
+import org.eqasim.core.simulation.mode_choice.utilities.predictors.PredictorUtils;
+import org.eqasim.ile_de_france.mode_choice.IDFModeChoiceModule;
+import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFCarPassengerVariables;
+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.PlanElement;
+import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.core.router.TripStructureUtils;
+
+import com.google.common.base.Verify;
+
+public class IDFCarPassengerPredictor extends CachedVariablePredictor {
+ @Override
+ public IDFCarPassengerVariables predict(Person person, DiscreteModeChoiceTrip trip,
+ List extends PlanElement> elements) {
+ double passengerTravelTime_min = 0.0;
+ double accessEgressTime_min = 0.0;
+
+ boolean foundCar = false;
+
+ for (Leg leg : TripStructureUtils.getLegs(elements)) {
+ if (leg.getMode().equals(IDFModeChoiceModule.CAR_PASSENGER)) {
+ Verify.verify(!foundCar);
+ passengerTravelTime_min += leg.getTravelTime().seconds() / 60.0;
+ } else if (leg.getMode().equals(TransportMode.walk)) {
+ accessEgressTime_min += leg.getTravelTime().seconds() / 60.0;
+ } else {
+ throw new IllegalStateException("Unexpected mode in passenger chain: " + leg.getMode());
+ }
+ }
+
+ double euclideanDistance_km = PredictorUtils.calculateEuclideanDistance_km(trip);
+
+ return new IDFCarPassengerVariables(passengerTravelTime_min, euclideanDistance_km, accessEgressTime_min);
+ }
+}
\ No newline at end of file
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFParkingPredictor.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFParkingPredictor.java
new file mode 100644
index 000000000..0e17f84fa
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFParkingPredictor.java
@@ -0,0 +1,45 @@
+package org.eqasim.ile_de_france.mode_choice.utilities.predictors;
+
+import java.util.List;
+
+import org.eqasim.core.simulation.mode_choice.utilities.predictors.CachedVariablePredictor;
+import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFParkingVariables;
+import org.eqasim.ile_de_france.parking.ParkingPressure;
+import org.eqasim.ile_de_france.parking.ParkingTariff;
+import org.matsim.api.core.v01.population.Person;
+import org.matsim.api.core.v01.population.PlanElement;
+import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+
+import com.google.inject.Inject;
+
+public class IDFParkingPredictor extends CachedVariablePredictor {
+ private final ParkingPressure parkingPressure;
+ private final ParkingTariff parkingTariff;
+
+ @Inject
+ public IDFParkingPredictor(ParkingPressure parkingPressure, ParkingTariff parkingTariff) {
+ this.parkingPressure = parkingPressure;
+ this.parkingTariff = parkingTariff;
+ }
+
+ @Override
+ protected IDFParkingVariables predict(Person person, DiscreteModeChoiceTrip trip,
+ List extends PlanElement> elements) {
+ double originParkingPressure = parkingPressure.getParkingPressure(
+ trip.getOriginActivity().getLinkId());
+
+ double destinationParkingPressure = parkingPressure.getParkingPressure(
+ trip.getDestinationActivity().getLinkId());
+
+ double parkingPressure = originParkingPressure + destinationParkingPressure;
+
+ double originParkingTariff_EUR_h = parkingTariff.getParkingTariff(trip.getOriginActivity().getLinkId());
+ double destinationParkingTariff_EUR_h = parkingTariff
+ .getParkingTariff(trip.getDestinationActivity().getLinkId());
+
+ return new IDFParkingVariables( //
+ parkingPressure, //
+ originParkingTariff_EUR_h, //
+ destinationParkingTariff_EUR_h);
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFPersonPredictor.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFPersonPredictor.java
index e4d5d5ca0..b28a40a16 100644
--- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFPersonPredictor.java
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFPersonPredictor.java
@@ -13,6 +13,11 @@ public class IDFPersonPredictor extends CachedVariablePredictor elements) {
boolean hasSubscription = IDFPredictorUtils.hasSubscription(person);
- return new IDFPersonVariables(hasSubscription);
+ boolean hasDrivingPermit = IDFPredictorUtils.hasDrivingLicense(person);
+ double householdIncomePerCU_EUR = IDFPredictorUtils.getHouseholdIncomePerCU(person);
+ String residenceMunicipalityId = IDFPredictorUtils.getMunicipalityId(person);
+
+ return new IDFPersonVariables(hasSubscription, hasDrivingPermit, householdIncomePerCU_EUR,
+ residenceMunicipalityId);
}
}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFPredictorUtils.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFPredictorUtils.java
index 1672f3c16..9131c21e0 100644
--- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFPredictorUtils.java
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFPredictorUtils.java
@@ -2,6 +2,7 @@
import org.matsim.api.core.v01.population.Activity;
import org.matsim.api.core.v01.population.Person;
+import org.matsim.core.population.PersonUtils;
public class IDFPredictorUtils {
static public boolean hasSubscription(Person person) {
@@ -9,8 +10,48 @@ static public boolean hasSubscription(Person person) {
return hasSubscription != null && hasSubscription;
}
- static public boolean isUrbanArea(Activity activity) {
- Boolean isUrban = (Boolean) activity.getAttributes().getAttribute("isUrban");
- return isUrban != null && isUrban;
+ static public boolean isOutside(Person person) {
+ Boolean isOutside = (Boolean) person.getAttributes().getAttribute("outside");
+ return isOutside != null && isOutside;
+ }
+
+ static public boolean hasDrivingLicense(Person person) {
+ return !"no".equals(PersonUtils.getLicense(person));
+ }
+
+ static public boolean hasCarAvailability(Person person) {
+ return !"none".equals((String) person.getAttributes().getAttribute("carAvailability"));
+ }
+
+ static public boolean hasBicycleAvailability(Person person) {
+ return !"none".equals((String) person.getAttributes().getAttribute("bicycleAvailability"));
+ }
+
+ static public double getHouseholdIncome(Person person) {
+ Double householdIncome = (Double) person.getAttributes().getAttribute("householdIncome");
+ return householdIncome;
+ }
+
+ static public double getConsumptionUnits(Person person) {
+ Double consumptionUnits = (Double) person.getAttributes().getAttribute("householdConsumptionUnits");
+ return consumptionUnits;
+ }
+
+ static public double getHouseholdIncomePerCU(Person person) {
+ return getHouseholdIncome(person) / getConsumptionUnits(person);
+ }
+
+ static public final String ACTIVITY_MUNICIPALITY_ID = "municipalityId";
+
+ static public String getMunicipalityId(Activity activity) {
+ String municipalityId = (String) activity.getAttributes().getAttribute(ACTIVITY_MUNICIPALITY_ID);
+ return municipalityId != null ? municipalityId : "";
+ }
+
+ static public final String RESIDENCE_MUNICIPALITY_ID = "residenceMunicipalityId";
+
+ static public String getMunicipalityId(Person person) {
+ String municipalityId = (String) person.getAttributes().getAttribute(RESIDENCE_MUNICIPALITY_ID);
+ return municipalityId != null ? municipalityId : "";
}
}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFPtPredictor.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFPtPredictor.java
new file mode 100644
index 000000000..bc57aa7bf
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFPtPredictor.java
@@ -0,0 +1,103 @@
+package org.eqasim.ile_de_france.mode_choice.utilities.predictors;
+
+import java.util.List;
+
+import org.eqasim.core.simulation.mode_choice.cost.CostModel;
+import org.eqasim.core.simulation.mode_choice.utilities.predictors.CachedVariablePredictor;
+import org.eqasim.core.simulation.mode_choice.utilities.predictors.PredictorUtils;
+import org.eqasim.ile_de_france.mode_choice.utilities.variables.IDFPtVariables;
+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.PlanElement;
+import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.pt.routes.TransitPassengerRoute;
+import org.matsim.pt.transitSchedule.api.TransitLine;
+import org.matsim.pt.transitSchedule.api.TransitRoute;
+import org.matsim.pt.transitSchedule.api.TransitSchedule;
+
+import com.google.inject.Inject;
+
+public class IDFPtPredictor extends CachedVariablePredictor {
+ private final TransitSchedule schedule;
+
+ @Inject
+ public IDFPtPredictor(TransitSchedule schedule) {
+ this.schedule = schedule;
+ }
+
+ protected CostModel getCostModel() {
+ return null;
+ }
+
+ @Override
+ public IDFPtVariables predict(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
+ int numberOfVehicularLegs = 0;
+
+ // Track relevant variables (from standard estimator)
+ double inVehicleTime_min = 0.0;
+ double waitingTime_min = 0.0;
+ double accessEgressTime_min = 0.0;
+
+ // Track IDF variables
+ int busCount = 0;
+ int subwayCount = 0;
+ int otherCount = 0;
+ int railCount = 0;
+
+ for (PlanElement element : elements) {
+ if (element instanceof Leg leg) {
+ switch (leg.getMode()) {
+ case TransportMode.walk:
+ case TransportMode.non_network_walk:
+ accessEgressTime_min += leg.getTravelTime().seconds() / 60.0;
+ break;
+ case TransportMode.transit_walk:
+ // different than standard estimator
+ accessEgressTime_min += leg.getTravelTime().seconds() / 60.0;
+ break;
+ case TransportMode.pt:
+ TransitPassengerRoute route = (TransitPassengerRoute) leg.getRoute();
+
+ double departureTime = leg.getDepartureTime().seconds();
+ double waitingTime = route.getBoardingTime().seconds() - departureTime;
+ double inVehicleTime = leg.getTravelTime().seconds() - waitingTime;
+
+ inVehicleTime_min += inVehicleTime / 60.0;
+ waitingTime_min += waitingTime / 60.0;
+
+ numberOfVehicularLegs++;
+ break;
+ default:
+ throw new IllegalStateException("Unknown mode in PT trip: " + leg.getMode());
+ }
+
+ if (leg.getRoute() instanceof TransitPassengerRoute route) {
+ TransitLine transitLine = schedule.getTransitLines().get(route.getLineId());
+ TransitRoute transitRoute = transitLine.getRoutes().get(route.getRouteId());
+ String transportMode = transitRoute.getTransportMode();
+
+ if (transportMode.equals("bus")) {
+ busCount++;
+ } else if (transportMode.equals("subway")) {
+ subwayCount++;
+ } else if (transportMode.equals("rail")) {
+ railCount++;
+ } else {
+ otherCount++;
+ }
+ }
+ }
+ }
+
+ int numberOfLineSwitches = Math.max(0, numberOfVehicularLegs - 1);
+
+ double euclideanDistance_km = PredictorUtils.calculateEuclideanDistance_km(trip);
+
+ boolean isOnlyBus = busCount > 0 && subwayCount == 0 && railCount == 0 && otherCount == 0;
+ boolean isWithoutRail = railCount == 0;
+
+ return new IDFPtVariables(inVehicleTime_min, waitingTime_min, accessEgressTime_min, numberOfLineSwitches,
+ euclideanDistance_km, isOnlyBus, isWithoutRail);
+ }
+}
\ No newline at end of file
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFSpatialPredictor.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFSpatialPredictor.java
index dbafe6437..2ecb25af0 100644
--- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFSpatialPredictor.java
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/predictors/IDFSpatialPredictor.java
@@ -8,16 +8,23 @@
import org.matsim.api.core.v01.population.PlanElement;
import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
-import com.google.inject.Singleton;
-
-@Singleton
public class IDFSpatialPredictor extends CachedVariablePredictor {
@Override
protected IDFSpatialVariables predict(Person person, DiscreteModeChoiceTrip trip,
List extends PlanElement> elements) {
- boolean hasUrbanOrigin = IDFPredictorUtils.isUrbanArea(trip.getOriginActivity());
- boolean hasUrbanDestination = IDFPredictorUtils.isUrbanArea(trip.getDestinationActivity());
+ String originMunicipalityId = IDFPredictorUtils.getMunicipalityId(trip.getOriginActivity());
+ String destinationMunicipalityId = IDFPredictorUtils.getMunicipalityId(trip.getDestinationActivity());
+
+ boolean isOriginInsideParis = isInsideParis(originMunicipalityId);
+ boolean isDestinationInsideParis = isInsideParis(destinationMunicipalityId);
+
+ return new IDFSpatialVariables( //
+ isOriginInsideParis && isDestinationInsideParis, //
+ isOriginInsideParis ^ isDestinationInsideParis, //
+ originMunicipalityId, destinationMunicipalityId);
+ }
- return new IDFSpatialVariables(hasUrbanOrigin, hasUrbanDestination);
+ static private boolean isInsideParis(String municipalityId) {
+ return municipalityId.startsWith("75");
}
}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFCarPassengerVariables.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFCarPassengerVariables.java
new file mode 100644
index 000000000..21523bc33
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFCarPassengerVariables.java
@@ -0,0 +1,15 @@
+package org.eqasim.ile_de_france.mode_choice.utilities.variables;
+
+import org.eqasim.core.simulation.mode_choice.utilities.variables.BaseVariables;
+
+public class IDFCarPassengerVariables implements BaseVariables {
+ final public double travelTime_min;
+ final public double euclideanDistance_km;
+ final public double accessEgressTime_min;
+
+ public IDFCarPassengerVariables(double travelTime_min, double euclideanDistance_km, double accessEgressTime_min) {
+ this.travelTime_min = travelTime_min;
+ this.euclideanDistance_km = euclideanDistance_km;
+ this.accessEgressTime_min = accessEgressTime_min;
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFParkingVariables.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFParkingVariables.java
new file mode 100644
index 000000000..99f3b7300
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFParkingVariables.java
@@ -0,0 +1,17 @@
+package org.eqasim.ile_de_france.mode_choice.utilities.variables;
+
+import org.eqasim.core.simulation.mode_choice.utilities.variables.BaseVariables;
+
+public class IDFParkingVariables implements BaseVariables {
+ public final double parkingPressure;
+
+ public final double originParkingTariff_EUR_h;
+ public final double destinationParkingTariff_EUR_h;
+
+ public IDFParkingVariables(double parkingPressure, double originParkingTariff_EUR_h,
+ double destinationParkingTariff_EUR_h) {
+ this.parkingPressure = parkingPressure;
+ this.originParkingTariff_EUR_h = originParkingTariff_EUR_h;
+ this.destinationParkingTariff_EUR_h = destinationParkingTariff_EUR_h;
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFPersonVariables.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFPersonVariables.java
index f3d821c52..a6625c646 100644
--- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFPersonVariables.java
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFPersonVariables.java
@@ -4,8 +4,15 @@
public class IDFPersonVariables implements BaseVariables {
public final boolean hasSubscription;
+ public final boolean hasDrivingPermit;
+ public final double householdIncomePerCU_EUR;
+ public final String residenceMunicipalityId;
- public IDFPersonVariables(boolean hasSubscription) {
+ public IDFPersonVariables(boolean hasSubscription, boolean hasDrivingPermit, double householdIncomePerCU_EUR,
+ String residenceMunicipalityId) {
this.hasSubscription = hasSubscription;
+ this.hasDrivingPermit = hasDrivingPermit;
+ this.householdIncomePerCU_EUR = householdIncomePerCU_EUR;
+ this.residenceMunicipalityId = residenceMunicipalityId;
}
}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFPtVariables.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFPtVariables.java
new file mode 100644
index 000000000..0154687c0
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFPtVariables.java
@@ -0,0 +1,27 @@
+package org.eqasim.ile_de_france.mode_choice.utilities.variables;
+
+import org.eqasim.core.simulation.mode_choice.utilities.variables.BaseVariables;
+
+public class IDFPtVariables implements BaseVariables {
+ public final double inVehicleTime_min;
+ public final double waitingTime_min;
+ public final double accessEgressTime_min;
+ public final int numberOfLineSwitches;
+ public final double euclideanDistance_km;
+
+ public final boolean isOnlyBus;
+ public final boolean isWithoutRail;
+
+ public IDFPtVariables(double inVehicleTime_min, double waitingTime_min, double accessEgressTime_min,
+ int numberOfLineSwitches, double euclideanDistance_km, boolean isOnlyBus,
+ boolean isWithoutRail) {
+ this.inVehicleTime_min = inVehicleTime_min;
+ this.waitingTime_min = waitingTime_min;
+ this.accessEgressTime_min = accessEgressTime_min;
+ this.numberOfLineSwitches = numberOfLineSwitches;
+ this.euclideanDistance_km = euclideanDistance_km;
+
+ this.isOnlyBus = isOnlyBus;
+ this.isWithoutRail = isWithoutRail;
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFSpatialVariables.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFSpatialVariables.java
index 6edc67e88..4673d433a 100644
--- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFSpatialVariables.java
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/variables/IDFSpatialVariables.java
@@ -3,11 +3,17 @@
import org.eqasim.core.simulation.mode_choice.utilities.variables.BaseVariables;
public class IDFSpatialVariables implements BaseVariables {
- public final boolean hasUrbanOrigin;
- public final boolean hasUrbanDestination;
+ public final boolean isInsideParisBoundary;
+ public final boolean isCrossingParisBoundary;
- public IDFSpatialVariables(boolean hasUrbanOrigin, boolean hasUrbanDestination) {
- this.hasUrbanOrigin = hasUrbanOrigin;
- this.hasUrbanDestination = hasUrbanDestination;
+ public final String originMunicipalityId;
+ public final String destinationMunicipalityId;
+
+ public IDFSpatialVariables(boolean isInsideParisBoundary, boolean isCrossingParisBoundary,
+ String originMunicipalityId, String destinationMunicipalityId) {
+ this.isInsideParisBoundary = isInsideParisBoundary;
+ this.isCrossingParisBoundary = isCrossingParisBoundary;
+ this.originMunicipalityId = originMunicipalityId;
+ this.destinationMunicipalityId = destinationMunicipalityId;
}
}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/parking/IDFParkingModule.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/parking/IDFParkingModule.java
new file mode 100644
index 000000000..20676194e
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/parking/IDFParkingModule.java
@@ -0,0 +1,25 @@
+package org.eqasim.ile_de_france.parking;
+
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.core.controler.AbstractModule;
+
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
+
+public class IDFParkingModule extends AbstractModule {
+ @Override
+ public void install() {
+ }
+
+ @Provides
+ @Singleton
+ ParkingPressure provideParkingPressure(Network network) {
+ return new ParkingPressure(network);
+ }
+
+ @Provides
+ @Singleton
+ ParkingTariff provideParkingTariff(Network network) {
+ return new ParkingTariff(network);
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/parking/ParkingPressure.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/parking/ParkingPressure.java
new file mode 100644
index 000000000..4b31b227f
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/parking/ParkingPressure.java
@@ -0,0 +1,29 @@
+package org.eqasim.ile_de_france.parking;
+
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.IdMap;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+
+import com.google.inject.Singleton;
+
+@Singleton
+public class ParkingPressure {
+ static public final String LINK_ATTRIBUTE = "parkingPressure";
+
+ private final IdMap values = new IdMap<>(Link.class);
+
+ public ParkingPressure(Network network) {
+ for (Link link : network.getLinks().values()) {
+ Double value = (Double) link.getAttributes().getAttribute(LINK_ATTRIBUTE);
+
+ if (value != null) {
+ values.put(link.getId(), value);
+ }
+ }
+ }
+
+ public double getParkingPressure(Id link) {
+ return values.getOrDefault(link, 0.0);
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/parking/ParkingTariff.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/parking/ParkingTariff.java
new file mode 100644
index 000000000..3a03745d5
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/parking/ParkingTariff.java
@@ -0,0 +1,29 @@
+package org.eqasim.ile_de_france.parking;
+
+import org.matsim.api.core.v01.Id;
+import org.matsim.api.core.v01.IdMap;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+
+import com.google.inject.Singleton;
+
+@Singleton
+public class ParkingTariff {
+ static public final String LINK_ATTRIBUTE = "parkingTariff_EUR_h";
+
+ private final IdMap values = new IdMap<>(Link.class);
+
+ public ParkingTariff(Network network) {
+ for (Link link : network.getLinks().values()) {
+ Double value = (Double) link.getAttributes().getAttribute(LINK_ATTRIBUTE);
+
+ if (value != null) {
+ values.put(link.getId(), value);
+ }
+ }
+ }
+
+ public double getParkingTariff(Id link) {
+ return values.getOrDefault(link, 0.0);
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/PredictionWriter.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/PredictionWriter.java
new file mode 100644
index 000000000..95996fe7e
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/PredictionWriter.java
@@ -0,0 +1,220 @@
+package org.eqasim.ile_de_france.probing;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.eqasim.core.misc.ParallelProgress;
+import org.eqasim.core.simulation.mode_choice.cost.CostModel;
+import org.eqasim.core.simulation.mode_choice.cost.CostModelWithPreviousTrips;
+import org.eqasim.core.simulation.mode_choice.utilities.UtilityEstimator;
+import org.eqasim.core.simulation.mode_choice.utilities.UtilityEstimatorWithPreviousTrips;
+import org.eqasim.core.simulation.mode_choice.utilities.predictors.VariablePredictor;
+import org.eqasim.core.simulation.mode_choice.utilities.predictors.VariablePredictorWithPreviousTrips;
+import org.eqasim.core.simulation.mode_choice.utilities.variables.BaseVariables;
+import org.matsim.api.core.v01.population.Person;
+import org.matsim.api.core.v01.population.PlanElement;
+import org.matsim.api.core.v01.population.Population;
+import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.contribs.discrete_mode_choice.model.mode_availability.ModeAvailability;
+import org.matsim.contribs.discrete_mode_choice.replanning.TripListConverter;
+import org.matsim.core.router.TripRouter;
+import org.matsim.facilities.ActivityFacilities;
+import org.matsim.facilities.FacilitiesUtils;
+import org.matsim.facilities.Facility;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+public class PredictionWriter {
+ private final Population population;
+ private final TripRouter tripRouter;
+ private final ActivityFacilities facilities;
+ private final File outputPath;
+
+ private final Set modes = new HashSet<>();
+ private final List> predictorEntries = new LinkedList<>();
+ private final List> predictorEntriesWithPreviousTrips = new LinkedList<>();
+ private final List estimatorEntries = new LinkedList<>();
+ private final List costModelEntries = new LinkedList<>();
+ private final List availabilityEntries = new LinkedList<>();
+
+ public PredictionWriter(Population population, TripRouter tripRouter, ActivityFacilities facilities,
+ File outputPath) {
+ this.population = population;
+ this.tripRouter = tripRouter;
+ this.facilities = facilities;
+ this.outputPath = outputPath;
+ }
+
+ public PredictionWriter addPredictor(String name, String mode,
+ VariablePredictor predictor) {
+ predictorEntries.add(new PredictorEntry<>(name, mode, predictor));
+ modes.add(mode);
+ return this;
+ }
+
+ public PredictionWriter addPredictor(String name, VariablePredictor predictor) {
+ predictorEntries.add(new PredictorEntry<>(name, null, predictor));
+ return this;
+ }
+
+ public PredictionWriter addPredictor(String name, String mode,
+ VariablePredictorWithPreviousTrips predictor) {
+ predictorEntriesWithPreviousTrips.add(new PredictorEntryWithPreviousTrip<>(name, mode, predictor));
+ modes.add(mode);
+ return this;
+ }
+
+ public PredictionWriter addPredictor(String name,
+ VariablePredictorWithPreviousTrips predictor) {
+ predictorEntriesWithPreviousTrips.add(new PredictorEntryWithPreviousTrip<>(name, null, predictor));
+ return this;
+ }
+
+ public PredictionWriter addEstimator(String mode, UtilityEstimator estimator) {
+ estimatorEntries.add(new EstimatorEntry(mode, estimator));
+ modes.add(mode);
+ return this;
+ }
+
+ public PredictionWriter addCostModel(String mode, CostModel model) {
+ costModelEntries.add(new CostModelEntry(mode, model));
+ modes.add(mode);
+ return this;
+ }
+
+ public PredictionWriter addAvailability(String name, ModeAvailability availability) {
+ availabilityEntries.add(new AvailabilityEntry(name, availability));
+ return this;
+ }
+
+ public void run() {
+ List result = new LinkedList<>();
+
+ ParallelProgress progress = new ParallelProgress("Writing predictions ...", population.getPersons().size());
+ progress.start();
+
+ for (Person person : population.getPersons().values()) {
+ DiscreteModeChoiceTrip trip = new TripListConverter().convert(person.getSelectedPlan()).get(0);
+ trip.setDepartureTime(trip.getOriginActivity().getEndTime().seconds());
+
+ Facility originFacility = FacilitiesUtils.toFacility(trip.getOriginActivity(), facilities);
+ Facility destinationFacility = FacilitiesUtils.toFacility(trip.getDestinationActivity(), facilities);
+
+ List> predictions = new LinkedList<>();
+ Map utilities = new HashMap<>();
+ Map costs = new HashMap<>();
+ Map> availabilities = new HashMap<>();
+
+ for (String mode : modes) {
+ List extends PlanElement> tripElements = tripRouter.calcRoute(mode, originFacility,
+ destinationFacility,
+ trip.getDepartureTime(), person, trip.getTripAttributes());
+
+ for (var entry : predictorEntries) {
+ if (mode.equals(entry.mode)) {
+ predictions.add(new PredictionEntry<>(entry.name, entry.mode,
+ entry.predictor.predictVariables(person, trip, tripElements)));
+ }
+ }
+
+ for (var entry : predictorEntriesWithPreviousTrips) {
+ if (mode.equals(entry.mode)) {
+ predictions.add(new PredictionEntry<>(entry.name, entry.mode,
+ entry.predictor.predictVariables(person, trip, tripElements, Collections.emptyList())));
+ }
+ }
+
+ for (var entry : estimatorEntries) {
+ if (mode.equals(entry.mode)) {
+ if (entry.estimator instanceof UtilityEstimatorWithPreviousTrips) {
+ utilities.put(mode, ((UtilityEstimatorWithPreviousTrips) entry.estimator)
+ .estimateUtility(person, trip, tripElements, Collections.emptyList()));
+ } else {
+ utilities.put(mode, entry.estimator.estimateUtility(person, trip, tripElements));
+ }
+ }
+ }
+
+ for (var entry : costModelEntries) {
+ if (mode.equals(entry.mode)) {
+ if (entry.model instanceof CostModelWithPreviousTrips) {
+ costs.put(mode, ((CostModelWithPreviousTrips) entry.model).calculateCost_MU(person, trip,
+ tripElements, Collections.emptyList()));
+ } else {
+ costs.put(mode, entry.model.calculateCost_MU(person, trip, tripElements));
+ }
+ }
+ }
+ }
+
+ for (var entry : predictorEntries) {
+ if (entry.mode == null) {
+ predictions.add(new PredictionEntry<>(entry.name, null,
+ entry.predictor.predictVariables(person, trip, null)));
+ }
+ }
+
+ for (var entry : predictorEntriesWithPreviousTrips) {
+ if (entry.mode == null) {
+ predictions.add(new PredictionEntry<>(entry.name, null,
+ entry.predictor.predictVariables(person, trip, null, null)));
+ }
+ }
+
+ for (var entry : availabilityEntries) {
+ availabilities.put(entry.name,
+ new HashSet<>(entry.availability.getAvailableModes(person, Collections.singletonList(trip))));
+ }
+
+ result.add(new PersonEntry(person.getId().toString(), predictions, utilities, costs, availabilities));
+ progress.update(1);
+ }
+
+ try {
+ new ObjectMapper().writeValue(outputPath, result);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ try {
+ progress.close();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public record PredictorEntryWithPreviousTrip(
+ String name, String mode, VariablePredictorWithPreviousTrips predictor) {
+ }
+
+ public record PredictorEntry(
+ String name, String mode, VariablePredictor predictor) {
+ }
+
+ public record EstimatorEntry(
+ String mode, UtilityEstimator estimator) {
+ }
+
+ public record CostModelEntry(
+ String mode, CostModel model) {
+ }
+
+ public record AvailabilityEntry(
+ String name, ModeAvailability availability) {
+ }
+
+ public record PredictionEntry(
+ String name, String mode, T variables) {
+ }
+
+ public record PersonEntry(String personId, List> predictions, Map utilities,
+ Map costs, Map> availabilities) {
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/ProbeConfigGroup.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/ProbeConfigGroup.java
new file mode 100644
index 000000000..ae3affa97
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/ProbeConfigGroup.java
@@ -0,0 +1,22 @@
+package org.eqasim.ile_de_france.probing;
+
+import org.matsim.core.config.Config;
+import org.matsim.core.config.ReflectiveConfigGroup;
+
+public class ProbeConfigGroup extends ReflectiveConfigGroup {
+ static public final String NAME = "eqasim:probe";
+
+ @Parameter
+ public boolean useProbeTravelTimes = false;
+
+ @Parameter
+ public boolean useProbeAvailability = false;
+
+ public ProbeConfigGroup() {
+ super(NAME);
+ }
+
+ static public ProbeConfigGroup get(Config config) {
+ return (ProbeConfigGroup) config.getModules().get(NAME);
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/ProbeModeAvailability.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/ProbeModeAvailability.java
new file mode 100644
index 000000000..f034d5f9a
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/ProbeModeAvailability.java
@@ -0,0 +1,27 @@
+package org.eqasim.ile_de_france.probing;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.matsim.api.core.v01.population.Person;
+import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.contribs.discrete_mode_choice.model.mode_availability.ModeAvailability;
+
+public class ProbeModeAvailability implements ModeAvailability {
+ static public final String ATTRIBUTE = "probe:modeAvailability";
+
+ @Override
+ public Collection getAvailableModes(Person person, List trips) {
+ String raw = (String) person.getAttributes().getAttribute(ATTRIBUTE);
+
+ Set modes = new HashSet<>();
+
+ for (String mode : raw.split(",")) {
+ modes.add(mode.strip());
+ }
+
+ return modes;
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/ProbeModule.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/ProbeModule.java
new file mode 100644
index 000000000..51f81c7d5
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/ProbeModule.java
@@ -0,0 +1,115 @@
+package org.eqasim.ile_de_france.probing;
+
+import java.io.File;
+import java.util.Set;
+
+import org.eqasim.core.simulation.mode_choice.utilities.estimators.WalkUtilityEstimator;
+import org.eqasim.core.simulation.mode_choice.utilities.predictors.BikePredictor;
+import org.eqasim.core.simulation.mode_choice.utilities.predictors.CarPredictor;
+import org.eqasim.core.simulation.mode_choice.utilities.predictors.WalkPredictor;
+import org.eqasim.ile_de_france.mode_choice.IDFModeAvailability;
+import org.eqasim.ile_de_france.mode_choice.costs.IDFCarCostModel;
+import org.eqasim.ile_de_france.mode_choice.costs.IDFPtCostModel;
+import org.eqasim.ile_de_france.mode_choice.utilities.estimators.IDFBicycleUtilityEstimator;
+import org.eqasim.ile_de_france.mode_choice.utilities.estimators.IDFCarPassengerUtilityEstimator;
+import org.eqasim.ile_de_france.mode_choice.utilities.estimators.IDFCarUtilityEstimator;
+import org.eqasim.ile_de_france.mode_choice.utilities.estimators.IDFPtUtilityEstimator;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFCarPassengerPredictor;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFParkingPredictor;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFPersonPredictor;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFPtPredictor;
+import org.eqasim.ile_de_france.mode_choice.utilities.predictors.IDFSpatialPredictor;
+import org.matsim.api.core.v01.population.Population;
+import org.matsim.contribs.discrete_mode_choice.model.mode_availability.ModeAvailability;
+import org.matsim.core.controler.AbstractModule;
+import org.matsim.core.controler.OutputDirectoryHierarchy;
+import org.matsim.core.router.NetworkRoutingProvider;
+import org.matsim.core.router.RoutingModule;
+import org.matsim.core.router.TripRouter;
+import org.matsim.facilities.ActivityFacilities;
+
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import com.google.inject.Provides;
+import com.google.inject.Singleton;
+import com.google.inject.name.Names;
+
+public class ProbeModule extends AbstractModule {
+ @Override
+ public void install() {
+ ProbeConfigGroup config = ProbeConfigGroup.get(getConfig());
+
+ if (config.useProbeTravelTimes) {
+ for (String mode : Set.of("car", "car_passenger")) {
+ // delegate
+ bind(Key.get(RoutingModule.class, Names.named("base_" + mode)))
+ .toProvider(new NetworkRoutingProvider(mode));
+
+ // override
+ addRoutingModuleBinding(mode).toProvider(new Provider<>() {
+ @Inject
+ Injector injector;
+
+ @Override
+ public ProbeNetworkRoutingModule get() {
+ RoutingModule delegate = injector
+ .getInstance(Key.get(RoutingModule.class, Names.named("base_" + mode)));
+ return new ProbeNetworkRoutingModule(delegate, mode);
+ }
+ });
+ }
+
+ // explicit bindings are required
+ bind(IDFCarUtilityEstimator.class);
+ bind(IDFCarPassengerUtilityEstimator.class);
+ bind(IDFPtUtilityEstimator.class);
+ bind(IDFBicycleUtilityEstimator.class);
+ bind(WalkUtilityEstimator.class);
+ bind(IDFCarCostModel.class);
+ bind(IDFPtCostModel.class);
+ bind(IDFModeAvailability.class);
+ }
+
+ if (config.useProbeAvailability) {
+ bind(ModeAvailability.class).toInstance(new ProbeModeAvailability());
+ }
+ }
+
+ @Provides
+ @Singleton
+ PredictionWriter providePredictionWriter(Population population, ActivityFacilities facilities,
+ TripRouter tripRouter, OutputDirectoryHierarchy outputHierarchy, Injector injector) {
+ File outputPath = new File(outputHierarchy.getOutputFilename("predictions.json"));
+ PredictionWriter writer = new PredictionWriter(population, tripRouter, facilities, outputPath);
+
+ // mode specific predictors
+ writer.addPredictor("car", "car", injector.getInstance(CarPredictor.class));
+ writer.addPredictor("pt", "pt", injector.getInstance(IDFPtPredictor.class));
+ writer.addPredictor("car_passenger", "car_passenger", injector.getInstance(IDFCarPassengerPredictor.class));
+ writer.addPredictor("bicycle", "bicycle", injector.getInstance(BikePredictor.class));
+ writer.addPredictor("walk", "walk", injector.getInstance(WalkPredictor.class));
+
+ // mode independent predictors
+ writer.addPredictor("person", injector.getInstance(IDFPersonPredictor.class));
+ writer.addPredictor("spatial", injector.getInstance(IDFSpatialPredictor.class));
+ writer.addPredictor("parking", injector.getInstance(IDFParkingPredictor.class));
+
+ // mode estimators
+ writer.addEstimator("car", injector.getInstance(IDFCarUtilityEstimator.class));
+ writer.addEstimator("car_passenger", injector.getInstance(IDFCarPassengerUtilityEstimator.class));
+ writer.addEstimator("pt", injector.getInstance(IDFPtUtilityEstimator.class));
+ writer.addEstimator("bicycle", injector.getInstance(IDFBicycleUtilityEstimator.class));
+ writer.addEstimator("walk", injector.getInstance(WalkUtilityEstimator.class));
+
+ // mode costs
+ writer.addCostModel("car", injector.getInstance(IDFCarCostModel.class));
+ writer.addCostModel("pt", injector.getInstance(IDFPtCostModel.class));
+
+ // availability
+ writer.addAvailability("standard", injector.getInstance(IDFModeAvailability.class));
+
+ return writer;
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/ProbeNetworkRoutingModule.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/ProbeNetworkRoutingModule.java
new file mode 100644
index 000000000..d67269239
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/ProbeNetworkRoutingModule.java
@@ -0,0 +1,37 @@
+package org.eqasim.ile_de_france.probing;
+
+import java.util.List;
+
+import org.matsim.api.core.v01.population.Leg;
+import org.matsim.api.core.v01.population.PlanElement;
+import org.matsim.core.router.RoutingModule;
+import org.matsim.core.router.RoutingRequest;
+
+public class ProbeNetworkRoutingModule implements RoutingModule {
+ private final RoutingModule delegate;
+ private final String mode;
+ private final String attribute;
+
+ public ProbeNetworkRoutingModule(RoutingModule delegate, String mode) {
+ this.delegate = delegate;
+ this.mode = mode;
+ this.attribute = "probeTravelTime:" + mode;
+ }
+
+ @Override
+ public List extends PlanElement> calcRoute(RoutingRequest request) {
+ double probeTravelTime = (Double) request.getPerson().getAttributes().getAttribute(attribute);
+ List extends PlanElement> elements = delegate.calcRoute(request);
+
+ for (PlanElement element : elements) {
+ if (element instanceof Leg leg) {
+ if (leg.getMode().equals(mode)) {
+ leg.setTravelTime(probeTravelTime);
+ leg.getRoute().setTravelTime(probeTravelTime);
+ }
+ }
+ }
+
+ return elements;
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/RunPrediction.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/RunPrediction.java
new file mode 100644
index 000000000..a0ccdef1e
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/probing/RunPrediction.java
@@ -0,0 +1,37 @@
+package org.eqasim.ile_de_france.probing;
+
+import java.io.IOException;
+import java.util.concurrent.ExecutionException;
+
+import org.eqasim.core.simulation.EqasimConfigurator;
+import org.matsim.api.core.v01.Scenario;
+import org.matsim.core.config.CommandLine;
+import org.matsim.core.config.Config;
+import org.matsim.core.config.ConfigUtils;
+import org.matsim.core.controler.Controler;
+import org.matsim.core.scenario.ScenarioUtils;
+
+public class RunPrediction {
+ public static void main(String[] args)
+ throws CommandLine.ConfigurationException, InterruptedException, IOException, ExecutionException {
+ CommandLine cmd = new CommandLine.Builder(args) //
+ .requireOptions("config-path") //
+ .build();
+
+ // Loading the config
+ EqasimConfigurator configurator = EqasimConfigurator.getInstance(cmd);
+
+ Config config = ConfigUtils.loadConfig(cmd.getOptionStrict("config-path"));
+ configurator.updateConfig(config);
+ cmd.applyConfiguration(config);
+
+ Scenario scenario = ScenarioUtils.createScenario(config);
+ configurator.configureScenario(scenario);
+ ScenarioUtils.loadScenario(scenario);
+ configurator.adjustScenario(scenario);
+
+ Controler controller = new Controler(scenario);
+ configurator.configureController(controller);
+ controller.getInjector().getInstance(PredictionWriter.class).run();
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/scenario/RunAdaptConfig.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/scenario/RunAdaptConfig.java
index 4c2bee67e..d2ff7afa9 100644
--- a/ile_de_france/src/main/java/org/eqasim/ile_de_france/scenario/RunAdaptConfig.java
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/scenario/RunAdaptConfig.java
@@ -1,16 +1,27 @@
package org.eqasim.ile_de_france.scenario;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
import org.eqasim.core.components.config.ConfigAdapter;
import org.eqasim.core.components.config.EqasimConfigGroup;
+import org.eqasim.core.simulation.mode_choice.EqasimModeChoiceModule;
+import org.eqasim.core.simulation.mode_choice.epsilon.AdaptConfigForEpsilon;
+import org.eqasim.core.simulation.termination.EqasimTerminationConfigGroup;
import org.eqasim.ile_de_france.IDFConfigurator;
import org.eqasim.ile_de_france.mode_choice.IDFModeChoiceModule;
import org.matsim.api.core.v01.TransportMode;
import org.matsim.contribs.discrete_mode_choice.modules.config.DiscreteModeChoiceConfigGroup;
+import org.matsim.contribs.discrete_mode_choice.modules.config.VehicleTourConstraintConfigGroup;
import org.matsim.core.config.CommandLine;
import org.matsim.core.config.CommandLine.ConfigurationException;
import org.matsim.core.config.Config;
import org.matsim.core.config.groups.QSimConfigGroup;
import org.matsim.core.config.groups.QSimConfigGroup.VehiclesSource;
+import org.matsim.core.config.groups.RoutingConfigGroup.AccessEgressType;
+import org.matsim.core.config.groups.RoutingConfigGroup.TeleportedModeParams;
+import org.matsim.core.config.groups.ScoringConfigGroup.ModeParams;
import org.matsim.core.config.groups.VehiclesConfigGroup;
public class RunAdaptConfig {
@@ -20,6 +31,30 @@ static public void main(String[] args) throws ConfigurationException {
}
static public void adaptConfiguration(Config config, String prefix) {
+ // MATSim: routing
+ config.routing().setAccessEgressType(AccessEgressType.accessEgressModeToLink);
+
+ Set networkModes = new HashSet<>(config.routing().getNetworkModes());
+ networkModes.add(IDFModeChoiceModule.CAR_PASSENGER);
+ config.routing().setNetworkModes(networkModes);
+
+ TeleportedModeParams bicycleRouteParams = new TeleportedModeParams();
+ bicycleRouteParams.setMode("bicycle");
+ bicycleRouteParams.setTeleportedModeSpeed(9.1 / 3.6);
+ bicycleRouteParams.setBeelineDistanceFactor(1.3);
+ config.routing().addTeleportedModeParams(bicycleRouteParams);
+
+ TeleportedModeParams walkRouteParams = config.routing().getTeleportedModeParams().get(TransportMode.walk);
+ walkRouteParams.setTeleportedModeSpeed(3.25 / 3.6);
+ walkRouteParams.setBeelineDistanceFactor(1.3);
+
+ // MATSim: scoring
+ for (String mode : Arrays.asList(IDFModeChoiceModule.BICYCLE, IDFModeChoiceModule.CAR_PASSENGER)) {
+ ModeParams modeScoringParams = new ModeParams(mode);
+ modeScoringParams.setMarginalUtilityOfTraveling(-1.0);
+ config.scoring().addModeParams(modeScoringParams);
+ }
+
// Adjust eqasim config
EqasimConfigGroup eqasimConfig = EqasimConfigGroup.get(config);
@@ -27,20 +62,31 @@ static public void adaptConfiguration(Config config, String prefix) {
eqasimConfig.setCostModel(TransportMode.pt, IDFModeChoiceModule.PT_COST_MODEL_NAME);
eqasimConfig.setEstimator(TransportMode.car, IDFModeChoiceModule.CAR_ESTIMATOR_NAME);
- eqasimConfig.setEstimator(TransportMode.bike, IDFModeChoiceModule.BIKE_ESTIMATOR_NAME);
+ eqasimConfig.setEstimator(TransportMode.pt, IDFModeChoiceModule.PT_ESTIMATOR_NAME);
+ eqasimConfig.setEstimator(IDFModeChoiceModule.BICYCLE, IDFModeChoiceModule.BICYCLE_ESTIMATOR_NAME);
+ eqasimConfig.setEstimator(IDFModeChoiceModule.CAR_PASSENGER, IDFModeChoiceModule.CAR_PASSENGER_ESTIMATOR_NAME);
+ eqasimConfig.removeEstimator(TransportMode.bike);
+ // Discrete mode choice
DiscreteModeChoiceConfigGroup dmcConfig = (DiscreteModeChoiceConfigGroup) config.getModules()
.get(DiscreteModeChoiceConfigGroup.GROUP_NAME);
dmcConfig.setModeAvailability(IDFModeChoiceModule.MODE_AVAILABILITY_NAME);
+ dmcConfig.setCachedModes(Arrays.asList("car", IDFModeChoiceModule.BICYCLE, "pt", "walk",
+ IDFModeChoiceModule.CAR_PASSENGER, "truck"));
- // Calibration results for 5%
+ Set tripConstraints = new HashSet<>(dmcConfig.getTripConstraints());
+ tripConstraints.remove(EqasimModeChoiceModule.PASSENGER_CONSTRAINT_NAME);
+ dmcConfig.setTripConstraints(tripConstraints);
- if (eqasimConfig.getSampleSize() == 0.05) {
- // Adjust flow and storage capacity
- config.qsim().setFlowCapFactor(0.045);
- config.qsim().setStorageCapFactor(0.045);
- }
+ VehicleTourConstraintConfigGroup vehicleTourConstraint = dmcConfig.getVehicleTourConstraintConfig();
+ vehicleTourConstraint.setRestrictedModes(Arrays.asList("car", IDFModeChoiceModule.BICYCLE));
+
+ // Major crossing penalty from calibration
+ eqasimConfig.setCrossingPenalty(0.0);
+
+ // Epsilon
+ AdaptConfigForEpsilon.run(config);
// Vehicles
QSimConfigGroup qsimConfig = config.qsim();
@@ -48,5 +94,10 @@ static public void adaptConfiguration(Config config, String prefix) {
VehiclesConfigGroup vehiclesConfig = config.vehicles();
vehiclesConfig.setVehiclesFile(prefix + "vehicles.xml.gz");
+
+ // Convergence
+ EqasimTerminationConfigGroup terminationConfig = EqasimTerminationConfigGroup.getOrCreate(config);
+ terminationConfig.setModes(
+ Arrays.asList("car", IDFModeChoiceModule.CAR_PASSENGER, "pt", IDFModeChoiceModule.BICYCLE, "walk"));
}
}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/scenario/RunAdjustNetwork.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/scenario/RunAdjustNetwork.java
new file mode 100644
index 000000000..e41331952
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/scenario/RunAdjustNetwork.java
@@ -0,0 +1,49 @@
+package org.eqasim.ile_de_france.scenario;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Optional;
+
+import org.eqasim.core.scenario.cutter.extent.ScenarioExtent;
+import org.eqasim.core.scenario.cutter.extent.ShapeScenarioExtent;
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.core.config.CommandLine;
+import org.matsim.core.config.CommandLine.ConfigurationException;
+import org.matsim.core.network.NetworkUtils;
+import org.matsim.core.network.io.MatsimNetworkReader;
+import org.matsim.core.network.io.NetworkWriter;
+
+public class RunAdjustNetwork {
+ static public void main(String[] args) throws ConfigurationException, InterruptedException, IOException {
+ CommandLine cmd = new CommandLine.Builder(args) //
+ .requireOptions("input-path", "output-path") //
+ .allowOptions("capacity-factor", "speed-factor", "extent-path") //
+ .build();
+
+ String inputPath = cmd.getOptionStrict("input-path");
+ String outputPath = cmd.getOptionStrict("output-path");
+
+ double capacityFactor = cmd.getOption("capacity-factor").map(Double::parseDouble).orElse(1.0);
+ double speedFactor = cmd.getOption("speed-factor").map(Double::parseDouble).orElse(1.0);
+
+ ScenarioExtent extent = null;
+ if (cmd.hasOption("extent-path")) {
+ String extentPath = cmd.getOptionStrict("extent-path");
+ extent = new ShapeScenarioExtent.Builder(new File(extentPath), Optional.empty(),
+ Optional.empty()).build();
+ }
+
+ Network network = NetworkUtils.createNetwork();
+ new MatsimNetworkReader(network).readFile(inputPath);
+
+ for (Link link : network.getLinks().values()) {
+ if (extent == null || extent.isInside(link.getCoord())) {
+ link.setCapacity(link.getCapacity() * capacityFactor);
+ link.setFreespeed(link.getFreespeed() * speedFactor);
+ }
+ }
+
+ new NetworkWriter(network).write(outputPath);
+ }
+}
diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/scenario/RunConfigureNetwork.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/scenario/RunConfigureNetwork.java
new file mode 100644
index 000000000..20da41f39
--- /dev/null
+++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/scenario/RunConfigureNetwork.java
@@ -0,0 +1,42 @@
+package org.eqasim.ile_de_france.scenario;
+
+import java.io.IOException;
+
+import org.matsim.api.core.v01.network.Link;
+import org.matsim.api.core.v01.network.Network;
+import org.matsim.api.core.v01.network.Node;
+import org.matsim.core.config.CommandLine;
+import org.matsim.core.config.CommandLine.ConfigurationException;
+import org.matsim.core.network.NetworkUtils;
+import org.matsim.core.network.io.MatsimNetworkReader;
+import org.matsim.core.network.io.NetworkWriter;
+
+public class RunConfigureNetwork {
+ static public void main(String[] args) throws ConfigurationException, InterruptedException, IOException {
+ CommandLine cmd = new CommandLine.Builder(args) //
+ .requireOptions("input-path", "output-path") //
+ .allowOptions("node-capacity") //
+ .build();
+
+ String inputPath = cmd.getOptionStrict("input-path");
+ String outputPath = cmd.getOptionStrict("output-path");
+
+ double nodeCapacity = cmd.getOption("node-capacity").map(Double::parseDouble).orElse(1800.0);
+
+ Network network = NetworkUtils.createNetwork();
+ new MatsimNetworkReader(network).readFile(inputPath);
+
+ for (Node node : network.getNodes().values()) {
+ // calculate incoming lanes and distribute node capacity over them
+ double incomingLaneCount = node.getInLinks().values().stream().mapToDouble(Link::getNumberOfLanes).sum();
+ double laneCapacity = nodeCapacity / incomingLaneCount;
+
+ for (Link link : node.getInLinks().values()) {
+ // set incoming link capacity by distributing outgoing capacity
+ link.setCapacity(link.getNumberOfLanes() * laneCapacity);
+ }
+ }
+
+ new NetworkWriter(network).write(outputPath);
+ }
+}
diff --git a/ile_de_france/src/test/java/org/eqasim/ile_de_france/TestCorisica.java b/ile_de_france/src/test/java/org/eqasim/ile_de_france/TestCorisica.java
index d61600183..b350ad7e0 100644
--- a/ile_de_france/src/test/java/org/eqasim/ile_de_france/TestCorisica.java
+++ b/ile_de_france/src/test/java/org/eqasim/ile_de_france/TestCorisica.java
@@ -9,22 +9,29 @@
import java.util.concurrent.ExecutionException;
import org.apache.commons.io.FileUtils;
+import org.eqasim.core.components.config.EqasimConfigGroup;
import org.eqasim.core.scenario.RunInsertVehicles;
import org.eqasim.core.scenario.cutter.RunScenarioCutter;
import org.eqasim.core.standalone_mode_choice.RunStandaloneModeChoice;
+import org.eqasim.ile_de_france.mode_choice.IDFModeChoiceModule;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.matsim.api.core.v01.Scenario;
import org.matsim.api.core.v01.events.handler.PersonDepartureEventHandler;
+import org.matsim.contribs.discrete_mode_choice.modules.config.DiscreteModeChoiceConfigGroup;
import org.matsim.core.api.experimental.events.EventsManager;
import org.matsim.core.config.CommandLine;
import org.matsim.core.config.CommandLine.ConfigurationException;
import org.matsim.core.config.Config;
import org.matsim.core.config.ConfigUtils;
import org.matsim.core.config.ConfigWriter;
+import org.matsim.core.config.groups.RoutingConfigGroup;
+import org.matsim.core.config.groups.ScoringConfigGroup;
import org.matsim.core.config.groups.QSimConfigGroup.VehiclesSource;
+import org.matsim.core.config.groups.RoutingConfigGroup.TeleportedModeParams;
+import org.matsim.core.config.groups.ScoringConfigGroup.ModeParams;
import org.matsim.core.events.EventsUtils;
import org.matsim.core.events.MatsimEventsReader;
import org.matsim.core.population.io.PopulationReader;
@@ -49,6 +56,25 @@ private void adjustConfig() throws ConfigurationException {
configurator.updateConfig(config);
config.vehicles().setVehiclesFile("corsica_vehicles.xml.gz");
config.qsim().setVehiclesSource(VehiclesSource.fromVehiclesData);
+
+ EqasimConfigGroup eqasimConfig = EqasimConfigGroup.get(config);
+ eqasimConfig.setEstimator("bike", IDFModeChoiceModule.BICYCLE_ESTIMATOR_NAME);
+ eqasimConfig.setEstimator("bicycle", IDFModeChoiceModule.BICYCLE_ESTIMATOR_NAME);
+
+ DiscreteModeChoiceConfigGroup dmcConfig = DiscreteModeChoiceConfigGroup.getOrCreate(config);
+ dmcConfig.setModeAvailability(IDFModeChoiceModule.MODE_AVAILABILITY_NAME);
+
+ RoutingConfigGroup routingConfig = config.routing();
+
+ TeleportedModeParams bicycleRoutingParams = new TeleportedModeParams("bicycle");
+ bicycleRoutingParams.setBeelineDistanceFactor(1.3);
+ bicycleRoutingParams.setTeleportedModeSpeed(9.3 / 3.6);
+ routingConfig.addTeleportedModeParams(bicycleRoutingParams);
+
+ ScoringConfigGroup scoringConfig = config.scoring();
+ ModeParams bicycleScoringParams = new ModeParams("bicycle");
+ scoringConfig.addModeParams(bicycleScoringParams);
+
new ConfigWriter(config).write("corsica_test/corsica_config.xml");
}
diff --git a/los_angeles/src/main/java/org/eqasim/los_angeles/mode_choice/utilities/estimators/LosAngelesCarUtilityEstimator.java b/los_angeles/src/main/java/org/eqasim/los_angeles/mode_choice/utilities/estimators/LosAngelesCarUtilityEstimator.java
index 5820f01a9..ebfb95684 100644
--- a/los_angeles/src/main/java/org/eqasim/los_angeles/mode_choice/utilities/estimators/LosAngelesCarUtilityEstimator.java
+++ b/los_angeles/src/main/java/org/eqasim/los_angeles/mode_choice/utilities/estimators/LosAngelesCarUtilityEstimator.java
@@ -13,6 +13,7 @@
import org.matsim.api.core.v01.population.Person;
import org.matsim.api.core.v01.population.PlanElement;
import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate;
import com.google.inject.Inject;
@@ -41,9 +42,9 @@ protected double estimateMonetaryCostUtility(CarVariables variables) {
}
@Override
- public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
+ public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements, List previousTrips) {
LosAngelesPersonVariables variables = predictor.predictVariables(person, trip, elements);
- CarVariables variables_car = carPredictor.predict(person, trip, elements);
+ CarVariables variables_car = carPredictor.predict(person, trip, elements, previousTrips);
double utility = 0.0;
diff --git a/san_francisco/src/main/java/org/eqasim/san_francisco/mode_choice/utilities/estimators/SanFranciscoCarUtilityEstimator.java b/san_francisco/src/main/java/org/eqasim/san_francisco/mode_choice/utilities/estimators/SanFranciscoCarUtilityEstimator.java
index 186cff0ad..6b7f9cfab 100644
--- a/san_francisco/src/main/java/org/eqasim/san_francisco/mode_choice/utilities/estimators/SanFranciscoCarUtilityEstimator.java
+++ b/san_francisco/src/main/java/org/eqasim/san_francisco/mode_choice/utilities/estimators/SanFranciscoCarUtilityEstimator.java
@@ -13,6 +13,7 @@
import org.matsim.api.core.v01.population.Person;
import org.matsim.api.core.v01.population.PlanElement;
import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate;
import com.google.inject.Inject;
@@ -41,9 +42,9 @@ protected double estimateMonetaryCostUtility(CarVariables variables) {
}
@Override
- public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
+ public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements, List previousTrips) {
SanFranciscoPersonVariables variables = predictor.predictVariables(person, trip, elements);
- CarVariables variables_car = carPredictor.predict(person, trip, elements);
+ CarVariables variables_car = carPredictor.predict(person, trip, elements, previousTrips);
double utility = 0.0;
diff --git a/sao_paulo/src/main/java/org/eqasim/sao_paulo/mode_choice/utilities/estimators/SaoPauloCarUtilityEstimator.java b/sao_paulo/src/main/java/org/eqasim/sao_paulo/mode_choice/utilities/estimators/SaoPauloCarUtilityEstimator.java
index 379f48979..1e7d52c9f 100644
--- a/sao_paulo/src/main/java/org/eqasim/sao_paulo/mode_choice/utilities/estimators/SaoPauloCarUtilityEstimator.java
+++ b/sao_paulo/src/main/java/org/eqasim/sao_paulo/mode_choice/utilities/estimators/SaoPauloCarUtilityEstimator.java
@@ -12,6 +12,7 @@
import org.matsim.api.core.v01.population.Person;
import org.matsim.api.core.v01.population.PlanElement;
import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate;
import com.google.inject.Inject;
@@ -34,9 +35,9 @@ protected double estimateRegionalUtility(SaoPauloPersonVariables variables) {
}
@Override
- public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
+ public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements, List previousTrips) {
SaoPauloPersonVariables variables = predictor.predictVariables(person, trip, elements);
- CarVariables variables_car = carPredictor.predict(person, trip, elements);
+ CarVariables variables_car = carPredictor.predict(person, trip, elements, previousTrips);
double utility = 0.0;
diff --git a/switzerland/src/main/java/org/eqasim/switzerland/zurich/mode_choice/utilities/estimators/ZurichCarUtilityEstimator.java b/switzerland/src/main/java/org/eqasim/switzerland/zurich/mode_choice/utilities/estimators/ZurichCarUtilityEstimator.java
index 06e7a9bbe..fceb8ebd7 100644
--- a/switzerland/src/main/java/org/eqasim/switzerland/zurich/mode_choice/utilities/estimators/ZurichCarUtilityEstimator.java
+++ b/switzerland/src/main/java/org/eqasim/switzerland/zurich/mode_choice/utilities/estimators/ZurichCarUtilityEstimator.java
@@ -15,6 +15,7 @@
import org.matsim.api.core.v01.population.Person;
import org.matsim.api.core.v01.population.PlanElement;
import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
+import org.matsim.contribs.discrete_mode_choice.model.trip_based.candidates.TripCandidate;
import com.google.inject.Inject;
@@ -62,8 +63,8 @@ protected double estimateCityUtility(ZurichTripVariables variables) {
}
@Override
- public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements) {
- CarVariables variables = predictor.predictVariables(person, trip, elements);
+ public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List extends PlanElement> elements, List previousTrips) {
+ CarVariables variables = predictor.predictVariables(person, trip, elements, previousTrips);
ZurichPersonVariables personVariables = personPredictor.predictVariables(person, trip, elements);
ZurichTripVariables tripVariables = tripPredictor.predictVariables(person, trip, elements);