Skip to content

Commit

Permalink
Merge pull request #3631 from moia-oss/spatialprefilterdrt
Browse files Browse the repository at this point in the history
DRT: request specific prefiltering of fleet (incl spatial implementation)
  • Loading branch information
nkuehnel authored Jan 2, 2025
2 parents 74ac44f + cfe432d commit 66f596a
Show file tree
Hide file tree
Showing 11 changed files with 508 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@

package org.matsim.contrib.drt.extension;

import java.util.Optional;
import java.util.function.Supplier;

import javax.annotation.Nullable;

import org.matsim.contrib.drt.extension.companions.DrtCompanionParams;
import org.matsim.contrib.drt.extension.services.services.params.DrtServicesParams;
import org.matsim.contrib.drt.extension.insertion.spatialFilter.DrtSpatialRequestFleetFilterParams;
import org.matsim.contrib.drt.extension.operations.DrtOperationsParams;
import org.matsim.contrib.drt.extension.services.services.params.DrtServicesParams;
import org.matsim.contrib.drt.optimizer.constraints.DefaultDrtOptimizationConstraintsSet;
import org.matsim.contrib.drt.optimizer.constraints.DrtOptimizationConstraintsSet;
import org.matsim.contrib.drt.run.DrtConfigGroup;

import javax.annotation.Nullable;
import java.util.Optional;
import java.util.function.Supplier;

/**
* @author Steffen Axer
* <p>
Expand All @@ -47,6 +47,9 @@ public class DrtWithExtensionsConfigGroup extends DrtConfigGroup {
@Nullable
private DrtServicesParams drtServicesParams;

@Nullable
private DrtSpatialRequestFleetFilterParams drtSpatialRequestFleetFilterParams;

public DrtWithExtensionsConfigGroup() {
this(DefaultDrtOptimizationConstraintsSet::new);
}
Expand All @@ -64,6 +67,10 @@ public DrtWithExtensionsConfigGroup(Supplier<DrtOptimizationConstraintsSet> drtO
// Optional
addDefinition(DrtServicesParams.SET_TYPE, DrtServicesParams::new, () -> drtServicesParams,
params -> drtServicesParams = (DrtServicesParams) params);

// Optional
addDefinition(DrtSpatialRequestFleetFilterParams.SET_NAME, DrtSpatialRequestFleetFilterParams::new, () -> drtSpatialRequestFleetFilterParams,
params -> drtSpatialRequestFleetFilterParams = (DrtSpatialRequestFleetFilterParams) params);
}

public Optional<DrtCompanionParams> getDrtCompanionParams() {
Expand All @@ -78,4 +85,7 @@ public Optional<DrtServicesParams> getServicesParams() {
return Optional.ofNullable(drtServicesParams);
}

public Optional<DrtSpatialRequestFleetFilterParams> getSpatialRequestFleetFilterParams() {
return Optional.ofNullable(drtSpatialRequestFleetFilterParams);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

package org.matsim.contrib.drt.extension.edrt.run;

import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.matsim.api.core.v01.network.Network;
import org.matsim.contrib.drt.extension.edrt.EDrtActionCreator;
import org.matsim.contrib.drt.extension.edrt.optimizer.EDrtOptimizer;
Expand All @@ -29,18 +31,12 @@
import org.matsim.contrib.drt.extension.edrt.schedule.EDrtTaskFactoryImpl;
import org.matsim.contrib.drt.extension.edrt.scheduler.EmptyVehicleChargingScheduler;
import org.matsim.contrib.drt.optimizer.*;
import org.matsim.contrib.drt.optimizer.constraints.DefaultDrtOptimizationConstraintsSet;
import org.matsim.contrib.drt.optimizer.constraints.DrtOptimizationConstraintsSet;
import org.matsim.contrib.drt.optimizer.depot.DepotFinder;
import org.matsim.contrib.drt.optimizer.insertion.CostCalculationStrategy;
import org.matsim.contrib.drt.optimizer.insertion.DefaultInsertionCostCalculator;
import org.matsim.contrib.drt.optimizer.insertion.DefaultUnplannedRequestInserter;
import org.matsim.contrib.drt.optimizer.insertion.DrtInsertionSearch;
import org.matsim.contrib.drt.optimizer.insertion.InsertionCostCalculator;
import org.matsim.contrib.drt.optimizer.insertion.UnplannedRequestInserter;
import org.matsim.contrib.drt.optimizer.insertion.*;
import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingStrategy;
import org.matsim.contrib.drt.passenger.DrtOfferAcceptor;
import org.matsim.contrib.drt.passenger.DefaultOfferAcceptor;
import org.matsim.contrib.drt.passenger.DrtOfferAcceptor;
import org.matsim.contrib.drt.prebooking.PrebookingActionCreator;
import org.matsim.contrib.drt.run.DrtConfigGroup;
import org.matsim.contrib.drt.schedule.DrtTaskFactory;
Expand Down Expand Up @@ -69,9 +65,6 @@
import org.matsim.core.router.util.TravelDisutility;
import org.matsim.core.router.util.TravelTime;

import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
* @author Michal Maciejewski (michalm)
*/
Expand Down Expand Up @@ -128,14 +121,16 @@ public EmptyVehicleChargingScheduler get() {
addModalComponent(QSimScopeForkJoinPoolHolder.class,
() -> new QSimScopeForkJoinPoolHolder(drtCfg.numberOfThreads));

bindModal(RequestFleetFilter.class).toProvider(modalProvider(getter -> RequestFleetFilter.none));

bindModal(UnplannedRequestInserter.class).toProvider(modalProvider(
getter -> new DefaultUnplannedRequestInserter(drtCfg, getter.getModal(Fleet.class),
getter.get(MobsimTimer.class), getter.get(EventsManager.class),
getter.getModal(RequestInsertionScheduler.class),
getter.getModal(VehicleEntry.EntryFactory.class), getter.getModal(DrtInsertionSearch.class),
getter.getModal(DrtRequestInsertionRetryQueue.class), getter.getModal(DrtOfferAcceptor.class),
getter.getModal(QSimScopeForkJoinPoolHolder.class).getPool(),
getter.getModal(PassengerStopDurationProvider.class)))).asEagerSingleton();
getter.getModal(PassengerStopDurationProvider.class), getter.getModal(RequestFleetFilter.class)))).asEagerSingleton();

