Skip to content

Commit 459c32c

Browse files
authored
feat(fiss): enable VehicleBehavior.wait for FISS (matsim-org#4885)
* Make QLinkI.makeVehicleAvailableToNextDriver accessible * fix(fiss): fix instantaneous vehicle teleportation in FISS * test(fiss): add vehicle behavior wait scenario to FISS integration tests
1 parent ff3b453 commit 459c32c

5 files changed

Lines changed: 459 additions & 107 deletions

File tree

contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/fiss/FISS.java

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,23 @@
2626
import org.matsim.core.gbl.MatsimRandom;
2727
import org.matsim.core.mobsim.dsim.DistributedDepartureHandler;
2828
import org.matsim.core.mobsim.framework.MobsimAgent;
29+
import org.matsim.core.mobsim.framework.MobsimDriverAgent;
2930
import org.matsim.core.mobsim.framework.PlanAgent;
3031
import org.matsim.core.mobsim.qsim.InternalInterface;
3132
import org.matsim.core.mobsim.qsim.TeleportationEngine;
3233
import org.matsim.core.mobsim.qsim.interfaces.DepartureHandler;
3334
import org.matsim.core.mobsim.qsim.interfaces.MobsimEngine;
3435
import org.matsim.core.mobsim.qsim.pt.TransitDriverAgent;
3536
import org.matsim.core.mobsim.qsim.qnetsimengine.NetworkModeDepartureHandler;
37+
import org.matsim.core.mobsim.qsim.qnetsimengine.QLinkI;
38+
import org.matsim.core.mobsim.qsim.qnetsimengine.QNetsimEngineI;
39+
import org.matsim.core.mobsim.qsim.qnetsimengine.QVehicle;
3640
import org.matsim.core.population.routes.NetworkRoute;
3741
import org.matsim.core.router.util.TravelTime;
3842
import org.matsim.vehicles.Vehicle;
3943
import org.matsim.vehicles.VehicleType;
4044

45+
import java.util.PriorityQueue;
4146
import java.util.Random;
4247

4348
/**
@@ -77,12 +82,15 @@ public class FISS implements NetworkModeDepartureHandler, DistributedDepartureHa
7782
private final MatsimServices matsimServices;
7883
private final QSimConfigGroup qsimConfig;
7984
private final Scenario scenario;
85+
private final QNetsimEngineI qNetsimEngine;
8086

87+
private final PriorityQueue<VehicleArrivalEntry> vehicleArrivals = new PriorityQueue<>();
8188

8289
@Inject
8390
FISS(MatsimServices matsimServices, Scenario scenario, FISSConfigGroup fissConfigGroup, Network network, TeleportationEngine teleport,
8491
@Named(TransportMode.car) TravelTime travelTime,
85-
@Named("base-network-mode-departure-handler") NetworkModeDepartureHandler networkModeDepartureHandler) {
92+
@Named("base-network-mode-departure-handler") NetworkModeDepartureHandler networkModeDepartureHandler,
93+
QNetsimEngineI qNetsimEngine) {
8694
this.delegate = networkModeDepartureHandler;
8795
this.fissConfigGroup = fissConfigGroup;
8896
this.network = network;
@@ -92,6 +100,7 @@ public class FISS implements NetworkModeDepartureHandler, DistributedDepartureHa
92100
this.scenario = scenario;
93101
this.qsimConfig = scenario.getConfig().qsim();
94102
this.random = MatsimRandom.getLocalInstance();
103+
this.qNetsimEngine = qNetsimEngine;
95104
}
96105

97106
@Override
@@ -109,53 +118,56 @@ public boolean handleDeparture(double now, MobsimAgent agent, Id<Link> linkId) {
109118
}
110119

111120
// This updates the travel time. Teleportation departure is handled further down.
121+
double newTravelTime = 0.;
122+
Id<Link> destinationLinkId = agent.getDestinationLinkId();
112123
if (agent instanceof PlanAgent planAgent) {
113124
Leg currentLeg = (Leg) planAgent.getCurrentPlanElement();
114125
NetworkRoute networkRoute = (NetworkRoute) currentLeg.getRoute();
115126
Person person = planAgent.getCurrentPlan().getPerson();
116127
Vehicle vehicle = this.scenario.getVehicles().getVehicles().get(networkRoute.getVehicleId());
117128

118-
double newTravelTime = calcQSimTravelTime(networkRoute, now, person, vehicle);
129+
newTravelTime = calcQSimTravelTime(networkRoute, now, person, vehicle);
119130
LOG.debug("New travelTime: {}, was {}", newTravelTime, networkRoute.getTravelTime().orElseGet(() -> Double.NaN));
120131

121132
networkRoute.setTravelTime(newTravelTime);
122133
}
123134

124-
// !!!! The following is not a teleportation-pull (as it is in NetworkModeDpHandler...), but a teleportation-push. yyyy Are we sure
125-
// that these are the same? E.g. if the vehicle needs to be made available for the next driver? kai, feb'25
126-
127-
// // remove vehicle of teleported agent from parking spot
128-
// QVehicle removedVehicle = null;
129-
// if (agent instanceof MobsimDriverAgent driverAgent) {
130-
// Id<Vehicle> vehicleId = driverAgent.getPlannedVehicleId();
131-
// QLinkI qLinkI = (QLinkI) this.qNetsimEngine.getNetsimNetwork().getNetsimLink(linkId);
132-
// removedVehicle = qLinkI.removeParkedVehicle(vehicleId);
133-
// if (removedVehicle == null) {
134-
// throw new RuntimeException(
135-
// "Could not remove parked vehicle with id " + vehicleId + " on the link id "
136-
// + qNetsimEngine.getVehicles().get(vehicleId).getCurrentLinkId()
137-
// + ". Maybe it is currently used by someone else?"
138-
// + " (In which case ignoring this exception would lead to duplication of this vehicle.) "
139-
// + "Maybe was never placed onto a link?");
140-
// }
141-
// // yyyy need to check how this is handled elsewhere. I think that with some config settings we wait until the vehicle becomes available--???
142-
// }
135+
// Remove vehicle from departure link and schedule delayed arrival at
136+
// destination.
137+
if (agent instanceof MobsimDriverAgent driverAgent) {
138+
Id<Vehicle> vehicleId = driverAgent.getPlannedVehicleId();
139+
QLinkI qLinkI = (QLinkI) this.qNetsimEngine.getNetsimNetwork().getNetsimLink(linkId);
140+
QVehicle removedVehicle = qLinkI.removeParkedVehicle(vehicleId);
141+
if (removedVehicle == null) {
142+
// Vehicle not yet available on this link — may still be in transit from another
143+
// teleported agent's trip. Fall back to standard departure handling: with
144+
// VehicleBehavior.wait the agent will wait until the vehicle arrives via
145+
// doSimStep.
146+
LOG.info(
147+
"Vehicle {} not found on link {} for agent {} at time {}. Falling back to standard departure (agent will wait for vehicle).",
148+
vehicleId, linkId, driverAgent.getId(), now);
149+
return delegate.handleDeparture(now, agent, linkId);
150+
}
151+
double arrivalTime = now + newTravelTime;
152+
vehicleArrivals.add(new VehicleArrivalEntry(arrivalTime, removedVehicle, destinationLinkId));
153+
}
154+
143155
boolean result = teleport.handleDeparture(now, agent, linkId);
144156
Gbl.assertIf(result); // otherwise we are now confused
145157

146-
// teleport vehicle right after agent
147-
// if (removedVehicle != null) {
148-
// Id<Link> destinationLinkId = agent.getDestinationLinkId();
149-
// QLinkI qLinkDest = (QLinkI) this.qNetsimEngine.getNetsimNetwork().getNetsimLink(destinationLinkId);
150-
// qLinkDest.addParkedVehicle(removedVehicle);
151-
// }
152-
153158
return result;
154159

155160
}
156161

157162
@Override
158163
public void doSimStep(double time) {
164+
// Deliver vehicles that have reached their arrival time.
165+
while (!vehicleArrivals.isEmpty() && vehicleArrivals.peek().arrivalTime <= time) {
166+
VehicleArrivalEntry entry = vehicleArrivals.poll();
167+
QLinkI qLinkDest = (QLinkI) this.qNetsimEngine.getNetsimNetwork().getNetsimLink(entry.destinationLinkId);
168+
qLinkDest.addParkedVehicle(entry.vehicle);
169+
qLinkDest.makeVehicleAvailableToNextDriver(entry.vehicle);
170+
}
159171
teleport.doSimStep(time);
160172
}
161173

@@ -248,4 +260,12 @@ private boolean switchOffFISS() {
248260
public void setInternalInterface(InternalInterface internalInterface) {
249261
teleport.setInternalInterface(internalInterface);
250262
}
263+
264+
private record VehicleArrivalEntry(double arrivalTime, QVehicle vehicle,
265+
Id<Link> destinationLinkId) implements Comparable<VehicleArrivalEntry> {
266+
@Override
267+
public int compareTo(VehicleArrivalEntry o) {
268+
return Double.compare(this.arrivalTime, o.arrivalTime);
269+
}
270+
}
251271
}

contribs/drt-extensions/src/main/java/org/matsim/contrib/drt/extension/fiss/FISSConfigGroup.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,10 @@ public FISSConfigGroup() {
5151
super.checkConsistency( config );
5252

5353
switch( config.qsim().getVehicleBehavior() ){
54-
case teleport -> {
54+
case teleport, wait -> {
5555
}
5656
default -> {
57-
throw new RuntimeException( "FISS only works together with vehicle behavior=teleport. See code for more info." );
58-
// This was previously implemented such that it also ran through with other settings. However, it would teleport the
59-
// vehicle immediately to its destination, thus leading to a faulty physical modelling of "wait" or "exception". I
60-
// can't say if a possibly waiting agent would wait for the driver of the vehicle, or for the vehicle itself; this
61-
// would need to be checked. kai, feb'25
57+
throw new RuntimeException( "FISS only works together with vehicle behavior teleport or wait." );
6258
}
6359
}
6460

0 commit comments

Comments
 (0)