Simple example - MayerTh/RVRPSimulator GitHub Wiki
After building the maven project vrpsim-parent with the [maven goal] (https://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html) install
, all sources are build and pushed in the local maven repository so that we can use the artifacts for our first simple example project.
So let's create a new maven project and add the dependencies to RVRPSimulator in the POM as follows.
<dependency>
<groupId>de.terministic</groupId>
<artifactId>vrpsim-core</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>de.terministic</groupId>
<artifactId>vrpsim-visualization</artifactId>
<version>0.0.1</version>
</dependency>
Since we would like to run our simulation with the visualization, but don't need any utilities like random structure or network generators, we are including only the core and the visualization. If you are using eclipse, your setup should look like in following figure.
![eclipse setup] (https://github.com/MayerTh/RVRPSimulator/blob/master/vrpsim-examples/example/01.setup.PNG)
Let's create the class ´SimpleExample´ in the package rvrpsimulator.simple.example
. The created class has to extend the class vrpsim.visualization.Visualisation
and has to implement the standard java entry point method public static void main(String[] args)
. Since the class Visualisation
extends the class Application
from package javafx.application
we can first try to simply call the method void launch(String[] args)
from class Application
, which is the standard entry point for every [JavaFx] (http://docs.oracle.com/javase/8/javafx/get-started-tutorial/hello_world.htm) application. So we code as follows and run our project as Java Application.
![first launch] (https://github.com/MayerTh/RVRPSimulator/blob/master/vrpsim-examples/example/02.first_launch.PNG)
As you can see after running the application, nothing actually happened, except that you get a friendly reminder, that you have to initialize the Visualization with the MainProgram
, the VRPSimulationModel
and a simulation end time from type ITime
(also in the log file).
vrpsim.visualization.Visualisation has to be initialized by calling void
init(MainProgramm mainProgram, VRPSimulationModel simulationModel, ITime simulationEndTime).
The MainProgram
, which represents the simulator engine, and the simulation end time are easy. We can just write the following:
MainProgramm mainProgram = new MainProgramm();
IClock clock = mainProgram.getSimulationClock();
ITime simulationEndTime = clock.getCurrentSimulationTime().createTimeFrom(1000.0);
Note, that the MainProgram
is holding the simulation clock. The RVRPSimulator implementation is following the simulation component concept from [Law & Kelton] (https://www.amazon.de/Simulation-Modeling-Analysis-Averill-Law/dp/0071255192). The simulation end time from type ITime
, we can generate with the current simulation time and a simple double value. In general the unit of the time is not important, it can be minutes or hours or even days, months, years. The important part is, that we use the same unit during the whole simulation model generating process.
The creation of the VRPSimulationModel
is a bit more difficult, we have to define the network, the VRP structure, means all vehicles, customers, depots, and so on. And finally we also have to define the behaviour of the structure elements. So let's create a new class named SimpleModelGenerator
and add the following public method.
public VRPSimulationModel getSimulationModel() {
return null;
}
In our main method we add the code for instantiating the new class VRPSimulationModel
, we initialize and launch the visualization. We are done with the source code from the main method already. The code should now look like following:
public static void main(String[] args) {
// Creating the main program, the simulation end time from a double value and the
// model with our helper class.
MainProgramm mainProgram = new MainProgramm();
IClock clock = mainProgram.getSimulationClock();
ITime simulationEndTime = clock.getCurrentSimulationTime().createTimeFrom(1000.0);
VRPSimulationModel model = new SimpleModelGenerator().getSimulationModel();
// Initialize the visualization.
init(mainProgram, model, simulationEndTime);
// Launch the visualization.
launch(args);
}
As mentioned, the creation of the VRPSimulationModel is a bit more difficult, we have to define the network, the VRP structure, means all vehicles, customers, depots, and so on. And finally we also have to define the behaviour of the structure elements. So let's start with defining the network. Following figure shows the network we would like to create. Note that we will create direct ways between all five nodes.
![first launch] (https://github.com/MayerTh/RVRPSimulator/blob/master/vrpsim-examples/example/03.network.png)
The vehicle routing problem network is represented with the class Network
, which has the following constructor.
public Network(final List<INode> nodes, final List<IWay> ways) { ... }
So for the network representation we only have to create a list of INode
and a list of IWay
. Note, that we will use the standard implementations of these interfaces, which are DefaultNode
and DefaultWay
. The constructor of a DefaultNode
only requires parameters from type VRPSimulationModelElementParameters
, which every simulation model elements needs and a location. So let's have a look how to create a node.
// Creating the parameters with an id and a priority.
VRPSimulationModelElementParameters parameters1 = new VRPSimulationModelElementParameters("Node1", 1);
// Creating the node, the z coordinate of the location is 0.
INode node1 = new DefaultNode(parameters1, new Location(10, 50, 0));
The constructor of DefaultWay
requires a few more parameters. Beside others we have to define a time and a distance function. RVRPSimulator provides also default implementations for these function. See the section [Network elements] (https://github.com/MayerTh/RVRPSimulator/wiki/Network-elements) to get a closer look at what possibilities these functions are offering. So let's create ways like this:
VRPSimulationModelElementParameters para = new VRPSimulationModelElementParameters(wayCounter++ + "", 1);
ITimeFunction travelTimeFunction = new LinearMotionTravelTimeFunction();
IDistanceFunction distanceFunction = new Euclidean2DDistanceFunction();
double maxSpeed = 50.0;
INode source = node_1;
INode target = node_2;
IWay way = new DefaultWay(para, travelTimeFunction, source, target, distanceFunction, maxSpeed);
NOTE, that maxSpeed
is a physical value which unit is depending on a time and a distance. It is in your responsibility, to model consistent units (remember e.g. the simulation end time, the coordinates for the nodes and the distance function). For our example, the code for generating the complete network with all nodes and all ways looks like follows. Note, that I created the method getNetwork()
to generate the network within the class SimpleModelGenerator
.
private Network getNetwork() {
List<INode> nodes = new ArrayList<INode>();
VRPSimulationModelElementParameters parameters1 = new VRPSimulationModelElementParameters("Node1", 1);
INode node1 = new DefaultNode(parameters1, new Location(10, 50, 0));
nodes.add(node1);
VRPSimulationModelElementParameters parameters2 = new VRPSimulationModelElementParameters("Node2", 1);
INode node2 = new DefaultNode(parameters2, new Location(50, 50, 0));
nodes.add(node2);
VRPSimulationModelElementParameters parameters3 = new VRPSimulationModelElementParameters("Node3", 1);
INode node3 = new DefaultNode(parameters3, new Location(10, 10, 0));
nodes.add(node3);
VRPSimulationModelElementParameters parameters4 = new VRPSimulationModelElementParameters("Node4", 1);
INode node4 = new DefaultNode(parameters4, new Location(50, 10, 0));
nodes.add(node4);
VRPSimulationModelElementParameters parameters5 = new VRPSimulationModelElementParameters("Node5", 1);
INode node5 = new DefaultNode(parameters5, new Location(30, 30, 0));
nodes.add(node5);
List<IWay> ways = new ArrayList<IWay>();
int wayCounter = 1;
for (INode node_1 : nodes) {
for (INode node_2 : nodes) {
if (!node_1.equals(node_2)) {
VRPSimulationModelElementParameters para = new VRPSimulationModelElementParameters(
wayCounter++ + "", 1);
ITimeFunction travelTimeFunction = new LinearMotionTravelTimeFunction();
IDistanceFunction distanceFunction = new Euclidean2DDistanceFunction();
double maxSpeed = 50.0;
INode source = node_1;
INode target = node_2;
IWay way = new DefaultWay(para, travelTimeFunction, source, target, distanceFunction, maxSpeed);
node_1.addWay(way);
ways.add(way);
}
}
}
Network network = new Network(nodes, ways);
return network;
}
Before we can start our application again to visualize our created model network we have to create an empty [structure] (https://github.com/MayerTh/RVRPSimulator/wiki/Structure-elements), to build a complete simulation model, which we need for the initialization of the Visualization
. The complete structure is represented with the RVRPSimulator class Structure
. The constructor from Structure
looks like following:
/**
* @param storableParameters - available goods
* @param depots - available depots
* @param customers - available customer
* @param vehicles - available vehicle
* @param drivers - available driver
* @param occasionalDrivers - available occasional driver
*/
public Structure(StorableParameters storableParameters, List<IDepot> depots, List<ICustomer> customers,
List<IVehicle> vehicles, List<IDriver> drivers, List<IOccasionalDriver> occasionalDrivers)
As you can see, to create the structure of our simulation model we have to define all structure elements existing in our problem to simulate. But let's concentrate on these StorableParameters
first. StorableParameters
are characterizing any IStorable
existing. An IStorable
is the representation of a good transported in our system. Let's have a look at following code:
private final CanStoreType canStoreType = new CanStoreType("pizza-carton-storage"); //1
private final StorableType storableType = new StorableType("pizza-carton", canStoreType); //2
private final String capacityUnit = "piece"; //3
private final Capacity singleCapacity = new Capacity(capacityUnit, 1.0); //4
private final StorableParameters storableParameters =
new StorableParameters(1, singleCapacity, storableType); //5
In the first line we are creating a CanStoreType
with the string "carton". An CanStoreType
is the type of an instance of ICanStore
. For example a Compartment
implements ICanStore
. But why we need a type for something where I can put things in here? This question is answered with the second line of code. In this line a StorableType
is created, also with a string (here "pizza-carton") and the "CanStoreType". That means we define here, which type of good can be stored in which type of storage. So in this example we define, that we have "pizza-carton-storage" where we can store "pizza-carton" inside. What is if we would like to transport our "pizza-carton" not only in "pizza-carton-storage"? Therefore StorableType
provides following constructor: public StorableType(String id, List<CanStoreType> canStoreTypes)
. RVRPSimulator model supports also several StorableParameters
, but you can read more about this in the [documentation] (https://github.com/MayerTh/RVRPSimulator/wiki/Structure-elements). Line 3, 4 and 5 are defining a Capacity
where RVRPSimulator engine can calculate with, and finally the StorableParameters
. Let's create the method getStructure(Network network)
in the class SimpleModelGenerator
and generate an empty structure with the defined storable parameters. The code of the class SimpleModelGenerator
looks now like following:
public class SimpleModelGenerator {
private final CanStoreType canStoreType = new CanStoreType("pizza-carton-storage"); //1
private final StorableType storableType = new StorableType("pizza-carton", canStoreType); //2
private final String capacityUnit = "piece"; //3
private final Capacity singleCapacity = new Capacity(capacityUnit, 1.0); //4
private final StorableParameters storableParameters =
new StorableParameters(1, singleCapacity, storableType); //5
public VRPSimulationModel getSimulationModel() {
VRPSimulationModelParameters vrpSimulationModelParameters =
new VRPSimulationModelParameters("Simple Example", "Owner");
Network network = getNetwork();
Structure structure = getStructure(network);
VRPSimulationModel model = new VRPSimulationModel(vrpSimulationModelParameters, structure, network);
return model;
}
private Structure getStructure(Network network) {
// Empty lists
List<IDepot> depots = new ArrayList<IDepot>();
List<ICustomer> customers = new ArrayList<ICustomer>();
List<IVehicle> vehicles = new ArrayList<IVehicle>();
List<IDriver> drivers = new ArrayList<IDriver>();
List<IOccasionalDriver> occasionalDrivers = new ArrayList<IOccasionalDriver>();
// Return the empty structure
return new Structure(storableParameters, depots, customers, vehicles, drivers, occasionalDrivers);
}
private Network getNetwork() {
...
}
}
So let's run our project as Java Application again, we should see our generated Network now. Note, that the ways are not visualized.
![First network visualization] (https://github.com/MayerTh/RVRPSimulator/blob/master/vrpsim-examples/example/04.network_visu.png)
By clicking on a node, an movable popup opens, which shows details for the node.
![First network visualization] (https://github.com/MayerTh/RVRPSimulator/blob/master/vrpsim-examples/example/04.network_visu_2.png)
But currently there are no details. So let's create the rest of the structure. The plan is to define customers for each node 1,2,3,4 and a depot at node 5. Additionally we define a vehicle and a driver first located at node 5. Note, that we use only default implementations for the structure elements except for the depot. For the depot we are using the implementation SourceDepot
from IDepot
provided from RVRPSimulator. The reason is that a SourceDepot
has an infinite amount of storables inside, whenever somebody likes to have storables, it generates them. The DefaultDepot
does not provide that service for us. So let's have a look at the method getDepot(Network network)
we generated in our class SimpleModelGenerator
.
private List<IDepot> getDepots(Network network) {
// Id + prio + where is the depot located.
VRPSimulationModelElementParameters vrpSimulationModelElementParameters = new VRPSimulationModelElementParameters("Depot1", 1); //1
VRPSimulationModelStructureElementParameters vrpSimulationModelStructureElementParameters = new VRPSimulationModelStructureElementParameters(network.getNetworkService().getNetworkElement("Node5")); //2
// Are there frequently coming new storables into the depot?
UncertainParamters arrivalParameters = new UncertainParamters(); //3
// We create our first compartment where we can store
CanStoreParameters cartonStorageParameters = new CanStoreParameters(
this.canStoreType,
new Capacity(this.capacityUnit, 1000.0),
new LIFOLoadingPolicy(),
new StorableGenerator(this.storableParameters));
ICanStore cartonStorage = new Compartment(cartonStorageParameters);
DefaultStorageManager storageManager = new DefaultStorageManager(new DefaultStorage(cartonStorage));
ITimeFunction serviceTimeFunction = new DeterministicTimeFunction(10.0);
// The depot
IDepot depot = new SourceDepot(vrpSimulationModelElementParameters,vrpSimulationModelStructureElementParameters, arrivalParameters, storageManager, serviceTimeFunction);
// Return a list with our depot.
List<IDepot> depots = new ArrayList<IDepot>();
depots.add(depot);
return depots;
}
The fist two lines are defining the general parameters for the depot, means the depot id (every simulation model element has to have one), the priority, and the location. We get the location with the help of the node id (here "Node5") from the Network
with the help of the NetworkService
.
Let's have a look at the UncertainParamters
, which can carry several UncertainParameterContainer
. These containers hold three distribution functions, one to define a start time, one to define a cycle time and the last one to define an amount. A distribution function can also be a deterministic value. But we would like to have a depot from type SourceDepot
, so we would not like to model an cyclic arrival from goods with uncertain characteristics at this depot. But our depot needs a storage, actually not only a storage, it needs a StorageManager
, which organizes a storage. The depot itself acts only as interface in front of the storage. RVRPSimulator provides the class Compartment
, which have to be instantiated with CanStoreParameters
. More about the storage concept you can find in the [documentation] (https://github.com/MayerTh/RVRPSimulator/wiki/Structure-elements). The depot also needs an implementation of the interface ITimeFunction
, it is used to calculate the time a vehicle spent by loading at the depot. Let's say the service time at a depot is always 10.
We can update our getStructure(Network network)
method now as follows, and start our project as Java Application. The depot is visualized at node 5 now. Note, that '~' visualizes, that you have an infinite amount of storage and storables.
private Structure getStructure(Network network) {
List<IDepot> depots = getDepots(network);
List<ICustomer> customers = new ArrayList<ICustomer>();
List<IVehicle> vehicles = new ArrayList<IVehicle>();
List<IDriver> drivers = new ArrayList<IDriver>();
List<IOccasionalDriver> occasionalDrivers = new ArrayList<IOccasionalDriver>();
return new Structure(storableParameters, depots, customers, vehicles, drivers, occasionalDrivers);
}
![First depot] (https://github.com/MayerTh/RVRPSimulator/blob/master/vrpsim-examples/example/05.depot.png)
The modeling of the customers is similar. Except that we model now a consumption of goods by defining an UncertainParameterContainer
. Following code shows the instantiating of UncertainParamters
with one container. The container is instantiating with StorableParameters
to describe which kind of goods are ordered and three implementations of 'IDistributionFunction'. The amount of goods ordered is 2, the earliest time the delivery can happens is at simulation time 0 and the latest time is simulation time 500. Since we use the implementation StaticCustomerWithConsumption
of ICustomer
, not only the described Order
is generated, the amount of ordered goods is also consumed at simulation time 500.
UncertainParamters consumptionParameters = new UncertainParamters(
new UncertainParamters.UncertainParameterContainer(
this.storableParameters,
new DeterministicDistributionFunction(2.0) /* consumption number */,
new DeterministicDistributionFunction(0.0) /* earliest due date */,
new DeterministicDistributionFunction(500.0) /* latest due date */,
false, /* adapt DueDates to simulation time */ false /* is cyclic */));
The rest of the code to generate the 4 customers, which similar to the creation of the depot, is as follows. Note that I also created a new method getCustomer(Network network)
.
private List<ICustomer> getCustomer(Network network) {
List<ICustomer> customers = new ArrayList<ICustomer>();
for (int i = 1; i <= 4; i++) {
VRPSimulationModelElementParameters elementParameters = new VRPSimulationModelElementParameters("Customer" + i, 1);
VRPSimulationModelStructureElementParameters structureElementParameters = new VRPSimulationModelStructureElementParameters(network.getNetworkService().getNetworkElement("Node" + i));
UncertainParamters consumptionParameters = new UncertainParamters(
new UncertainParamters.UncertainParameterContainer(
this.storableParameters,
new DeterministicDistributionFunction(2.0) /* consumption number */,
new DeterministicDistributionFunction(0.0) /* earliest due date */,
new DeterministicDistributionFunction(500.0) /* latest due date */,
false, /* adapt DueDates to simulation time */ false /* is cyclic */));
// We create our first compartment where we can store
CanStoreParameters cartonStorageParameters = new CanStoreParameters(
this.canStoreType,
new Capacity(this.capacityUnit, 100.0),
new LIFOLoadingPolicy(),
new StorableGenerator(this.storableParameters));
ICanStore cartonStorage = new Compartment(cartonStorageParameters);
DefaultStorageManager storageManager = new DefaultStorageManager(new DefaultStorage(cartonStorage));
ITimeFunction serviceTime = new DeterministicTimeFunction(10.0);
StaticCustomerWithConsumption defaultCustomer = new StaticCustomerWithConsumption(elementParameters, structureElementParameters, consumptionParameters, storageManager, serviceTime);
customers.add(defaultCustomer);
}
return customers;
}
Before we start our project again, let's model the vehicle and the driver first. Creating the vehicle with the class DefaultVehicle
is very similar to creating the depot. We have to model the standard parameters and a storage, where we put all the pizza cartons. Note, that we are not modeling breakdowns for our vehicle. The driver is represented with the class DefaultDriver
and is the simplest to model, because there is no storage attached to a driver. Note, that we are not modeling sick leaves for our driver. Following the code for the vehicle and the driver. I created the methods getVehicle(Network network)
and getDriver(Network network)
also in the class SimpleModelGenerator
.
private List<IVehicle> getVehicle(Network network) {
// Id + prio + where is the depot located.
VRPSimulationModelElementParameters elementParameters = new VRPSimulationModelElementParameters("Vehicle1", 1);
VRPSimulationModelStructureElementParameters structureElementParameters = new VRPSimulationModelStructureElementParameters(network.getNetworkService().getNetworkElement("Node5"));
// We are modeling no vehicle breakdowns.
UncertainParamters breakdownParameters = new UncertainParamters();
// A vehicle has of course also a storage.
CanStoreParameters cartonStorageParameters = new CanStoreParameters(
canStoreType,
new Capacity(capacityUnit, 4.0),
new LIFOLoadingPolicy(),
new StorableGenerator(this.storableParameters));
ICanStore cartonStorage = new Compartment(cartonStorageParameters);
DefaultStorageManager storageManager = new DefaultStorageManager(new DefaultStorage(cartonStorage));
double maxSpeed = 100.0;
IVehicle vehicle = new DefaultVehicle(elementParameters, structureElementParameters, breakdownParameters,storageManager, maxSpeed);
List<IVehicle> vehicles = new ArrayList<IVehicle>();
vehicles.add(vehicle);
return vehicles;
}
private List<IDriver> getDriver(Network network) {
// Id + prio + where is the depot located.
VRPSimulationModelElementParameters elementParameters = new VRPSimulationModelElementParameters("Driver1", 0);
VRPSimulationModelStructureElementParameters structureElementParameters = new VRPSimulationModelStructureElementParameters(network.getNetworkService().getNetworkElement("Node5"));
// We are modeling no sick leaves of a driver.
UncertainParamters breakdownParameters = new UncertainParamters();
DefaultDriver driver = new DefaultDriver(elementParameters, structureElementParameters, breakdownParameters);
List<IDriver> drivers = new ArrayList<IDriver>();
drivers.add(driver);
return drivers;
}
After updating the method getStructure(Network network)
as follows, we can start our project again.
private Structure getStructure(Network network) {
List<IDepot> depots = getDepots(network);
List<ICustomer> customers = getCustomer(network);
List<IVehicle> vehicles = getVehicle(network);
List<IDriver> drivers = getDriver(network);
List<IOccasionalDriver> occasionalDrivers = new ArrayList<IOccasionalDriver>();
return new Structure(storableParameters, depots, customers, vehicles, drivers, occasionalDrivers);
}
That's what you should see after starting the updated project. Wohoo!
![Network and structure] (https://github.com/MayerTh/RVRPSimulator/blob/master/vrpsim-examples/example/06.structure_network.png)
We see now the whole network and the structure of our problem. We have the 4 customers, the depot, the vehicle and the driver. What do you think will happen, when we start the simulation. Maybe nothing, because we did not model any behaviour yet, isn't? No, we modeled behaviour already. We modeled the consumption of 2 goods for all customers at the simulation time 500. So let's try what happens when we start the simulation.
Oho, we get an exception:
vrpsim.core.model.util.exceptions.detail.StorageOutOfStockException: There is no storable in the following compartments anymore: [pizza-carton-storage].
at vrpsim.core.model.structure.util.storage.DefaultStorageManager.unload(DefaultStorageManager.java:123)
at vrpsim.core.model.structure.AbstractVRPSimulationModelStructureElementWithStorage.unload(AbstractVRPSimulationModelStructureElementWithStorage.java:102)
at vrpsim.core.model.structure.customer.DefaultCustomer.processEvent(DefaultCustomer.java:136)
at vrpsim.core.simulator.MainProgramm.runStep(MainProgramm.java:179)
at vrpsim.visualization.view.RootLayoutController$1.call(RootLayoutController.java:120)
at vrpsim.visualization.view.RootLayoutController$1.call(RootLayoutController.java:116)
at javafx.concurrent.Task$TaskCallable.call(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
But why? We can answer this question with a sharp look on the exception itself, or we check the log file of the simulation vrpsimulator.log
, created into the root of you project folder (if not visible refresh your view). In the log file we can read, that at simulation time 500, an event from type CONSUMPTION_EVENT
is triggered. Then we get information from the DefaultCustomer
, that he tries to consume 2, but the current capacity is 0. RVRPSimulator does not allow negative stock amounts, so the try to consume more than available ends up in a StorageOutOfStockException
. In general RVRPSimulator is implemented quite rigorous.
3240 [Thread-4] INFO vrpsim.core.simulator.MainProgramm - Current simulation time: 500.0
3241 [Thread-4] DEBUG vrpsim.core.simulator.MainProgramm - Event (type=CONSUMPTION_EVENT) from DefaultCustomer will be executed and 0 observers will be notified.
3241 [Thread-4] DEBUG v.c.m.s.customer.DefaultCustomer - DefaultCustomer with id Customer1 will consum 2 from type pizza-carton. Current capacity 0.0.
3242 [Thread-4] ERROR v.c.m.s.customer.DefaultCustomer - StorageOutOfStockException: There is no storable in the following compartments anymore: [pizza-carton-storage].
So what can we do? Mhm... simple, [we have to deliver pizza-cartons to our customers with our vehicle] (/MayerTh/RVRPSimulator/wiki/Simple-example,-adding-behaviour).