Skip to content

Commit

Permalink
feat(dvrp): generic loads and changeable capacities (#3627)
Browse files Browse the repository at this point in the history
* feat(dvrp): generic loads and changeable capacities

* fix: DefaultCapacityReconfigurationLogic

* fix: Proper use of DefaultCapacityReconfigurationLogic in RunDrtExampleWithChangingCapacitiesTest

* Fix: Preventing DefaultRequestInsertionScheduler from adding passengers in CapacityChangeTask

* fix: considering the case when vehicle is currently in a CapacityChangeTask

* feat: compatibility of PrebookingActionCreator with CapacityChangeTask

* fix: handling the case where current task is a capacity change

* chore: code cleanup

* fix: DefaultCapacityReconfigurationLogic

* fix: CapacityChangeSchedulerEngine

* fix: targeting fleet vehicles in CapacityLoadAnalysisHandler

* refactoring of load code

* fix whitespaces

* feat: configurable DefaultCapacityReconfigurationLogic installation

* fix: allowCapacityChangeBeforeDayStarts=false by default

* add hashCode

* fix tests

* fix: updated reference files

---------

Co-authored-by: Tarek CHOUAKI <[email protected]>
Co-authored-by: Dr. Steffen Axer <[email protected]>
Co-authored-by: Sebastian HÖRL <[email protected]>
Co-authored-by: Sebastian Hörl <[email protected]>
  • Loading branch information
5 people authored Feb 14, 2025
1 parent c0a6c24 commit 9402f16
Show file tree
Hide file tree
Showing 156 changed files with 3,703 additions and 530 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.matsim.contrib.dvrp.fleet.DvrpVehicleSpecification;
import org.matsim.contrib.dvrp.fleet.FleetWriter;
import org.matsim.contrib.dvrp.fleet.ImmutableDvrpVehicleSpecification;
import org.matsim.contrib.dvrp.load.IntegerLoadType;
import org.matsim.core.config.ConfigUtils;
import org.matsim.core.gbl.MatsimRandom;
import org.matsim.core.network.io.MatsimNetworkReader;
Expand Down Expand Up @@ -77,7 +78,7 @@ public static void main(String[] args) {
.build());

}
new FleetWriter(vehicles.stream()).write(taxisFile);
new FleetWriter(vehicles.stream(), new IntegerLoadType("passengers")).write(taxisFile);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
import org.matsim.contrib.drt.extension.DrtWithExtensionsConfigGroup;
import org.matsim.contrib.dvrp.fleet.DvrpVehicleSpecification;
import org.matsim.contrib.dvrp.fleet.FleetSpecification;
import org.matsim.contrib.dvrp.passenger.PassengerGroupIdentifier;
import org.matsim.contrib.dvrp.load.IntegerLoad;
import org.matsim.contrib.dvrp.passenger.PassengerGroupIdentifier;
import org.matsim.core.controler.events.AfterMobsimEvent;
import org.matsim.core.controler.events.BeforeMobsimEvent;
import org.matsim.core.controler.listener.AfterMobsimListener;
Expand Down Expand Up @@ -80,10 +81,18 @@ final class DrtCompanionRideGenerator implements BeforeMobsimListener, AfterMobs
final DrtWithExtensionsConfigGroup drtWithExtensionsConfigGroup) {
this.scenario = scenario;
this.drtMode = drtMode;

for (var vehicle : fleet.getVehicleSpecifications().values()) {
// need IntegerType to determine the maxCapacity, but this could also be a config option /sh, januar 2025
Preconditions.checkArgument(vehicle.getCapacity() instanceof IntegerLoad, "Companions require a IntegerLoadType in the current implementation.");
}

this.maxCapacity = fleet.getVehicleSpecifications()
.values()
.stream()
.mapToInt(DvrpVehicleSpecification::getCapacity)
.map(DvrpVehicleSpecification::getCapacity)
.map(IntegerLoad.class::cast)
.mapToInt(IntegerLoad::getValue)
.max()
.orElse(0);
installSampler(drtWithExtensionsConfigGroup);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,19 @@
import org.matsim.contrib.drt.optimizer.VehicleDataEntryFactoryImpl;
import org.matsim.contrib.drt.optimizer.VehicleEntry;
import org.matsim.contrib.dvrp.fleet.DvrpVehicle;
import org.matsim.contrib.dvrp.load.DvrpLoadType;
import org.matsim.contrib.dvrp.run.DvrpMode;
import org.matsim.contrib.dvrp.run.DvrpModes;
import org.matsim.contrib.dvrp.schedule.Schedule;
import org.matsim.contrib.dvrp.schedule.Schedule.ScheduleStatus;
import org.matsim.contrib.dvrp.schedule.Task;
import org.matsim.contrib.dvrp.schedule.Task.TaskStatus;
import org.matsim.contrib.ev.fleet.Battery;
import org.matsim.contrib.evrp.ETask;
import org.matsim.contrib.evrp.EvDvrpVehicle;
import org.matsim.contrib.evrp.tracker.ETaskTracker;
import org.matsim.core.modal.ModalProviders;

import com.google.inject.Provider;
import com.google.inject.Singleton;

/**
* @author michalm
Expand All @@ -51,9 +54,9 @@ public EVehicleEntry(VehicleEntry entry, double socBeforeFinalStay) {
private final double minimumRelativeSoc;
private final VehicleDataEntryFactoryImpl entryFactory;

public EDrtVehicleDataEntryFactory(double minimumRelativeSoc) {
public EDrtVehicleDataEntryFactory(double minimumRelativeSoc, DvrpLoadType loadType) {
this.minimumRelativeSoc = minimumRelativeSoc;
entryFactory = new VehicleDataEntryFactoryImpl();
entryFactory = new VehicleDataEntryFactoryImpl(loadType);
}

@Override
Expand Down Expand Up @@ -101,16 +104,18 @@ public VehicleEntry create(DvrpVehicle vehicle, double currentTime) {
return entry == null ? null : new EVehicleEntry(entry, chargeBeforeNextTask);
}

public static class EDrtVehicleDataEntryFactoryProvider implements Provider<VehicleEntry.EntryFactory> {
@Singleton
public static class EDrtVehicleDataEntryFactoryProvider extends ModalProviders.AbstractProvider<DvrpMode, EDrtVehicleDataEntryFactory> {
private final double minimumRelativeSoc;

public EDrtVehicleDataEntryFactoryProvider(double minimumRelativeSoc) {
public EDrtVehicleDataEntryFactoryProvider(String mode, double minimumRelativeSoc) {
super(mode, DvrpModes::mode);
this.minimumRelativeSoc = minimumRelativeSoc;
}

@Override
public EDrtVehicleDataEntryFactory get() {
return new EDrtVehicleDataEntryFactory(minimumRelativeSoc);
return new EDrtVehicleDataEntryFactory(minimumRelativeSoc, getModalInstance(DvrpLoadType.class));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,7 @@ public EmptyVehicleChargingScheduler get() {

install(DrtModeOptimizerQSimModule.getInsertionSearchQSimModule(drtCfg));

bindModal(VehicleEntry.EntryFactory.class).toProvider(
EDrtVehicleDataEntryFactory.EDrtVehicleDataEntryFactoryProvider.class).asEagerSingleton();
bindModal(VehicleEntry.EntryFactory.class).to(modalKey(EDrtVehicleDataEntryFactory.class));

DrtOptimizationConstraintsSet defaultConstraintsSet = drtCfg.addOrGetDrtOptimizationConstraintsParams().addOrGetDefaultDrtOptimizationConstraintsSet();
bindModal(CostCalculationStrategy.class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

import java.net.URL;

import org.matsim.contrib.drt.extension.edrt.optimizer.EDrtVehicleDataEntryFactory;
import org.matsim.contrib.drt.extension.edrt.optimizer.EDrtVehicleDataEntryFactory.EDrtVehicleDataEntryFactoryProvider;
import org.matsim.contrib.drt.run.DrtConfigGroup;
import org.matsim.contrib.drt.run.MultiModeDrtConfigGroup;
Expand Down Expand Up @@ -64,8 +65,8 @@ public static Controler createControler(Config config, boolean otfvis) {
controler.addOverridingModule(new AbstractDvrpModeModule(drtCfg.getMode()) {
@Override
public void install() {
bind(EDrtVehicleDataEntryFactoryProvider.class).toInstance(
new EDrtVehicleDataEntryFactoryProvider(MIN_RELATIVE_SOC));
bindModal(EDrtVehicleDataEntryFactory.class).toProvider(
new EDrtVehicleDataEntryFactoryProvider(getMode(), MIN_RELATIVE_SOC));
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.matsim.contrib.drt.optimizer.insertion.CostCalculationStrategy;
import org.matsim.contrib.drt.passenger.DrtOfferAcceptor;
import org.matsim.contrib.drt.run.DrtConfigGroup;
import org.matsim.contrib.dvrp.load.DvrpLoadType;
import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule;
import org.matsim.core.router.costcalculators.OnlyTimeDependentTravelDisutility;
import org.matsim.core.router.speedy.SpeedyALTFactory;
Expand Down Expand Up @@ -302,7 +303,7 @@ private void configureRangeContraint(List<Key<? extends DrtInsertionConstraint>>
distanceApproximator = null;
}

return new VehicleRangeConstraint(vehicleRangeSupplier, distanceCalculator, distanceApproximator);
return new VehicleRangeConstraint(vehicleRangeSupplier, distanceCalculator, distanceApproximator, getter.getModal(DvrpLoadType.class));
}));

constraintBindings.add(modalKey(VehicleRangeConstraint.class));
Expand Down Expand Up @@ -448,7 +449,7 @@ private void configureVehicleDistanceObjective() {
distanceCalculator = getter.getModal(RoutingDistanceCalculator.class);
}

return new VehicleDistanceObjective(vehicleDistanceWeights, distanceCalculator);
return new VehicleDistanceObjective(vehicleDistanceWeights, distanceCalculator, getter.getModal(DvrpLoadType.class));
}));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public boolean checkInsertion(DrtRequest drtRequest, Insertion insertion, Detour

private boolean isValidInsertionForExclusiveRequest(DrtRequest drtRequest, Insertion insertion,
DetourTimeInfo detourTimeInfo) {
if (insertion.pickup.previousWaypoint.getOutgoingOccupancy() > 0) {
if (!insertion.pickup.previousWaypoint.getOutgoingOccupancy().isEmpty()) {
// attempt to attach pickup to existing trip
return false;
}
Expand Down Expand Up @@ -67,4 +67,4 @@ private boolean isValidInsertionForStandardRequest(DrtRequest drtRequest, Insert
static public interface ExclusivityVoter {
boolean isExclusive(DrtRequest request);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,21 @@
import org.matsim.contrib.drt.optimizer.insertion.InsertionGenerator.Insertion;
import org.matsim.contrib.drt.passenger.DrtRequest;
import org.matsim.contrib.dvrp.fleet.DvrpVehicle;
import org.matsim.contrib.dvrp.load.DvrpLoadType;

public class VehicleRangeConstraint implements DrtInsertionConstraint {
private final InsertionDistanceCalculator insertionCalculator = new InsertionDistanceCalculator();

private final InsertionDistanceCalculator insertionCalculator;
private final VehicleRangeSupplier rangeSupplier;
private final DistanceCalculator distanceCalculator;
private final DistanceApproximator distanceApproximator;

public VehicleRangeConstraint(VehicleRangeSupplier rangeSupplier, DistanceCalculator distanceEstimator,
@Nullable DistanceApproximator distanceApproximator) {
@Nullable DistanceApproximator distanceApproximator, DvrpLoadType loadType) {
this.rangeSupplier = rangeSupplier;
this.distanceCalculator = distanceEstimator;
this.distanceApproximator = distanceApproximator;
this.insertionCalculator = new InsertionDistanceCalculator(loadType);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

import org.matsim.api.core.v01.network.Link;
import org.matsim.contrib.drt.optimizer.VehicleEntry;
Expand All @@ -12,13 +13,26 @@
import org.matsim.contrib.drt.optimizer.insertion.InsertionGenerator.Insertion;
import org.matsim.contrib.drt.passenger.AcceptedDrtRequest;
import org.matsim.contrib.drt.schedule.DrtStopTask;
import org.matsim.contrib.dvrp.load.DvrpLoad;
import org.matsim.contrib.dvrp.load.DvrpLoadType;
import org.matsim.contrib.dvrp.load.IntegerLoad;
import org.matsim.contrib.dvrp.load.IntegerLoadType;
import org.matsim.contrib.dvrp.schedule.DriveTask;
import org.matsim.contrib.dvrp.schedule.Schedule;
import org.matsim.contrib.dvrp.schedule.Schedule.ScheduleStatus;
import org.matsim.contrib.dvrp.schedule.Task;
import org.matsim.contrib.dvrp.tracker.OnlineDriveTaskTracker;

import com.google.common.base.Preconditions;

public class InsertionDistanceCalculator {
private final IntegerLoad emptyLoad;

public InsertionDistanceCalculator(DvrpLoadType loadType) {
Preconditions.checkArgument(loadType instanceof IntegerLoadType, "Distance calculation currently only works with IntegerLoadType");
this.emptyLoad = (IntegerLoad) loadType.getEmptyLoad();
}

public VehicleDistance calculateScheduledDistance(VehicleEntry vehicleEntry) {
Schedule schedule = vehicleEntry.vehicle.getSchedule();

Expand All @@ -27,22 +41,22 @@ public VehicleDistance calculateScheduledDistance(VehicleEntry vehicleEntry) {
double passengerDistance = 0.0;

if (!schedule.getStatus().equals(ScheduleStatus.UNPLANNED)) {
int occupancy = 0;
IntegerLoad occupancy = emptyLoad;

for (Task task : schedule.getTasks()) {
if (task instanceof DrtStopTask) {
occupancy += getPassengers(((DrtStopTask) task).getPickupRequests().values());
occupancy -= getPassengers(((DrtStopTask) task).getDropoffRequests().values());
occupancy = occupancy.add(Objects.requireNonNullElse(getPassengers(((DrtStopTask) task).getPickupRequests().values()), emptyLoad));
occupancy = occupancy.subtract(Objects.requireNonNullElse(getPassengers(((DrtStopTask) task).getDropoffRequests().values()), vehicleEntry.vehicle.getCapacity()));
}

if (task instanceof DriveTask) {
double taskDistance = calculateTaskDistance((DriveTask) task);

if (occupancy == 0) {
if (occupancy.isEmpty()) {
emptyDistance += taskDistance;
} else {
occupiedDistance += taskDistance;
passengerDistance += taskDistance * occupancy;
passengerDistance += occupancy.getValue() * taskDistance;
}
}
}
Expand All @@ -51,8 +65,12 @@ public VehicleDistance calculateScheduledDistance(VehicleEntry vehicleEntry) {
return new VehicleDistance(occupiedDistance, emptyDistance, passengerDistance);
}

private int getPassengers(Collection<AcceptedDrtRequest> requests) {
return requests.stream().mapToInt(r -> r.getPassengerIds().size()).sum();
private DvrpLoad getPassengers(Collection<AcceptedDrtRequest> requests) {
DvrpLoad load = emptyLoad;
for(AcceptedDrtRequest acceptedDrtRequest: requests) {
load = load.add(acceptedDrtRequest.getLoad());
}
return load;
}

public VehicleDistance calculateInsertionDistance(Insertion insertion, DetourTimeInfo detourTimeInfo,
Expand All @@ -65,7 +83,7 @@ public VehicleDistance calculateInsertionDistance(Insertion insertion, DetourTim
final Link pickupNewLink = insertion.pickup.newWaypoint.getLink();
final Link pickupToLink = insertion.pickup.nextWaypoint.getLink();

int beforePickupOccupancy = insertion.pickup.previousWaypoint.getOutgoingOccupancy();
IntegerLoad beforePickupOccupancy = (IntegerLoad) insertion.pickup.previousWaypoint.getOutgoingOccupancy();

double beforePickupDistance = distanceEstimator
.calculateDistance(insertion.pickup.previousWaypoint.getDepartureTime(), pickupFromLink, pickupNewLink);
Expand All @@ -74,7 +92,7 @@ public VehicleDistance calculateInsertionDistance(Insertion insertion, DetourTim
pickupNewLink, pickupToLink);

addedDistances.add(new DistanceEntry(beforePickupDistance, beforePickupOccupancy));
addedDistances.add(new DistanceEntry(afterPickupDistance, beforePickupOccupancy + 1));
addedDistances.add(new DistanceEntry(afterPickupDistance, beforePickupOccupancy.add(insertion.insertedLoad)));

if (insertion.pickup.previousWaypoint instanceof Waypoint.Start) {
Task currentTask = insertion.vehicleEntry.vehicle.getSchedule().getCurrentTask();
Expand All @@ -88,12 +106,12 @@ public VehicleDistance calculateInsertionDistance(Insertion insertion, DetourTim
removedStartDistance += tracker.getPath().getLink(k).getLength();
}

int startOccupancy = insertion.pickup.previousWaypoint.getOutgoingOccupancy();
IntegerLoad startOccupancy = (IntegerLoad) insertion.pickup.previousWaypoint.getOutgoingOccupancy();
removedDistances.add(new DistanceEntry(removedStartDistance, startOccupancy));
}
} else {
int startIndex = ((Waypoint.Stop) insertion.pickup.previousWaypoint).task.getTaskIdx();
int occupancy = insertion.pickup.previousWaypoint.getOutgoingOccupancy();
IntegerLoad occupancy = (IntegerLoad) insertion.pickup.previousWaypoint.getOutgoingOccupancy();

final int endIndex;
if (insertion.pickup.nextWaypoint instanceof Waypoint.End) {
Expand All @@ -108,8 +126,8 @@ public VehicleDistance calculateInsertionDistance(Insertion insertion, DetourTim
Task task = insertion.vehicleEntry.vehicle.getSchedule().getTasks().get(index);

if (task instanceof DrtStopTask) {
occupancy += getPassengers(((DrtStopTask) task).getPickupRequests().values());
occupancy -= getPassengers(((DrtStopTask) task).getDropoffRequests().values());
occupancy = occupancy.add(getPassengers(((DrtStopTask) task).getPickupRequests().values()));
occupancy = occupancy.add(getPassengers(((DrtStopTask) task).getDropoffRequests().values()));
}

if (task instanceof DriveTask) {
Expand All @@ -125,24 +143,24 @@ public VehicleDistance calculateInsertionDistance(Insertion insertion, DetourTim
? insertion.dropoff.newWaypoint.getLink()
: insertion.dropoff.nextWaypoint.getLink();

final int beforeDropoffOccupancy;
final IntegerLoad beforeDropoffOccupancy;
if (insertion.dropoff.index > insertion.pickup.index) {
beforeDropoffOccupancy = insertion.dropoff.previousWaypoint.getOutgoingOccupancy() + 1;
beforeDropoffOccupancy = (IntegerLoad) insertion.dropoff.previousWaypoint.getOutgoingOccupancy().add(insertion.insertedLoad);
double beforeDropoffDistance = distanceEstimator.calculateDistance(
insertion.dropoff.previousWaypoint.getDepartureTime(), dropoffFromLink, dropoffNewLink);

addedDistances.add(new DistanceEntry(beforeDropoffDistance, beforeDropoffOccupancy));
} else {
beforeDropoffOccupancy = beforePickupOccupancy + 1;
beforeDropoffOccupancy = beforePickupOccupancy.add(insertion.insertedLoad);
}

double afterDropoffDistance = distanceEstimator.calculateDistance(detourTimeInfo.dropoffDetourInfo.arrivalTime,
dropoffNewLink, dropoffToLink);
addedDistances.add(new DistanceEntry(afterDropoffDistance, beforeDropoffOccupancy - 1));
addedDistances.add(new DistanceEntry(afterDropoffDistance, beforeDropoffOccupancy.subtract(insertion.insertedLoad)));

if (insertion.dropoff.index > insertion.pickup.index) {
int startIndex = ((Waypoint.Stop) insertion.dropoff.previousWaypoint).task.getTaskIdx();
int occupancy = insertion.dropoff.previousWaypoint.getOutgoingOccupancy();
IntegerLoad occupancy = (IntegerLoad) insertion.dropoff.previousWaypoint.getOutgoingOccupancy();

final int endIndex;
if (insertion.dropoff.nextWaypoint instanceof Waypoint.End) {
Expand All @@ -155,8 +173,8 @@ public VehicleDistance calculateInsertionDistance(Insertion insertion, DetourTim
Task task = insertion.vehicleEntry.vehicle.getSchedule().getTasks().get(index);

if (task instanceof DrtStopTask) {
occupancy += getPassengers(((DrtStopTask) task).getPickupRequests().values());
occupancy -= getPassengers(((DrtStopTask) task).getDropoffRequests().values());
occupancy = occupancy.add(getPassengers(((DrtStopTask) task).getPickupRequests().values()));
occupancy = occupancy.subtract(getPassengers(((DrtStopTask) task).getDropoffRequests().values()));
}

if (task instanceof DriveTask) {
Expand All @@ -171,20 +189,20 @@ public VehicleDistance calculateInsertionDistance(Insertion insertion, DetourTim
double passengerDistance = 0.0;

for (var entry : addedDistances) {
if (entry.occupancy == 0) {
if (entry.occupancy.isEmpty()) {
emptyDriveDistance += entry.distance;
} else {
occupiedDriveDistance += entry.distance;
passengerDistance += entry.distance * entry.occupancy;
passengerDistance += entry.occupancy.getValue() * entry.distance;
}
}

for (var entry : removedDistances) {
if (entry.occupancy == 0) {
if (entry.occupancy.isEmpty()) {
emptyDriveDistance -= entry.distance;
} else {
occupiedDriveDistance -= entry.distance;
passengerDistance -= entry.hashCode() * entry.occupancy;
passengerDistance -= entry.occupancy.getValue() * entry.distance;
}
}

Expand All @@ -208,6 +226,6 @@ public double totalDriveDistance() {
}
}

private record DistanceEntry(double distance, int occupancy) {
private record DistanceEntry(double distance, IntegerLoad occupancy) {
}
}
Loading

0 comments on commit 9402f16

Please sign in to comment.