Skip to content
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
cb07539
feat: ActivityTourFinderWithExcludedActivities enabled in ile_de_france
Mar 12, 2024
1a9ad4c
fix: optional eqasim termination (#204) (#205)
tkchouaki Mar 18, 2024
e7710de
fix: bug in ActivityTourFinderWithExcludedActivities
Mar 18, 2024
ef37784
chore: testing standalone mode choice IsolatedOutsideTrips tour filter
Mar 18, 2024
6909cfa
feat: base MultiModeFeederDrt simulation capability
Mar 20, 2024
736e814
feat: default integration of FeederDrt into the mode choice
Mar 20, 2024
7a6a384
feat: AdaptConfigForDrt
Mar 20, 2024
84a2e8d
feat: Feeder Drt now supported by default in EqasimConfigurator
Mar 20, 2024
3058cbb
fix: problematic shuffle of mode names
Mar 20, 2024
eefde37
fix: FeederDrtRoutingModule shouldn't be bound as a singleton
Mar 20, 2024
6f21c60
feat: testing feeder drt functionality
Mar 20, 2024
132245e
fix: drt analysis
Mar 21, 2024
a11e60a
feet: FeederDrtAnalysisModule
Mar 21, 2024
e2b3f7b
feat: configurable access egress transit stop modes
Mar 21, 2024
363050a
feat: fast failing when no transit stop is available for intermodality
Mar 21, 2024
36a45e0
refractor: MultiModeFeederDrtConfigGroup name in the xml
Mar 21, 2024
e998bb6
Merge branch 'cut_simulations' into feeder
tkchouaki Mar 21, 2024
daeada0
fix: empty accessEgressModeName not handled correctly
Mar 22, 2024
b5b0a4e
Merge remote-tracking branch 'origin/feeder' into feeder
Mar 22, 2024
43802e2
feat: DrtModeAvailabilityWrapper
Mar 22, 2024
882521f
feat: FeederDrtModeAvailabilityWrapper
Mar 22, 2024
6c7866f
fix: FeederDrtModeAvailabilityWrapper
Mar 25, 2024
8aaeba3
feat: constraining switching between feeder and drt next to outside a…
Mar 26, 2024
c2cfca2
feat: ensuring that feeder alternatives do not have drt segments next…
Mar 26, 2024
798806d
feat: access and egress stop selection is now behind an interface. Cl…
Mar 26, 2024
e413b83
chore: update doc with a page for DRT
Mar 26, 2024
f74045b
Merge branch 'develop' into feeder
tkchouaki Mar 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
import org.eqasim.core.components.transit.EqasimTransitModule;
import org.eqasim.core.components.transit.EqasimTransitQSimModule;
import org.eqasim.core.simulation.mode_choice.epsilon.EpsilonModule;
import org.eqasim.core.simulation.modes.feeder_drt.MultiModeFeederDrtModule;
import org.eqasim.core.simulation.modes.feeder_drt.config.MultiModeFeederDrtConfigGroup;
import org.eqasim.core.simulation.modes.feeder_drt.mode_choice.EqasimFeederDrtModeChoiceModule;
import org.eqasim.core.simulation.termination.EqasimTerminationConfigGroup;
import org.eqasim.core.simulation.termination.EqasimTerminationModule;
import org.eqasim.core.simulation.termination.mode_share.ModeShareModule;
Expand Down Expand Up @@ -80,6 +83,7 @@ public EqasimConfigurator() {

this.registerOptionalConfigGroup(new DvrpConfigGroup(), Collections.singleton(new DvrpModule()));
this.registerOptionalConfigGroup(new EqasimTerminationConfigGroup(), List.of(new EqasimTerminationModule(), new ModeShareModule()));
this.registerOptionalConfigGroup(new MultiModeFeederDrtConfigGroup(), List.of(new MultiModeFeederDrtModule(), new EqasimFeederDrtModeChoiceModule()));
}

public ConfigGroup[] getConfigGroups() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.eqasim.core.simulation.mode_choice.tour_finder;

import org.matsim.contribs.discrete_mode_choice.components.tour_finder.ActivityTourFinder;
import org.matsim.contribs.discrete_mode_choice.components.tour_finder.TourFinder;
import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;

import java.util.*;
import java.util.stream.Collectors;


/**
* This tour finder works similarly to the ActivityTourFinder with the difference that it allows to build tours guaranteeing that activities of certain types are excluded, thus preventing trips related to these activities from being considered in the mode choice.
* This tour finder relies on a delegate tour finder to first build a set of tours. Then a process is applied on those tours to further split them in sequences of trips not containing trips related to excluded activities and sequences containing only such trips.
* The typical use case for this tour finder is to exclude trips related to 'outside' activities from being altered.
* This can be done by using the {@link org.matsim.contribs.discrete_mode_choice.components.tour_finder.ActivityTourFinder} with activityTypes={home, outside} as a delegate tour finder and 'outside' as an excluded activity type.
* Using the above construction on the plan 'home -> leisure -> outside -> work -> shop -> home' will produce the following 'tours' (separated with ;)
* home -> leisure; leisure -> outside; outside -> work; work -> shop -> hope.
* Using the {@link org.eqasim.core.simulation.mode_choice.filters.OutsideFilter} then allows to consider the first and last 'tours' for mode choice.
* This then allows to consider more trips in the mode choice than when using the base ActivityTourFinder coupled with the OutsideTourFilter which will divide the previous plan
* into the two 'tours' home -> leisure -> outside and outside -> work -> shop -> home and filter both of them out.
*/
public class ActivityTourFinderWithExcludedActivities implements TourFinder {

private final Set<String> excludedActivityTypes;
private final ActivityTourFinder delegate;

public ActivityTourFinderWithExcludedActivities(Collection<String> excludedActivityTypes, ActivityTourFinder delegate) {
this.excludedActivityTypes = new HashSet<>(excludedActivityTypes);
this.delegate = delegate;
}

@Override
public List<List<DiscreteModeChoiceTrip>> findTours(List<DiscreteModeChoiceTrip> trips) {
List<List<DiscreteModeChoiceTrip>> baseTours = this.delegate.findTours(trips);
return baseTours.stream().flatMap(tour -> this.isolateActivityTrips(tour).stream()).collect(Collectors.toList());
}

private List<List<DiscreteModeChoiceTrip>> isolateActivityTrips(List<DiscreteModeChoiceTrip> trips) {
List<List<DiscreteModeChoiceTrip>> tours = new ArrayList<>();

boolean isExcluding;
boolean wasExcluding = this.isTripExcluded(trips.get(0));

List<DiscreteModeChoiceTrip> currentTour = new ArrayList<>();

for(int i=0; i<trips.size(); i++) {
DiscreteModeChoiceTrip currentTrip = trips.get(i);
if (i>0) {
isExcluding = this.isTripExcluded(currentTrip);
if(isExcluding != wasExcluding) {
tours.add(currentTour);
wasExcluding = isExcluding;
currentTour = new ArrayList<>();
}
}
currentTour.add(trips.get(i));
}

if(currentTour.size() > 0) {
tours.add(currentTour);
}
return tours;
}

private boolean isTripExcluded(DiscreteModeChoiceTrip trip) {
return this.excludedActivityTypes.contains(trip.getOriginActivity().getType()) || this.excludedActivityTypes.contains(trip.getDestinationActivity().getType());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.events.*;
import org.matsim.api.core.v01.events.handler.*;
import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic;
import org.matsim.vehicles.Vehicle;

import java.util.HashMap;
Expand Down Expand Up @@ -100,7 +101,7 @@ public void handleEvent(PersonArrivalEvent event) {

@Override
public void handleEvent(ActivityStartEvent event) {
if (vehicleRegistry.isFleet(event.getPersonId())) {
if (this.vehicleRegistry.isFleet(event.getPersonId()) && !VrpAgentLogic.BEFORE_SCHEDULE_ACTIVITY_TYPE.equals(event.getActType()) && !VrpAgentLogic.AFTER_SCHEDULE_ACTIVITY_TYPE.equals(event.getActType())) {
String mode = vehicleRegistry.getMode(event.getPersonId());
Id<Vehicle> vehicleId = Id.createVehicleId(event.getPersonId());

Expand All @@ -121,10 +122,7 @@ public void handleEvent(ActivityStartEvent event) {

@Override
public void handleEvent(ActivityEndEvent event) {
// Here we want to skip activity type 'BeforeVrpSchedule'
// Since the current version of the VehicleRegistry considers the vehicle to be part of the fleet only after the TaskStartedEvent
// It is safe to just check the vehicle against the fleet here.
if (vehicleRegistry.isFleet(event.getPersonId())) {
if (this.vehicleRegistry.isFleet(event.getPersonId()) && !VrpAgentLogic.BEFORE_SCHEDULE_ACTIVITY_TYPE.equals(event.getActType()) && !VrpAgentLogic.AFTER_SCHEDULE_ACTIVITY_TYPE.equals(event.getActType())) {
String mode = vehicleRegistry.getMode(event.getPersonId());
Id<Vehicle> vehicleId = Id.createVehicleId(event.getPersonId());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
import org.eqasim.core.simulation.modes.drt.analysis.utils.LinkFinder;
import org.eqasim.core.simulation.modes.drt.analysis.utils.VehicleRegistry;
import org.matsim.api.core.v01.network.Network;
import org.matsim.contrib.drt.util.DrtEventsReaders;
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.events.EventsUtils;
import org.matsim.core.events.MatsimEventsReader;
import org.matsim.core.network.NetworkUtils;
import org.matsim.core.network.io.MatsimNetworkReader;

Expand All @@ -19,7 +19,7 @@
import java.util.Set;
import java.util.stream.Collectors;

public class RunPassengerAnalysis {
public class RunDrtPassengerAnalysis {
static public void main(String[] args) throws ConfigurationException, IOException {
CommandLine cmd = new CommandLine.Builder(args) //
.requireOptions("events-path", "network-path", "output-path", "modes") //
Expand All @@ -44,7 +44,7 @@ static public void main(String[] args) throws ConfigurationException, IOExceptio
eventsManager.addHandler(listener);

eventsManager.initProcessing();
new MatsimEventsReader(eventsManager).readFile(eventsPath);
DrtEventsReaders.createEventsReader(eventsManager).readFile(eventsPath);
eventsManager.finishProcessing();

new PassengerAnalysisWriter(listener).writeRides(new File(outputPath));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import org.eqasim.core.simulation.modes.drt.analysis.utils.LinkFinder;
import org.eqasim.core.simulation.modes.drt.analysis.utils.VehicleRegistry;
import org.matsim.api.core.v01.network.Network;
import org.matsim.contrib.drt.util.DrtEventsReaders;
import org.matsim.core.api.experimental.events.EventsManager;
import org.matsim.core.config.CommandLine;
import org.matsim.core.config.CommandLine.ConfigurationException;
Expand All @@ -16,7 +17,7 @@
import java.io.File;
import java.io.IOException;

public class RunDvrpVehicleAnalysis {
public class RunDrtVehicleAnalysis {
static public void main(String[] args) throws ConfigurationException, IOException {
CommandLine cmd = new CommandLine.Builder(args) //
.requireOptions("events-path", "network-path", "movements-output-path", "activities-output-path") //
Expand All @@ -39,7 +40,7 @@ static public void main(String[] args) throws ConfigurationException, IOExceptio
eventsManager.addHandler(listener);

eventsManager.initProcessing();
new MatsimEventsReader(eventsManager).readFile(eventsPath);
DrtEventsReaders.createEventsReader(eventsManager).readFile(eventsPath);
eventsManager.finishProcessing();

new VehicleAnalysisWriter(listener).writeMovements(new File(movementsOutputPath));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.eqasim.core.simulation.modes.drt.mode_choice;

import org.matsim.api.core.v01.population.Person;
import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup;
import org.matsim.contribs.discrete_mode_choice.model.DiscreteModeChoiceTrip;
import org.matsim.contribs.discrete_mode_choice.model.mode_availability.ModeAvailability;

import java.util.Collection;
import java.util.List;

public class DrtModeAvailabilityWrapper implements ModeAvailability {
private final Collection<String> drtModes;
private final ModeAvailability delegate;

public DrtModeAvailabilityWrapper(MultiModeDrtConfigGroup multiModeDrtConfigGroup, ModeAvailability delegate) {
this.drtModes = multiModeDrtConfigGroup.modes().toList();
this.delegate = delegate;
}

@Override
public Collection<String> getAvailableModes(Person person, List<DiscreteModeChoiceTrip> trips) {
Collection<String> modes = this.delegate.getAvailableModes(person, trips);
modes.addAll(this.drtModes);
return modes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public static void adapt(Config config, Map<String, String> vehiclesPathByDrtMod

}

private static Map<String, Map<String, String>> extractDrtInfo(String[] drtModeNames, Map<String, String[]> values) {
public static Map<String, Map<String, String>> extractDrtInfo(String[] drtModeNames, Map<String, String[]> values) {
Map<String, Map<String, String>> result = new HashMap<>();
if(drtModeNames.length == 0) {
throw new IllegalStateException("No drt modes provided");
Expand Down Expand Up @@ -137,7 +137,7 @@ public static void main(String[] args) throws CommandLine.ConfigurationException

String inputConfigPath = cmd.getOptionStrict("input-config-path");
String outputConfigPath = cmd.getOptionStrict("output-config-path");
String[] modeNames = Arrays.stream(cmd.getOption("mode-names").orElse("drt").split(",")).collect(Collectors.toSet()).toArray(String[]::new);
String[] modeNames = Arrays.stream(cmd.getOption("mode-names").orElse("drt").split(",")).toList().toArray(String[]::new);
String[] vehiclesPath = cmd.getOptionStrict("vehicles-paths").split(",");
String qsimEndtime = cmd.getOption("qsim-endtime").orElse("30:00:00");
String[] costModel = cmd.getOption("cost-models").orElse(EqasimModeChoiceModule.ZERO_COST_MODEL_NAME).split(",");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.eqasim.core.simulation.modes.feeder_drt;

import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
import org.eqasim.core.simulation.modes.feeder_drt.config.FeederDrtConfigGroup;
import org.eqasim.core.simulation.modes.feeder_drt.router.FeederDrtRoutingModule;
import org.matsim.api.core.v01.network.Network;
import org.matsim.api.core.v01.population.Population;
import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule;
import org.matsim.contrib.dvrp.run.DvrpMode;
import org.matsim.contrib.dvrp.run.DvrpModes;
import org.matsim.core.modal.ModalAnnotationCreator;
import org.matsim.core.router.RoutingModule;
import org.matsim.pt.transitSchedule.api.TransitSchedule;

import java.util.Map;


public class FeederDrtModeModule extends AbstractDvrpModeModule {

private final FeederDrtConfigGroup config;

public FeederDrtModeModule(FeederDrtConfigGroup feederDrtConfigGroup) {
super(feederDrtConfigGroup.mode);
this.config = feederDrtConfigGroup;
}


@Override
public void install() {
FeederDrtConfigGroup feederDrtConfigGroup = this.config;
addRoutingModuleBinding(this.config.mode).toProvider(new Provider<>() {
@Inject
private Map<String, Provider<RoutingModule>> routingModuleProviders;

@Inject
private Injector injector;

@Inject
private Population population;

@Inject
private TransitSchedule transitSchedule;

@Override
public RoutingModule get() {
RoutingModule ptRoutingModule = routingModuleProviders.get(feederDrtConfigGroup.ptModeName).get();
RoutingModule drtRoutingModule = routingModuleProviders.get(feederDrtConfigGroup.accessEgressModeName).get();
ModalAnnotationCreator<DvrpMode> modalAnnotationCreator = DvrpModes::mode;
Provider<Network> networkProvider = injector.getProvider(modalAnnotationCreator.key(Network.class, feederDrtConfigGroup.accessEgressModeName));
return new FeederDrtRoutingModule(feederDrtConfigGroup.mode, feederDrtConfigGroup.getAccessEgressTransitStopModes(), drtRoutingModule, ptRoutingModule, population.getFactory(), transitSchedule, networkProvider.get());
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.eqasim.core.simulation.modes.feeder_drt;

import com.google.inject.Inject;
import org.eqasim.core.simulation.mode_choice.AbstractEqasimExtension;
import org.eqasim.core.simulation.modes.feeder_drt.analysis.FeederDrtAnalysisModule;
import org.eqasim.core.simulation.modes.feeder_drt.config.FeederDrtConfigGroup;
import org.eqasim.core.simulation.modes.feeder_drt.config.MultiModeFeederDrtConfigGroup;

public class MultiModeFeederDrtModule extends AbstractEqasimExtension {
@Inject
MultiModeFeederDrtConfigGroup multiModeFeederDrtConfigGroup;

@Override
protected void installEqasimExtension() {
for(FeederDrtConfigGroup feederDrtConfigGroup: this.multiModeFeederDrtConfigGroup.getModalElements()) {
install(new FeederDrtModeModule(feederDrtConfigGroup));
}
if(multiModeFeederDrtConfigGroup.performAnalysis) {
install(new FeederDrtAnalysisModule());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.eqasim.core.simulation.modes.feeder_drt.analysis;

import com.google.inject.Inject;
import org.eqasim.core.components.config.EqasimConfigGroup;
import org.eqasim.core.simulation.modes.drt.analysis.utils.VehicleRegistry;
import org.eqasim.core.simulation.modes.feeder_drt.analysis.passengers.FeederTripSequenceListener;
import org.eqasim.core.simulation.modes.feeder_drt.analysis.passengers.FeederTripSequenceWriter;
import org.eqasim.core.simulation.modes.feeder_drt.config.MultiModeFeederDrtConfigGroup;
import org.matsim.api.core.v01.network.Network;
import org.matsim.core.controler.OutputDirectoryHierarchy;
import org.matsim.core.controler.events.IterationEndsEvent;
import org.matsim.core.controler.events.IterationStartsEvent;
import org.matsim.core.controler.events.ShutdownEvent;
import org.matsim.core.controler.listener.IterationEndsListener;
import org.matsim.core.controler.listener.IterationStartsListener;
import org.matsim.core.controler.listener.ShutdownListener;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;

public class FeederDrtAnalysisListener implements IterationStartsListener, IterationEndsListener, ShutdownListener {

private static final String FEEDER_TRIPS_FILE_NAME = "eqasim_feeder_drt_trips.csv";
private final OutputDirectoryHierarchy outputDirectory;
private final int analysisInterval;
private boolean isActive = false;
private final VehicleRegistry vehicleRegistry;
private final FeederTripSequenceListener feederTripSequenceListener;

@Inject
public FeederDrtAnalysisListener(EqasimConfigGroup config, MultiModeFeederDrtConfigGroup multiModeFeederDrtConfigGroup,
OutputDirectoryHierarchy outputDirectory, Network network) {
this.outputDirectory = outputDirectory;
this.analysisInterval = config.getAnalysisInterval();
this.vehicleRegistry = new VehicleRegistry();

feederTripSequenceListener = new FeederTripSequenceListener(multiModeFeederDrtConfigGroup.getModeConfigs(), vehicleRegistry, network);
}

@Override
public void notifyIterationEnds(IterationEndsEvent event) {
try {
if(isActive) {
event.getServices().getEvents().removeHandler(feederTripSequenceListener);
event.getServices().getEvents().removeHandler(vehicleRegistry);

String feederTripsPath = outputDirectory.getIterationFilename(event.getIteration(), FEEDER_TRIPS_FILE_NAME);
new FeederTripSequenceWriter(feederTripSequenceListener).writeTripItems(new File(feederTripsPath));
}
}catch (IOException e) {
throw new RuntimeException(e);
}

}

@Override
public void notifyIterationStarts(IterationStartsEvent event) {
if (analysisInterval > 0) {
isActive = event.getIteration() % analysisInterval == 0 || event.isLastIteration();
}
if (isActive) {
event.getServices().getEvents().addHandler(feederTripSequenceListener);
event.getServices().getEvents().addHandler(vehicleRegistry);
}
}

@Override
public void notifyShutdown(ShutdownEvent event) {
try {
File iterationPath = new File(outputDirectory.getIterationFilename(event.getIteration(), FEEDER_TRIPS_FILE_NAME));
File outputPath = new File(outputDirectory.getOutputFilename(FEEDER_TRIPS_FILE_NAME));
Files.copy(iterationPath.toPath(), outputPath.toPath());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.eqasim.core.simulation.modes.feeder_drt.analysis;

import com.google.inject.Singleton;
import org.matsim.core.controler.AbstractModule;

public class FeederDrtAnalysisModule extends AbstractModule {

@Override
public void install() {
bind(FeederDrtAnalysisListener.class).in(Singleton.class);
addControlerListenerBinding().to(FeederDrtAnalysisListener.class);
}
}
Loading