-
Notifications
You must be signed in to change notification settings - Fork 456
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
360 additions
and
9 deletions.
There are no files selected for viewing
191 changes: 191 additions & 0 deletions
191
contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngine.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,191 @@ | ||
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 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<Id<Request>, Set<MobsimPassengerAgent>> activePassengers = new HashMap<>(); | ||
|
||
//accessed in doSimStep() and handleEvent() (potential data races) | ||
private final Queue<PassengerRequestRejectedEvent> rejectedRequestsEvents = new ConcurrentLinkedQueue<>(); | ||
|
||
private final Set<MobsimPassengerAgent> 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.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(); | ||
while (!rejectedRequestsEvents.isEmpty()) { | ||
Set<MobsimPassengerAgent> 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<Link> fromLinkId) { | ||
if (!agent.getMode().equals(mode)) { | ||
return false; | ||
} | ||
|
||
MobsimPassengerAgent passenger = (MobsimPassengerAgent)agent; | ||
internalInterface.registerAdditionalAgentOnLink(passenger); | ||
currentDepartures.add(passenger); | ||
return true; | ||
} | ||
|
||
private void handleDepartures() { | ||
Map<Id<PassengerGroupIdentifier.PassengerGroup>, List<MobsimPassengerAgent>> agentGroups | ||
= currentDepartures.stream().collect(Collectors.groupingBy(groupIdentifier)); | ||
|
||
for (List<MobsimPassengerAgent> group : agentGroups.values()) { | ||
|
||
Iterator<MobsimPassengerAgent> iterator = group.iterator(); | ||
MobsimAgent firstAgent = iterator.next(); | ||
Id<Link> toLinkId = firstAgent.getDestinationLinkId(); | ||
Route route = ((Leg) ((PlanAgent) firstAgent).getCurrentPlanElement()).getRoute(); | ||
|
||
while(iterator.hasNext()) { | ||
MobsimAgent next = iterator.next(); | ||
Gbl.assertIf(firstAgent.getCurrentLinkId().equals(next.getCurrentLinkId())); | ||
Gbl.assertIf(toLinkId.equals(next.getDestinationLinkId())); | ||
} | ||
|
||
PassengerRequest request = requestCreator.createRequest(internalPassengerHandling.createRequestId(), | ||
group.stream().map(Identifiable::getId).toList(), route, | ||
getLink(firstAgent.getCurrentLinkId()), getLink(toLinkId), | ||
mobsimTimer.getTimeOfDay(), mobsimTimer.getTimeOfDay()); | ||
validateAndSubmitRequest(new HashSet<>(group), request, mobsimTimer.getTimeOfDay()); | ||
} | ||
|
||
currentDepartures.clear(); | ||
} | ||
|
||
private void validateAndSubmitRequest(Set<MobsimPassengerAgent> 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<Link> 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); | ||
} | ||
|
||
@Override | ||
public boolean tryPickUpPassengers(PassengerPickupActivity pickupActivity, MobsimDriverAgent driver, | ||
Id<Request> 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<Request> 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<PassengerEngine> 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)); | ||
} | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
21 changes: 21 additions & 0 deletions
21
contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/passenger/PassengerGroupIdentifier.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package org.matsim.contrib.dvrp.passenger; | ||
|
||
import org.matsim.api.core.v01.Id; | ||
import org.matsim.core.mobsim.framework.MobsimPassengerAgent; | ||
|
||
import java.util.function.Function; | ||
|
||
/** | ||
* Provides a method to identify the passenger group id of an agent. | ||
* @author nkuehnel / MOIA | ||
*/ | ||
public interface PassengerGroupIdentifier extends Function<MobsimPassengerAgent, Id<PassengerGroupIdentifier.PassengerGroup>> { | ||
|
||
class PassengerGroup { | ||
private PassengerGroup(){} | ||
} | ||
|
||
@Override | ||
Id<PassengerGroupIdentifier.PassengerGroup> apply(MobsimPassengerAgent agent); | ||
|
||
} |
131 changes: 131 additions & 0 deletions
131
contribs/dvrp/src/test/java/org/matsim/contrib/dvrp/passenger/GroupPassengerEngineTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
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.*; | ||
import org.matsim.api.core.v01.network.Network; | ||
import org.matsim.api.core.v01.population.Person; | ||
import org.matsim.contrib.dvrp.examples.onetaxi.OneTaxiActionCreator; | ||
import org.matsim.contrib.dvrp.examples.onetaxi.OneTaxiOptimizer; | ||
import org.matsim.contrib.dvrp.examples.onetaxi.OneTaxiRequest; | ||
import org.matsim.contrib.dvrp.fleet.DvrpVehicle; | ||
import org.matsim.contrib.dvrp.fleet.DvrpVehicleImpl; | ||
import org.matsim.contrib.dvrp.fleet.Fleet; | ||
import org.matsim.contrib.dvrp.fleet.ImmutableDvrpVehicleSpecification; | ||
import org.matsim.contrib.dvrp.optimizer.Request; | ||
import org.matsim.contrib.dvrp.optimizer.VrpOptimizer; | ||
import org.matsim.contrib.dvrp.run.AbstractDvrpModeQSimModule; | ||
import org.matsim.contrib.dvrp.run.DvrpModes; | ||
import org.matsim.contrib.dvrp.run.MobsimTimerProvider; | ||
import org.matsim.contrib.dvrp.vrpagent.VrpAgentLogic; | ||
import org.matsim.contrib.dvrp.vrpagent.VrpAgentSourceQSimModule; | ||
import org.matsim.contrib.dynagent.run.DynActivityEngine; | ||
import org.matsim.core.events.MobsimScopeEventHandlingModule; | ||
import org.matsim.core.mobsim.framework.MobsimTimer; | ||
import org.matsim.core.mobsim.qsim.QSim; | ||
import org.matsim.core.mobsim.qsim.QSimBuilder; | ||
import org.matsim.vehicles.VehicleType; | ||
import org.matsim.vehicles.VehicleUtils; | ||
|
||
import java.util.List; | ||
import java.util.Set; | ||
|
||
import static org.matsim.contrib.dvrp.passenger.PassengerEngineTestFixture.*; | ||
|
||
public class GroupPassengerEngineTest { | ||
|
||
private final PassengerEngineTestFixture fixture = new PassengerEngineTestFixture(); | ||
|
||
private final Id<DvrpVehicle> VEHICLE_ID = Id.create("taxi1", DvrpVehicle.class); | ||
private final DvrpVehicle oneTaxi = new DvrpVehicleImpl(ImmutableDvrpVehicleSpecification.newBuilder() | ||
.id(VEHICLE_ID) | ||
.serviceBeginTime(0) | ||
.serviceEndTime(3600) | ||
.startLinkId(fixture.linkAB.getId()) | ||
.capacity(1) | ||
.build(), fixture.linkAB); | ||
private final Fleet fleet = () -> ImmutableMap.of(oneTaxi.getId(), oneTaxi); | ||
|
||
@Test | ||
public void test_group() { | ||
double departureTime = 0; | ||
Id<Person> person1 = Id.createPersonId("1"); | ||
Id<Person> person2 = Id.createPersonId("2"); | ||
|
||
fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, person2); | ||
fixture.addPersonWithLeg(fixture.linkAB, fixture.linkBA, departureTime, person1); | ||
|
||
PassengerRequestValidator requestValidator = request -> Set.of();//valid | ||
createQSim(requestValidator, OneTaxiOptimizer.class).run(); | ||
|
||
double pickupStartTime = 1; | ||
double pickupEndTime = pickupStartTime + OneTaxiOptimizer.PICKUP_DURATION; | ||
double taxiDepartureTime = pickupEndTime + 1; | ||
double taxiEntersLinkBATime = taxiDepartureTime + 1; | ||
double taxiArrivalTime = taxiEntersLinkBATime + (fixture.linkBA.getLength() / fixture.linkBA.getFreespeed()); | ||
double dropoffEndTime = taxiArrivalTime + OneTaxiOptimizer.DROPOFF_DURATION; | ||
|
||
//1 second delay between pickupEndTime and taxiDepartureTime is not considered in schedules | ||
double scheduledDropoffTime = dropoffEndTime - pickupStartTime - 1; | ||
|
||
var requestId = Id.create("taxi_0", Request.class); | ||
fixture.assertPassengerEvents( | ||
Sets.newHashSet(person1, person2), | ||
new ActivityEndEvent(departureTime, person2, fixture.linkAB.getId(), null, START_ACTIVITY), | ||
new PersonDepartureEvent(departureTime, person2, fixture.linkAB.getId(), MODE, MODE), | ||
new ActivityEndEvent(departureTime, person1, fixture.linkAB.getId(), null, START_ACTIVITY), | ||
new PersonDepartureEvent(departureTime, person1, fixture.linkAB.getId(), MODE, MODE), | ||
new PassengerRequestScheduledEvent(departureTime, MODE, requestId, List.of(person1, person2), VEHICLE_ID, 0, | ||
scheduledDropoffTime), | ||
new PersonEntersVehicleEvent(pickupStartTime, person1, Id.createVehicleId(VEHICLE_ID)), | ||
new PassengerPickedUpEvent(pickupStartTime, MODE, requestId, person1, VEHICLE_ID), | ||
new PersonEntersVehicleEvent(pickupStartTime, person2, Id.createVehicleId(VEHICLE_ID)), | ||
new PassengerPickedUpEvent(pickupStartTime, MODE, requestId, person2, VEHICLE_ID), | ||
new PassengerDroppedOffEvent(dropoffEndTime, MODE, requestId, person1, VEHICLE_ID), | ||
new PersonLeavesVehicleEvent(dropoffEndTime, person1, Id.createVehicleId(VEHICLE_ID)), | ||
new PersonArrivalEvent(dropoffEndTime, person1, fixture.linkBA.getId(), MODE), | ||
new ActivityStartEvent(dropoffEndTime, person1, fixture.linkBA.getId(), null, END_ACTIVITY), | ||
new PassengerDroppedOffEvent(dropoffEndTime, MODE, requestId, person2, VEHICLE_ID), | ||
new PersonLeavesVehicleEvent(dropoffEndTime, person2, Id.createVehicleId(VEHICLE_ID)), | ||
new PersonArrivalEvent(dropoffEndTime, person2, fixture.linkBA.getId(), MODE), | ||
new ActivityStartEvent(dropoffEndTime, person2, fixture.linkBA.getId(), null, END_ACTIVITY)); | ||
} | ||
|
||
|
||
private QSim createQSim(PassengerRequestValidator requestValidator, Class<? extends VrpOptimizer> optimizerClass) { | ||
return new QSimBuilder(fixture.config).useDefaults() | ||
.addOverridingModule(new MobsimScopeEventHandlingModule()) | ||
.addQSimModule(new PassengerEngineQSimModule(MODE, PassengerEngineQSimModule.PassengerEngineType.WITH_GROUPS)) | ||
.addQSimModule(new VrpAgentSourceQSimModule(MODE)) | ||
.addQSimModule(new AbstractDvrpModeQSimModule(MODE) { | ||
@Override | ||
protected void configureQSim() { | ||
bindModal(Network.class).toInstance(fixture.network); | ||
bind(MobsimTimer.class).toProvider(MobsimTimerProvider.class).asEagerSingleton(); | ||
|
||
//requests | ||
bindModal(PassengerRequestCreator.class).to(OneTaxiRequest.OneTaxiRequestCreator.class) | ||
.asEagerSingleton(); | ||
bindModal(PassengerRequestValidator.class).toInstance(requestValidator); | ||
|
||
//supply | ||
addQSimComponentBinding(DynActivityEngine.COMPONENT_NAME).to(DynActivityEngine.class); | ||
bindModal(Fleet.class).toInstance(fleet); | ||
bindModal(VehicleType.class).toInstance(VehicleUtils.getDefaultVehicleType()); | ||
bindModal(VrpOptimizer.class).to(optimizerClass).asEagerSingleton(); | ||
bindModal(VrpAgentLogic.DynActionCreator.class).to(OneTaxiActionCreator.class) | ||
.asEagerSingleton(); | ||
|
||
//groups | ||
bindModal(PassengerGroupIdentifier.class).toInstance(agent -> Id.create("group1", PassengerGroupIdentifier.PassengerGroup.class)); | ||
} | ||
}) | ||
.configureQSimComponents(components -> { | ||
components.addComponent(DvrpModes.mode(MODE)); | ||
components.addNamedComponent(DynActivityEngine.COMPONENT_NAME); | ||
}) | ||
.build(fixture.scenario, fixture.eventsManager); | ||
} | ||
} |
Oops, something went wrong.