Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle motorcylces #171

Draft
wants to merge 13 commits into
base: develop
Choose a base branch
from
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ included in the (note yet determined) next version number.

**Development version**

- Add the motorcycle mode
- Updated to MATSim 14
- Isolated the mode choice model in a standalone runnable script
- Fixed LegIndex count between iterations in legs analysis
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.eqasim.core.scenario.cutter;

import org.matsim.core.config.Config;
import org.matsim.core.config.groups.QSimConfigGroup;

public class ConfigCutter {
private final String prefix;
Expand All @@ -16,5 +17,8 @@ public void run(Config config) {
config.households().setInputFile(prefix + "households.xml.gz");
config.transit().setTransitScheduleFile(prefix + "transit_schedule.xml.gz");
config.transit().setVehiclesFile(prefix + "transit_vehicles.xml.gz");
if (config.qsim().getVehiclesSource() == QSimConfigGroup.VehiclesSource.fromVehiclesData) {
config.vehicles().setVehiclesFile(prefix + "vehicles.xml.gz");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public class AnalysisOutputListener implements IterationStartsListener, Iteratio

private final int analysisInterval;
private boolean isAnalysisActive = false;
private boolean doPtAnalysis = true;

private final DistanceUnit scenarioDistanceUnit;
private final DistanceUnit analysisDistanceUnit;
Expand All @@ -51,6 +52,9 @@ public AnalysisOutputListener(EqasimConfigGroup config, OutputDirectoryHierarchy

this.analysisInterval = config.getAnalysisInterval();

// pt analysis throws an error when simulating pt in Qsim
this.doPtAnalysis = config.getUseScheduleBasedTransport();

this.tripAnalysisListener = tripListener;
this.legAnalysisListener = legListener;
this.ptAnalysisListener = ptListener;
Expand All @@ -65,7 +69,9 @@ public void notifyIterationStarts(IterationStartsEvent event) {
isAnalysisActive = true;
event.getServices().getEvents().addHandler(tripAnalysisListener);
event.getServices().getEvents().addHandler(legAnalysisListener);
event.getServices().getEvents().addHandler(ptAnalysisListener);
if (this.doPtAnalysis) {
event.getServices().getEvents().addHandler(ptAnalysisListener);
}
}
}
}
Expand All @@ -84,8 +90,10 @@ public void notifyIterationEnds(IterationEndsEvent event) {
new LegWriter(legAnalysisListener.getLegItems(), scenarioDistanceUnit, analysisDistanceUnit)
.write(outputDirectory.getIterationFilename(event.getIteration(), LEGS_FILE_NAME));

new PublicTransportLegWriter(ptAnalysisListener.getTripItems())
.write(outputDirectory.getIterationFilename(event.getIteration(), PT_FILE_NAME));
if (this.doPtAnalysis) {
new PublicTransportLegWriter(ptAnalysisListener.getTripItems())
.write(outputDirectory.getIterationFilename(event.getIteration(), PT_FILE_NAME));
}
}
} catch (IOException e) {
throw new RuntimeException(e);
Expand All @@ -99,8 +107,10 @@ public void notifyShutdown(ShutdownEvent event) {
new File(outputDirectory.getOutputFilename(TRIPS_FILE_NAME)).toPath());
Files.copy(new File(outputDirectory.getIterationFilename(event.getIteration(), LEGS_FILE_NAME)).toPath(),
new File(outputDirectory.getOutputFilename(LEGS_FILE_NAME)).toPath());
Files.copy(new File(outputDirectory.getIterationFilename(event.getIteration(), PT_FILE_NAME)).toPath(),
new File(outputDirectory.getOutputFilename(PT_FILE_NAME)).toPath());
if (this.doPtAnalysis) {
Files.copy(new File(outputDirectory.getIterationFilename(event.getIteration(), PT_FILE_NAME)).toPath(),
new File(outputDirectory.getOutputFilename(PT_FILE_NAME)).toPath());
}
} catch (IOException e) {
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@
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.MotorcycleUtilityEstimator;
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.MotorcyclePredictor;
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;
Expand All @@ -46,6 +48,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";
Expand All @@ -67,13 +70,15 @@ protected void installEqasimExtension() {
bindTripEstimator(UTILITY_ESTIMATOR_NAME).to(ModalUtilityEstimator.class);

bind(CarPredictor.class);
bind(MotorcyclePredictor.class);
bind(PtPredictor.class);
bind(BikePredictor.class);
bind(WalkPredictor.class);
bind(PersonPredictor.class);

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);
Expand Down Expand Up @@ -111,6 +116,12 @@ public CostModel provideCarCostModel(Map<String, Provider<CostModel>> factory, E
return getCostModel(factory, config, "car");
}

@Provides
@Named("motorcycle")
public CostModel provideMotorcycleCostModel(Map<String, Provider<CostModel>> factory, EqasimConfigGroup config) {
return getCostModel(factory, config, "motorcycle");
}

@Provides
@Named("pt")
public CostModel providePtCostModel(Map<String, Provider<CostModel>> factory, EqasimConfigGroup config) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 constantAccessEgressWalkTime_min = 0.0;
public double constantParkingSearchPenalty_min = 0.0;
}

public class PtParameters {
public double alpha_u = 0.0;
public double betaLineSwitch_u = 0.0;
Expand All @@ -36,6 +44,7 @@ public class WalkParameters {
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();
Expand Down
Original file line number Diff line number Diff line change
@@ -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<? extends PlanElement> 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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package org.eqasim.core.simulation.mode_choice.utilities.predictors;

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.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 java.util.List;

public class MotorcyclePredictor extends CachedVariablePredictor<MotorcycleVariables> {
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<? extends PlanElement> elements) {
if (elements.size() > 1) {
throw new IllegalStateException("We do not support multi-stage car trips yet.");
}

Leg leg = (Leg) elements.get(0);

double travelTime_min = leg.getTravelTime().seconds() / 60.0 + parameters.motorcycle.constantParkingSearchPenalty_min;
double cost_MU = costModel.calculateCost_MU(person, trip, elements);

double euclideanDistance_km = PredictorUtils.calculateEuclideanDistance_km(trip);
double accessEgressTime_min = parameters.motorcycle.constantAccessEgressWalkTime_min;

return new MotorcycleVariables(travelTime_min, cost_MU, euclideanDistance_km, accessEgressTime_min);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,96 @@
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.Person;
import org.matsim.contribs.discrete_mode_choice.modules.config.DiscreteModeChoiceConfigGroup;
import org.matsim.core.config.Config;
import org.matsim.core.config.groups.PlanCalcScoreConfigGroup;
import org.matsim.core.config.groups.QSimConfigGroup;
import org.matsim.vehicles.Vehicle;
import org.matsim.vehicles.VehicleUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.*;

public class IDFConfigurator extends EqasimConfigurator {
public void adjustScenario(Scenario scenario) {
// if there is a vehicles file defined in config, manually assign them to their agents
Config config = scenario.getConfig();
if (config.qsim().getVehiclesSource() == QSimConfigGroup.VehiclesSource.fromVehiclesData) {
for (Person person : scenario.getPopulation().getPersons().values()) {
Id<Vehicle> vehicleId = Id.createVehicleId(person.getId());
Id<Vehicle> carId = Id.createVehicleId(person.getId());
Id<Vehicle> motorcycleId = Id.createVehicleId(String.format("m_%s", person.getId()));
Map<String, Id<Vehicle>> modeVehicle = new HashMap<>();
modeVehicle.put("car", vehicleId);
modeVehicle.put("car", carId);
if (person.getAttributes().getAsMap().containsKey("motorcycleAvailability")) {
if (!((String) person.getAttributes().getAttribute("motorcycleAvailability")).equals("none")) {
modeVehicle.put("motorcycle", motorcycleId);
}
}
VehicleUtils.insertVehicleIdsIntoAttributes(person, modeVehicle);
}
}
}

public void adjustScenarioMotorcycle(Scenario scenario) {
// add motorcycle stuff
Config config = scenario.getConfig();
{ // QSim
List<String> qsimMainModes = new ArrayList<>(config.qsim().getMainModes());
qsimMainModes.add("motorcycle");
config.qsim().setMainModes(qsimMainModes);

config.qsim().setLinkDynamics(QSimConfigGroup.LinkDynamics.SeepageQ);

List<String> qsimSeepModes = new ArrayList<>(config.qsim().getSeepModes());
qsimSeepModes.add("motorcycle");
config.qsim().setSeepModes(qsimSeepModes);
}
{ // DMC
DiscreteModeChoiceConfigGroup dmcConfig = (DiscreteModeChoiceConfigGroup) config.getModules()
.get(DiscreteModeChoiceConfigGroup.GROUP_NAME);
Collection<String> cachedModes = dmcConfig.getCachedModes();
cachedModes.add("motorcycle");
dmcConfig.setCachedModes(cachedModes);

List<String> 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);
}
{ // planCalcScore
PlanCalcScoreConfigGroup.ModeParams modeParameters = new PlanCalcScoreConfigGroup.ModeParams("motorcycle");
modeParameters.setConstant(0.0);
modeParameters.setMarginalUtilityOfDistance(0.0);
modeParameters.setMonetaryDistanceRate(0.0);
config.planCalcScore().addModeParams(modeParameters);

config.planCalcScore().setWriteExperiencedPlans(true);
}
{ // plansCalcRoute
List<String> networkModes = new ArrayList<>(config.plansCalcRoute().getNetworkModes());
networkModes.add("motorcycle");
config.plansCalcRoute().setNetworkModes(networkModes);
}
{ // Network
Network network = scenario.getNetwork();
for (Link link : network.getLinks().values()) {
Set<String> modes = new HashSet<>(link.getAllowedModes());
if (modes.contains("car")) {
modes.add("motorcycle");
link.setAllowedModes(modes);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class RunSimulation {
static public void main(String[] args) throws ConfigurationException {
CommandLine cmd = new CommandLine.Builder(args) //
.requireOptions("config-path") //
.allowOptions("with-motorcycles")
.allowPrefixes("mode-choice-parameter", "cost-parameter") //
.build();

Expand All @@ -27,6 +28,11 @@ static public void main(String[] args) throws ConfigurationException {
ScenarioUtils.loadScenario(scenario);
configurator.adjustScenario(scenario);

boolean withMotorcycles = cmd.hasOption("with-motorcycles");
if (withMotorcycles) {
configurator.adjustScenarioMotorcycle(scenario);
}

Controler controller = new Controler(scenario);
configurator.configureController(controller);
controller.addOverridingModule(new EqasimAnalysisModule());
Expand Down
Loading
Loading