Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(dvrp): generic loads and changeable capacities #3627

Open
wants to merge 14 commits into
base: master
Choose a base branch
from

Conversation

tkchouaki
Copy link
Contributor

Generic DvrpLoad representation of DVRP vehicle capacities and occupancies

This PR, co-prepared with @sebhoerl, proposes a more generic and extensible definition of DVRP vehicle capacities and occupancies with two main goals in mind:

  • Allow users and developpers to define DRT services with heterogeneous capacities, not only in the maximum capacity but also the type of items that can be transported
  • Allow the capacity of a DvrpVehicle to change during the simulation

To achieve this, several features were implemented between the DVRP and DRT modules. Below we first present the implementation in detail, then we show how to use this functionality to build your own simulations with complex capacities and occupancies and changing capacities.

Implementation details

The DvrpLoad interface

Instead of the systematic use of integers to represent the capacities of DvrpVehicle and their occupancies, we hide these behind a new interface, DvrpLoad. All already existing operations on vehicle loads and occupancies were hidden behind one of the interface's methods:

  • Comparison: through the DvrpLoad::fitsIn method. The general meaning behind it is whether the current DvrpLoad can be put in a vehicle that has the given load as a capacity. It must support DvrpLoad objects of different implementations and return false if they are not compatible.
  • Addition and subtraction: through the DvrpLoad::addTo and DvrpLoad::subtract methods. Implementations must support null parameters, and return the current load in those cases. Moreover, negative values must be supported.
  • Checking whether the current load represents an empty one: through the DvrpLoad::isEmpty method.

For serialization purposes (reading and writing DvrpLoad objects from/to files), we also assume that a DvrpLoad is comprised of N-slots and that the value of each can be represented as a Number.

Moreover, for factoring purposes, we also define a DvrpLoadType interface that handles type-level logics, such as building new instances (representing an empty quantity using the DvrpLoadType::getEmptyLoad or from an array of numbers using the DvrpLoadType::fromArray method). The link between the DvrpLoad and DvrpLoadType is made with the DvrpLoad::getType method.

A default IntegerLoad that behaves the same way as the previous integer representation is proposed. As the IntegerLoad::fitsIn, IntegerLoad::addTo and IntegerLoad::subtract methods check if the type (an IntegerLoadType instance) is the same, one can build new mutually exclusive capacities and occupancies simply by extending the IntegerLoadType class.

Additionally, a MultipleIndependentSlotsLoad (with a MultipleIndependentSlotsLoadType) is proposed to allow defining capacities that consists of N-slots that have independent maximum occupancies.

The DvrpLoadSerializer interface

To allow reading and writing the generic DvrpLoad objects from input and to output files, we need to be able to convert DvrpLoad objects to and from strings. This is encapsulated behind a DvrpLoadSerializer interface. Note that by default, the serilized string representation of a DvrpLoad does not necessarily indicate its type. This is why the DvrpLoadSerializer::deSerialize takes the string representation and the DvrpLoadType id.

The DefaultDvrpLoadSerilizer implementation follows a slotName=value representation. However if the load type only has one slot, only a number is printed.

Changing DvrpVehicle capacities

The Dvrpvehicle interface is extended with a DvrpVehicle::setCapacity method. Furthermore, a CapacityChangeTask interface is added, that can be then present in a DvrpVehicle's schedule. Such a task lead to a VehicleCapacityChangeActivity where the capacity is actually changed.
This impacts the InsertionGenerator that can no longer assume that the vehicle's capacity remains constant. The current capacity during the day is then tracked and changes detected through the presence of CapacityChangeTask instances in the schedule. This is also reflected in VehicleDataEntryFactoryImpl.

To provide some basic capability of planning DvrpVehicle capacity changes at the beginning of the simulation, the org.matsim.contrib.drt.taas.capacities.CapacityChangeSchedulerEngine allows to do so through an implementation of the CapacityReconfigurationLogic interface. These allow to modify the capacities of vehicles either at the beginning of the iteration, without having to insert CapacityChangeTask, or during the day by schedulling such a task.