bindModal(InsertionCostCalculator.class).toProvider(modalProvider(
getter -> new DefaultInsertionCostCalculator(getter.getModal(CostCalculationStrategy.class),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/* *********************************************************************** *
* project: org.matsim.*
* *
* *********************************************************************** *
* *
* copyright : (C) 2024 by the members listed in the COPYING, *
* LICENSE and WARRANTY file. *
* email : info at matsim dot org *
* *
* *********************************************************************** *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* See also COPYING, LICENSE and WARRANTY file *
* *
* *********************************************************************** */

package org.matsim.contrib.drt.extension.insertion.spatialFilter;

import com.google.common.base.Verify;
import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
import org.matsim.contrib.common.util.ReflectiveConfigGroupWithConfigurableParameterSets;
import org.matsim.core.config.Config;

/**
* @author steffenaxer
*/
public class DrtSpatialRequestFleetFilterParams extends ReflectiveConfigGroupWithConfigurableParameterSets {

public static final String SET_NAME = "spatialRequestFleetFilter";

public DrtSpatialRequestFleetFilterParams() {
super(SET_NAME);
}

@Parameter
@Positive
@Comment("Expansion factor for the iterative expansion of search radius until max expansion or the" +
"minimum number of candidates is reached. Must be positive.")
public double expansionFactor = 2;

@Parameter
@PositiveOrZero
@Comment("Minimum expansion in map units (meters in most projections).")
public double minExpansion = 1000;

@Parameter
@PositiveOrZero
@Comment("Maximum expansion in map units (meters in most projections).")
public double maxExpansion = 5000;

@Parameter
@Comment("Returns the unfiltered fleet if the filter did not keep enough candidates.")
public boolean returnAllIfEmpty = true;

@Parameter
@Positive
@Comment("Minimum number of vehicle candidates the filter has to find.")
public int minCandidates = 1;

@Parameter
@PositiveOrZero
@Comment("Update interval of the periodically built spatial search tree of vehicle positions.")
public double updateInterval = 5 * 600;

@Override
protected void checkConsistency(Config config) {
super.checkConsistency(config);
Verify.verify(expansionFactor > 0, "Expansion factor must be greater than zero");
Verify.verify(minExpansion <= maxExpansion, "Max expansion must not be smaller than minimum expansion");
Verify.verify(minExpansion >= 0, "Expansion must be greater than zero");
Verify.verify(minCandidates > 0, "Minimum number of candidates must be positive");
Verify.verify(updateInterval >= 0, "Update interval must not be negative");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* *********************************************************************** *
* project: org.matsim.*
* *
* *********************************************************************** *
* *
* copyright : (C) 2024 by the members listed in the COPYING, *
* LICENSE and WARRANTY file. *
* email : info at matsim dot org *
* *
* *********************************************************************** *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* See also COPYING, LICENSE and WARRANTY file *
* *
* *********************************************************************** */

package org.matsim.contrib.drt.extension.insertion.spatialFilter;

import org.matsim.contrib.drt.extension.DrtWithExtensionsConfigGroup;
import org.matsim.contrib.drt.optimizer.insertion.RequestFleetFilter;
import org.matsim.contrib.drt.run.DrtConfigGroup;
import org.matsim.contrib.dvrp.fleet.Fleet;
import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule;
import org.matsim.core.mobsim.framework.MobsimTimer;

/**
* @author nkuehnel | MOIA
*/
public class SpatialFilterInsertionSearchQSimModule extends AbstractDvrpModeQSimModule {


private final DrtSpatialRequestFleetFilterParams drtSpatialRequestFleetFilterParams;

public SpatialFilterInsertionSearchQSimModule(DrtConfigGroup drtCfg) {
super(drtCfg.getMode());
if(drtCfg instanceof DrtWithExtensionsConfigGroup withExtensionsConfigGroup &&
withExtensionsConfigGroup.getSpatialRequestFleetFilterParams().isPresent()) {
drtSpatialRequestFleetFilterParams = withExtensionsConfigGroup.getSpatialRequestFleetFilterParams().get();
} else {
throw new RuntimeException("Requires DrtSpatialRequestFleetFilterParams to be set. Use DrtWithExtensionsConfigGroup " +
"to do so.");
}
}

public record SpatialInsertionFilterSettings(double expansionIncrement, double minExpansion, double maxExpansion,
boolean returnAllIfEmpty, int minCandidates, double updateInterval){}

@Override
protected void configureQSim() {
bindModal(RequestFleetFilter.class).toProvider(modalProvider(getter ->
new SpatialRequestFleetFilter(getter.getModal(Fleet.class), getter.get(MobsimTimer.class), drtSpatialRequestFleetFilterParams)
));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/* *********************************************************************** *
* project: org.matsim.*
* *
* *********************************************************************** *
* *
* copyright : (C) 2024 by the members listed in the COPYING, *
* LICENSE and WARRANTY file. *
* email : info at matsim dot org *
* *
* *********************************************************************** *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* See also COPYING, LICENSE and WARRANTY file *
* *
* *********************************************************************** */

package org.matsim.contrib.drt.extension.insertion.spatialFilter;

import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.Point;
import org.locationtech.jts.index.strtree.STRtree;
import org.matsim.api.core.v01.Id;
import org.matsim.api.core.v01.network.Link;
import org.matsim.contrib.drt.optimizer.VehicleEntry;
import org.matsim.contrib.drt.optimizer.insertion.RequestFleetFilter;
import org.matsim.contrib.drt.passenger.DrtRequest;
import org.matsim.contrib.drt.schedule.DrtStopTask;
import org.matsim.contrib.dvrp.fleet.DvrpVehicle;
import org.matsim.contrib.dvrp.fleet.Fleet;
import org.matsim.contrib.dvrp.schedule.DriveTask;
import org.matsim.contrib.dvrp.schedule.Schedule;
import org.matsim.contrib.dvrp.schedule.StayTask;
import org.matsim.contrib.dvrp.schedule.Task;
import org.matsim.contrib.dvrp.tracker.OnlineDriveTaskTracker;
import org.matsim.core.mobsim.framework.MobsimTimer;
import org.matsim.core.utils.geometry.GeometryUtils;

import java.util.*;

import static org.matsim.contrib.drt.schedule.DrtTaskBaseType.getBaseTypeOrElseThrow;

/**
* Filter that periodically updates a spatial search tree with current vehicle positions.
* For a given request, only returns "nearby" vehicles.
* Suitable for large scenarios with a certain degree of spatial coverage
* Reduces insertion generation downstream.
*
* The spatial filter will start with a minimum expansion around the request origin and will
* iteratively expand further by the increment factor until either the maximum expansion or
* a minimum number of candidates is found.
*
*
* @author nuehnel / MOIA
*/
public class SpatialRequestFleetFilter implements RequestFleetFilter {

private STRtree tree = new STRtree();

private final Fleet fleet;
private final MobsimTimer mobsimTimer;
private final double expansionIncrementFactor;
private final double maxExpansion;
private final double minExpansion;

private final boolean returnAllIfEmpty;

private final int minCandidates;

private final double updateInterval;

public SpatialRequestFleetFilter(Fleet fleet, MobsimTimer mobsimTimer,
DrtSpatialRequestFleetFilterParams params) {
this.fleet = fleet;
this.mobsimTimer = mobsimTimer;
this.expansionIncrementFactor = params.expansionFactor;
this.minExpansion = params.minExpansion;
this.maxExpansion = params.maxExpansion;
this.returnAllIfEmpty = params.returnAllIfEmpty;
this.minCandidates = params.minCandidates;
this.updateInterval = params.updateInterval;
}

@Override
public Collection<VehicleEntry> filter(DrtRequest drtRequest, Map<Id<DvrpVehicle>, VehicleEntry> vehicleEntries, double now) {
if ((mobsimTimer.getTimeOfDay() % updateInterval) == 0) {
buildTree();
}
return filterEntries(vehicleEntries, drtRequest);
}

private Collection<VehicleEntry> filterEntries(Map<Id<DvrpVehicle>, VehicleEntry> vehicleEntries, DrtRequest drtRequest) {
List<Id<DvrpVehicle>> result = Collections.emptyList();
Point point = GeometryUtils.createGeotoolsPoint(drtRequest.getFromLink().getToNode().getCoord());

for (double expansion = minExpansion; expansion <= maxExpansion && result.size() < minCandidates; expansion*= expansionIncrementFactor) {
Envelope envelopeInternal = point.getEnvelopeInternal();
envelopeInternal.expandBy(expansion);
result = tree.query(envelopeInternal);
}

if(result.size() < minCandidates) {
if(returnAllIfEmpty) {
return vehicleEntries.values();
}
return Collections.emptySet();
}
return extract(vehicleEntries, result);
}

private Collection<VehicleEntry> extract(Map<Id<DvrpVehicle>, VehicleEntry> vehicleEntries, List<Id<DvrpVehicle>> result) {
Set<VehicleEntry> extracted = new LinkedHashSet<>();
for (Id<DvrpVehicle> dvrpVehicleId : result) {
extracted.add(vehicleEntries.get(dvrpVehicleId));
}
return extracted;
}

private void buildTree() {
tree = new STRtree();
for (DvrpVehicle vehicle : fleet.getVehicles().values()) {
Schedule schedule = vehicle.getSchedule();
Task startTask;
Link start;
if (schedule.getStatus() == Schedule.ScheduleStatus.STARTED) {
startTask = schedule.getCurrentTask();
start = switch (getBaseTypeOrElseThrow(startTask)) {
case DRIVE -> {
var driveTask = (DriveTask) startTask;
var diversionPoint = ((OnlineDriveTaskTracker) driveTask.getTaskTracker()).getDiversionPoint();
yield diversionPoint != null ? diversionPoint.link : //diversion possible
driveTask.getPath().getToLink();// too late for diversion
}
case STOP -> ((DrtStopTask) startTask).getLink();
case STAY -> ((StayTask) startTask).getLink();
};
tree.insert(GeometryUtils.createGeotoolsPoint(start.getCoord()).getEnvelopeInternal(), vehicle.getId());
}
}
}
}
Loading

0 comments on commit 66f596a

Please sign in to comment.