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/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 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 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 elements) { - CarVariables variables = predictor.predictVariables(person, trip, elements); + public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List 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 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 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 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 elements) { + public CarVariables predict(Person person, DiscreteModeChoiceTrip trip, List 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 { + T predictVariables(Person person, DiscreteModeChoiceTrip trip, List 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 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 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 tripElements) { + Double followingParkingDuration = (Double) trip.getTripAttributes().getAttribute(FOLLOWING_PARKING_DURATION); + if (followingParkingDuration != null) { + return followingParkingDuration; + } + + List 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 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 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 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 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 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 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 elements) { - IDFSpatialVariables variables = spatialPredictor.predictVariables(person, trip, elements); + public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List 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 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 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 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 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 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 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 calcRoute(RoutingRequest request) { + double probeTravelTime = (Double) request.getPerson().getAttributes().getAttribute(attribute); + List 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 elements) { + public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List 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 elements) { + public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List 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 elements) { + public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List 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 elements) { - CarVariables variables = predictor.predictVariables(person, trip, elements); + public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List 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);