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

feat(drt): add/remove vehicles dynamically during qsim #3104

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,18 @@ public interface ProfileView {
private final String mode;
private final ProfileView view;
private final String outputFile;
private final boolean createdStacked;

public ProfileWriter(MatsimServices matsimServices, String mode, ProfileView profileView, String fileName) {
this(matsimServices, mode, profileView, fileName, true);
}

public ProfileWriter(MatsimServices matsimServices, String mode, ProfileView profileView, String fileName, boolean createdStacked) {
this.matsimServices = matsimServices;
this.mode = mode;
this.view = profileView;
this.outputFile = fileName;
this.createdStacked = createdStacked;
}

@Override
Expand All @@ -91,7 +97,10 @@ public void notifyIterationEnds(IterationEndsEvent event) {
if (createGraphsInterval > 0 && event.getIteration() % createGraphsInterval == 0) {
DefaultTableXYDataset xyDataset = createXYDataset(times, profiles);
generateImage(xyDataset, TimeProfileCharts.ChartType.Line);
generateImage(xyDataset, TimeProfileCharts.ChartType.StackedArea);

if (createdStacked) {
generateImage(xyDataset, TimeProfileCharts.ChartType.StackedArea);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.matsim.api.core.v01.network.Network;
import org.matsim.contrib.dvrp.fleet.DvrpVehicleImpl;
import org.matsim.contrib.dvrp.fleet.Fleet;
import org.matsim.contrib.dvrp.fleet.FleetCreator;
import org.matsim.contrib.dvrp.fleet.FleetSpecification;
import org.matsim.contrib.dvrp.fleet.Fleets;
import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule;
Expand All @@ -23,12 +24,12 @@ public EvShiftDvrpFleetQSimModule(String mode) {

@Override
public void configureQSim() {
bindModal(Fleet.class).toProvider(new ModalProviders.AbstractProvider<>(getMode(), DvrpModes::mode) {
bindModal(FleetCreator.class).toProvider(new ModalProviders.AbstractProvider<>(getMode(), DvrpModes::mode) {
@Inject
private ElectricFleet evFleet;

@Override
public Fleet get() {
public FleetCreator get() {
FleetSpecification fleetSpecification = getModalInstance(FleetSpecification.class);
Network network = getModalInstance(Network.class);
return Fleets.createCustomFleet(fleetSpecification,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import org.matsim.contrib.drt.vrpagent.DrtActionCreator;
import org.matsim.contrib.dvrp.fleet.DvrpVehicleImpl;
import org.matsim.contrib.dvrp.fleet.Fleet;
import org.matsim.contrib.dvrp.fleet.FleetCreator;
import org.matsim.contrib.dvrp.fleet.FleetSpecification;
import org.matsim.contrib.dvrp.fleet.Fleets;
import org.matsim.contrib.dvrp.passenger.PassengerHandler;
Expand Down Expand Up @@ -164,9 +165,9 @@ shiftsParams, new DefaultShiftStartLogic(), new DefaultAssignShiftToVehicleLogic

bindModal(VrpAgentLogic.DynActionCreator.class).to(modalKey(ShiftDrtActionCreator.class));

bindModal(Fleet.class).toProvider(new ModalProviders.AbstractProvider<>(getMode(), DvrpModes::mode) {
bindModal(FleetCreator.class).toProvider(new ModalProviders.AbstractProvider<>(getMode(), DvrpModes::mode) {
@Override
public Fleet get() {
public FleetCreator get() {
FleetSpecification fleetSpecification = getModalInstance(FleetSpecification.class);
Network network = getModalInstance(Network.class);
return Fleets.createCustomFleet(fleetSpecification,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import org.matsim.api.core.v01.network.Network;
import org.matsim.contrib.drt.extension.operations.shifts.fleet.DefaultShiftDvrpVehicle;
import org.matsim.contrib.dvrp.fleet.DvrpVehicleImpl;
import org.matsim.contrib.dvrp.fleet.Fleet;
import org.matsim.contrib.dvrp.fleet.FleetCreator;
import org.matsim.contrib.dvrp.fleet.FleetSpecification;
import org.matsim.contrib.dvrp.fleet.Fleets;
import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule;
Expand All @@ -21,10 +21,10 @@ public ShiftDvrpFleetQsimModule(String mode) {

@Override
public void configureQSim() {
bindModal(Fleet.class).toProvider(new ModalProviders.AbstractProvider<>(getMode(), DvrpModes::mode) {
bindModal(FleetCreator.class).toProvider(new ModalProviders.AbstractProvider<>(getMode(), DvrpModes::mode) {

@Override
public Fleet get() {
public FleetCreator get() {
FleetSpecification fleetSpecification = getModalInstance(FleetSpecification.class);
Network network = getModalInstance(Network.class);
return Fleets.createCustomFleet(fleetSpecification,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ public class DrtAnalysisControlerListener implements IterationEndsListener, Shut
private boolean vheaderWritten = false;
private final String runId;
private final DecimalFormat format = new DecimalFormat();
private final int maxcap;
private static final String notAvailableString = "NA";

private final String delimiter;
Expand All @@ -110,7 +109,6 @@ public class DrtAnalysisControlerListener implements IterationEndsListener, Shut
this.drtCfg = drtCfg;
this.qSimCfg = config.qsim();
runId = Optional.ofNullable(config.controller().getRunId()).orElse(notAvailableString);
maxcap = findMaxVehicleCapacity(fleet);

format.setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.US));
format.setMinimumIntegerDigits(1);
Expand Down Expand Up @@ -210,6 +208,8 @@ public void notifyIterationEnds(IterationEndsEvent event) {
event.getIteration());
double l_d = getTotalDistance(drtVehicleStats.getVehicleStates()) / (legs.size() * directDistanceMean);

int maxcap = drtVehicleStats.getVehicleStates().values().stream().mapToInt(v -> v.maxCapacity).max().orElse(0);

MinCountAndShareIdleVehiclesOverDay minCountAndShareIdleVehiclesOverDay = getMinCountAndShareIdleVehiclesOverDay();
String vehStats = summarizeVehicles(drtVehicleStats.getVehicleStates(), delimiter)
+ delimiter
Expand All @@ -219,7 +219,7 @@ public void notifyIterationEnds(IterationEndsEvent event) {
+ delimiter
+ format.format(minCountAndShareIdleVehiclesOverDay.minCountIdleVehiclesOverDay);
String occStats = summarizeDetailedOccupancyStats(drtVehicleStats.getVehicleStates(), delimiter, maxcap);
writeIterationVehicleStats(vehStats, occStats, event.getIteration());
writeIterationVehicleStats(vehStats, occStats, event.getIteration(), maxcap);
if (drtCfg.plotDetailedCustomerStats) {
String header = String.join(delimiter, //
"submissionTime", //
Expand Down Expand Up @@ -347,7 +347,7 @@ private void writeIterationPassengerStats(String summarizeLegs, int it) {
* @param summarizeVehicles
* @param it iteration
*/
private void writeIterationVehicleStats(String summarizeVehicles, String vehOcc, int it) {
private void writeIterationVehicleStats(String summarizeVehicles, String vehOcc, int it, int maxcap) {
try (var bw = getAppendingBufferedWriter("drt_vehicle_stats", ".csv")) {
if (!vheaderWritten) {
bw.write(line("runId", "iteration", "vehicles", "totalServiceDuration", "totalDistance", "totalEmptyDistance", "emptyRatio", "totalPassengerDistanceTraveled",
Expand Down Expand Up @@ -923,14 +923,6 @@ private static String summarizeVehicles(Map<Id<Vehicle>, DrtVehicleDistanceStats
format.format(d_p_d_t) + "");
}

/**
* @param fleet
* @return
*/
static int findMaxVehicleCapacity(FleetSpecification fleet) {
return fleet.getVehicleSpecifications().values().stream().mapToInt(DvrpVehicleSpecification::getCapacity).max().getAsInt();
}

private static String summarizeDetailedOccupancyStats(Map<Id<Vehicle>, DrtVehicleDistanceStats.VehicleState> vehicleDistances, String del,
int maxcap) {
DecimalFormat format = new DecimalFormat();
Expand All @@ -942,7 +934,7 @@ private static String summarizeDetailedOccupancyStats(Map<Id<Vehicle>, DrtVehicl
double[] sum = new double[maxcap + 1];

for (DrtVehicleDistanceStats.VehicleState state : vehicleDistances.values()) {
for (int i = 0; i <= maxcap; i++) {
for (int i = 0; i < state.totalDistanceByOccupancy.length; i++) {
sum[i] += state.totalDistanceByOccupancy[i];
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import org.matsim.contrib.drt.scheduler.EmptyVehicleRelocator;
import org.matsim.contrib.drt.sharingmetrics.SharingMetricsModule;
import org.matsim.contrib.dvrp.analysis.ExecutedScheduleCollector;
import org.matsim.contrib.dvrp.analysis.FleetSizeProfileCalculator;
import org.matsim.contrib.dvrp.analysis.FleetSizeProfileView;
import org.matsim.contrib.dvrp.fleet.FleetSpecification;
import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule;
import org.matsim.contrib.dvrp.schedule.Task;
Expand Down Expand Up @@ -99,7 +101,7 @@ public void install() {
addEventHandlerBinding().to(modalKey(ExecutedScheduleCollector.class));

bindModal(DrtVehicleDistanceStats.class).toProvider(
modalProvider(getter -> new DrtVehicleDistanceStats(getter.get(Network.class), drtCfg, getter.getModal(FleetSpecification.class))))
modalProvider(getter -> new DrtVehicleDistanceStats(getter.get(Network.class), drtCfg)))
.asEagerSingleton();
addEventHandlerBinding().to(modalKey(DrtVehicleDistanceStats.class));

Expand All @@ -124,6 +126,18 @@ public void install() {
getter -> new VehicleTaskProfileCalculator(getMode(), getter.getModal(FleetSpecification.class), 300,
getter.get(QSimConfigGroup.class)))).asEagerSingleton();
addEventHandlerBinding().to(modalKey(VehicleTaskProfileCalculator.class));

addControlerListenerBinding().toProvider(modalProvider(getter -> {
MatsimServices matsimServices = getter.get(MatsimServices.class);
String mode = drtCfg.getMode();
var profileView = new FleetSizeProfileView(getter.getModal(FleetSizeProfileCalculator.class));
return new ProfileWriter(matsimServices, mode, profileView, "fleet_size_profile", false);
}));

bindModal(FleetSizeProfileCalculator.class).toProvider(modalProvider(
getter -> new FleetSizeProfileCalculator(getMode(), getter.getModal(FleetSpecification.class), 300,
getter.get(QSimConfigGroup.class)))).asEagerSingleton();
addEventHandlerBinding().to(modalKey(FleetSizeProfileCalculator.class));

addControlerListenerBinding().toProvider(modalProvider(getter -> {
MatsimServices matsimServices = getter.get(MatsimServices.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@
import org.matsim.api.core.v01.network.Network;
import org.matsim.api.core.v01.population.Person;
import org.matsim.contrib.drt.run.DrtConfigGroup;
import org.matsim.contrib.dvrp.fleet.FleetSpecification;
import org.matsim.contrib.dvrp.fleet.VehicleAddedEvent;
import org.matsim.contrib.dvrp.fleet.VehicleAddedEventHandler;
import org.matsim.contrib.dvrp.fleet.VehicleRemovedEvent;
import org.matsim.contrib.dvrp.fleet.VehicleRemovedEventHandler;
import org.matsim.contrib.dvrp.optimizer.Request;
import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEvent;
import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEventHandler;
Expand All @@ -45,14 +48,16 @@
import org.matsim.vehicles.Vehicle;

import com.google.common.base.Preconditions;
import com.google.common.base.Verify;

/**
* @author jbischoff
* @author Michal Maciejewski
* @author Sebastian Hörl (sebhoerl), IRT SystemX
*/
public class DrtVehicleDistanceStats
implements PassengerPickedUpEventHandler, LinkEnterEventHandler, PassengerDroppedOffEventHandler,
TeleportationArrivalEventHandler {
TeleportationArrivalEventHandler, VehicleAddedEventHandler, VehicleRemovedEventHandler {

static class VehicleState {
final Map<Id<Person>, MutableDouble> distanceByPersonId = new HashMap<>();
Expand All @@ -61,8 +66,11 @@ static class VehicleState {
double totalPassengerTraveledDistance = 0; //in (passenger x meters)
final double[] totalDistanceByOccupancy;
final double serviceDuration;
final int maxCapacity;
boolean active = true;

private VehicleState(int maxCapacity, double serviceTime) {
this.maxCapacity = maxCapacity;
this.totalDistanceByOccupancy = new double[maxCapacity + 1];
this.serviceDuration = serviceTime;
}
Expand All @@ -85,44 +93,52 @@ private void linkEntered(Link link) {

private final String mode;
private final Network network;
private final FleetSpecification fleetSpecification;

public DrtVehicleDistanceStats(Network network, DrtConfigGroup drtCfg, FleetSpecification fleetSpecification) {
public DrtVehicleDistanceStats(Network network, DrtConfigGroup drtCfg) {
this.mode = drtCfg.getMode();
this.network = network;
this.fleetSpecification = fleetSpecification;
initializeVehicles();
}

@Override
public void reset(int iteration) {
vehicleStates.clear();
initializeVehicles();
}

private void initializeVehicles() {
int maxCapacity = DrtAnalysisControlerListener.findMaxVehicleCapacity(fleetSpecification);
fleetSpecification.getVehicleSpecifications()
.values()
.stream()
.forEach(spec -> vehicleStates.put(Id.createVehicleId(spec.getId()),
new VehicleState(maxCapacity, spec.getServiceEndTime() - spec.getServiceBeginTime())));
@Override
public void handleEvent(VehicleAddedEvent event) {
var state = vehicleStates.get(Id.createVehicleId(event.getDvrpVehicleId()));

if (state == null) {
vehicleStates.put(Id.createVehicleId(event.getDvrpVehicleId()), new VehicleState(event.getCapacity(), Double.NaN));
} else {
state.active = true;
Verify.verify(state.maxCapacity == event.getCapacity());
}
}

@Override
public void handleEvent(VehicleRemovedEvent event) {
Objects.requireNonNull(vehicleStates.get(Id.createVehicleId(event.getDvrpVehicleId()))).active = false;
}

@Override
public void handleEvent(PassengerPickedUpEvent event) {
if (event.getMode().equals(mode)) {
if (event.getVehicleId() != null) {
vehicleStates.get(Id.createVehicleId(event.getVehicleId())).distanceByPersonId.put(event.getPersonId(),
new MutableDouble());
var state = vehicleStates.get(Id.createVehicleId(event.getVehicleId()));

if (state.active) {
state.distanceByPersonId.put(event.getPersonId(),
new MutableDouble());
}
}
}
}

@Override
public void handleEvent(LinkEnterEvent event) {
VehicleState vehicleState = vehicleStates.get(event.getVehicleId());
if (vehicleState != null) {
if (vehicleState != null && vehicleState.active) {
vehicleState.linkEntered(network.getLinks().get(event.getLinkId()));
}
}
Expand All @@ -133,9 +149,13 @@ public void handleEvent(LinkEnterEvent event) {
public void handleEvent(PassengerDroppedOffEvent event) {
if (event.getMode().equals(mode)) {
if (event.getVehicleId() != null) {
double distance = vehicleStates.get(Id.createVehicleId(event.getVehicleId())).distanceByPersonId.remove(
event.getPersonId()).doubleValue();
travelDistances.put(event.getRequestId(), distance);
var state = vehicleStates.get(Id.createVehicleId(event.getVehicleId()));

if (state.active) {
double distance = state.distanceByPersonId.remove(
event.getPersonId()).doubleValue();
travelDistances.put(event.getRequestId(), distance);
}
} else {
Preconditions.checkArgument(
soonArrivingTeleportedRequests.put(event.getPersonId(), event.getRequestId()) == null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.matsim.contrib.drt.util;

import org.matsim.api.core.v01.network.Network;
import org.matsim.contrib.drt.schedule.DrtTaskFactory;
import org.matsim.contrib.dvrp.fleet.DvrpVehicle;
import org.matsim.contrib.dvrp.fleet.DvrpVehicleImpl;
import org.matsim.contrib.dvrp.fleet.DvrpVehicleSpecification;
import org.matsim.contrib.dvrp.fleet.FleetExtensionHelper;
import org.matsim.contrib.dvrp.fleet.FleetExtensionHelper.FleetExtensionHelperQSimModule;
import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule;

/**
* This is a convenience class that simplifies the process of adding VRP agents
* to a DRT fleet during the QSim. On top of DVRP's FleetExtensionHelper it also
* makes sure to initialize the first schedule task.
*
* @author Sebastian Hörl (sebhoerl), IRT SystemX
*/
public class DrtFleetExtensionHelper {
private final FleetExtensionHelper delegate;
private final DrtTaskFactory taskFactory;
private final Network network;

DrtFleetExtensionHelper(FleetExtensionHelper delegate, DrtTaskFactory taskFactory, Network network) {
this.delegate = delegate;
this.taskFactory = taskFactory;
this.network = network;
}

public DvrpVehicle addVehicle(DvrpVehicleSpecification specification) {
DvrpVehicle vehicle = new DvrpVehicleImpl(specification,
network.getLinks().get(specification.getStartLinkId()));

addVehicle(vehicle);
return vehicle;
}

public void addVehicle(DvrpVehicle vehicle) {
vehicle.getSchedule().addTask(taskFactory.createInitialTask(vehicle, vehicle.getServiceBeginTime(),
vehicle.getServiceEndTime(), vehicle.getStartLink()));

delegate.addVehicle(vehicle);
}

static public class DrtFleetExtensionHelperQSimModule extends AbstractDvrpModeQSimModule {
private final boolean installFleetExtensionHelper;

public DrtFleetExtensionHelperQSimModule(String mode) {
this(mode, true);
}

public DrtFleetExtensionHelperQSimModule(String mode, boolean installFleetExtensionHelper) {
super(mode);
this.installFleetExtensionHelper = installFleetExtensionHelper;
}

@Override
protected void configureQSim() {
if (installFleetExtensionHelper) {
install(new FleetExtensionHelperQSimModule(getMode()));
}

bindModal(DrtFleetExtensionHelper.class).toProvider(modalProvider(getter -> {
return new DrtFleetExtensionHelper(getter.getModal(FleetExtensionHelper.class),
getter.getModal(DrtTaskFactory.class), getter.getModal(Network.class));
})).asEagerSingleton();

addModalQSimComponentBinding().to(modalKey(FleetExtensionHelper.class));
}
}
}
Loading