From 739649a7c7714fde45b0f147f926641877454bfb Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Tue, 12 Dec 2023 14:11:50 +0100 Subject: [PATCH 1/6] consolidate group bookings and prebookings in passenger engine and sharing metrics --- .../PassengerRequestBookedEvent.java | 21 +++-- .../drt/prebooking/PrebookingManager.java | 62 ++++++------- .../prebooking/PrebookingModeQSimModule.java | 10 +-- .../logic/helpers/PrebookingQueue.java | 25 +++++- .../matsim/contrib/drt/run/DrtModeModule.java | 22 ++--- .../sharingmetrics/SharingMetricsModule.java | 2 +- .../sharingmetrics/SharingMetricsTracker.java | 40 ++++++--- .../drt/prebooking/PrebookingGroupTest.java | 80 +++++++++++++++++ .../prebooking/PrebookingTestEnvironment.java | 44 ++++----- .../drt/sharingmetrics/SharingFactorTest.java | 46 +++++----- .../passenger/DefaultPassengerEngine.java | 90 ++++++++++++++----- .../passenger/PassengerEngineQSimModule.java | 10 +-- .../passenger/PassengerGroupIdentifier.java | 7 +- .../PassengerRequestRejectedEvent.java | 3 +- ...ngineTest.java => PassengerGroupTest.java} | 17 +++- 15 files changed, 312 insertions(+), 167 deletions(-) create mode 100644 contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java rename contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/{GroupPassengerEngineTest.java => PassengerGroupTest.java} (93%) diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java index e58f8835dfb..c160f25c288 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java @@ -1,23 +1,24 @@ package org.matsim.contrib.drt.prebooking; -import java.util.List; -import java.util.Map; -import java.util.Objects; - import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.GenericEvent; import org.matsim.api.core.v01.population.Person; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.passenger.AbstractPassengerRequestEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + /** * @author Sebastian Hörl (sebhoerl), IRT SystemX */ public class PassengerRequestBookedEvent extends AbstractPassengerRequestEvent { public static final String EVENT_TYPE = "PassengerRequest booked"; - public PassengerRequestBookedEvent(double time, String mode, Id requestId, Id personId) { - super(time, mode, requestId, List.of(personId)); + public PassengerRequestBookedEvent(double time, String mode, Id requestId, List> personIds) { + super(time, mode, requestId, personIds); } @Override @@ -30,7 +31,11 @@ public static PassengerRequestBookedEvent convert(GenericEvent event) { double time = Double.parseDouble(attributes.get(ATTRIBUTE_TIME)); String mode = Objects.requireNonNull(attributes.get(ATTRIBUTE_MODE)); Id requestId = Id.create(attributes.get(ATTRIBUTE_REQUEST), Request.class); - Id personId = Id.createPersonId(attributes.get(ATTRIBUTE_PERSON)); - return new PassengerRequestBookedEvent(time, mode, requestId, personId); + String[] personIdsAttribute = attributes.get(ATTRIBUTE_PERSON).split(","); + List> personIds = new ArrayList<>(); + for (String person : personIdsAttribute) { + personIds.add(Id.create(person, Person.class)); + } + return new PassengerRequestBookedEvent(time, mode, requestId, personIds); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java index 1fc580715f4..8b35e2b1362 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java @@ -1,15 +1,7 @@ package org.matsim.contrib.drt.prebooking; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; - -import javax.annotation.Nullable; - +import com.google.common.base.Preconditions; +import com.google.common.base.Verify; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.IdMap; import org.matsim.api.core.v01.IdSet; @@ -25,14 +17,7 @@ import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; -import org.matsim.contrib.dvrp.passenger.AdvanceRequestProvider; -import org.matsim.contrib.dvrp.passenger.PassengerRequest; -import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator; -import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent; -import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEventHandler; -import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEvent; -import org.matsim.contrib.dvrp.passenger.PassengerRequestScheduledEventHandler; -import org.matsim.contrib.dvrp.passenger.PassengerRequestValidator; +import org.matsim.contrib.dvrp.passenger.*; import org.matsim.core.api.experimental.events.EventsManager; import org.matsim.core.mobsim.framework.MobsimAgent; import org.matsim.core.mobsim.framework.MobsimAgent.State; @@ -42,9 +27,12 @@ import org.matsim.core.mobsim.qsim.InternalInterface; import org.matsim.core.mobsim.qsim.agents.WithinDayAgentUtils; import org.matsim.core.mobsim.qsim.interfaces.MobsimEngine; +import org.matsim.core.utils.collections.Tuple; -import com.google.common.base.Preconditions; -import com.google.common.base.Verify; +import javax.annotation.Nullable; +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; /** * This class manages prebooked requests. One instance of PrebookingManager @@ -163,35 +151,43 @@ private void flushRejections(double now) { // collects new bookings that need to be submitted private final ConcurrentLinkedQueue bookingQueue = new ConcurrentLinkedQueue<>(); - public void prebook(MobsimAgent person, Leg leg, double earliestDepartureTime) { - Preconditions.checkArgument(leg.getMode().equals(mode), "Invalid mode for this prebooking manager"); - Preconditions.checkState(!person.getState().equals(State.ABORT), "Cannot prebook aborted agent"); + public void prebook(List> personsLegs, double earliestDepartureTime) { + for (Tuple personLeg : personsLegs) { + Preconditions.checkArgument(personLeg.getSecond().getMode().equals(mode), "Invalid mode for this prebooking manager"); + Preconditions.checkState(!personLeg.getFirst().getState().equals(State.ABORT), "Cannot prebook aborted agent"); + } Id requestId = createRequestId(); double now = mobsimTimer.getTimeOfDay(); - eventsManager.processEvent(new PassengerRequestBookedEvent(now, mode, requestId, person.getId())); + List> personIds = personsLegs.stream().map(p -> p.getFirst().getId()).toList(); + eventsManager.processEvent(new PassengerRequestBookedEvent(now, mode, requestId, personIds)); - PassengerRequest request = requestCreator.createRequest(requestId, List.of(person.getId()), leg.getRoute(), - getLink(leg.getRoute().getStartLinkId()), getLink(leg.getRoute().getEndLinkId()), earliestDepartureTime, + Leg representativeLeg = personsLegs.get(0).getSecond(); + PassengerRequest request = requestCreator.createRequest(requestId, personIds, representativeLeg.getRoute(), + getLink(representativeLeg.getRoute().getStartLinkId()), getLink(representativeLeg.getRoute().getEndLinkId()), earliestDepartureTime, now); Set violations = requestValidator.validateRequest(request); - Plan plan = WithinDayAgentUtils.getModifiablePlan(person); - int currentLegIndex = WithinDayAgentUtils.getCurrentPlanElementIndex(person); - int prebookingLegIndex = plan.getPlanElements().indexOf(leg); + for (Tuple personLeg : personsLegs) { + Plan plan = WithinDayAgentUtils.getModifiablePlan(personLeg.getFirst()); + int currentLegIndex = WithinDayAgentUtils.getCurrentPlanElementIndex(personLeg.getFirst()); + int prebookingLegIndex = plan.getPlanElements().indexOf(personLeg.getSecond()); - if (prebookingLegIndex <= currentLegIndex) { - violations = new HashSet<>(violations); - violations.add("past leg"); // the leg for which the booking was made has already happened + if (prebookingLegIndex <= currentLegIndex) { + violations = new HashSet<>(violations); + violations.add("past leg"); // the leg for which the booking was made has already happened + } } if (!violations.isEmpty()) { String cause = String.join(", ", violations); processRejection(request, cause); } else { - leg.getAttributes().putAttribute(requestAttribute, request.getId().toString()); + for (Tuple personLeg : personsLegs) { + personLeg.getSecond().getAttributes().putAttribute(requestAttribute, request.getId().toString()); + } bookingQueue.add(request); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingModeQSimModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingModeQSimModule.java index b3f6faa4bc7..badbb8624f5 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingModeQSimModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingModeQSimModule.java @@ -1,5 +1,6 @@ package org.matsim.contrib.drt.prebooking; +import com.google.inject.Singleton; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.Population; import org.matsim.contrib.drt.optimizer.VehicleEntry; @@ -15,10 +16,7 @@ import org.matsim.contrib.drt.vrpagent.DrtActionCreator; import org.matsim.contrib.dvrp.fleet.DvrpVehicleLookup; import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; -import org.matsim.contrib.dvrp.passenger.PassengerEngine; -import org.matsim.contrib.dvrp.passenger.PassengerHandler; -import org.matsim.contrib.dvrp.passenger.PassengerRequestCreator; -import org.matsim.contrib.dvrp.passenger.PassengerRequestValidator; +import org.matsim.contrib.dvrp.passenger.*; import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; import org.matsim.contrib.dvrp.schedule.ScheduleTimingUpdater; import org.matsim.core.api.experimental.events.EventsManager; @@ -27,8 +25,6 @@ import org.matsim.core.router.util.LeastCostPathCalculator; import org.matsim.core.router.util.TravelTime; -import com.google.inject.Singleton; - public class PrebookingModeQSimModule extends AbstractDvrpModeQSimModule { private final PrebookingParams prebookingParams; @@ -65,7 +61,7 @@ protected void configureQSim() { addModalQSimComponentBinding().to(modalKey(PrebookingManager.class)); bindModal(PrebookingQueue.class).toProvider(modalProvider(getter -> { - return new PrebookingQueue(getter.getModal(PrebookingManager.class)); + return new PrebookingQueue(getter.getModal(PrebookingManager.class), getter.getModal(PassengerGroupIdentifier.class)); })).in(Singleton.class); addModalQSimComponentBinding().to(modalKey(PrebookingQueue.class)); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java index ed04764c9b4..85547c814c7 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java @@ -1,14 +1,18 @@ package org.matsim.contrib.drt.prebooking.logic.helpers; -import java.util.PriorityQueue; +import java.util.*; +import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.population.Leg; import org.matsim.contrib.drt.prebooking.PrebookingManager; +import org.matsim.contrib.dvrp.passenger.PassengerGroupIdentifier; import org.matsim.core.mobsim.framework.MobsimAgent; +import org.matsim.core.mobsim.framework.MobsimPassengerAgent; import org.matsim.core.mobsim.framework.events.MobsimBeforeSimStepEvent; import org.matsim.core.mobsim.framework.listeners.MobsimBeforeSimStepListener; import com.google.common.base.Preconditions; +import org.matsim.core.utils.collections.Tuple; /** * This service helps to define a PrebookingLogic where at some point in time @@ -16,7 +20,7 @@ * request will happen at a specific time. Once we know that the request will be * sent, we can use the present class to put it in a queue, making sure the * request will be *booked* the the time we want. - * + * * @author Sebastian Hörl (sebhoerl), IRT SystemX */ public class PrebookingQueue implements MobsimBeforeSimStepListener { @@ -27,8 +31,11 @@ public class PrebookingQueue implements MobsimBeforeSimStepListener { private double currentTime = Double.NEGATIVE_INFINITY; - public PrebookingQueue(PrebookingManager prebookingManager) { + private final PassengerGroupIdentifier groupIdentifier; + + public PrebookingQueue(PrebookingManager prebookingManager, PassengerGroupIdentifier groupIdentifier) { this.prebookingManager = prebookingManager; + this.groupIdentifier = groupIdentifier; } @Override @@ -39,9 +46,19 @@ public void notifyMobsimBeforeSimStep(@SuppressWarnings("rawtypes") MobsimBefore private void performSubmissions(double time) { currentTime = time; + Map, List> groups = new LinkedHashMap<>(); while (!queue.isEmpty() && queue.peek().submissionTime <= time) { var item = queue.poll(); - prebookingManager.prebook(item.agent(), item.leg(), item.departuretime()); + Optional> groupId = groupIdentifier.getGroupId((MobsimPassengerAgent) item.agent); + if(groupId.isEmpty()) { + prebookingManager.prebook(List.of(new Tuple<>(item.agent(), item.leg())), item.departuretime()); + } else { + groups.computeIfAbsent(groupId.get(), k -> new ArrayList<>()).add(item); + } + } + for (List group : groups.values()) { + List> personsLegs = group.stream().map(entry -> new Tuple<>(entry.agent, entry.leg)).toList(); + prebookingManager.prebook(personsLegs, group.get(0).departuretime); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeModule.java index 677e5504c3c..a0cb1fc007c 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/run/DrtModeModule.java @@ -20,18 +20,16 @@ package org.matsim.contrib.drt.run; +import com.google.inject.Key; +import com.google.inject.Singleton; +import com.google.inject.name.Names; import org.matsim.api.core.v01.network.Network; import org.matsim.contrib.drt.analysis.DrtEventSequenceCollector; import org.matsim.contrib.drt.fare.DrtFareHandler; import org.matsim.contrib.drt.optimizer.rebalancing.RebalancingModule; import org.matsim.contrib.drt.prebooking.analysis.PrebookingModeAnalysisModule; import org.matsim.contrib.drt.speedup.DrtSpeedUp; -import org.matsim.contrib.drt.stops.DefaultStopTimeCalculator; -import org.matsim.contrib.drt.stops.MinimumStopDurationAdapter; -import org.matsim.contrib.drt.stops.PassengerStopDurationProvider; -import org.matsim.contrib.drt.stops.PrebookingStopTimeCalculator; -import org.matsim.contrib.drt.stops.StaticPassengerStopDurationProvider; -import org.matsim.contrib.drt.stops.StopTimeCalculator; +import org.matsim.contrib.drt.stops.*; import org.matsim.contrib.dvrp.fleet.FleetModule; import org.matsim.contrib.dvrp.fleet.FleetSpecification; import org.matsim.contrib.dvrp.router.DvrpModeRoutingNetworkModule; @@ -44,10 +42,6 @@ import org.matsim.core.router.costcalculators.TravelDisutilityFactory; import org.matsim.core.router.util.TravelTime; -import com.google.inject.Key; -import com.google.inject.Singleton; -import com.google.inject.name.Names; - /** * @author michalm (Michal Maciejewski) */ @@ -87,15 +81,15 @@ public void install() { getter.getModal(DrtEventSequenceCollector.class)))).asEagerSingleton(); addControlerListenerBinding().to(modalKey(DrtSpeedUp.class)); }); - + bindModal(PassengerStopDurationProvider.class).toProvider(modalProvider(getter -> { return StaticPassengerStopDurationProvider.of(drtCfg.stopDuration, 0.0); })); - + bindModal(DefaultStopTimeCalculator.class).toProvider(modalProvider(getter -> { return new DefaultStopTimeCalculator(drtCfg.stopDuration); })).in(Singleton.class); - + if (drtCfg.getPrebookingParams().isEmpty()) { bindModal(StopTimeCalculator.class).toProvider(modalProvider(getter -> { return new DefaultStopTimeCalculator(drtCfg.stopDuration); @@ -105,7 +99,7 @@ public void install() { PassengerStopDurationProvider provider = getter.getModal(PassengerStopDurationProvider.class); return new MinimumStopDurationAdapter(new PrebookingStopTimeCalculator(provider), drtCfg.stopDuration); })); - + install(new PrebookingModeAnalysisModule(getMode())); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsModule.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsModule.java index 2ebe3777ac1..4e1c4c31b09 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsModule.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsModule.java @@ -21,7 +21,7 @@ public SharingMetricsModule(DrtConfigGroup drtConfigGroup) { @Override public void install() { bindModal(SharingMetricsTracker.class).toProvider(modalProvider(getter -> - new SharingMetricsTracker(personId -> true))).asEagerSingleton(); + new SharingMetricsTracker())).asEagerSingleton(); addEventHandlerBinding().to(modalKey(SharingMetricsTracker.class)); bindModal(SharingMetricsControlerListener.class).toProvider(modalProvider(getter -> new SharingMetricsControlerListener(getConfig(), drtConfigGroup, diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsTracker.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsTracker.java index f5393b29144..593361f0cdd 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsTracker.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/sharingmetrics/SharingMetricsTracker.java @@ -2,6 +2,8 @@ import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.drt.passenger.events.DrtRequestSubmittedEvent; +import org.matsim.contrib.drt.passenger.events.DrtRequestSubmittedEventHandler; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEvent; @@ -16,9 +18,7 @@ /** * @author nkuehnel / MOIA */ -public class SharingMetricsTracker implements PassengerPickedUpEventHandler, PassengerDroppedOffEventHandler, MobsimBeforeCleanupListener { - - private final GroupPredicate groupPredicate; +public class SharingMetricsTracker implements DrtRequestSubmittedEventHandler, PassengerPickedUpEventHandler, PassengerDroppedOffEventHandler, MobsimBeforeCleanupListener { record Segment(double start, int occupancy) { } @@ -27,27 +27,34 @@ record Segment(double start, int occupancy) { private final Map, List> segments = new HashMap<>(); + private final Map, List>> knownGroups = new HashMap<>(); + private final Map, Double> sharingFactors = new HashMap<>(); private final Map, Boolean> poolingRate = new HashMap<>(); - public interface GroupPredicate { - boolean isGroupRepresentative(Id personId); - } - public SharingMetricsTracker(GroupPredicate groupPredicate) { - this.groupPredicate = groupPredicate; + public SharingMetricsTracker() { } + @Override + public void handleEvent(DrtRequestSubmittedEvent event) { + knownGroups.put(event.getRequestId(), new ArrayList<>(event.getPersonIds())); + } @Override public void handleEvent(PassengerDroppedOffEvent event) { - if (groupPredicate.isGroupRepresentative(event.getPersonId())) { + + List> passengers = knownGroups.get(event.getRequestId()); + passengers.remove(event.getPersonId()); + + if (passengers.isEmpty()) { + // all passengers of the group have been dropped off + knownGroups.remove(event.getRequestId()); List> occupancy = map.get(event.getVehicleId()); occupancy.remove(event.getRequestId()); occupancy.forEach(p -> segments.get(p).add(new Segment(event.getTime(), occupancy.size()))); - List finishedSegments = segments.remove(event.getRequestId()); double total = 0; @@ -82,9 +89,15 @@ public void handleEvent(PassengerDroppedOffEvent event) { @Override public void handleEvent(PassengerPickedUpEvent event) { - if (groupPredicate.isGroupRepresentative(event.getPersonId())) { - map.computeIfAbsent(event.getVehicleId(), vehicleId -> new ArrayList<>()); - List> occupancy = map.get(event.getVehicleId()); + List> occupancy = map.computeIfAbsent(event.getVehicleId(), vehicleId -> new ArrayList<>()); + if (occupancy.contains(event.getRequestId())) { + if (knownGroups.get(event.getRequestId()).size() > 1) { + //group request, skip for additional persons + return; + } else { + throw new RuntimeException("Single rider request picked up twice!"); + } + } else { occupancy.add(event.getRequestId()); occupancy.forEach( p -> segments.computeIfAbsent(p, requestId -> new ArrayList<>()).add(new Segment(event.getTime(), occupancy.size())) @@ -98,6 +111,7 @@ public void notifyMobsimBeforeCleanup(MobsimBeforeCleanupEvent e) { segments.clear(); poolingRate.clear(); sharingFactors.clear(); + knownGroups.clear(); } public Map, Double> getSharingFactors() { diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java new file mode 100644 index 00000000000..2a9829ed379 --- /dev/null +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java @@ -0,0 +1,80 @@ +package org.matsim.contrib.drt.prebooking; + +import org.junit.Rule; +import org.junit.Test; +import org.matsim.api.core.v01.Id; +import org.matsim.contrib.drt.prebooking.logic.AttributeBasedPrebookingLogic; +import org.matsim.contrib.drt.run.DrtConfigGroup; +import org.matsim.contrib.dvrp.passenger.PassengerGroupIdentifier; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; +import org.matsim.core.controler.Controler; +import org.matsim.core.mobsim.framework.MobsimPassengerAgent; +import org.matsim.testcases.MatsimTestUtils; + +import java.util.Optional; + +import static org.junit.Assert.assertEquals; + +public class PrebookingGroupTest { + + @Rule + public MatsimTestUtils utils = new MatsimTestUtils(); + + + static PrebookingParams installPrebooking(Controler controller) { + return installPrebooking(controller, true); + } + + static PrebookingParams installPrebooking(Controler controller, boolean installLogic) { + DrtConfigGroup drtConfig = DrtConfigGroup.getSingleModeDrtConfig(controller.getConfig()); + drtConfig.addParameterSet(new PrebookingParams()); + + if (installLogic) { + AttributeBasedPrebookingLogic.install(controller, drtConfig); + } + controller.addOverridingModule(new AbstractDvrpModeModule(drtConfig.getMode()) { + @Override + public void install() { + bindModal(PassengerGroupIdentifier.class).toInstance(new PassengerGroupIdentifier() { + @Override + public Optional> getGroupId(MobsimPassengerAgent agent) { + return Optional.of(Id.create("group", PassengerGroup.class)); + } + }); + } + }); + + return drtConfig.getPrebookingParams().get(); + } + + @Test + public void oneRequestArrivingLate() { + + // copy of prebooking test but with two persons in one request + PrebookingTestEnvironment environment = new PrebookingTestEnvironment(utils) // + .addVehicle("vehicleA", 1, 1) // + // 1800 indicated but only departing 2000 + .addRequest("personA", 0, 0, 5, 5, 2000.0, 0.0, 2000.0 - 200.0) // + .addRequest("personB", 0, 0, 5, 5, 2000.0, 0.0, 2000.0 - 200.0) // + .configure(600.0, 1.3, 600.0, 60.0) // + .endTime(10.0 * 3600.0); + + Controler controller = environment.build(); + installPrebooking(controller); + controller.run(); + + PrebookingTestEnvironment.RequestInfo requestInfoA = environment.getRequestInfo().get("personA"); + assertEquals(0.0, requestInfoA.submissionTime, 1e-3); + assertEquals(2060.0, requestInfoA.pickupTime, 1e-3); + assertEquals(2271.0, requestInfoA.dropoffTime, 1e-3); + + PrebookingTestEnvironment.RequestInfo requestInfoB = environment.getRequestInfo().get("personB"); + assertEquals(0.0, requestInfoB.submissionTime, 1e-3); + assertEquals(2060.0, requestInfoB.pickupTime, 1e-3); + assertEquals(2271.0, requestInfoB.dropoffTime, 1e-3); + + // assert both persons are part of same drt request + assertEquals(requestInfoA.drtRequestId, requestInfoB.drtRequestId); + } +} + diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTestEnvironment.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTestEnvironment.java index 8e1f6394388..a80d8a42386 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTestEnvironment.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTestEnvironment.java @@ -1,11 +1,5 @@ package org.matsim.contrib.drt.prebooking; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; - import org.apache.commons.lang3.tuple.Pair; import org.matsim.api.core.v01.Coord; import org.matsim.api.core.v01.Id; @@ -14,12 +8,7 @@ import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.network.NetworkFactory; import org.matsim.api.core.v01.network.Node; -import org.matsim.api.core.v01.population.Activity; -import org.matsim.api.core.v01.population.Leg; -import org.matsim.api.core.v01.population.Person; -import org.matsim.api.core.v01.population.Plan; -import org.matsim.api.core.v01.population.Population; -import org.matsim.api.core.v01.population.PopulationFactory; +import org.matsim.api.core.v01.population.*; import org.matsim.contrib.drt.optimizer.insertion.DrtInsertionSearchParams; import org.matsim.contrib.drt.optimizer.insertion.selective.SelectiveInsertionSearchParams; import org.matsim.contrib.drt.passenger.events.DrtRequestSubmittedEvent; @@ -35,12 +24,7 @@ import org.matsim.contrib.dvrp.fleet.FleetSpecification; import org.matsim.contrib.dvrp.fleet.FleetSpecificationImpl; import org.matsim.contrib.dvrp.fleet.ImmutableDvrpVehicleSpecification; -import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEvent; -import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEventHandler; -import org.matsim.contrib.dvrp.passenger.PassengerPickedUpEvent; -import org.matsim.contrib.dvrp.passenger.PassengerPickedUpEventHandler; -import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEvent; -import org.matsim.contrib.dvrp.passenger.PassengerRequestRejectedEventHandler; +import org.matsim.contrib.dvrp.passenger.*; import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; import org.matsim.contrib.dvrp.run.DvrpConfigGroup; import org.matsim.contrib.dvrp.run.DvrpModule; @@ -61,6 +45,11 @@ import org.matsim.core.scenario.ScenarioUtils; import org.matsim.testcases.MatsimTestUtils; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + public class PrebookingTestEnvironment { private final MatsimTestUtils utils; @@ -361,6 +350,7 @@ public class RequestInfo { public double dropoffTime = Double.NaN; public List submissionTimes = new LinkedList<>(); + public Id drtRequestId = null; } private Map requestInfo = new HashMap<>(); @@ -382,17 +372,21 @@ private class RequestListener implements DrtRequestSubmittedEventHandler, Passen PassengerDroppedOffEventHandler, PassengerRequestRejectedEventHandler { @Override public void handleEvent(DrtRequestSubmittedEvent event) { - String ids = event.getPersonIds().stream().map(Object::toString).collect(Collectors.joining("-")); - requestInfo.computeIfAbsent(ids, id -> new RequestInfo()).submissionTime = event - .getTime(); - requestInfo.computeIfAbsent(ids, id -> new RequestInfo()).submissionTimes - .add(event.getTime()); - + for (Id personId : event.getPersonIds()) { + requestInfo.computeIfAbsent(personId.toString(), id -> new RequestInfo()).submissionTime = event + .getTime(); + requestInfo.computeIfAbsent(personId.toString(), id -> new RequestInfo()).drtRequestId = event + .getRequestId(); + requestInfo.computeIfAbsent(personId.toString(), id -> new RequestInfo()).submissionTimes + .add(event.getTime()); + } } @Override public void handleEvent(PassengerRequestRejectedEvent event) { - requestInfo.computeIfAbsent(event.getPersonIds().stream().map(Object::toString).collect(Collectors.joining("-")), id -> new RequestInfo()).rejected = true; + for (Id personId : event.getPersonIds()) { + requestInfo.computeIfAbsent(personId.toString(), id -> new RequestInfo()).rejected = true; + } } @Override diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java index e98a066d0ce..225edd6c331 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/sharingmetrics/SharingFactorTest.java @@ -3,7 +3,7 @@ import org.junit.Assert; import org.junit.Test; import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.population.Person; +import org.matsim.contrib.drt.passenger.events.DrtRequestSubmittedEvent; import org.matsim.contrib.dvrp.fleet.DvrpVehicle; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.passenger.PassengerDroppedOffEvent; @@ -11,8 +11,7 @@ import org.matsim.core.events.ParallelEventsManager; import org.matsim.testcases.MatsimTestUtils; -import java.util.HashSet; -import java.util.Set; +import java.util.List; /** * @author nkuehnel / MOIA @@ -31,20 +30,8 @@ public void testDrtSharingFactorHandler() { var personId2 = Id.createPersonId("p2"); - Set> groupRepresentatives = new HashSet<>(); - - // two separate bookings - groupRepresentatives.add(personId1); - groupRepresentatives.add(personId2); - - ParallelEventsManager events = new ParallelEventsManager(false); - SharingMetricsTracker sharingFactorTracker = new SharingMetricsTracker(new SharingMetricsTracker.GroupPredicate() { - @Override - public boolean isGroupRepresentative(Id personId) { - return groupRepresentatives.contains(personId); - } - }); + SharingMetricsTracker sharingFactorTracker = new SharingMetricsTracker(); events.addHandler(sharingFactorTracker); events.initProcessing(); @@ -55,6 +42,7 @@ public boolean isGroupRepresentative(Id personId) { var requestId = Id.create(0, Request.class); Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId, List.of(personId1), null, null, 0, 0, 0, 0, 0)); events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId, personId1, vehicleId)); events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId, personId1, vehicleId)); events.flush(); @@ -74,6 +62,8 @@ public boolean isGroupRepresentative(Id personId) { Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1), null, null, 0, 0, 0, 0, 0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId2, List.of(personId2), null, null, 0, 0, 0, 0, 0)); events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId1, personId1, vehicleId)); events.processEvent(new PassengerPickedUpEvent(300.0, mode, requestId2, personId2, vehicleId)); @@ -101,6 +91,8 @@ public boolean isGroupRepresentative(Id personId) { Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1), null, null, 0, 0, 0, 0, 0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId2, List.of(personId2), null, null, 0, 0, 0, 0, 0)); events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); events.processEvent(new PassengerPickedUpEvent(200.0, mode, requestId2, personId2, vehicleId)); events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId1, personId1, vehicleId)); @@ -129,6 +121,9 @@ public boolean isGroupRepresentative(Id personId) { Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1), null, null, 0, 0, 0, 0, 0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId2, List.of(personId2), null, null, 0, 0, 0, 0, 0)); + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); events.processEvent(new PassengerPickedUpEvent(200.0, mode, requestId2, personId2, vehicleId)); events.processEvent(new PassengerDroppedOffEvent(300.0, mode, requestId2, personId2, vehicleId)); @@ -143,7 +138,7 @@ public boolean isGroupRepresentative(Id personId) { Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId2)); Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId2)); Assert.assertTrue(sharingFactorTracker.getPoolingRates().get(requestId2)); - Assert.assertEquals((100. ) / (50.), sharingFactorTracker.getSharingFactors().get(requestId2), MatsimTestUtils.EPSILON); + Assert.assertEquals((100.) / (50.), sharingFactorTracker.getSharingFactors().get(requestId2), MatsimTestUtils.EPSILON); } //clean up @@ -157,6 +152,9 @@ public boolean isGroupRepresentative(Id personId) { Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1), null, null, 0, 0, 0, 0, 0)); + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId2, List.of(personId2), null, null, 0, 0, 0, 0, 0)); + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId2, personId2, vehicleId)); events.processEvent(new PassengerDroppedOffEvent(200.0, mode, requestId1, personId1, vehicleId)); @@ -179,27 +177,23 @@ public boolean isGroupRepresentative(Id personId) { sharingFactorTracker.notifyMobsimBeforeCleanup(null); { - // two persons part of a group, only person 1 is representative -> not pooled - groupRepresentatives.remove(personId2); + // two persons part of a group -> not pooled var requestId1 = Id.create(0, Request.class); - var requestId2 = Id.create(1, Request.class); Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId1)); - Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); + + events.processEvent(new DrtRequestSubmittedEvent(0.0, mode, requestId1, List.of(personId1, personId2), null, null, 0, 0, 0, 0, 0)); events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId1, vehicleId)); - events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId2, personId2, vehicleId)); + events.processEvent(new PassengerPickedUpEvent(100.0, mode, requestId1, personId2, vehicleId)); events.processEvent(new PassengerDroppedOffEvent(200.0, mode, requestId1, personId1, vehicleId)); - events.processEvent(new PassengerDroppedOffEvent(200.0, mode, requestId2, personId2, vehicleId)); + events.processEvent(new PassengerDroppedOffEvent(200.0, mode, requestId1, personId2, vehicleId)); events.flush(); Assert.assertNotNull(sharingFactorTracker.getPoolingRates().get(requestId1)); Assert.assertNotNull(sharingFactorTracker.getSharingFactors().get(requestId1)); Assert.assertFalse(sharingFactorTracker.getPoolingRates().get(requestId1)); Assert.assertEquals(1., sharingFactorTracker.getSharingFactors().get(requestId1), MatsimTestUtils.EPSILON); - - Assert.assertNull(sharingFactorTracker.getPoolingRates().get(requestId2)); - Assert.assertNull(sharingFactorTracker.getSharingFactors().get(requestId2)); } } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java index 6a19b93d470..7e11e5e8dd8 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/DefaultPassengerEngine.java @@ -24,9 +24,11 @@ import com.google.common.base.Verify; import org.matsim.api.core.v01.Id; +import org.matsim.api.core.v01.Identifiable; import org.matsim.api.core.v01.network.Link; import org.matsim.api.core.v01.network.Network; import org.matsim.api.core.v01.population.Leg; +import org.matsim.api.core.v01.population.Person; import org.matsim.api.core.v01.population.Route; import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; @@ -73,8 +75,12 @@ public final class DefaultPassengerEngine implements PassengerEngine, PassengerR //accessed in doSimStep() and handleEvent() (potential data races) private final Queue rejectedRequestsEvents = new ConcurrentLinkedQueue<>(); + private final PassengerGroupIdentifier passengerGroupIdentifier; + private final Map, List> groupDepartureStage = new LinkedHashMap<>(); + + DefaultPassengerEngine(String mode, EventsManager eventsManager, MobsimTimer mobsimTimer, PassengerRequestCreator requestCreator, - VrpOptimizer optimizer, Network network, PassengerRequestValidator requestValidator, AdvanceRequestProvider advanceRequestProvider) { + VrpOptimizer optimizer, Network network, PassengerRequestValidator requestValidator, AdvanceRequestProvider advanceRequestProvider, PassengerGroupIdentifier passengerGroupIdentifier) { this.mode = mode; this.mobsimTimer = mobsimTimer; this.requestCreator = requestCreator; @@ -83,6 +89,7 @@ public final class DefaultPassengerEngine implements PassengerEngine, PassengerR this.requestValidator = requestValidator; this.eventsManager = eventsManager; this.advanceRequestProvider = advanceRequestProvider; + this.passengerGroupIdentifier = passengerGroupIdentifier; internalPassengerHandling = new InternalPassengerHandling(mode, eventsManager); } @@ -99,6 +106,9 @@ public void onPrepareSim() { @Override public void doSimStep(double time) { + + handleGroupDepartures(time); + // If prebooked requests are rejected (by the optimizer, through an // event) after submission, but before departure, the PassengerEngine does not // know this agent yet. Hence, we wait with setting the state to abort until the @@ -128,45 +138,81 @@ public void doSimStep(double time) { } } - @Override - public void afterSim() { - } + private void handleGroupDepartures(double now) { - @Override - public boolean handleDeparture(double now, MobsimAgent agent, Id fromLinkId) { - if (!agent.getMode().equals(mode)) { - return false; + groupDepartureStage.values().forEach(g -> g.sort(Comparator.comparing(Identifiable::getId))); + for (List group : groupDepartureStage.values()) { + handleDepartureImpl(now, group); } + groupDepartureStage.clear(); + } - MobsimPassengerAgent passenger = (MobsimPassengerAgent)agent; - internalInterface.registerAdditionalAgentOnLink(passenger); + private void handleDepartureImpl(double now, List group) { + List> groupIds = group.stream().map(Identifiable::getId).toList(); + + MobsimPassengerAgent representative = group.get(0); - Id toLinkId = passenger.getDestinationLinkId(); + Id fromLinkId = representative.getCurrentLinkId(); + Id toLinkId = representative.getDestinationLinkId(); + + Preconditions.checkArgument(group.stream().allMatch(a -> a.getCurrentLinkId().equals(fromLinkId))); + Preconditions.checkArgument(group.stream().allMatch(a -> a.getDestinationLinkId().equals(toLinkId))); // try to find a prebooked requests that is associated to this leg - Leg leg = (Leg)((PlanAgent)passenger).getCurrentPlanElement(); - PassengerRequest request = advanceRequestProvider.retrieveRequest(agent, leg); + Leg leg = (Leg)((PlanAgent)representative).getCurrentPlanElement(); + PassengerRequest request = advanceRequestProvider.retrieveRequest(representative, leg); if (request == null) { // immediate request - Route route = ((Leg)((PlanAgent)passenger).getCurrentPlanElement()).getRoute(); + Route route = leg.getRoute(); request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), - List.of(passenger.getId()), route, getLink(fromLinkId), getLink(toLinkId), now, now); + groupIds, route, getLink(fromLinkId), getLink(toLinkId), now, now); // must come before validateAndSubmitRequest (to come before rejection event) - eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), List.of(passenger.getId()))); - activePassengers.put(request.getId(), List.of(passenger)); + eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), groupIds)); + activePassengers.put(request.getId(), group); - validateAndSubmitRequest(List.of(passenger), request, now); + validateAndSubmitRequest(group, request, now); } else { // advance request - eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), List.of(passenger.getId()))); - activePassengers.put(request.getId(), List.of(passenger)); + + Preconditions.checkArgument(request.getPassengerIds().size() == group.size()); + Preconditions.checkArgument(request.getPassengerIds().containsAll(groupIds)); + + for (MobsimPassengerAgent agent : group) { + eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), List.of(agent.getId()))); + } + + activePassengers.put(request.getId(), group); PassengerPickupActivity pickupActivity = waitingForPassenger.remove(request.getId()); if (pickupActivity != null) { // the vehicle is already waiting for the request, notify it - pickupActivity.notifyPassengersAreReadyForDeparture(List.of(passenger), now); + pickupActivity.notifyPassengersAreReadyForDeparture(group, now); } } + } + + @Override + public void afterSim() { + } + + @Override + public boolean handleDeparture(double now, MobsimAgent agent, Id fromLinkId) { + if (!agent.getMode().equals(mode)) { + return false; + } + + MobsimPassengerAgent passenger = (MobsimPassengerAgent)agent; + internalInterface.registerAdditionalAgentOnLink(passenger); + + Optional> groupId = passengerGroupIdentifier.getGroupId(passenger); + + if(groupId.isPresent()) { + groupDepartureStage.computeIfAbsent(groupId.get(), k -> new ArrayList<>()).add(passenger); + //terminate early as more group members may depart later + return true; + } + + handleDepartureImpl(now, List.of(passenger)); return true; } @@ -252,7 +298,7 @@ public static Provider createProvider(String mode) { public DefaultPassengerEngine get() { return new DefaultPassengerEngine(getMode(), eventsManager, mobsimTimer, getModalInstance(PassengerRequestCreator.class), getModalInstance(VrpOptimizer.class), getModalInstance(Network.class), getModalInstance(PassengerRequestValidator.class), - getModalInstance(AdvanceRequestProvider.class)); + getModalInstance(AdvanceRequestProvider.class), getModalInstance(PassengerGroupIdentifier.class)); } }; } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineQSimModule.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineQSimModule.java index e9d806af179..25372c12000 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineQSimModule.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerEngineQSimModule.java @@ -4,13 +4,15 @@ import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; +import java.util.Optional; + /** * dvrp needs {@link PassengerHandler} bound to something. It is first bound to a {@link PassengerEngine} so it can also do stuff on its own. Then, * {@link PassengerEngine} is bound to one of the existing implementations. */ public class PassengerEngineQSimModule extends AbstractDvrpModeQSimModule { public enum PassengerEngineType { - DEFAULT, WITH_PREBOOKING, TELEPORTING, WITH_GROUPS + DEFAULT, WITH_PREBOOKING, TELEPORTING } private final PassengerEngineType type; @@ -27,6 +29,8 @@ public PassengerEngineQSimModule(String mode, PassengerEngineType type) { @Override protected void configureQSim() { bindModal(PassengerHandler.class).to(modalKey(PassengerEngine.class)); + bindModal(PassengerGroupIdentifier.class).toInstance(agent -> Optional.empty()); + // (PassengerEngine is a more powerful interface.) addMobsimScopeEventHandlerBinding().to(modalKey(PassengerEngine.class)); @@ -44,10 +48,6 @@ protected void configureQSim() { addModalComponent( PassengerEngine.class, TeleportingPassengerEngine.createProvider( getMode() ) ); return; } - case WITH_GROUPS -> { - addModalComponent( PassengerEngine.class, GroupPassengerEngine.createProvider( getMode() ) ); - return; - } default -> throw new IllegalStateException( "Type: " + type + " is not supported" ); } } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerGroupIdentifier.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerGroupIdentifier.java index 8dba2bdac52..0a644c87a72 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerGroupIdentifier.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerGroupIdentifier.java @@ -3,19 +3,18 @@ import org.matsim.api.core.v01.Id; import org.matsim.core.mobsim.framework.MobsimPassengerAgent; -import java.util.function.Function; +import java.util.Optional; /** * Provides a method to identify the passenger group id of an agent. * @author nkuehnel / MOIA */ -public interface PassengerGroupIdentifier extends Function> { +public interface PassengerGroupIdentifier { class PassengerGroup { private PassengerGroup(){} } - @Override - Id apply(MobsimPassengerAgent agent); + Optional> getGroupId(MobsimPassengerAgent agent); } diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestRejectedEvent.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestRejectedEvent.java index 0d17138700d..13c131743db 100644 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestRejectedEvent.java +++ b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerRequestRejectedEvent.java @@ -70,7 +70,8 @@ public static PassengerRequestRejectedEvent convert(GenericEvent event) { List> personIds = new ArrayList<>(); for (String person : personIdsAttribute) { personIds.add(Id.create(person, Person.class)); - } String cause = Objects.requireNonNull(attributes.get(ATTRIBUTE_CAUSE)); + } + String cause = Objects.requireNonNull(attributes.get(ATTRIBUTE_CAUSE)); return new PassengerRequestRejectedEvent(time, mode, requestId, personIds, cause); } } diff --git a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngineTest.java b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/PassengerGroupTest.java similarity index 93% rename from contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngineTest.java rename to contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/PassengerGroupTest.java index 2f34014ca2f..45f9bcb8417 100644 --- a/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngineTest.java +++ b/contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/PassengerGroupTest.java @@ -1,7 +1,6 @@ package org.matsim.contrib.dvrp.passenger; import com.google.common.collect.ImmutableMap; -import org.apache.commons.compress.utils.Sets; import org.junit.Test; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.events.*; @@ -30,11 +29,15 @@ import org.matsim.vehicles.VehicleUtils; import java.util.List; +import java.util.Optional; import java.util.Set; import static org.matsim.contrib.dvrp.passenger.PassengerEngineTestFixture.*; -public class GroupPassengerEngineTest { +/** + * @author nkuehnel / MOIA + */ +public class PassengerGroupTest { private final PassengerEngineTestFixture fixture = new PassengerEngineTestFixture(); @@ -99,7 +102,7 @@ public void test_group() { private QSim createQSim(PassengerRequestValidator requestValidator, Class optimizerClass) { return new QSimBuilder(fixture.config).useDefaults() .addOverridingModule(new MobsimScopeEventHandlingModule()) - .addQSimModule(new PassengerEngineQSimModule(MODE, PassengerEngineQSimModule.PassengerEngineType.WITH_GROUPS)) + .addQSimModule(new PassengerEngineQSimModule(MODE, PassengerEngineQSimModule.PassengerEngineType.DEFAULT)) .addQSimModule(new VrpAgentSourceQSimModule(MODE)) .addQSimModule(new AbstractDvrpModeQSimModule(MODE) { @Override @@ -111,6 +114,7 @@ protected void configureQSim() { bindModal(PassengerRequestCreator.class).to(OneTaxiRequest.OneTaxiRequestCreator.class) .asEagerSingleton(); bindModal(PassengerRequestValidator.class).toInstance(requestValidator); + bindModal(AdvanceRequestProvider.class).toInstance(AdvanceRequestProvider.NONE); //supply addQSimComponentBinding(DynActivityEngine.COMPONENT_NAME).to(DynActivityEngine.class); @@ -120,8 +124,13 @@ protected void configureQSim() { bindModal(VrpAgentLogic.DynActionCreator.class).to(OneTaxiActionCreator.class) .asEagerSingleton(); + } + }) + .addOverridingQSimModule(new AbstractDvrpModeQSimModule(MODE) { + @Override + protected void configureQSim() { //groups - bindModal(PassengerGroupIdentifier.class).toInstance(agent -> Id.create("group1", PassengerGroupIdentifier.PassengerGroup.class)); + bindModal(PassengerGroupIdentifier.class).toInstance(agent -> Optional.of(Id.create("group1", PassengerGroupIdentifier.PassengerGroup.class))); } }) .configureQSimComponents(components -> { From ca90abeb9f362c9ade320ea537db9b56c394d1c6 Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Tue, 12 Dec 2023 14:18:13 +0100 Subject: [PATCH 2/6] remove deprecated group passenger engine --- .../dvrp/passenger/GroupPassengerEngine.java | 231 ------------------ 1 file changed, 231 deletions(-) delete mode 100644 contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngine.java diff --git a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngine.java b/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngine.java deleted file mode 100644 index a5c2f9e9c84..00000000000 --- a/contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngine.java +++ /dev/null @@ -1,231 +0,0 @@ -package org.matsim.contrib.dvrp.passenger; - -import com.google.common.base.Preconditions; -import com.google.common.base.Verify; -import jakarta.inject.Inject; -import jakarta.inject.Provider; -import org.matsim.api.core.v01.Id; -import org.matsim.api.core.v01.Identifiable; -import org.matsim.api.core.v01.network.Link; -import org.matsim.api.core.v01.network.Network; -import org.matsim.api.core.v01.population.Leg; -import org.matsim.api.core.v01.population.Route; -import org.matsim.contrib.dvrp.optimizer.Request; -import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; -import org.matsim.contrib.dvrp.run.DvrpModes; -import org.matsim.core.api.experimental.events.EventsManager; -import org.matsim.core.gbl.Gbl; -import org.matsim.core.mobsim.framework.*; -import org.matsim.core.mobsim.qsim.InternalInterface; -import org.matsim.core.modal.ModalProviders; - -import java.util.*; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.stream.Collectors; - -public class GroupPassengerEngine implements PassengerEngine, PassengerRequestRejectedEventHandler { - - private final String mode; - private final MobsimTimer mobsimTimer; - - private final EventsManager eventsManager; - - private final PassengerRequestCreator requestCreator; - private final VrpOptimizer optimizer; - private final Network network; - private final PassengerRequestValidator requestValidator; - - private final InternalPassengerHandling internalPassengerHandling; - - private InternalInterface internalInterface; - - - //accessed in doSimStep() and handleDeparture() (no need to sync) - private final Map, List> activePassengers = new HashMap<>(); - - // holds vehicle stop activities for requests that have not arrived at departure point yet - private final Map, PassengerPickupActivity> waitingForPassenger = new HashMap<>(); - - //accessed in doSimStep() and handleEvent() (potential data races) - private final Queue rejectedRequestsEvents = new ConcurrentLinkedQueue<>(); - - private final Set currentDepartures = new LinkedHashSet<>(); - - private final PassengerGroupIdentifier groupIdentifier; - - GroupPassengerEngine(String mode, EventsManager eventsManager, MobsimTimer mobsimTimer, PassengerRequestCreator requestCreator, VrpOptimizer optimizer, Network network, PassengerRequestValidator requestValidator, PassengerGroupIdentifier groupIdentifier) { - this.mode = mode; - this.eventsManager = eventsManager; - this.mobsimTimer = mobsimTimer; - this.requestCreator = requestCreator; - this.optimizer = optimizer; - this.network = network; - this.requestValidator = requestValidator; - this.groupIdentifier = groupIdentifier; - - internalPassengerHandling = new InternalPassengerHandling(mode, eventsManager); - } - - @Override - public void setInternalInterface(InternalInterface internalInterface) { - this.internalInterface = internalInterface; - internalPassengerHandling.setInternalInterface(internalInterface); - } - - @Override - public void onPrepareSim() { - } - - @Override - public void doSimStep(double time) { - handleDepartures(time); - while (!rejectedRequestsEvents.isEmpty()) { - List passengers = activePassengers.remove(rejectedRequestsEvents.poll().getRequestId()); - //not much else can be done for immediate requests - //set the passenger agent to abort - the event will be thrown by the QSim - for (MobsimPassengerAgent passenger : passengers) { - passenger.setStateToAbort(mobsimTimer.getTimeOfDay()); - internalInterface.arrangeNextAgentState(passenger); - } - } - } - - @Override - public void afterSim() { - } - - @Override - public boolean handleDeparture(double now, MobsimAgent agent, Id fromLinkId) { - - if (!agent.getMode().equals(mode)) { - return false; - } - - MobsimPassengerAgent passenger = (MobsimPassengerAgent)agent; - internalInterface.registerAdditionalAgentOnLink(passenger); - currentDepartures.add(passenger); - return true; - } - - private void handleDepartures(double now) { - Map, List> agentGroups = - currentDepartures.stream().collect( - Collectors.groupingBy( - groupIdentifier, - Collectors.collectingAndThen(Collectors.toList(), list -> { - list.sort(Comparator.comparing(MobsimPassengerAgent::getId)); - return list; - }))); - - for (List group : agentGroups.values()) { - - Iterator iterator = group.iterator(); - MobsimAgent firstAgent = iterator.next(); - MobsimPassengerAgent passenger = (MobsimPassengerAgent) firstAgent; - - Id toLinkId = firstAgent.getDestinationLinkId(); - - while (iterator.hasNext()) { - MobsimAgent next = iterator.next(); - Gbl.assertIf(firstAgent.getCurrentLinkId().equals(next.getCurrentLinkId())); - Gbl.assertIf(toLinkId.equals(next.getDestinationLinkId())); - } - - Route route = ((Leg)((PlanAgent)passenger).getCurrentPlanElement()).getRoute(); - - PassengerRequest request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), - group.stream().map(Identifiable::getId).toList(), route, - getLink(firstAgent.getCurrentLinkId()), getLink(toLinkId), - now, now); - - - // must come before validateAndSubmitRequest (to come before rejection event) - eventsManager.processEvent(new PassengerWaitingEvent(now, mode, request.getId(), group.stream().map(Identifiable::getId).toList())); - - validateAndSubmitRequest(group, request, mobsimTimer.getTimeOfDay()); - } - currentDepartures.clear(); - } - - private void validateAndSubmitRequest(List passengers, PassengerRequest request, double now) { - activePassengers.put(request.getId(), passengers); - if (internalPassengerHandling.validateRequest(request, requestValidator, now)) { - //need to synchronise to address cases where requestSubmitted() may: - // - be called from outside DepartureHandlers - // - interfere with VrpOptimizer.nextTask() - // - impact VrpAgentLogic.computeNextAction() - synchronized (optimizer) { - //optimizer can also reject request if cannot handle it - // (async operation, notification comes via the events channel) - optimizer.requestSubmitted(request); - } - } - } - - private Link getLink(Id linkId) { - return Preconditions.checkNotNull(network.getLinks().get(linkId), "Link id=%s does not exist in network for mode %s. Agent departs from a link that does not belong to that network?", linkId, mode); - } - - /** - * There are two ways of interacting with the PassengerEngine: - *

- * - (1) The stop activity tries to pick up a passenger and receives whether the - * pickup succeeded or not (see tryPickUpPassenger). In the classic - * implementation, the vehicle only calls tryPickUpPassenger at the time when it - * actually wants to pick up the person (at the end of the activity). It may - * happen that the person is not present yet. In that case, the pickup request - * is saved and notifyPassengerReady is called on the stop activity upen - * departure of the agent. - *

- * - (2) If pickup and dropoff times are handled more flexibly by the stop - * activity, it might want to detect whether an agent is ready to be picked up, - * then start an "interaction time" and only after perform the actual pickup. - * For that purpose, we have queryPickUpPassenger, which indicates whether the - * agent is already there, and, if not, makes sure that the stop activity is - * notified once the agent arrives for departure. - */ - @Override - public boolean notifyWaitForPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId) { - - if (!activePassengers.containsKey(requestId)) { - waitingForPassenger.put(requestId, pickupActivity); - return false; - } - - return true; - } - - @Override - public boolean tryPickUpPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, Id requestId, double now) { - boolean pickedUp = internalPassengerHandling.tryPickUpPassengers(driver, activePassengers.get(requestId), requestId, now); - Verify.verify(pickedUp, "Not possible without prebooking"); - return pickedUp; - } - - @Override - public void dropOffPassengers(MobsimDriverAgent driver, Id requestId, double now) { - internalPassengerHandling.dropOffPassengers(driver, activePassengers.remove(requestId), requestId, now); - } - - @Override - public void handleEvent(PassengerRequestRejectedEvent event) { - if (event.getMode().equals(mode)) { - rejectedRequestsEvents.add(event); - } - } - - public static Provider createProvider(String mode) { - return new ModalProviders.AbstractProvider<>(mode, DvrpModes::mode) { - @Inject - private EventsManager eventsManager; - - @Inject - private MobsimTimer mobsimTimer; - - @Override - public GroupPassengerEngine get() { - return new GroupPassengerEngine(getMode(), eventsManager, mobsimTimer, getModalInstance(PassengerRequestCreator.class), getModalInstance(VrpOptimizer.class), getModalInstance(Network.class), getModalInstance(PassengerRequestValidator.class), getModalInstance(PassengerGroupIdentifier.class)); - } - }; - } -} From 338c9ed66723c7a983d81aea8642943c2a5d787f Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Tue, 12 Dec 2023 14:49:30 +0100 Subject: [PATCH 3/6] passenger group identifier bound as qsim module in test --- .../contrib/drt/prebooking/PrebookingGroupTest.java | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java index 2a9829ed379..b03d0e783a6 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java @@ -6,7 +6,7 @@ import org.matsim.contrib.drt.prebooking.logic.AttributeBasedPrebookingLogic; import org.matsim.contrib.drt.run.DrtConfigGroup; import org.matsim.contrib.dvrp.passenger.PassengerGroupIdentifier; -import org.matsim.contrib.dvrp.run.AbstractDvrpModeModule; +import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; import org.matsim.core.controler.Controler; import org.matsim.core.mobsim.framework.MobsimPassengerAgent; import org.matsim.testcases.MatsimTestUtils; @@ -32,15 +32,10 @@ static PrebookingParams installPrebooking(Controler controller, boolean installL if (installLogic) { AttributeBasedPrebookingLogic.install(controller, drtConfig); } - controller.addOverridingModule(new AbstractDvrpModeModule(drtConfig.getMode()) { + controller.addOverridingQSimModule(new AbstractDvrpModeQSimModule(drtConfig.getMode()) { @Override - public void install() { - bindModal(PassengerGroupIdentifier.class).toInstance(new PassengerGroupIdentifier() { - @Override - public Optional> getGroupId(MobsimPassengerAgent agent) { - return Optional.of(Id.create("group", PassengerGroup.class)); - } - }); + protected void configureQSim() { + bindModal(PassengerGroupIdentifier.class).toInstance(agent -> Optional.of(Id.create("group", PassengerGroupIdentifier.PassengerGroup.class))); } }); From 8e7949fb020750a298c78fce50f83daf14ca6ad0 Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Wed, 13 Dec 2023 11:11:14 +0100 Subject: [PATCH 4/6] use person leg records instead of tuples in prebooking classes --- .../PassengerRequestBookedEvent.java | 10 ++----- .../drt/prebooking/PrebookingManager.java | 27 ++++++++++--------- .../logic/helpers/PrebookingQueue.java | 14 +++++----- .../drt/prebooking/PrebookingGroupTest.java | 1 - 4 files changed, 22 insertions(+), 30 deletions(-) diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java index c160f25c288..7d14a3650e6 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PassengerRequestBookedEvent.java @@ -6,10 +6,7 @@ import org.matsim.contrib.dvrp.optimizer.Request; import org.matsim.contrib.dvrp.passenger.AbstractPassengerRequestEvent; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; /** * @author Sebastian Hörl (sebhoerl), IRT SystemX @@ -32,10 +29,7 @@ public static PassengerRequestBookedEvent convert(GenericEvent event) { String mode = Objects.requireNonNull(attributes.get(ATTRIBUTE_MODE)); Id requestId = Id.create(attributes.get(ATTRIBUTE_REQUEST), Request.class); String[] personIdsAttribute = attributes.get(ATTRIBUTE_PERSON).split(","); - List> personIds = new ArrayList<>(); - for (String person : personIdsAttribute) { - personIds.add(Id.create(person, Person.class)); - } + List> personIds = Arrays.stream(personIdsAttribute).map(Id::createPersonId).toList(); return new PassengerRequestBookedEvent(time, mode, requestId, personIds); } } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java index 8b35e2b1362..497a125f131 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java @@ -27,7 +27,6 @@ import org.matsim.core.mobsim.qsim.InternalInterface; import org.matsim.core.mobsim.qsim.agents.WithinDayAgentUtils; import org.matsim.core.mobsim.qsim.interfaces.MobsimEngine; -import org.matsim.core.utils.collections.Tuple; import javax.annotation.Nullable; import java.util.*; @@ -128,6 +127,8 @@ public void handleEvent(PersonStuckEvent event) { private record RejectionItem(Id requestId, List> personIds, String cause) { } + public record PersonLeg(MobsimAgent agent, Leg leg){} + private final ConcurrentLinkedQueue rejections = new ConcurrentLinkedQueue<>(); private void processRejection(PassengerRequest request, String cause) { @@ -151,29 +152,29 @@ private void flushRejections(double now) { // collects new bookings that need to be submitted private final ConcurrentLinkedQueue bookingQueue = new ConcurrentLinkedQueue<>(); - public void prebook(List> personsLegs, double earliestDepartureTime) { - for (Tuple personLeg : personsLegs) { - Preconditions.checkArgument(personLeg.getSecond().getMode().equals(mode), "Invalid mode for this prebooking manager"); - Preconditions.checkState(!personLeg.getFirst().getState().equals(State.ABORT), "Cannot prebook aborted agent"); + public void prebook(List personsLegs, double earliestDepartureTime) { + for (PersonLeg personLeg : personsLegs) { + Preconditions.checkArgument(personLeg.leg().getMode().equals(mode), "Invalid mode for this prebooking manager"); + Preconditions.checkState(!personLeg.agent().getState().equals(State.ABORT), "Cannot prebook aborted agent"); } Id requestId = createRequestId(); double now = mobsimTimer.getTimeOfDay(); - List> personIds = personsLegs.stream().map(p -> p.getFirst().getId()).toList(); + List> personIds = personsLegs.stream().map(p -> p.agent().getId()).toList(); eventsManager.processEvent(new PassengerRequestBookedEvent(now, mode, requestId, personIds)); - Leg representativeLeg = personsLegs.get(0).getSecond(); + Leg representativeLeg = personsLegs.get(0).leg(); PassengerRequest request = requestCreator.createRequest(requestId, personIds, representativeLeg.getRoute(), getLink(representativeLeg.getRoute().getStartLinkId()), getLink(representativeLeg.getRoute().getEndLinkId()), earliestDepartureTime, now); Set violations = requestValidator.validateRequest(request); - for (Tuple personLeg : personsLegs) { - Plan plan = WithinDayAgentUtils.getModifiablePlan(personLeg.getFirst()); - int currentLegIndex = WithinDayAgentUtils.getCurrentPlanElementIndex(personLeg.getFirst()); - int prebookingLegIndex = plan.getPlanElements().indexOf(personLeg.getSecond()); + for (PersonLeg personLeg : personsLegs) { + Plan plan = WithinDayAgentUtils.getModifiablePlan(personLeg.agent()); + int currentLegIndex = WithinDayAgentUtils.getCurrentPlanElementIndex(personLeg.agent()); + int prebookingLegIndex = plan.getPlanElements().indexOf(personLeg.leg()); if (prebookingLegIndex <= currentLegIndex) { violations = new HashSet<>(violations); @@ -185,8 +186,8 @@ public void prebook(List> personsLegs, double earliestDe String cause = String.join(", ", violations); processRejection(request, cause); } else { - for (Tuple personLeg : personsLegs) { - personLeg.getSecond().getAttributes().putAttribute(requestAttribute, request.getId().toString()); + for (PersonLeg personLeg : personsLegs) { + personLeg.leg().getAttributes().putAttribute(requestAttribute, request.getId().toString()); } bookingQueue.add(request); } diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java index 85547c814c7..d1e954b6341 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java @@ -1,7 +1,6 @@ package org.matsim.contrib.drt.prebooking.logic.helpers; -import java.util.*; - +import com.google.common.base.Preconditions; import org.matsim.api.core.v01.Id; import org.matsim.api.core.v01.population.Leg; import org.matsim.contrib.drt.prebooking.PrebookingManager; @@ -11,8 +10,7 @@ import org.matsim.core.mobsim.framework.events.MobsimBeforeSimStepEvent; import org.matsim.core.mobsim.framework.listeners.MobsimBeforeSimStepListener; -import com.google.common.base.Preconditions; -import org.matsim.core.utils.collections.Tuple; +import java.util.*; /** * This service helps to define a PrebookingLogic where at some point in time @@ -51,14 +49,14 @@ private void performSubmissions(double time) { var item = queue.poll(); Optional> groupId = groupIdentifier.getGroupId((MobsimPassengerAgent) item.agent); if(groupId.isEmpty()) { - prebookingManager.prebook(List.of(new Tuple<>(item.agent(), item.leg())), item.departuretime()); + prebookingManager.prebook(List.of(new PrebookingManager.PersonLeg(item.agent(), item.leg())), item.departureTime()); } else { groups.computeIfAbsent(groupId.get(), k -> new ArrayList<>()).add(item); } } for (List group : groups.values()) { - List> personsLegs = group.stream().map(entry -> new Tuple<>(entry.agent, entry.leg)).toList(); - prebookingManager.prebook(personsLegs, group.get(0).departuretime); + List personsLegs = group.stream().map(entry -> new PrebookingManager.PersonLeg(entry.agent, entry.leg)).toList(); + prebookingManager.prebook(personsLegs, group.get(0).departureTime); } } @@ -79,7 +77,7 @@ public void schedule(double submissionTime, MobsimAgent agent, Leg leg, double d } } - private record ScheduledSubmission(double submissionTime, MobsimAgent agent, Leg leg, double departuretime, + private record ScheduledSubmission(double submissionTime, MobsimAgent agent, Leg leg, double departureTime, int sequence) implements Comparable { @Override public int compareTo(ScheduledSubmission o) { diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java index b03d0e783a6..ec4c307b229 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java @@ -8,7 +8,6 @@ import org.matsim.contrib.dvrp.passenger.PassengerGroupIdentifier; import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; import org.matsim.core.controler.Controler; -import org.matsim.core.mobsim.framework.MobsimPassengerAgent; import org.matsim.testcases.MatsimTestUtils; import java.util.Optional; From db74a9a83125b389f9f7db727e3c825937deee30 Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Tue, 19 Dec 2023 17:10:20 +0100 Subject: [PATCH 5/6] add additional prebook method for delegation --- .../org/matsim/contrib/drt/prebooking/PrebookingManager.java | 4 ++++ .../contrib/drt/prebooking/logic/helpers/PrebookingQueue.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java index 497a125f131..ed79cd0bf9f 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/PrebookingManager.java @@ -152,6 +152,10 @@ private void flushRejections(double now) { // collects new bookings that need to be submitted private final ConcurrentLinkedQueue bookingQueue = new ConcurrentLinkedQueue<>(); + public void prebook(MobsimAgent agent, Leg leg, double earliestDepartureTime) { + prebook(List.of(new PersonLeg(agent, leg)), earliestDepartureTime); + } + public void prebook(List personsLegs, double earliestDepartureTime) { for (PersonLeg personLeg : personsLegs) { Preconditions.checkArgument(personLeg.leg().getMode().equals(mode), "Invalid mode for this prebooking manager"); diff --git a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java index d1e954b6341..788a430c069 100644 --- a/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java +++ b/contribs/drt/src/main/java/org/matsim/contrib/drt/prebooking/logic/helpers/PrebookingQueue.java @@ -49,7 +49,7 @@ private void performSubmissions(double time) { var item = queue.poll(); Optional> groupId = groupIdentifier.getGroupId((MobsimPassengerAgent) item.agent); if(groupId.isEmpty()) { - prebookingManager.prebook(List.of(new PrebookingManager.PersonLeg(item.agent(), item.leg())), item.departureTime()); + prebookingManager.prebook(item.agent(), item.leg(), item.departureTime()); } else { groups.computeIfAbsent(groupId.get(), k -> new ArrayList<>()).add(item); } From bf5b0a011e336d8cd5dcb982f9d7c226c4750d83 Mon Sep 17 00:00:00 2001 From: nkuehnel Date: Wed, 20 Dec 2023 15:04:41 +0100 Subject: [PATCH 6/6] fix junit for PrebookingGroupTest --- .../drt/prebooking/PrebookingGroupTest.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java index ec4c307b229..c609ec084f8 100644 --- a/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java +++ b/contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingGroupTest.java @@ -1,7 +1,8 @@ package org.matsim.contrib.drt.prebooking; -import org.junit.Rule; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; import org.matsim.api.core.v01.Id; import org.matsim.contrib.drt.prebooking.logic.AttributeBasedPrebookingLogic; import org.matsim.contrib.drt.run.DrtConfigGroup; @@ -12,11 +13,10 @@ import java.util.Optional; -import static org.junit.Assert.assertEquals; public class PrebookingGroupTest { - @Rule + @RegisterExtension public MatsimTestUtils utils = new MatsimTestUtils(); @@ -58,17 +58,17 @@ public void oneRequestArrivingLate() { controller.run(); PrebookingTestEnvironment.RequestInfo requestInfoA = environment.getRequestInfo().get("personA"); - assertEquals(0.0, requestInfoA.submissionTime, 1e-3); - assertEquals(2060.0, requestInfoA.pickupTime, 1e-3); - assertEquals(2271.0, requestInfoA.dropoffTime, 1e-3); + Assertions.assertEquals(0.0, requestInfoA.submissionTime, 1e-3); + Assertions.assertEquals(2060.0, requestInfoA.pickupTime, 1e-3); + Assertions.assertEquals(2271.0, requestInfoA.dropoffTime, 1e-3); PrebookingTestEnvironment.RequestInfo requestInfoB = environment.getRequestInfo().get("personB"); - assertEquals(0.0, requestInfoB.submissionTime, 1e-3); - assertEquals(2060.0, requestInfoB.pickupTime, 1e-3); - assertEquals(2271.0, requestInfoB.dropoffTime, 1e-3); + Assertions.assertEquals(0.0, requestInfoB.submissionTime, 1e-3); + Assertions.assertEquals(2060.0, requestInfoB.pickupTime, 1e-3); + Assertions.assertEquals(2271.0, requestInfoB.dropoffTime, 1e-3); // assert both persons are part of same drt request - assertEquals(requestInfoA.drtRequestId, requestInfoB.drtRequestId); + Assertions.assertEquals(requestInfoA.drtRequestId, requestInfoB.drtRequestId); } }