A use case illustrating a simulation where vehicle capacities are changed during the day is present in the unit tests (Namely RunDrtExampleWithChangingCapacitiesTest) with two variations. One basic implementation that sets all vehicles to change capacities at the middle of the day, and another one that attempts to distribute vehicle capacities during the day according to the demand (implemented in the org.matsim.contrib.drt.taas.capacities.DefaultCapacityReconfigurationLogic).

Integration in the Dvrp module

The capacity of DvrpVehicle objects comes from the one in the underlying DvrpVehicleSpecification. The FleetModule handles the two ways of constructing DVRP fleets: from dvrp fleet files or from regular MATSim vehicles file. In the former, the capacity XML attribute is used while in the latter, the number of seats in the vehicle's type is used. To allow building DvrpLoad instances from those two sources, we implement two interfaces: DvrpLoadFromFleet and DvrpLoadFromVehicle. The implementation of DvrpLoadFromFleet that is binded in the current Dvrp mode is passed to the FleetReader while the implementation of DvrpLoadFromVehicle is passed to DvrpVehicleSpecificationWithMatsimVehicle::createFleetSpecificationFromMatsimVehicles

The default implementation of DvrpLoadFromFleet (defined anonymously in the FleetModule behaves the previous implementation, interpreting the integer capacity attribute of the XML file with the IntegerLoadType that is bound in the current Dvrp mode. To avoid having to change the dvrp_vehicles_v1.dtd, this default implementation does not directly allow to build DvrpLoads that are not from one IntegerLoadType. It is up to the developper to implement a custom DvrpLoadFromFleet that does this.

The default implementation of DvrpLoadFromVehicle (called DefaultDvrpLoadFromVehicle) considers the presence of dvrp:capacityType and dvrp:capacityValue attributes in the vehicles (or in the vehicle type) and uses a DvrpLoadSerializer to convert them into a DvrpLoad. If those attributes are not available, a fallback IntegerLoadType is used to interpret the vehicle type's number of seats, thus keeping the same behaviour as the previous implementation.

A DvrpLoadModule is added that performs the default bindings for the IntegerLoadType and the DvrpLoadSerializer. Currently this module is installed by the FleetModule.

To keep track of changing capacities, the new event type DvrpVehicleCapacityChanged is added (implemented by the VehicleCapacityChangedEvent class). It is fired every time the capacity of a DvrpVehicle is changed and also at the start of the first task of the vehicle's schedule. Below an XML example generated with this event.

<event time="21600.0" type="DvrpVehicleCapacityChanged" vehicleId="drt_veh_3_2" newCapacity="4" capacityType="goodsLoad"  />
	...
<event time="21600.0" type="DvrpVehicleCapacityChanged" vehicleId="drt_veh_4_1" newCapacity="4" capacityType="personsLoad"  />

Integration in the DRT module

The only point that needed adaptation in the DRT module concerns building the DvrpLoad representing the occupancy generated by a DRT request. While before it was just the number of passengers, here we propose an interface that encapsulates this: DvrpLoadFromDrtPassengers.

The default implementation of this interface, DefaultDvrpLoadFromDrtPassengers works on the person level, determining the load of each person and summing them to retrieve the overall DvrpLoad of a request (Attempting to sum incompatible loads will throw an exception). To determine the load/occupancy of a person, the following process is followed:

  • Attributes drt:loadType and drt:loadValue are looked for in the person's attribute.
  • If both are found, the DvrpLoadSerializer is used to interpret the attributes into a load object
  • If only the drt:loadType attribute is found, the string 1 is deserialized using that type.
  • If only the drt:loadValue is provided, the fallback IntegerLoadType (usually the one that is bound for that Drt mode) is used.
  • if neither of those attributes is provided, the fallback IntegerLoadType is used to build the DvrpLoad from the integer 1.

This allows, for instance, to simulate a service where different passengers of the same grouped request do not have the same occupancy. E.g, one passenger with one suitcase and another passenger with 2 suitcases will generate an overall DvrpLoad of 2 passengers and 3 suitcases.

This DvrpLoad is now a separate attribute of a DrtRequest. To track the load of each request, the PassengerRequestSubmittedEvent event is extended with two new XML attributes: load and loadType. Below an XML example

<event time="22096.0" type="DrtRequest submitted" mode="drt" request="drt_0" person="0001317" fromLink="452" toLink="284" load="1" loadType="goodsLoad" unsharedRideTime="468.55979999999636" unsharedRideDistance="6090.0" earliestDepartureTime="22096.0" latestPickupTime="22696.0" latestDropoffTime="23105.127739999996" maxRideDuration="Infinity"  />

Analysis

Analysis for capacities and loads can be activated by setting capacityLoadAnalysisInterval in the modal DRT config group to a non-zero value. In that case, two files will be created in each iteration directory: dvrp_capacities_[mode].csv and dvrp_loads_[mode].csv. The first file contains all intervals in which vehicles have specific capacity in the format

vehicle_id;start_time;end_time;type;slot;capacity

where the first columns help identify the vehicle and the time slot at which certain capacities are active, type and slot indicate the capacity type and capacity slot inside the activty type, and capacity the available amount. These information allow, for instance, to plot the available capacities in the fleet by hour. The second file tracks all requests and their loads by providing:

vehicle_id;request_id;pickup_time;dropoff_time;type;slot;load

The file allows, for instance, to plot the transported load for each capacity type throghout the day.

How to use ?

We describe below the steps to follow to implement two example tests. If you want to look at code examples, they are present in RunDrtExampleWithChangingCapacitiesTest

Simulating a fleet with fixed heterogeneous capacities and its related demand

Let's say the goal is to simulate a service where some vehicles can serve persons and the others can serve goods, both being able to be represented with an integer. The steps below can be followed to achieve that:

  1. Define two extensions of IntegerLoadType: PersonsLoadType and GoodsLoadType that both build IntegerLoad instances with the given type.
  2. (a) If the vehicles come from a dvrp fleet file: Define an implementation of DvrpLoadFromFleet that sets the vehicle's capacity to persons or goods according to its ID. (b) If the vehicles come from a vehicles file: You can add the attributes dvrp:capacityType and dvrp:capacityValue in the vehicles or factor in different vehicle types.
  3. (a) Define an implementation of DvrpLoadFromDrtPassengers the determines whether a request is a persons or a goods one. (b) Alternatively, modify your plans.xml to add the person attribute drt:loadType with the name of the appropriate load type.
  4. Bind a DvrpLoadSerializer that is aware of the two DvrpLoadTypes that are used in the simulation, the DefaultDvrpLoadSerializer can be built by passing them to the constructor.

Simulating a fleet with changing heterogeneous capacities and its related demand

Now let's say that vehicle capacities are to be re-configured during the day, the process above can be adapted as follows:

  1. Same as step 1 above.
  2. If you want all Dvrp vehicles to start with the same capacity (persons or goods), you can simply bind IntegerLoadType to the desired one. Otherwise, follow step 2.a or 2.b from above.
  3. One of steps 3.a or 3.b from above.
  4. Same as step 4 from above.
  5. Bind the proposed DefaultCapacityReconfigurationLogic or bind your own CapacityReconfigurationLogic implementation, an example is shown in RunDrtExampleWithChangingCapacitiesTest.SimpleCapacityConfiguration that configures all vehicles to switch capacities at 12pm.
  6. Add the CapacityChangeSchedulerEngine by passing the bound CapacityReconfigurationLogic.

Tarek CHOUAKI and others added 10 commits December 4, 2024 14:20
feat(dvrp): generic loads and changeable capacities

See merge request matsim/matsim-libs!95
# Conflicts:
#	contribs/drt/src/test/java/org/matsim/contrib/drt/prebooking/PrebookingTestEnvironment.java
…generic_loads'

Fix: Preventing DefaultRequestInsertionScheduler from adding passengers in CapacityChangeTask

See merge request matsim/matsim-libs!109
@steffenaxer
Copy link
Collaborator

LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants