From 1f64172c8b2f2356d9c560f8375d2189ff661593 Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Mon, 19 May 2025 16:14:08 +0200 Subject: [PATCH 01/21] fix(emissions): update detailed vs average lookup behavior based on command line options --- .../components/emissions/RunComputeEmissionsEvents.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/eqasim/core/components/emissions/RunComputeEmissionsEvents.java b/core/src/main/java/org/eqasim/core/components/emissions/RunComputeEmissionsEvents.java index f144cc03a..d3d6a1891 100644 --- a/core/src/main/java/org/eqasim/core/components/emissions/RunComputeEmissionsEvents.java +++ b/core/src/main/java/org/eqasim/core/components/emissions/RunComputeEmissionsEvents.java @@ -37,8 +37,6 @@ public static void main(String[] args) throws CommandLine.ConfigurationException EmissionsConfigGroup emissionsConfig = (EmissionsConfigGroup) config.getModules().get("emissions"); emissionsConfig.setHbefaVehicleDescriptionSource(EmissionsConfigGroup.HbefaVehicleDescriptionSource.asEngineInformationAttributes); - emissionsConfig.setDetailedVsAverageLookupBehavior( - EmissionsConfigGroup.DetailedVsAverageLookupBehavior.tryDetailedThenTechnologyAverageThenAverageTable); emissionsConfig.setNonScenarioVehicles(EmissionsConfigGroup.NonScenarioVehicles.abort); emissionsConfig.setHbefaTableConsistencyCheckingLevel(EmissionsConfigGroup.HbefaTableConsistencyCheckingLevel.consistent); @@ -48,6 +46,12 @@ public static void main(String[] args) throws CommandLine.ConfigurationException if (cmd.hasOption("hbefa-cold-detailed") && cmd.hasOption("hbefa-hot-detailed")) { emissionsConfig.setDetailedColdEmissionFactorsFile(cmd.getOptionStrict("hbefa-cold-detailed")); emissionsConfig.setDetailedWarmEmissionFactorsFile(cmd.getOptionStrict("hbefa-hot-detailed")); + emissionsConfig.setDetailedVsAverageLookupBehavior( + EmissionsConfigGroup.DetailedVsAverageLookupBehavior.tryDetailedThenTechnologyAverageThenAverageTable); + } + else { + emissionsConfig.setDetailedVsAverageLookupBehavior( + EmissionsConfigGroup.DetailedVsAverageLookupBehavior.directlyTryAverageTable); } Scenario scenario = ScenarioUtils.createScenario(config); From 7016a9e8c245307069dfa82575b6f91128f75c0d Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Sat, 20 Sep 2025 20:09:02 +0200 Subject: [PATCH 02/21] add motorcycle mode --- .../mode_choice/EqasimModeChoiceModule.java | 21 ++-- .../parameters/ModeParameters.java | 9 ++ .../MotorcycleUtilityEstimator.java | 54 ++++++++++ .../predictors/MotorcyclePredictor.java | 51 ++++++++++ .../variables/MotorcycleVariables.java | 16 +++ .../eqasim/ile_de_france/IDFConfigurator.java | 98 ++++++++++++++++++- .../mode_choice/IDFModeAvailability.java | 20 ++++ .../mode_choice/IDFModeChoiceModule.java | 6 ++ .../costs/IDFMotorcycleCostModel.java | 25 +++++ .../parameters/IDFCostParameters.java | 2 + .../parameters/IDFModeParameters.java | 16 +++ .../IDFMotorcycleUtilityEstimator.java | 53 ++++++++++ .../scenario/RunAdaptConfig.java | 2 + 13 files changed, 359 insertions(+), 14 deletions(-) create mode 100644 core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/estimators/MotorcycleUtilityEstimator.java create mode 100644 core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/MotorcyclePredictor.java create mode 100644 core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/variables/MotorcycleVariables.java create mode 100644 ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/costs/IDFMotorcycleCostModel.java create mode 100644 ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFMotorcycleUtilityEstimator.java diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/EqasimModeChoiceModule.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/EqasimModeChoiceModule.java index 8b1e04980..9c0d0f286 100644 --- a/core/src/main/java/org/eqasim/core/simulation/mode_choice/EqasimModeChoiceModule.java +++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/EqasimModeChoiceModule.java @@ -18,16 +18,8 @@ import org.eqasim.core.simulation.mode_choice.parameters.ModeParameters; import org.eqasim.core.simulation.mode_choice.utilities.EqasimUtilityEstimator; import org.eqasim.core.simulation.mode_choice.utilities.UtilityEstimator; -import org.eqasim.core.simulation.mode_choice.utilities.estimators.BikeUtilityEstimator; -import org.eqasim.core.simulation.mode_choice.utilities.estimators.CarUtilityEstimator; -import org.eqasim.core.simulation.mode_choice.utilities.estimators.PtUtilityEstimator; -import org.eqasim.core.simulation.mode_choice.utilities.estimators.WalkUtilityEstimator; -import org.eqasim.core.simulation.mode_choice.utilities.estimators.ZeroUtilityEstimator; -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.PersonPredictor; -import org.eqasim.core.simulation.mode_choice.utilities.predictors.PtPredictor; -import org.eqasim.core.simulation.mode_choice.utilities.predictors.WalkPredictor; +import org.eqasim.core.simulation.mode_choice.utilities.estimators.*; +import org.eqasim.core.simulation.mode_choice.utilities.predictors.*; import org.eqasim.core.simulation.modes.drt.mode_choice.constraints.DrtServiceAreaConstraint; import org.eqasim.core.simulation.modes.drt.mode_choice.constraints.DrtWalkConstraint; import org.eqasim.core.simulation.modes.drt.mode_choice.predictors.DefaultDrtPredictor; @@ -59,6 +51,7 @@ public class EqasimModeChoiceModule extends AbstractEqasimExtension { public static final String UTILITY_ESTIMATOR_NAME = "EqasimUtilityEstimator"; public static final String CAR_ESTIMATOR_NAME = "CarUtilityEstimator"; + public static final String MOTORCYCLE_ESTIMATOR_NAME = "MotorcycleUtilityEstimator"; public static final String PT_ESTIMATOR_NAME = "PtUtilityEstimator"; public static final String BIKE_ESTIMATOR_NAME = "BikeUtilityEstimator"; public static final String WALK_ESTIMATOR_NAME = "WalkUtilityEstimator"; @@ -83,6 +76,7 @@ protected void installEqasimExtension() { bindTripEstimator(UTILITY_ESTIMATOR_NAME).to(EqasimUtilityEstimator.class); bind(CarPredictor.class); + bind(MotorcyclePredictor.class); bind(PtPredictor.class); bind(BikePredictor.class); bind(WalkPredictor.class); @@ -91,6 +85,7 @@ protected void installEqasimExtension() { bindUtilityEstimator(ZERO_ESTIMATOR_NAME).to(ZeroUtilityEstimator.class); bindUtilityEstimator(CAR_ESTIMATOR_NAME).to(CarUtilityEstimator.class); + bindUtilityEstimator(MOTORCYCLE_ESTIMATOR_NAME).to(MotorcycleUtilityEstimator.class); bindUtilityEstimator(PT_ESTIMATOR_NAME).to(PtUtilityEstimator.class); bindUtilityEstimator(BIKE_ESTIMATOR_NAME).to(BikeUtilityEstimator.class); bindUtilityEstimator(WALK_ESTIMATOR_NAME).to(WalkUtilityEstimator.class); @@ -136,6 +131,12 @@ public CostModel provideCarCostModel(Map> factory, E return getCostModel(factory, config, "car"); } + @Provides + @Named("motorcycle") + public CostModel provideMotorcycleCostModel(Map> factory, EqasimConfigGroup config) { + return getCostModel(factory, config, "motorcycle"); + } + @Provides @Named("pt") public CostModel providePtCostModel(Map> factory, EqasimConfigGroup config) { diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/parameters/ModeParameters.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/parameters/ModeParameters.java index 08232d9a8..a412b573c 100644 --- a/core/src/main/java/org/eqasim/core/simulation/mode_choice/parameters/ModeParameters.java +++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/parameters/ModeParameters.java @@ -11,6 +11,14 @@ public class CarParameters { public double constantParkingSearchPenalty_min = 0.0; } + public class MotorcycleParameters { + public double alpha_u = 0.0; + public double betaTravelTime_u_min = 0.0; + + public double additionalAccessEgressWalkTime_min = 0.0; + public double constantParkingSearchPenalty_min = 0.0; + } + public class PtParameters { public double alpha_u = 0.0; public double betaLineSwitch_u = 0.0; @@ -44,6 +52,7 @@ public class DrtParameters { public double betaCost_u_MU = 0.0; public final CarParameters car = new CarParameters(); + public final MotorcycleParameters motorcycle = new MotorcycleParameters(); public final PtParameters pt = new PtParameters(); public final BikeParameters bike = new BikeParameters(); public final WalkParameters walk = new WalkParameters(); diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/estimators/MotorcycleUtilityEstimator.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/estimators/MotorcycleUtilityEstimator.java new file mode 100644 index 000000000..57b95118d --- /dev/null +++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/estimators/MotorcycleUtilityEstimator.java @@ -0,0 +1,54 @@ +package org.eqasim.core.simulation.mode_choice.utilities.estimators; + +import com.google.inject.Inject; +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.predictors.MotorcyclePredictor; +import org.eqasim.core.simulation.mode_choice.utilities.variables.MotorcycleVariables; +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 java.util.List; + +public class MotorcycleUtilityEstimator implements UtilityEstimator { + private final ModeParameters parameters; + private final MotorcyclePredictor predictor; + + @Inject + public MotorcycleUtilityEstimator(ModeParameters parameters, MotorcyclePredictor predictor) { + this.parameters = parameters; + this.predictor = predictor; + } + + protected double estimateConstantUtility() { + return parameters.motorcycle.alpha_u; + } + + protected double estimateTravelTimeUtility(MotorcycleVariables variables) { + return parameters.motorcycle.betaTravelTime_u_min * variables.travelTime_min; + } + + protected double estimateAccessEgressTimeUtility(MotorcycleVariables variables) { + return parameters.walk.betaTravelTime_u_min * variables.accessEgressTime_min; + } + + protected double estimateMonetaryCostUtility(MotorcycleVariables variables) { + return parameters.betaCost_u_MU * EstimatorUtils.interaction(variables.euclideanDistance_km, + parameters.referenceEuclideanDistance_km, parameters.lambdaCostEuclideanDistance) * variables.cost_MU; + } + + @Override + public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List elements) { + MotorcycleVariables variables = predictor.predictVariables(person, trip, elements); + + double utility = 0.0; + + utility += estimateConstantUtility(); + utility += estimateTravelTimeUtility(variables); + utility += estimateAccessEgressTimeUtility(variables); + utility += estimateMonetaryCostUtility(variables); + + return utility; + } +} diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/MotorcyclePredictor.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/MotorcyclePredictor.java new file mode 100644 index 000000000..a61ee8ea3 --- /dev/null +++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/MotorcyclePredictor.java @@ -0,0 +1,51 @@ +package org.eqasim.core.simulation.mode_choice.utilities.predictors; + +import com.google.common.base.Verify; +import com.google.inject.Inject; +import com.google.inject.name.Named; +import org.eqasim.core.simulation.mode_choice.cost.CostModel; +import org.eqasim.core.simulation.mode_choice.parameters.ModeParameters; +import org.eqasim.core.simulation.mode_choice.utilities.variables.MotorcycleVariables; +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 java.util.List; + +public class MotorcyclePredictor extends CachedVariablePredictor { + private final CostModel costModel; + private final ModeParameters parameters; + + @Inject + public MotorcyclePredictor(ModeParameters parameters, @Named("motorcycle") CostModel costModel) { + this.costModel = costModel; + this.parameters = parameters; + } + + @Override + public MotorcycleVariables predict(Person person, DiscreteModeChoiceTrip trip, List elements) { + double motorcycleTravelTime_min = parameters.motorcycle.constantParkingSearchPenalty_min; + double accessEgressTime_min = parameters.motorcycle.additionalAccessEgressWalkTime_min; + + boolean foundCar = false; + + for (Leg leg : TripStructureUtils.getLegs(elements)) { + if (leg.getMode().equals(TransportMode.motorcycle)) { + Verify.verify(!foundCar); + motorcycleTravelTime_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 car chain: " + leg.getMode()); + } + } + + double cost_MU = costModel.calculateCost_MU(person, trip, elements); + double euclideanDistance_km = PredictorUtils.calculateEuclideanDistance_km(trip); + + return new MotorcycleVariables(motorcycleTravelTime_min, cost_MU, euclideanDistance_km, accessEgressTime_min); + } +} diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/variables/MotorcycleVariables.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/variables/MotorcycleVariables.java new file mode 100644 index 000000000..0f08beaf3 --- /dev/null +++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/variables/MotorcycleVariables.java @@ -0,0 +1,16 @@ +package org.eqasim.core.simulation.mode_choice.utilities.variables; + +public class MotorcycleVariables implements BaseVariables { + final public double travelTime_min; + final public double cost_MU; + final public double euclideanDistance_km; + final public double accessEgressTime_min; + + public MotorcycleVariables(double travelTime_min, double cost_MU, double euclideanDistance_km, + double accessEgressTime_min) { + this.travelTime_min = travelTime_min; + this.cost_MU = cost_MU; + this.euclideanDistance_km = euclideanDistance_km; + this.accessEgressTime_min = accessEgressTime_min; + } +} 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..60304cf32 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 @@ -1,13 +1,103 @@ package org.eqasim.ile_de_france; +import org.eqasim.core.components.config.EqasimConfigGroup; import org.eqasim.core.simulation.EqasimConfigurator; import org.eqasim.ile_de_france.mode_choice.IDFModeChoiceModule; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.*; import org.matsim.core.config.CommandLine; +import org.matsim.core.config.Config; +import org.matsim.core.config.groups.QSimConfigGroup; +import org.matsim.contribs.discrete_mode_choice.modules.config.DiscreteModeChoiceConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; + +import java.util.*; public class IDFConfigurator extends EqasimConfigurator { - public IDFConfigurator(CommandLine cmd) { - super(cmd); + public IDFConfigurator(CommandLine cmd) { + super(cmd); + + registerModule(new IDFModeChoiceModule(cmd)); + } + + @Override + public void adjustScenario(Scenario scenario) { + super.adjustScenario(scenario); + + // add motorcycle stuff + Config config = scenario.getConfig(); + { // QSim + List qsimMainModes = new ArrayList<>(config.qsim().getMainModes()); + qsimMainModes.add("motorcycle"); + config.qsim().setMainModes(qsimMainModes); + + config.qsim().setLinkDynamics(QSimConfigGroup.LinkDynamics.SeepageQ); + + List qsimSeepModes = new ArrayList<>(config.qsim().getSeepModes()); + qsimSeepModes.add("motorcycle"); + config.qsim().setSeepModes(qsimSeepModes); + } + { // DMC + DiscreteModeChoiceConfigGroup dmcConfig = (DiscreteModeChoiceConfigGroup) config.getModules() + .get(DiscreteModeChoiceConfigGroup.GROUP_NAME); + Collection cachedModes = dmcConfig.getCachedModes(); + cachedModes.add("motorcycle"); + dmcConfig.setCachedModes(cachedModes); + + List vehicleConstraintModes = new ArrayList<>(dmcConfig.getVehicleTourConstraintConfig().getRestrictedModes()); + vehicleConstraintModes.add("motorcycle"); + dmcConfig.getVehicleTourConstraintConfig().setRestrictedModes(vehicleConstraintModes); + } + { // Eqasim + EqasimConfigGroup eqasimConfig = EqasimConfigGroup.get(config); + eqasimConfig.setEstimator(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_ESTIMATOR_NAME); + eqasimConfig.setCostModel(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_COST_MODEL_NAME); - registerModule(new IDFModeChoiceModule(cmd)); - } + // TODO : do we need to add motorcycles to this ? + // + // + } + { // Scoring + ScoringConfigGroup.ModeParams modeParameters = new ScoringConfigGroup.ModeParams("motorcycle"); + modeParameters.setConstant(0.0); + modeParameters.setMarginalUtilityOfDistance(0.0); + modeParameters.setMonetaryDistanceRate(0.0); + config.scoring().addModeParams(modeParameters); + } + { // Routing + List networkModes = new ArrayList<>(config.routing().getNetworkModes()); + networkModes.add("motorcycle"); + config.routing().setNetworkModes(networkModes); + } + { // Network + Network network = scenario.getNetwork(); + for (Link link : network.getLinks().values()) { + Set modes = new HashSet<>(link.getAllowedModes()); + if (modes.contains("car")) { + modes.add("motorcycle"); + link.setAllowedModes(modes); + } + } + } + { // Population ; force motorcycle starting mode +// Population population = scenario.getPopulation(); +// for (Person person : population.getPersons().values()) { +// String motorcycleAvailability = (String) person.getAttributes().getAttribute("motorcycleAvailability"); +// if ("some".equals(motorcycleAvailability) | "all".equals(motorcycleAvailability)) { +// Plan plan = person.getSelectedPlan(); +// for (PlanElement element : plan.getPlanElements()) { +// if (element instanceof Leg) { +// Leg leg = (Leg) element; +// leg.setMode(TransportMode.motorcycle); +// leg.setRoutingMode(TransportMode.motorcycle); +// } +// } +// } +// } + } + } } 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 b193c2bb6..a502a5404 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 @@ -34,6 +34,26 @@ public Collection getAvailableModes(Person person, List elements) { + return costParameters.motorcycleCost_EUR_km * getInVehicleDistance_km(elements); + } +} 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..deaa70604 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 motorcycleCost_EUR_km = 0.0; public static IDFCostParameters buildDefault() { IDFCostParameters parameters = new IDFCostParameters(); parameters.carCost_EUR_km = 0.15; + parameters.motorcycleCost_EUR_km = 0.15; 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..356820e87 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 @@ -8,11 +8,17 @@ public class IDFCarParameters { public double betaCrossingUrbanArea; } + public class IDFMotorcycleParameters { + public double betaInsideUrbanArea; + public double betaCrossingUrbanArea; + } + public class IDFBikeParameters { public double betaInsideUrbanArea; } public final IDFCarParameters idfCar = new IDFCarParameters(); + public final IDFMotorcycleParameters idfMotorcycle = new IDFMotorcycleParameters(); public final IDFBikeParameters idfBike = new IDFBikeParameters(); public static IDFModeParameters buildDefault() { @@ -33,6 +39,16 @@ public static IDFModeParameters buildDefault() { parameters.idfCar.betaInsideUrbanArea = -0.5; parameters.idfCar.betaCrossingUrbanArea = -1.0; + // Motorcycle ; copy of Car for now + parameters.motorcycle.alpha_u = 1.35; + parameters.motorcycle.betaTravelTime_u_min = -0.06; + + parameters.motorcycle.additionalAccessEgressWalkTime_min = 4.0; + parameters.motorcycle.constantParkingSearchPenalty_min = 4.0; + + parameters.idfMotorcycle.betaInsideUrbanArea = -0.5; + parameters.idfMotorcycle.betaCrossingUrbanArea = -1.0; + // PT parameters.pt.alpha_u = 0.0; parameters.pt.betaLineSwitch_u = -0.17; diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFMotorcycleUtilityEstimator.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFMotorcycleUtilityEstimator.java new file mode 100644 index 000000000..367eaaa36 --- /dev/null +++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFMotorcycleUtilityEstimator.java @@ -0,0 +1,53 @@ +package org.eqasim.ile_de_france.mode_choice.utilities.estimators; + +import com.google.inject.Inject; +import org.eqasim.core.simulation.mode_choice.utilities.estimators.MotorcycleUtilityEstimator; +import org.eqasim.core.simulation.mode_choice.utilities.predictors.MotorcyclePredictor; +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 java.util.List; + +public class IDFMotorcycleUtilityEstimator extends MotorcycleUtilityEstimator { + private final IDFModeParameters parameters; + private final IDFSpatialPredictor spatialPredictor; + + @Inject + public IDFMotorcycleUtilityEstimator(IDFModeParameters parameters, IDFSpatialPredictor spatialPredictor, + MotorcyclePredictor motorcyclePredictor) { + super(parameters, motorcyclePredictor); + + this.parameters = parameters; + this.spatialPredictor = spatialPredictor; + } + + protected double estimateUrbanUtility(IDFSpatialVariables variables) { + double utility = 0.0; + + if (variables.hasUrbanOrigin && variables.hasUrbanDestination) { + utility += parameters.idfMotorcycle.betaInsideUrbanArea; + } + + if (variables.hasUrbanOrigin || variables.hasUrbanDestination) { + utility += parameters.idfMotorcycle.betaCrossingUrbanArea; + } + + 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/scenario/RunAdaptConfig.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/scenario/RunAdaptConfig.java index 4c2bee67e..a956fb827 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 @@ -24,9 +24,11 @@ static public void adaptConfiguration(Config config, String prefix) { EqasimConfigGroup eqasimConfig = EqasimConfigGroup.get(config); eqasimConfig.setCostModel(TransportMode.car, IDFModeChoiceModule.CAR_COST_MODEL_NAME); + eqasimConfig.setCostModel(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_COST_MODEL_NAME); eqasimConfig.setCostModel(TransportMode.pt, IDFModeChoiceModule.PT_COST_MODEL_NAME); eqasimConfig.setEstimator(TransportMode.car, IDFModeChoiceModule.CAR_ESTIMATOR_NAME); + eqasimConfig.setEstimator(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_ESTIMATOR_NAME); eqasimConfig.setEstimator(TransportMode.bike, IDFModeChoiceModule.BIKE_ESTIMATOR_NAME); DiscreteModeChoiceConfigGroup dmcConfig = (DiscreteModeChoiceConfigGroup) config.getModules() From 4d15a0941015017f876bcb4cf8819afc97959e2c Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Sun, 21 Sep 2025 16:08:23 +0200 Subject: [PATCH 03/21] run adjustScenario from configurator on population routing --- .../org/eqasim/core/scenario/routing/RunPopulationRouting.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java b/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java index 71cf654d2..0980f9d47 100644 --- a/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java +++ b/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java @@ -40,6 +40,7 @@ static public void main(String[] args) throws ConfigurationException, Interrupte .orElse(Runtime.getRuntime().availableProcessors()); Scenario scenario = ScenarioUtils.loadScenario(config); + configurator.adjustScenario(scenario); if (scenario.getActivityFacilities() != null) { for (ActivityFacility facility : scenario.getActivityFacilities().getFacilities().values()) { From 76435847a65a548a945130158f8e40d7ca3dc4bf Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Wed, 24 Sep 2025 10:59:39 +0200 Subject: [PATCH 04/21] move motorcycle stuff to core --- .../core/scenario/config/GenerateConfig.java | 6 +-- .../scenario/preparation/RunPreparation.java | 14 +++++++ .../routing/RunPopulationRouting.java | 1 - .../eqasim/ile_de_france/IDFConfigurator.java | 42 ------------------- 4 files changed, 17 insertions(+), 46 deletions(-) diff --git a/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java b/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java index e37e57d94..f5277ffd4 100644 --- a/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java +++ b/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java @@ -32,10 +32,10 @@ public class GenerateConfig { protected final List ACTIVITY_TYPES = Arrays.asList("home", "work", "education", "shop", "leisure", "other", "freight_loading", "freight_unloading", "outside"); - protected final List MODES = Arrays.asList("walk", "bike", "pt", "car", "car_passenger", "truck", - "outside"); + protected final List MODES = Arrays.asList("walk", "bike", "pt", "car", "car_passenger", "motorcycle", + "truck", "outside"); - private final List NETWORK_MODES = Arrays.asList("car", "car_passenger", "truck"); + private final List NETWORK_MODES = Arrays.asList("car", "car_passenger", "truck", "motorcycle"); private final CommandLine cmd; private final String prefix; diff --git a/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java b/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java index 29bbfe8fd..2e435cba0 100644 --- a/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java +++ b/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java @@ -3,6 +3,8 @@ import org.eqasim.core.scenario.cutter.network.RoadNetwork; import org.eqasim.core.scenario.preparation.FacilityPlacement.OSMFacilityPlacementVoter; 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; import org.matsim.core.config.ConfigUtils; @@ -14,6 +16,9 @@ import org.matsim.facilities.FacilitiesWriter; import org.matsim.facilities.MatsimFacilitiesReader; +import java.util.HashSet; +import java.util.Set; + public class RunPreparation { static public void main(String[] args) throws ConfigurationException, InterruptedException { CommandLine cmd = new CommandLine.Builder(args) // @@ -38,6 +43,15 @@ static public void main(String[] args) throws ConfigurationException, Interrupte FacilityPlacement facilityPlacement = new FacilityPlacement(numberOfThreads, batchSize, roadNetwork, voter); facilityPlacement.run(scenario.getActivityFacilities()); + // add motorcycle mode + for (Link link : roadNetwork.getLinks().values()) { + Set modes = new HashSet<>(link.getAllowedModes()); + if (modes.contains("car")) { + modes.add("motorcycle"); + link.setAllowedModes(modes); + } + } + // Fix freight activities (TODO: should go to the pipeline) FreightAssignment freightAssignment = new FreightAssignment(scenario.getNetwork(), scenario.getActivityFacilities()); diff --git a/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java b/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java index 0980f9d47..71cf654d2 100644 --- a/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java +++ b/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java @@ -40,7 +40,6 @@ static public void main(String[] args) throws ConfigurationException, Interrupte .orElse(Runtime.getRuntime().availableProcessors()); Scenario scenario = ScenarioUtils.loadScenario(config); - configurator.adjustScenario(scenario); if (scenario.getActivityFacilities() != null) { for (ActivityFacility facility : scenario.getActivityFacilities().getFacilities().values()) { 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 60304cf32..aea095fad 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 @@ -56,48 +56,6 @@ public void adjustScenario(Scenario scenario) { EqasimConfigGroup eqasimConfig = EqasimConfigGroup.get(config); eqasimConfig.setEstimator(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_ESTIMATOR_NAME); eqasimConfig.setCostModel(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_COST_MODEL_NAME); - - // TODO : do we need to add motorcycles to this ? - // - // - } - { // Scoring - ScoringConfigGroup.ModeParams modeParameters = new ScoringConfigGroup.ModeParams("motorcycle"); - modeParameters.setConstant(0.0); - modeParameters.setMarginalUtilityOfDistance(0.0); - modeParameters.setMonetaryDistanceRate(0.0); - config.scoring().addModeParams(modeParameters); - } - { // Routing - List networkModes = new ArrayList<>(config.routing().getNetworkModes()); - networkModes.add("motorcycle"); - config.routing().setNetworkModes(networkModes); - } - { // Network - Network network = scenario.getNetwork(); - for (Link link : network.getLinks().values()) { - Set modes = new HashSet<>(link.getAllowedModes()); - if (modes.contains("car")) { - modes.add("motorcycle"); - link.setAllowedModes(modes); - } - } - } - { // Population ; force motorcycle starting mode -// Population population = scenario.getPopulation(); -// for (Person person : population.getPersons().values()) { -// String motorcycleAvailability = (String) person.getAttributes().getAttribute("motorcycleAvailability"); -// if ("some".equals(motorcycleAvailability) | "all".equals(motorcycleAvailability)) { -// Plan plan = person.getSelectedPlan(); -// for (PlanElement element : plan.getPlanElements()) { -// if (element instanceof Leg) { -// Leg leg = (Leg) element; -// leg.setMode(TransportMode.motorcycle); -// leg.setRoutingMode(TransportMode.motorcycle); -// } -// } -// } -// } } } } From 7cd125010a01b8cbd0a735661bb82893da5e95fe Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Wed, 24 Sep 2025 16:14:59 +0200 Subject: [PATCH 05/21] small change --- .../org/eqasim/core/scenario/preparation/RunPreparation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java b/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java index 2e435cba0..ad071ad52 100644 --- a/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java +++ b/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java @@ -44,7 +44,7 @@ static public void main(String[] args) throws ConfigurationException, Interrupte facilityPlacement.run(scenario.getActivityFacilities()); // add motorcycle mode - for (Link link : roadNetwork.getLinks().values()) { + for (Link link : scenario.getNetwork().getLinks().values()) { Set modes = new HashSet<>(link.getAllowedModes()); if (modes.contains("car")) { modes.add("motorcycle"); From 5ab541c515e1011b10251779bf8167650c514897 Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Thu, 2 Oct 2025 14:59:12 +0200 Subject: [PATCH 06/21] put everything but motorcycle activation outside --- .../components/config/EqasimConfigGroup.java | 2 + .../core/scenario/config/GenerateConfig.java | 6 ++- .../eqasim/ile_de_france/IDFConfigurator.java | 42 ------------------- .../mode_choice/IDFModeAvailability.java | 16 ++----- 4 files changed, 10 insertions(+), 56 deletions(-) diff --git a/core/src/main/java/org/eqasim/core/components/config/EqasimConfigGroup.java b/core/src/main/java/org/eqasim/core/components/config/EqasimConfigGroup.java index e61777734..1b628219b 100644 --- a/core/src/main/java/org/eqasim/core/components/config/EqasimConfigGroup.java +++ b/core/src/main/java/org/eqasim/core/components/config/EqasimConfigGroup.java @@ -44,6 +44,8 @@ public class EqasimConfigGroup extends ReflectiveConfigGroup { private boolean useScheduleBasedTransport = true; + private boolean useMotorcycles = false; + private boolean usePseudoRandomErrors = false; public EqasimConfigGroup() { diff --git a/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java b/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java index f5277ffd4..c92629812 100644 --- a/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java +++ b/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java @@ -158,7 +158,7 @@ protected void adaptConfiguration(Config config) { dmcConfig.setTripEstimator(EqasimModeChoiceModule.UTILITY_ESTIMATOR_NAME); dmcConfig.setTourEstimator(EstimatorModule.CUMULATIVE); - dmcConfig.setCachedModes(Arrays.asList("car", "bike", "pt", "walk", "car_passenger", "truck")); + dmcConfig.setCachedModes(Arrays.asList("car", "bike", "pt", "walk", "car_passenger", "truck", "motorcycle")); dmcConfig.setTourFinder(TourFinderModule.ACTIVITY_BASED); dmcConfig.getActivityTourFinderConfigGroup().setActivityTypes(Arrays.asList("home", "outside")); @@ -170,7 +170,7 @@ protected void adaptConfiguration(Config config) { EqasimModeChoiceModule.PASSENGER_CONSTRAINT_NAME, EqasimModeChoiceModule.OUTSIDE_CONSTRAINT_NAME)); dmcConfig.setHomeFinder(EqasimModeChoiceModule.HOME_FINDER); - dmcConfig.getVehicleTourConstraintConfig().setRestrictedModes(Arrays.asList("car", "bike")); + dmcConfig.getVehicleTourConstraintConfig().setRestrictedModes(Arrays.asList("car", "bike", "motorcycle")); dmcConfig.setTourFilters(Arrays.asList(EqasimModeChoiceModule.OUTSIDE_FILTER_NAME, EqasimModeChoiceModule.TOUR_LENGTH_FILTER_NAME)); @@ -178,6 +178,7 @@ protected void adaptConfiguration(Config config) { // Set up modes eqasimConfig.setEstimator(TransportMode.car, EqasimModeChoiceModule.CAR_ESTIMATOR_NAME); + eqasimConfig.setEstimator(TransportMode.motorcycle, EqasimModeChoiceModule.MOTORCYCLE_ESTIMATOR_NAME); eqasimConfig.setEstimator(TransportMode.pt, EqasimModeChoiceModule.PT_ESTIMATOR_NAME); eqasimConfig.setEstimator(TransportMode.bike, EqasimModeChoiceModule.BIKE_ESTIMATOR_NAME); eqasimConfig.setEstimator(TransportMode.walk, EqasimModeChoiceModule.WALK_ESTIMATOR_NAME); @@ -187,6 +188,7 @@ protected void adaptConfiguration(Config config) { } eqasimConfig.setCostModel(TransportMode.car, EqasimModeChoiceModule.ZERO_COST_MODEL_NAME); + eqasimConfig.setCostModel(TransportMode.motorcycle, EqasimModeChoiceModule.ZERO_COST_MODEL_NAME); eqasimConfig.setCostModel(TransportMode.pt, EqasimModeChoiceModule.ZERO_COST_MODEL_NAME); // To make sure trips arriving later than the next activity end time are taken into account when routing the next trip during mode choice 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 aea095fad..b9e4ae072 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 @@ -1,21 +1,9 @@ package org.eqasim.ile_de_france; -import org.eqasim.core.components.config.EqasimConfigGroup; import org.eqasim.core.simulation.EqasimConfigurator; import org.eqasim.ile_de_france.mode_choice.IDFModeChoiceModule; -import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; -import org.matsim.api.core.v01.TransportMode; -import org.matsim.api.core.v01.network.Link; -import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.population.*; import org.matsim.core.config.CommandLine; -import org.matsim.core.config.Config; -import org.matsim.core.config.groups.QSimConfigGroup; -import org.matsim.contribs.discrete_mode_choice.modules.config.DiscreteModeChoiceConfigGroup; -import org.matsim.core.config.groups.ScoringConfigGroup; - -import java.util.*; public class IDFConfigurator extends EqasimConfigurator { public IDFConfigurator(CommandLine cmd) { @@ -27,35 +15,5 @@ public IDFConfigurator(CommandLine cmd) { @Override public void adjustScenario(Scenario scenario) { super.adjustScenario(scenario); - - // add motorcycle stuff - Config config = scenario.getConfig(); - { // QSim - List qsimMainModes = new ArrayList<>(config.qsim().getMainModes()); - qsimMainModes.add("motorcycle"); - config.qsim().setMainModes(qsimMainModes); - - config.qsim().setLinkDynamics(QSimConfigGroup.LinkDynamics.SeepageQ); - - List qsimSeepModes = new ArrayList<>(config.qsim().getSeepModes()); - qsimSeepModes.add("motorcycle"); - config.qsim().setSeepModes(qsimSeepModes); - } - { // DMC - DiscreteModeChoiceConfigGroup dmcConfig = (DiscreteModeChoiceConfigGroup) config.getModules() - .get(DiscreteModeChoiceConfigGroup.GROUP_NAME); - Collection cachedModes = dmcConfig.getCachedModes(); - cachedModes.add("motorcycle"); - dmcConfig.setCachedModes(cachedModes); - - List vehicleConstraintModes = new ArrayList<>(dmcConfig.getVehicleTourConstraintConfig().getRestrictedModes()); - vehicleConstraintModes.add("motorcycle"); - dmcConfig.getVehicleTourConstraintConfig().setRestrictedModes(vehicleConstraintModes); - } - { // Eqasim - EqasimConfigGroup eqasimConfig = EqasimConfigGroup.get(config); - eqasimConfig.setEstimator(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_ESTIMATOR_NAME); - eqasimConfig.setCostModel(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_COST_MODEL_NAME); - } } } 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 a502a5404..529968909 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 @@ -35,20 +35,12 @@ public Collection getAvailableModes(Person person, List Date: Thu, 2 Oct 2025 16:37:58 +0200 Subject: [PATCH 07/21] add core/melun_test to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f12897823..e43780d14 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ dependency-reduced-pom.xml *.iml *.idea simulation_output +core/melun_test \ No newline at end of file From 42cf93d07c0526707201075bcb1e97ce5f7d6bee Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Sat, 20 Sep 2025 20:09:02 +0200 Subject: [PATCH 08/21] add motorcycle mode --- .../mode_choice/EqasimModeChoiceModule.java | 21 ++-- .../parameters/ModeParameters.java | 9 ++ .../MotorcycleUtilityEstimator.java | 54 ++++++++++ .../predictors/MotorcyclePredictor.java | 51 ++++++++++ .../variables/MotorcycleVariables.java | 16 +++ .../eqasim/ile_de_france/IDFConfigurator.java | 98 ++++++++++++++++++- .../mode_choice/IDFModeAvailability.java | 20 ++++ .../mode_choice/IDFModeChoiceModule.java | 6 ++ .../costs/IDFMotorcycleCostModel.java | 25 +++++ .../parameters/IDFCostParameters.java | 2 + .../parameters/IDFModeParameters.java | 16 +++ .../IDFMotorcycleUtilityEstimator.java | 53 ++++++++++ .../scenario/RunAdaptConfig.java | 2 + 13 files changed, 359 insertions(+), 14 deletions(-) create mode 100644 core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/estimators/MotorcycleUtilityEstimator.java create mode 100644 core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/MotorcyclePredictor.java create mode 100644 core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/variables/MotorcycleVariables.java create mode 100644 ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/costs/IDFMotorcycleCostModel.java create mode 100644 ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFMotorcycleUtilityEstimator.java diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/EqasimModeChoiceModule.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/EqasimModeChoiceModule.java index 8b1e04980..9c0d0f286 100644 --- a/core/src/main/java/org/eqasim/core/simulation/mode_choice/EqasimModeChoiceModule.java +++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/EqasimModeChoiceModule.java @@ -18,16 +18,8 @@ import org.eqasim.core.simulation.mode_choice.parameters.ModeParameters; import org.eqasim.core.simulation.mode_choice.utilities.EqasimUtilityEstimator; import org.eqasim.core.simulation.mode_choice.utilities.UtilityEstimator; -import org.eqasim.core.simulation.mode_choice.utilities.estimators.BikeUtilityEstimator; -import org.eqasim.core.simulation.mode_choice.utilities.estimators.CarUtilityEstimator; -import org.eqasim.core.simulation.mode_choice.utilities.estimators.PtUtilityEstimator; -import org.eqasim.core.simulation.mode_choice.utilities.estimators.WalkUtilityEstimator; -import org.eqasim.core.simulation.mode_choice.utilities.estimators.ZeroUtilityEstimator; -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.PersonPredictor; -import org.eqasim.core.simulation.mode_choice.utilities.predictors.PtPredictor; -import org.eqasim.core.simulation.mode_choice.utilities.predictors.WalkPredictor; +import org.eqasim.core.simulation.mode_choice.utilities.estimators.*; +import org.eqasim.core.simulation.mode_choice.utilities.predictors.*; import org.eqasim.core.simulation.modes.drt.mode_choice.constraints.DrtServiceAreaConstraint; import org.eqasim.core.simulation.modes.drt.mode_choice.constraints.DrtWalkConstraint; import org.eqasim.core.simulation.modes.drt.mode_choice.predictors.DefaultDrtPredictor; @@ -59,6 +51,7 @@ public class EqasimModeChoiceModule extends AbstractEqasimExtension { public static final String UTILITY_ESTIMATOR_NAME = "EqasimUtilityEstimator"; public static final String CAR_ESTIMATOR_NAME = "CarUtilityEstimator"; + public static final String MOTORCYCLE_ESTIMATOR_NAME = "MotorcycleUtilityEstimator"; public static final String PT_ESTIMATOR_NAME = "PtUtilityEstimator"; public static final String BIKE_ESTIMATOR_NAME = "BikeUtilityEstimator"; public static final String WALK_ESTIMATOR_NAME = "WalkUtilityEstimator"; @@ -83,6 +76,7 @@ protected void installEqasimExtension() { bindTripEstimator(UTILITY_ESTIMATOR_NAME).to(EqasimUtilityEstimator.class); bind(CarPredictor.class); + bind(MotorcyclePredictor.class); bind(PtPredictor.class); bind(BikePredictor.class); bind(WalkPredictor.class); @@ -91,6 +85,7 @@ protected void installEqasimExtension() { bindUtilityEstimator(ZERO_ESTIMATOR_NAME).to(ZeroUtilityEstimator.class); bindUtilityEstimator(CAR_ESTIMATOR_NAME).to(CarUtilityEstimator.class); + bindUtilityEstimator(MOTORCYCLE_ESTIMATOR_NAME).to(MotorcycleUtilityEstimator.class); bindUtilityEstimator(PT_ESTIMATOR_NAME).to(PtUtilityEstimator.class); bindUtilityEstimator(BIKE_ESTIMATOR_NAME).to(BikeUtilityEstimator.class); bindUtilityEstimator(WALK_ESTIMATOR_NAME).to(WalkUtilityEstimator.class); @@ -136,6 +131,12 @@ public CostModel provideCarCostModel(Map> factory, E return getCostModel(factory, config, "car"); } + @Provides + @Named("motorcycle") + public CostModel provideMotorcycleCostModel(Map> factory, EqasimConfigGroup config) { + return getCostModel(factory, config, "motorcycle"); + } + @Provides @Named("pt") public CostModel providePtCostModel(Map> factory, EqasimConfigGroup config) { diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/parameters/ModeParameters.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/parameters/ModeParameters.java index 08232d9a8..a412b573c 100644 --- a/core/src/main/java/org/eqasim/core/simulation/mode_choice/parameters/ModeParameters.java +++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/parameters/ModeParameters.java @@ -11,6 +11,14 @@ public class CarParameters { public double constantParkingSearchPenalty_min = 0.0; } + public class MotorcycleParameters { + public double alpha_u = 0.0; + public double betaTravelTime_u_min = 0.0; + + public double additionalAccessEgressWalkTime_min = 0.0; + public double constantParkingSearchPenalty_min = 0.0; + } + public class PtParameters { public double alpha_u = 0.0; public double betaLineSwitch_u = 0.0; @@ -44,6 +52,7 @@ public class DrtParameters { public double betaCost_u_MU = 0.0; public final CarParameters car = new CarParameters(); + public final MotorcycleParameters motorcycle = new MotorcycleParameters(); public final PtParameters pt = new PtParameters(); public final BikeParameters bike = new BikeParameters(); public final WalkParameters walk = new WalkParameters(); diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/estimators/MotorcycleUtilityEstimator.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/estimators/MotorcycleUtilityEstimator.java new file mode 100644 index 000000000..57b95118d --- /dev/null +++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/estimators/MotorcycleUtilityEstimator.java @@ -0,0 +1,54 @@ +package org.eqasim.core.simulation.mode_choice.utilities.estimators; + +import com.google.inject.Inject; +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.predictors.MotorcyclePredictor; +import org.eqasim.core.simulation.mode_choice.utilities.variables.MotorcycleVariables; +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 java.util.List; + +public class MotorcycleUtilityEstimator implements UtilityEstimator { + private final ModeParameters parameters; + private final MotorcyclePredictor predictor; + + @Inject + public MotorcycleUtilityEstimator(ModeParameters parameters, MotorcyclePredictor predictor) { + this.parameters = parameters; + this.predictor = predictor; + } + + protected double estimateConstantUtility() { + return parameters.motorcycle.alpha_u; + } + + protected double estimateTravelTimeUtility(MotorcycleVariables variables) { + return parameters.motorcycle.betaTravelTime_u_min * variables.travelTime_min; + } + + protected double estimateAccessEgressTimeUtility(MotorcycleVariables variables) { + return parameters.walk.betaTravelTime_u_min * variables.accessEgressTime_min; + } + + protected double estimateMonetaryCostUtility(MotorcycleVariables variables) { + return parameters.betaCost_u_MU * EstimatorUtils.interaction(variables.euclideanDistance_km, + parameters.referenceEuclideanDistance_km, parameters.lambdaCostEuclideanDistance) * variables.cost_MU; + } + + @Override + public double estimateUtility(Person person, DiscreteModeChoiceTrip trip, List elements) { + MotorcycleVariables variables = predictor.predictVariables(person, trip, elements); + + double utility = 0.0; + + utility += estimateConstantUtility(); + utility += estimateTravelTimeUtility(variables); + utility += estimateAccessEgressTimeUtility(variables); + utility += estimateMonetaryCostUtility(variables); + + return utility; + } +} diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/MotorcyclePredictor.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/MotorcyclePredictor.java new file mode 100644 index 000000000..a61ee8ea3 --- /dev/null +++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/predictors/MotorcyclePredictor.java @@ -0,0 +1,51 @@ +package org.eqasim.core.simulation.mode_choice.utilities.predictors; + +import com.google.common.base.Verify; +import com.google.inject.Inject; +import com.google.inject.name.Named; +import org.eqasim.core.simulation.mode_choice.cost.CostModel; +import org.eqasim.core.simulation.mode_choice.parameters.ModeParameters; +import org.eqasim.core.simulation.mode_choice.utilities.variables.MotorcycleVariables; +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 java.util.List; + +public class MotorcyclePredictor extends CachedVariablePredictor { + private final CostModel costModel; + private final ModeParameters parameters; + + @Inject + public MotorcyclePredictor(ModeParameters parameters, @Named("motorcycle") CostModel costModel) { + this.costModel = costModel; + this.parameters = parameters; + } + + @Override + public MotorcycleVariables predict(Person person, DiscreteModeChoiceTrip trip, List elements) { + double motorcycleTravelTime_min = parameters.motorcycle.constantParkingSearchPenalty_min; + double accessEgressTime_min = parameters.motorcycle.additionalAccessEgressWalkTime_min; + + boolean foundCar = false; + + for (Leg leg : TripStructureUtils.getLegs(elements)) { + if (leg.getMode().equals(TransportMode.motorcycle)) { + Verify.verify(!foundCar); + motorcycleTravelTime_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 car chain: " + leg.getMode()); + } + } + + double cost_MU = costModel.calculateCost_MU(person, trip, elements); + double euclideanDistance_km = PredictorUtils.calculateEuclideanDistance_km(trip); + + return new MotorcycleVariables(motorcycleTravelTime_min, cost_MU, euclideanDistance_km, accessEgressTime_min); + } +} diff --git a/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/variables/MotorcycleVariables.java b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/variables/MotorcycleVariables.java new file mode 100644 index 000000000..0f08beaf3 --- /dev/null +++ b/core/src/main/java/org/eqasim/core/simulation/mode_choice/utilities/variables/MotorcycleVariables.java @@ -0,0 +1,16 @@ +package org.eqasim.core.simulation.mode_choice.utilities.variables; + +public class MotorcycleVariables implements BaseVariables { + final public double travelTime_min; + final public double cost_MU; + final public double euclideanDistance_km; + final public double accessEgressTime_min; + + public MotorcycleVariables(double travelTime_min, double cost_MU, double euclideanDistance_km, + double accessEgressTime_min) { + this.travelTime_min = travelTime_min; + this.cost_MU = cost_MU; + this.euclideanDistance_km = euclideanDistance_km; + this.accessEgressTime_min = accessEgressTime_min; + } +} 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..60304cf32 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 @@ -1,13 +1,103 @@ package org.eqasim.ile_de_france; +import org.eqasim.core.components.config.EqasimConfigGroup; import org.eqasim.core.simulation.EqasimConfigurator; import org.eqasim.ile_de_france.mode_choice.IDFModeChoiceModule; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.*; import org.matsim.core.config.CommandLine; +import org.matsim.core.config.Config; +import org.matsim.core.config.groups.QSimConfigGroup; +import org.matsim.contribs.discrete_mode_choice.modules.config.DiscreteModeChoiceConfigGroup; +import org.matsim.core.config.groups.ScoringConfigGroup; + +import java.util.*; public class IDFConfigurator extends EqasimConfigurator { - public IDFConfigurator(CommandLine cmd) { - super(cmd); + public IDFConfigurator(CommandLine cmd) { + super(cmd); + + registerModule(new IDFModeChoiceModule(cmd)); + } + + @Override + public void adjustScenario(Scenario scenario) { + super.adjustScenario(scenario); + + // add motorcycle stuff + Config config = scenario.getConfig(); + { // QSim + List qsimMainModes = new ArrayList<>(config.qsim().getMainModes()); + qsimMainModes.add("motorcycle"); + config.qsim().setMainModes(qsimMainModes); + + config.qsim().setLinkDynamics(QSimConfigGroup.LinkDynamics.SeepageQ); + + List qsimSeepModes = new ArrayList<>(config.qsim().getSeepModes()); + qsimSeepModes.add("motorcycle"); + config.qsim().setSeepModes(qsimSeepModes); + } + { // DMC + DiscreteModeChoiceConfigGroup dmcConfig = (DiscreteModeChoiceConfigGroup) config.getModules() + .get(DiscreteModeChoiceConfigGroup.GROUP_NAME); + Collection cachedModes = dmcConfig.getCachedModes(); + cachedModes.add("motorcycle"); + dmcConfig.setCachedModes(cachedModes); + + List vehicleConstraintModes = new ArrayList<>(dmcConfig.getVehicleTourConstraintConfig().getRestrictedModes()); + vehicleConstraintModes.add("motorcycle"); + dmcConfig.getVehicleTourConstraintConfig().setRestrictedModes(vehicleConstraintModes); + } + { // Eqasim + EqasimConfigGroup eqasimConfig = EqasimConfigGroup.get(config); + eqasimConfig.setEstimator(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_ESTIMATOR_NAME); + eqasimConfig.setCostModel(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_COST_MODEL_NAME); - registerModule(new IDFModeChoiceModule(cmd)); - } + // TODO : do we need to add motorcycles to this ? + // + // + } + { // Scoring + ScoringConfigGroup.ModeParams modeParameters = new ScoringConfigGroup.ModeParams("motorcycle"); + modeParameters.setConstant(0.0); + modeParameters.setMarginalUtilityOfDistance(0.0); + modeParameters.setMonetaryDistanceRate(0.0); + config.scoring().addModeParams(modeParameters); + } + { // Routing + List networkModes = new ArrayList<>(config.routing().getNetworkModes()); + networkModes.add("motorcycle"); + config.routing().setNetworkModes(networkModes); + } + { // Network + Network network = scenario.getNetwork(); + for (Link link : network.getLinks().values()) { + Set modes = new HashSet<>(link.getAllowedModes()); + if (modes.contains("car")) { + modes.add("motorcycle"); + link.setAllowedModes(modes); + } + } + } + { // Population ; force motorcycle starting mode +// Population population = scenario.getPopulation(); +// for (Person person : population.getPersons().values()) { +// String motorcycleAvailability = (String) person.getAttributes().getAttribute("motorcycleAvailability"); +// if ("some".equals(motorcycleAvailability) | "all".equals(motorcycleAvailability)) { +// Plan plan = person.getSelectedPlan(); +// for (PlanElement element : plan.getPlanElements()) { +// if (element instanceof Leg) { +// Leg leg = (Leg) element; +// leg.setMode(TransportMode.motorcycle); +// leg.setRoutingMode(TransportMode.motorcycle); +// } +// } +// } +// } + } + } } 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 b193c2bb6..a502a5404 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 @@ -34,6 +34,26 @@ public Collection getAvailableModes(Person person, List elements) { + return costParameters.motorcycleCost_EUR_km * getInVehicleDistance_km(elements); + } +} 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..deaa70604 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 motorcycleCost_EUR_km = 0.0; public static IDFCostParameters buildDefault() { IDFCostParameters parameters = new IDFCostParameters(); parameters.carCost_EUR_km = 0.15; + parameters.motorcycleCost_EUR_km = 0.15; 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..356820e87 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 @@ -8,11 +8,17 @@ public class IDFCarParameters { public double betaCrossingUrbanArea; } + public class IDFMotorcycleParameters { + public double betaInsideUrbanArea; + public double betaCrossingUrbanArea; + } + public class IDFBikeParameters { public double betaInsideUrbanArea; } public final IDFCarParameters idfCar = new IDFCarParameters(); + public final IDFMotorcycleParameters idfMotorcycle = new IDFMotorcycleParameters(); public final IDFBikeParameters idfBike = new IDFBikeParameters(); public static IDFModeParameters buildDefault() { @@ -33,6 +39,16 @@ public static IDFModeParameters buildDefault() { parameters.idfCar.betaInsideUrbanArea = -0.5; parameters.idfCar.betaCrossingUrbanArea = -1.0; + // Motorcycle ; copy of Car for now + parameters.motorcycle.alpha_u = 1.35; + parameters.motorcycle.betaTravelTime_u_min = -0.06; + + parameters.motorcycle.additionalAccessEgressWalkTime_min = 4.0; + parameters.motorcycle.constantParkingSearchPenalty_min = 4.0; + + parameters.idfMotorcycle.betaInsideUrbanArea = -0.5; + parameters.idfMotorcycle.betaCrossingUrbanArea = -1.0; + // PT parameters.pt.alpha_u = 0.0; parameters.pt.betaLineSwitch_u = -0.17; diff --git a/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFMotorcycleUtilityEstimator.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFMotorcycleUtilityEstimator.java new file mode 100644 index 000000000..367eaaa36 --- /dev/null +++ b/ile_de_france/src/main/java/org/eqasim/ile_de_france/mode_choice/utilities/estimators/IDFMotorcycleUtilityEstimator.java @@ -0,0 +1,53 @@ +package org.eqasim.ile_de_france.mode_choice.utilities.estimators; + +import com.google.inject.Inject; +import org.eqasim.core.simulation.mode_choice.utilities.estimators.MotorcycleUtilityEstimator; +import org.eqasim.core.simulation.mode_choice.utilities.predictors.MotorcyclePredictor; +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 java.util.List; + +public class IDFMotorcycleUtilityEstimator extends MotorcycleUtilityEstimator { + private final IDFModeParameters parameters; + private final IDFSpatialPredictor spatialPredictor; + + @Inject + public IDFMotorcycleUtilityEstimator(IDFModeParameters parameters, IDFSpatialPredictor spatialPredictor, + MotorcyclePredictor motorcyclePredictor) { + super(parameters, motorcyclePredictor); + + this.parameters = parameters; + this.spatialPredictor = spatialPredictor; + } + + protected double estimateUrbanUtility(IDFSpatialVariables variables) { + double utility = 0.0; + + if (variables.hasUrbanOrigin && variables.hasUrbanDestination) { + utility += parameters.idfMotorcycle.betaInsideUrbanArea; + } + + if (variables.hasUrbanOrigin || variables.hasUrbanDestination) { + utility += parameters.idfMotorcycle.betaCrossingUrbanArea; + } + + 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/scenario/RunAdaptConfig.java b/ile_de_france/src/main/java/org/eqasim/ile_de_france/scenario/RunAdaptConfig.java index 4c2bee67e..a956fb827 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 @@ -24,9 +24,11 @@ static public void adaptConfiguration(Config config, String prefix) { EqasimConfigGroup eqasimConfig = EqasimConfigGroup.get(config); eqasimConfig.setCostModel(TransportMode.car, IDFModeChoiceModule.CAR_COST_MODEL_NAME); + eqasimConfig.setCostModel(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_COST_MODEL_NAME); eqasimConfig.setCostModel(TransportMode.pt, IDFModeChoiceModule.PT_COST_MODEL_NAME); eqasimConfig.setEstimator(TransportMode.car, IDFModeChoiceModule.CAR_ESTIMATOR_NAME); + eqasimConfig.setEstimator(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_ESTIMATOR_NAME); eqasimConfig.setEstimator(TransportMode.bike, IDFModeChoiceModule.BIKE_ESTIMATOR_NAME); DiscreteModeChoiceConfigGroup dmcConfig = (DiscreteModeChoiceConfigGroup) config.getModules() From 79a08ebbc439d46ba9c7b31f94df2f0fd5f1af91 Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Sun, 21 Sep 2025 16:08:23 +0200 Subject: [PATCH 09/21] run adjustScenario from configurator on population routing --- .../org/eqasim/core/scenario/routing/RunPopulationRouting.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java b/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java index 71cf654d2..0980f9d47 100644 --- a/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java +++ b/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java @@ -40,6 +40,7 @@ static public void main(String[] args) throws ConfigurationException, Interrupte .orElse(Runtime.getRuntime().availableProcessors()); Scenario scenario = ScenarioUtils.loadScenario(config); + configurator.adjustScenario(scenario); if (scenario.getActivityFacilities() != null) { for (ActivityFacility facility : scenario.getActivityFacilities().getFacilities().values()) { From 4b2b5bbfdc64737894ebe1f8936d8a780b364364 Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Wed, 24 Sep 2025 10:59:39 +0200 Subject: [PATCH 10/21] move motorcycle stuff to core --- .../core/scenario/config/GenerateConfig.java | 6 +-- .../scenario/preparation/RunPreparation.java | 14 +++++++ .../routing/RunPopulationRouting.java | 1 - .../eqasim/ile_de_france/IDFConfigurator.java | 42 ------------------- 4 files changed, 17 insertions(+), 46 deletions(-) diff --git a/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java b/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java index e37e57d94..f5277ffd4 100644 --- a/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java +++ b/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java @@ -32,10 +32,10 @@ public class GenerateConfig { protected final List ACTIVITY_TYPES = Arrays.asList("home", "work", "education", "shop", "leisure", "other", "freight_loading", "freight_unloading", "outside"); - protected final List MODES = Arrays.asList("walk", "bike", "pt", "car", "car_passenger", "truck", - "outside"); + protected final List MODES = Arrays.asList("walk", "bike", "pt", "car", "car_passenger", "motorcycle", + "truck", "outside"); - private final List NETWORK_MODES = Arrays.asList("car", "car_passenger", "truck"); + private final List NETWORK_MODES = Arrays.asList("car", "car_passenger", "truck", "motorcycle"); private final CommandLine cmd; private final String prefix; diff --git a/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java b/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java index 29bbfe8fd..2e435cba0 100644 --- a/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java +++ b/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java @@ -3,6 +3,8 @@ import org.eqasim.core.scenario.cutter.network.RoadNetwork; import org.eqasim.core.scenario.preparation.FacilityPlacement.OSMFacilityPlacementVoter; 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; import org.matsim.core.config.ConfigUtils; @@ -14,6 +16,9 @@ import org.matsim.facilities.FacilitiesWriter; import org.matsim.facilities.MatsimFacilitiesReader; +import java.util.HashSet; +import java.util.Set; + public class RunPreparation { static public void main(String[] args) throws ConfigurationException, InterruptedException { CommandLine cmd = new CommandLine.Builder(args) // @@ -38,6 +43,15 @@ static public void main(String[] args) throws ConfigurationException, Interrupte FacilityPlacement facilityPlacement = new FacilityPlacement(numberOfThreads, batchSize, roadNetwork, voter); facilityPlacement.run(scenario.getActivityFacilities()); + // add motorcycle mode + for (Link link : roadNetwork.getLinks().values()) { + Set modes = new HashSet<>(link.getAllowedModes()); + if (modes.contains("car")) { + modes.add("motorcycle"); + link.setAllowedModes(modes); + } + } + // Fix freight activities (TODO: should go to the pipeline) FreightAssignment freightAssignment = new FreightAssignment(scenario.getNetwork(), scenario.getActivityFacilities()); diff --git a/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java b/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java index 0980f9d47..71cf654d2 100644 --- a/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java +++ b/core/src/main/java/org/eqasim/core/scenario/routing/RunPopulationRouting.java @@ -40,7 +40,6 @@ static public void main(String[] args) throws ConfigurationException, Interrupte .orElse(Runtime.getRuntime().availableProcessors()); Scenario scenario = ScenarioUtils.loadScenario(config); - configurator.adjustScenario(scenario); if (scenario.getActivityFacilities() != null) { for (ActivityFacility facility : scenario.getActivityFacilities().getFacilities().values()) { 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 60304cf32..aea095fad 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 @@ -56,48 +56,6 @@ public void adjustScenario(Scenario scenario) { EqasimConfigGroup eqasimConfig = EqasimConfigGroup.get(config); eqasimConfig.setEstimator(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_ESTIMATOR_NAME); eqasimConfig.setCostModel(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_COST_MODEL_NAME); - - // TODO : do we need to add motorcycles to this ? - // - // - } - { // Scoring - ScoringConfigGroup.ModeParams modeParameters = new ScoringConfigGroup.ModeParams("motorcycle"); - modeParameters.setConstant(0.0); - modeParameters.setMarginalUtilityOfDistance(0.0); - modeParameters.setMonetaryDistanceRate(0.0); - config.scoring().addModeParams(modeParameters); - } - { // Routing - List networkModes = new ArrayList<>(config.routing().getNetworkModes()); - networkModes.add("motorcycle"); - config.routing().setNetworkModes(networkModes); - } - { // Network - Network network = scenario.getNetwork(); - for (Link link : network.getLinks().values()) { - Set modes = new HashSet<>(link.getAllowedModes()); - if (modes.contains("car")) { - modes.add("motorcycle"); - link.setAllowedModes(modes); - } - } - } - { // Population ; force motorcycle starting mode -// Population population = scenario.getPopulation(); -// for (Person person : population.getPersons().values()) { -// String motorcycleAvailability = (String) person.getAttributes().getAttribute("motorcycleAvailability"); -// if ("some".equals(motorcycleAvailability) | "all".equals(motorcycleAvailability)) { -// Plan plan = person.getSelectedPlan(); -// for (PlanElement element : plan.getPlanElements()) { -// if (element instanceof Leg) { -// Leg leg = (Leg) element; -// leg.setMode(TransportMode.motorcycle); -// leg.setRoutingMode(TransportMode.motorcycle); -// } -// } -// } -// } } } } From 90fa63fe80b61d09d2050e38bac74cb1438d19d3 Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Wed, 24 Sep 2025 16:14:59 +0200 Subject: [PATCH 11/21] small change --- .../org/eqasim/core/scenario/preparation/RunPreparation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java b/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java index 2e435cba0..ad071ad52 100644 --- a/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java +++ b/core/src/main/java/org/eqasim/core/scenario/preparation/RunPreparation.java @@ -44,7 +44,7 @@ static public void main(String[] args) throws ConfigurationException, Interrupte facilityPlacement.run(scenario.getActivityFacilities()); // add motorcycle mode - for (Link link : roadNetwork.getLinks().values()) { + for (Link link : scenario.getNetwork().getLinks().values()) { Set modes = new HashSet<>(link.getAllowedModes()); if (modes.contains("car")) { modes.add("motorcycle"); From da3d0a11179d0d10359107c3f8b355e5f4770431 Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Thu, 2 Oct 2025 14:59:12 +0200 Subject: [PATCH 12/21] put everything but motorcycle activation outside --- .../components/config/EqasimConfigGroup.java | 2 + .../core/scenario/config/GenerateConfig.java | 6 ++- .../eqasim/ile_de_france/IDFConfigurator.java | 42 ------------------- .../mode_choice/IDFModeAvailability.java | 16 ++----- 4 files changed, 10 insertions(+), 56 deletions(-) diff --git a/core/src/main/java/org/eqasim/core/components/config/EqasimConfigGroup.java b/core/src/main/java/org/eqasim/core/components/config/EqasimConfigGroup.java index e61777734..1b628219b 100644 --- a/core/src/main/java/org/eqasim/core/components/config/EqasimConfigGroup.java +++ b/core/src/main/java/org/eqasim/core/components/config/EqasimConfigGroup.java @@ -44,6 +44,8 @@ public class EqasimConfigGroup extends ReflectiveConfigGroup { private boolean useScheduleBasedTransport = true; + private boolean useMotorcycles = false; + private boolean usePseudoRandomErrors = false; public EqasimConfigGroup() { diff --git a/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java b/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java index f5277ffd4..c92629812 100644 --- a/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java +++ b/core/src/main/java/org/eqasim/core/scenario/config/GenerateConfig.java @@ -158,7 +158,7 @@ protected void adaptConfiguration(Config config) { dmcConfig.setTripEstimator(EqasimModeChoiceModule.UTILITY_ESTIMATOR_NAME); dmcConfig.setTourEstimator(EstimatorModule.CUMULATIVE); - dmcConfig.setCachedModes(Arrays.asList("car", "bike", "pt", "walk", "car_passenger", "truck")); + dmcConfig.setCachedModes(Arrays.asList("car", "bike", "pt", "walk", "car_passenger", "truck", "motorcycle")); dmcConfig.setTourFinder(TourFinderModule.ACTIVITY_BASED); dmcConfig.getActivityTourFinderConfigGroup().setActivityTypes(Arrays.asList("home", "outside")); @@ -170,7 +170,7 @@ protected void adaptConfiguration(Config config) { EqasimModeChoiceModule.PASSENGER_CONSTRAINT_NAME, EqasimModeChoiceModule.OUTSIDE_CONSTRAINT_NAME)); dmcConfig.setHomeFinder(EqasimModeChoiceModule.HOME_FINDER); - dmcConfig.getVehicleTourConstraintConfig().setRestrictedModes(Arrays.asList("car", "bike")); + dmcConfig.getVehicleTourConstraintConfig().setRestrictedModes(Arrays.asList("car", "bike", "motorcycle")); dmcConfig.setTourFilters(Arrays.asList(EqasimModeChoiceModule.OUTSIDE_FILTER_NAME, EqasimModeChoiceModule.TOUR_LENGTH_FILTER_NAME)); @@ -178,6 +178,7 @@ protected void adaptConfiguration(Config config) { // Set up modes eqasimConfig.setEstimator(TransportMode.car, EqasimModeChoiceModule.CAR_ESTIMATOR_NAME); + eqasimConfig.setEstimator(TransportMode.motorcycle, EqasimModeChoiceModule.MOTORCYCLE_ESTIMATOR_NAME); eqasimConfig.setEstimator(TransportMode.pt, EqasimModeChoiceModule.PT_ESTIMATOR_NAME); eqasimConfig.setEstimator(TransportMode.bike, EqasimModeChoiceModule.BIKE_ESTIMATOR_NAME); eqasimConfig.setEstimator(TransportMode.walk, EqasimModeChoiceModule.WALK_ESTIMATOR_NAME); @@ -187,6 +188,7 @@ protected void adaptConfiguration(Config config) { } eqasimConfig.setCostModel(TransportMode.car, EqasimModeChoiceModule.ZERO_COST_MODEL_NAME); + eqasimConfig.setCostModel(TransportMode.motorcycle, EqasimModeChoiceModule.ZERO_COST_MODEL_NAME); eqasimConfig.setCostModel(TransportMode.pt, EqasimModeChoiceModule.ZERO_COST_MODEL_NAME); // To make sure trips arriving later than the next activity end time are taken into account when routing the next trip during mode choice 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 aea095fad..b9e4ae072 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 @@ -1,21 +1,9 @@ package org.eqasim.ile_de_france; -import org.eqasim.core.components.config.EqasimConfigGroup; import org.eqasim.core.simulation.EqasimConfigurator; import org.eqasim.ile_de_france.mode_choice.IDFModeChoiceModule; -import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.Scenario; -import org.matsim.api.core.v01.TransportMode; -import org.matsim.api.core.v01.network.Link; -import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.population.*; import org.matsim.core.config.CommandLine; -import org.matsim.core.config.Config; -import org.matsim.core.config.groups.QSimConfigGroup; -import org.matsim.contribs.discrete_mode_choice.modules.config.DiscreteModeChoiceConfigGroup; -import org.matsim.core.config.groups.ScoringConfigGroup; - -import java.util.*; public class IDFConfigurator extends EqasimConfigurator { public IDFConfigurator(CommandLine cmd) { @@ -27,35 +15,5 @@ public IDFConfigurator(CommandLine cmd) { @Override public void adjustScenario(Scenario scenario) { super.adjustScenario(scenario); - - // add motorcycle stuff - Config config = scenario.getConfig(); - { // QSim - List qsimMainModes = new ArrayList<>(config.qsim().getMainModes()); - qsimMainModes.add("motorcycle"); - config.qsim().setMainModes(qsimMainModes); - - config.qsim().setLinkDynamics(QSimConfigGroup.LinkDynamics.SeepageQ); - - List qsimSeepModes = new ArrayList<>(config.qsim().getSeepModes()); - qsimSeepModes.add("motorcycle"); - config.qsim().setSeepModes(qsimSeepModes); - } - { // DMC - DiscreteModeChoiceConfigGroup dmcConfig = (DiscreteModeChoiceConfigGroup) config.getModules() - .get(DiscreteModeChoiceConfigGroup.GROUP_NAME); - Collection cachedModes = dmcConfig.getCachedModes(); - cachedModes.add("motorcycle"); - dmcConfig.setCachedModes(cachedModes); - - List vehicleConstraintModes = new ArrayList<>(dmcConfig.getVehicleTourConstraintConfig().getRestrictedModes()); - vehicleConstraintModes.add("motorcycle"); - dmcConfig.getVehicleTourConstraintConfig().setRestrictedModes(vehicleConstraintModes); - } - { // Eqasim - EqasimConfigGroup eqasimConfig = EqasimConfigGroup.get(config); - eqasimConfig.setEstimator(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_ESTIMATOR_NAME); - eqasimConfig.setCostModel(TransportMode.motorcycle, IDFModeChoiceModule.MOTORCYCLE_COST_MODEL_NAME); - } } } 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 a502a5404..529968909 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 @@ -35,20 +35,12 @@ public Collection getAvailableModes(Person person, List Date: Thu, 2 Oct 2025 16:37:58 +0200 Subject: [PATCH 13/21] add core/melun_test to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f12897823..e43780d14 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ dependency-reduced-pom.xml *.iml *.idea simulation_output +core/melun_test \ No newline at end of file From 5070dc7fcf33abcffcae2020721758d9fc7ea2eb Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Mon, 6 Oct 2025 14:39:03 +0200 Subject: [PATCH 14/21] cleanup --- .../org/eqasim/core/components/config/EqasimConfigGroup.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/main/java/org/eqasim/core/components/config/EqasimConfigGroup.java b/core/src/main/java/org/eqasim/core/components/config/EqasimConfigGroup.java index 4996f09d4..9ca90df98 100644 --- a/core/src/main/java/org/eqasim/core/components/config/EqasimConfigGroup.java +++ b/core/src/main/java/org/eqasim/core/components/config/EqasimConfigGroup.java @@ -50,8 +50,6 @@ public class EqasimConfigGroup extends ReflectiveConfigGroup { private boolean useScheduleBasedTransport = true; - private boolean useMotorcycles = false; - private boolean usePseudoRandomErrors = false; private final Set additionalAvailableModes = new HashSet<>(); From c97023f2fdf1699c5ac580d7d022654aab3bd09d Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Wed, 8 Oct 2025 10:53:17 +0200 Subject: [PATCH 15/21] add TestMotorcycles --- core/src/main/resources/melun/config.xml | 23 ++- .../test/java/org/eqasim/TestMotorcycles.java | 187 ++++++++++++++++++ 2 files changed, 207 insertions(+), 3 deletions(-) create mode 100644 core/src/test/java/org/eqasim/TestMotorcycles.java diff --git a/core/src/main/resources/melun/config.xml b/core/src/main/resources/melun/config.xml index c2668c938..26b932de3 100644 --- a/core/src/main/resources/melun/config.xml +++ b/core/src/main/resources/melun/config.xml @@ -4,7 +4,7 @@ - + @@ -59,7 +59,7 @@ - + @@ -196,6 +196,10 @@ + + + + @@ -208,6 +212,10 @@ + + + + @@ -536,6 +544,15 @@ + + + + + + + + + @@ -633,7 +650,7 @@ - + diff --git a/core/src/test/java/org/eqasim/TestMotorcycles.java b/core/src/test/java/org/eqasim/TestMotorcycles.java new file mode 100644 index 000000000..e9f91dac3 --- /dev/null +++ b/core/src/test/java/org/eqasim/TestMotorcycles.java @@ -0,0 +1,187 @@ +package org.eqasim; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.io.FileUtils; +import org.eqasim.core.simulation.EqasimConfigurator; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Scenario; +import org.matsim.api.core.v01.TransportMode; +import org.matsim.api.core.v01.events.handler.PersonDepartureEventHandler; +import org.matsim.api.core.v01.network.Link; +import org.matsim.api.core.v01.network.Network; +import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Person; +import org.matsim.api.core.v01.population.Plan; +import org.matsim.api.core.v01.population.PlanElement; +import org.matsim.api.core.v01.population.Population; +import org.matsim.api.core.v01.population.PopulationWriter; +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.groups.ControllerConfigGroup; +import org.matsim.core.config.groups.QSimConfigGroup.LinkDynamics; +import org.matsim.core.controler.Controler; +import org.matsim.core.events.EventsUtils; +import org.matsim.core.events.MatsimEventsReader; +import org.matsim.core.network.NetworkUtils; +import org.matsim.core.scenario.ScenarioUtils; +import org.matsim.vehicles.MatsimVehicleReader; +import org.matsim.vehicles.MatsimVehicleWriter; +import org.matsim.vehicles.PersonVehicles; +import org.matsim.vehicles.Vehicle; +import org.matsim.vehicles.VehicleType; +import org.matsim.vehicles.VehicleUtils; +import org.matsim.vehicles.Vehicles; + +public class TestMotorcycles { + + @Before + public void setUp() throws IOException { + URL fixtureUrl = getClass().getResource("/melun"); + FileUtils.copyDirectory(new File(fixtureUrl.getPath()), new File("melun_test/input")); + } + + @After + public void tearDown() throws IOException { + FileUtils.deleteDirectory(new File("melun_test")); + } + + private void runAddMotorcycles() { + Vehicles vehicles = VehicleUtils.createVehiclesContainer(); + new MatsimVehicleReader(vehicles).readFile("melun_test/input/vehicles.xml.gz"); + + VehicleType motorcycleType = VehicleUtils.createVehicleType(Id.create("motorcycleType", VehicleType.class)); + motorcycleType.setNetworkMode(TransportMode.motorcycle); + vehicles.addVehicleType(motorcycleType); + + for (Vehicle vehicle: vehicles.getVehicles().values()) { + Id id = vehicle.getId(); + if (id.toString().endsWith("car")) { + Id new_id = Id.create(id.toString().replace("car", "motorcycle"), Vehicle.class); + Vehicle new_vehicle = VehicleUtils.createVehicle(new_id, motorcycleType); + vehicles.addVehicle(new_vehicle); + } + } + + MatsimVehicleWriter writer = new MatsimVehicleWriter(vehicles); + writer.writeFile("melun_test/input/vehicles.xml.gz"); + } + + private void runModifyConfig() { + Config config = ConfigUtils.loadConfig("melun_test/input/config.xml"); + config.controller().setOutputDirectory("melun_test/output"); + + Collection mainModes = new HashSet<>(config.qsim().getMainModes()); + mainModes.add(TransportMode.motorcycle); + config.qsim().setMainModes(mainModes); + + config.qsim().setLinkDynamics(LinkDynamics.SeepageQ); + + Collection seepModes = new HashSet<>(config.qsim().getSeepModes()); + seepModes.add(TransportMode.motorcycle); + config.qsim().setSeepModes(seepModes); + + ConfigUtils.writeConfig(config, "melun_test/input/config.xml"); + } + + private void runModifyNetwork() { + Config config = ConfigUtils.loadConfig("melun_test/input/config.xml"); + Scenario scenario = ScenarioUtils.loadScenario(config); + Network network = scenario.getNetwork(); + for (Link link : network.getLinks().values()) { + Set modes = new HashSet<>(link.getAllowedModes()); + if (modes.contains("car")) { + modes.add("motorcycle"); + link.setAllowedModes(modes); + } + } + NetworkUtils.writeNetwork(network, "melun_test/input/network.xml.gz"); + } + + private void runModifyPlans() { + Config config = ConfigUtils.loadConfig("melun_test/input/config.xml"); + Scenario scenario = ScenarioUtils.loadScenario(config); + Population population = scenario.getPopulation(); + + for (Person person : population.getPersons().values()) { + Id personId = person.getId(); + PersonVehicles person_vehicles = (PersonVehicles) person.getAttributes().getAttribute("vehicles"); + person_vehicles.addModeVehicleIfAbsent(TransportMode.motorcycle, Id.create(personId.toString() + ":motorcycle", Vehicle.class)); + + for (Plan plan : person.getPlans()) { + for (PlanElement element : plan.getPlanElements()) { + if (element instanceof Leg) { + Leg leg = (Leg) element; + if (leg.getMode().equals("car")) { + leg.setMode("motorcycle"); + } + } + } + } + } + + PopulationWriter writer = new PopulationWriter(population); + writer.write("melun_test/input/population.xml.gz"); + } + + private void runMelunSimulation() throws ConfigurationException { + EqasimConfigurator eqasimConfigurator = new TestConfigurator(); + Config config = ConfigUtils.loadConfig("melun_test/input/config.xml"); + eqasimConfigurator.updateConfig(config); + ((ControllerConfigGroup) config.getModules().get(ControllerConfigGroup.GROUP_NAME)) + .setOutputDirectory("melun_test/output"); + + Scenario scenario = ScenarioUtils.createScenario(config); + eqasimConfigurator.configureScenario(scenario); + ScenarioUtils.loadScenario(scenario); + eqasimConfigurator.adjustScenario(scenario); + + Controler controller = new Controler(scenario); + eqasimConfigurator.configureController(controller); + controller.run(); + + Map counts = countLegs("melun_test/output/output_events.xml.gz"); + Assert.assertEquals(16, (long) counts.get("motorcycle")); + } + + @Test + public void runTestMotorcycles() throws CommandLine.ConfigurationException, IOException { + runAddMotorcycles(); + runModifyConfig(); + runModifyNetwork(); + runModifyPlans(); + runMelunSimulation(); + } + + static Map countLegs(String eventsPath) { + EventsManager manager = EventsUtils.createEventsManager(); + + Map counts = new HashMap<>(); + manager.addHandler((PersonDepartureEventHandler) event -> { + counts.compute(event.getLegMode(), (k, v) -> v == null ? 1 : v + 1); + }); + + new MatsimEventsReader(manager).readFile(eventsPath); + + System.out.println("Counts:"); + for (Map.Entry entry : counts.entrySet()) { + System.out.println(" " + entry.getKey() + " " + entry.getValue()); + } + + return counts; + } +} From 88960b6cebd99e90374017ab53debbb02fa72f5a Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Fri, 10 Oct 2025 11:40:22 +0200 Subject: [PATCH 16/21] update config to latest version --- core/src/main/resources/melun/config.xml | 464 +++++++++++++---------- 1 file changed, 259 insertions(+), 205 deletions(-) diff --git a/core/src/main/resources/melun/config.xml b/core/src/main/resources/melun/config.xml index 26b932de3..4bd19277c 100644 --- a/core/src/main/resources/melun/config.xml +++ b/core/src/main/resources/melun/config.xml @@ -4,7 +4,7 @@ - + @@ -12,8 +12,8 @@ - - + + @@ -24,7 +24,7 @@ - + @@ -33,6 +33,7 @@ + @@ -59,7 +60,7 @@ - + @@ -67,7 +68,7 @@ - + @@ -98,25 +99,6 @@ - - - - - - - - - - - - - - - - - - - @@ -125,14 +107,18 @@ - + + + - - + + + + - + @@ -140,28 +126,34 @@ - - + + + + + + - + - + - + - + - + - + @@ -180,28 +172,32 @@ - + - + + - + - + + + + - + - + @@ -213,7 +209,7 @@ - + @@ -241,6 +237,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -280,40 +306,178 @@ - + - + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - + + + @@ -322,7 +486,7 @@ - + @@ -544,15 +708,6 @@ - - - - - - - - - @@ -607,6 +762,15 @@ + + + + + + + + + @@ -627,151 +791,28 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - + + + + @@ -780,10 +821,23 @@ + + + + + + + + + + + + + @@ -818,7 +872,7 @@ - + @@ -830,7 +884,7 @@ - + From 35127b1b1b5941d68d7799ced240940afdfa00e3 Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Fri, 10 Oct 2025 11:45:29 +0200 Subject: [PATCH 17/21] fix config --- core/src/main/resources/melun/config.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/melun/config.xml b/core/src/main/resources/melun/config.xml index 4bd19277c..df1154954 100644 --- a/core/src/main/resources/melun/config.xml +++ b/core/src/main/resources/melun/config.xml @@ -12,7 +12,7 @@ - + From 161ca50e1a57615b750d93e2be84e5d634fca014 Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Fri, 10 Oct 2025 15:08:57 +0200 Subject: [PATCH 18/21] fix config and update test assertion --- core/src/main/resources/melun/config.xml | 2 +- .../src/test/java/org/eqasim/TestMotorcycles.java | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/core/src/main/resources/melun/config.xml b/core/src/main/resources/melun/config.xml index df1154954..73c2c007e 100644 --- a/core/src/main/resources/melun/config.xml +++ b/core/src/main/resources/melun/config.xml @@ -126,7 +126,7 @@ - + diff --git a/core/src/test/java/org/eqasim/TestMotorcycles.java b/core/src/test/java/org/eqasim/TestMotorcycles.java index e9f91dac3..9beccb0be 100644 --- a/core/src/test/java/org/eqasim/TestMotorcycles.java +++ b/core/src/test/java/org/eqasim/TestMotorcycles.java @@ -90,7 +90,7 @@ private void runModifyConfig() { config.qsim().setMainModes(mainModes); config.qsim().setLinkDynamics(LinkDynamics.SeepageQ); - + Collection seepModes = new HashSet<>(config.qsim().getSeepModes()); seepModes.add(TransportMode.motorcycle); config.qsim().setSeepModes(seepModes); @@ -117,17 +117,24 @@ private void runModifyPlans() { Scenario scenario = ScenarioUtils.loadScenario(config); Population population = scenario.getPopulation(); + int replaceOnceEvery = 10; + int counter = 0; + for (Person person : population.getPersons().values()) { Id personId = person.getId(); PersonVehicles person_vehicles = (PersonVehicles) person.getAttributes().getAttribute("vehicles"); person_vehicles.addModeVehicleIfAbsent(TransportMode.motorcycle, Id.create(personId.toString() + ":motorcycle", Vehicle.class)); - + for (Plan plan : person.getPlans()) { for (PlanElement element : plan.getPlanElements()) { if (element instanceof Leg) { Leg leg = (Leg) element; if (leg.getMode().equals("car")) { - leg.setMode("motorcycle"); + counter += 1; + if (counter >= replaceOnceEvery) { + counter = 0; + leg.setMode("motorcycle"); + } } } } @@ -155,7 +162,7 @@ private void runMelunSimulation() throws ConfigurationException { controller.run(); Map counts = countLegs("melun_test/output/output_events.xml.gz"); - Assert.assertEquals(16, (long) counts.get("motorcycle")); + Assert.assertEquals(427, (long) counts.get("motorcycle")); } @Test From 687cb4962972a2ef231c1dab2ab3633e9f0ed722 Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Fri, 10 Oct 2025 15:24:13 +0200 Subject: [PATCH 19/21] isolate motorcycle tests --- .../test/java/org/eqasim/TestMotorcycles.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/core/src/test/java/org/eqasim/TestMotorcycles.java b/core/src/test/java/org/eqasim/TestMotorcycles.java index 9beccb0be..80a311c3e 100644 --- a/core/src/test/java/org/eqasim/TestMotorcycles.java +++ b/core/src/test/java/org/eqasim/TestMotorcycles.java @@ -52,17 +52,17 @@ public class TestMotorcycles { @Before public void setUp() throws IOException { URL fixtureUrl = getClass().getResource("/melun"); - FileUtils.copyDirectory(new File(fixtureUrl.getPath()), new File("melun_test/input")); + FileUtils.copyDirectory(new File(fixtureUrl.getPath()), new File("melun_test_motorcycle/input")); } @After public void tearDown() throws IOException { - FileUtils.deleteDirectory(new File("melun_test")); + FileUtils.deleteDirectory(new File("melun_test_motorcycle")); } private void runAddMotorcycles() { Vehicles vehicles = VehicleUtils.createVehiclesContainer(); - new MatsimVehicleReader(vehicles).readFile("melun_test/input/vehicles.xml.gz"); + new MatsimVehicleReader(vehicles).readFile("melun_test_motorcycle/input/vehicles.xml.gz"); VehicleType motorcycleType = VehicleUtils.createVehicleType(Id.create("motorcycleType", VehicleType.class)); motorcycleType.setNetworkMode(TransportMode.motorcycle); @@ -78,12 +78,12 @@ private void runAddMotorcycles() { } MatsimVehicleWriter writer = new MatsimVehicleWriter(vehicles); - writer.writeFile("melun_test/input/vehicles.xml.gz"); + writer.writeFile("melun_test_motorcycle/input/vehicles.xml.gz"); } private void runModifyConfig() { - Config config = ConfigUtils.loadConfig("melun_test/input/config.xml"); - config.controller().setOutputDirectory("melun_test/output"); + Config config = ConfigUtils.loadConfig("melun_test_motorcycle/input/config.xml"); + config.controller().setOutputDirectory("melun_test_motorcycle/output"); Collection mainModes = new HashSet<>(config.qsim().getMainModes()); mainModes.add(TransportMode.motorcycle); @@ -95,11 +95,11 @@ private void runModifyConfig() { seepModes.add(TransportMode.motorcycle); config.qsim().setSeepModes(seepModes); - ConfigUtils.writeConfig(config, "melun_test/input/config.xml"); + ConfigUtils.writeConfig(config, "melun_test_motorcycle/input/config.xml"); } private void runModifyNetwork() { - Config config = ConfigUtils.loadConfig("melun_test/input/config.xml"); + Config config = ConfigUtils.loadConfig("melun_test_motorcycle/input/config.xml"); Scenario scenario = ScenarioUtils.loadScenario(config); Network network = scenario.getNetwork(); for (Link link : network.getLinks().values()) { @@ -109,11 +109,11 @@ private void runModifyNetwork() { link.setAllowedModes(modes); } } - NetworkUtils.writeNetwork(network, "melun_test/input/network.xml.gz"); + NetworkUtils.writeNetwork(network, "melun_test_motorcycle/input/network.xml.gz"); } private void runModifyPlans() { - Config config = ConfigUtils.loadConfig("melun_test/input/config.xml"); + Config config = ConfigUtils.loadConfig("melun_test_motorcycle/input/config.xml"); Scenario scenario = ScenarioUtils.loadScenario(config); Population population = scenario.getPopulation(); @@ -142,15 +142,15 @@ private void runModifyPlans() { } PopulationWriter writer = new PopulationWriter(population); - writer.write("melun_test/input/population.xml.gz"); + writer.write("melun_test_motorcycle/input/population.xml.gz"); } private void runMelunSimulation() throws ConfigurationException { EqasimConfigurator eqasimConfigurator = new TestConfigurator(); - Config config = ConfigUtils.loadConfig("melun_test/input/config.xml"); + Config config = ConfigUtils.loadConfig("melun_test_motorcycle/input/config.xml"); eqasimConfigurator.updateConfig(config); ((ControllerConfigGroup) config.getModules().get(ControllerConfigGroup.GROUP_NAME)) - .setOutputDirectory("melun_test/output"); + .setOutputDirectory("melun_test_motorcycle/output"); Scenario scenario = ScenarioUtils.createScenario(config); eqasimConfigurator.configureScenario(scenario); @@ -161,7 +161,7 @@ private void runMelunSimulation() throws ConfigurationException { eqasimConfigurator.configureController(controller); controller.run(); - Map counts = countLegs("melun_test/output/output_events.xml.gz"); + Map counts = countLegs("melun_test_motorcycle/output/output_events.xml.gz"); Assert.assertEquals(427, (long) counts.get("motorcycle")); } From f6546bd9903c308047930ece506ad4bb28802422 Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Fri, 10 Oct 2025 16:10:21 +0200 Subject: [PATCH 20/21] fix config and update the test withAbstractAsccess --- core/src/main/resources/melun/config.xml | 2 +- core/src/test/java/org/eqasim/TestSimulationPipeline.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/resources/melun/config.xml b/core/src/main/resources/melun/config.xml index 73c2c007e..328372a11 100644 --- a/core/src/main/resources/melun/config.xml +++ b/core/src/main/resources/melun/config.xml @@ -389,7 +389,7 @@ - + diff --git a/core/src/test/java/org/eqasim/TestSimulationPipeline.java b/core/src/test/java/org/eqasim/TestSimulationPipeline.java index 96126d2a3..3990f592c 100644 --- a/core/src/test/java/org/eqasim/TestSimulationPipeline.java +++ b/core/src/test/java/org/eqasim/TestSimulationPipeline.java @@ -395,7 +395,8 @@ public void testTransitWithAbstractAccess() throws CommandLine.ConfigurationExce "--input-config-path", "melun_test/input/config.xml", "--output-config-path", "melun_test/input/config_abstract_access.xml", "--mode-name", "ptWithAbstractAccess", - "--accesses-file-path", "melun_test/input/access_items.xml" + "--accesses-file-path", "melun_test/input/access_items.xml", + "--update-termination-modes", "false" }); From 0ea544907bfd66040b86f3c52e4e1bd995906555 Mon Sep 17 00:00:00 2001 From: Valentin LE BESCOND Date: Fri, 10 Oct 2025 16:52:00 +0200 Subject: [PATCH 21/21] fix TestEmissions --- .../test/java/org/eqasim/TestEmissions.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/core/src/test/java/org/eqasim/TestEmissions.java b/core/src/test/java/org/eqasim/TestEmissions.java index 012d17bd4..a3d5c3545 100644 --- a/core/src/test/java/org/eqasim/TestEmissions.java +++ b/core/src/test/java/org/eqasim/TestEmissions.java @@ -140,13 +140,13 @@ 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(4644, (long) counts.get("car")); + Assert.assertEquals(1554, (long) counts.get("car_passenger")); + Assert.assertEquals(11915, (long) counts.get("walk")); + Assert.assertEquals(633, (long) counts.getOrDefault("bike", 0L)); + Assert.assertEquals(2252, (long) counts.get("pt")); - SafeOsmHbefaMapping.defaultType = "URB/Loca/50"; + SafeOsmHbefaMapping.defaultType = "URB/Local/50"; RunComputeEmissionsEvents.main(new String[] { "--config-path", "melun_test/input/config.xml", "--hbefa-cold-avg", "sample_41_EFA_ColdStart_vehcat_2020average.csv", "--hbefa-hot-avg", @@ -155,7 +155,7 @@ private void runMelunEmissions() throws CommandLine.ConfigurationException, IOEx "sample_41_EFA_HOT_SubSegm_2020detailed.csv", "--eqasim-configurator", TestConfigurator.class.getName() }); - assertEquals(353707, countLines(new File("melun_test/output/output_emissions_events.xml.gz"))); + assertEquals(634633, countLines(new File("melun_test/output/output_emissions_events.xml.gz"))); RunExportEmissionsNetwork.main(new String[] { "--config-path", "melun_test/input/config.xml", "--pollutants", "PM,CO,NOx,Unknown", "--time-bin-size", "3600", @@ -163,19 +163,19 @@ private void runMelunEmissions() throws CommandLine.ConfigurationException, IOEx Collection features = ShapeFileReader.getAllFeatures("melun_test/output/emissions_network.shp"); - // NOTE: Locally, I always get 32527 lines here. On Github CI, it is always + // NOTE: Locally, I always get 42230 (old : 32527) lines here. On Github CI, it is always // 32528. No clue why this is, but all the previous tests on the events line // length etc. pass without a problem ... - // assertEquals(features.size(), 32527); + assertEquals(42230, features.size()); SimpleFeature feature = features.stream().filter(f -> f.getAttribute("link").toString().equals("163994") & f.getAttribute("time").toString().equals("43200")).findFirst().orElse(null); assertNotNull(feature); - double expectedPm = 0.045174881256541; - double expectedCo = 0.627553969527029; - double expectedNox = 0.810111846744523; + double expectedPm = 0.059894542675388; + double expectedCo = 0.893473893036039; + double expectedNox = 1.080511804659364; double expectedUnknown = Double.NaN; assertEquals(expectedPm, feature.getAttribute("PM"));