Part 1. Simple Supply Chain Model
Intro
In this part of the tutorial, we will create a supply chain simulation model in a form of a console application.
The model’s input data is hard-coded in the main
method.
The program will print the simulation results to the standard output.
Source code
You can find the complete source code of the 'Simple Supply Chain Model' tutorial here: https://github.com/amalgama-llc/supply-chain-tutorial-part-1
Thinking about the subject area
What we want to find out by running our simulation is how many requests were fulfilled in time (the so-called 'Service level') and the transportation expenses generated by trucks.
This naturally gives us an idea of our first two subject area entities:
Truck
and TransportationRequest
.
A Truck
will represent a physically moving vehicle that can have a state (e.g., available/busy).
A TransportationRequest
will carry the information about the cargo that needs to be moved from one point to another.
We will also need some kind of 'environment' for all our modeled entities,
a kind of object that knows everything about the current state of the system.
This object will be called Model
.
As soon as we know the whole 'state', we can do the necessary calculations of the modeling results.
We will place this calculation logic in a separate Statistics
class.
To simulate the 'outer world' that brings new transportation requests into our model,
we will add a new RequestGenerator
object.
The Model
contains and maintains the state of the modeled system during runtime.
There will also be a special object that sets the initial state of the model and holds some modeling parameters.
This object is called a Scenario
.
The Scenario
object is not changed during runtime.
Rather, the 'model state' in Model
is initialized by the information in the Scenario
.
So, basically, you will initialize the model with some kind of Scenario
, and then the model will conduct an 'experiment' based on that scenario.
For instance, Scenario
will set the number of Trucks in the system.
Cargo will be delivered across a network of warehouses and stores, collectively called assets.
The objects that are 'used' to do some useful work - transportation of goods, in our case - are typically called resources. In this version of our program we have only one type of resource - a Truck.
To do something useful with our Trucks, we will need a new entity representing an algorithm that uses some 'resources' to brings the modeled system (or its part) from one state to another.
We will call such object a Task
.
For example, a task goal may sound like "Do the Request-67 with Truck-25".
Inside the Task, some basic actions are performed to achieve the declared goal, e.g., the state of a specific Truck is changed.
Let’s think what the future delivery logic can be, to see what travel distance information will be required.
When a delivery starts, an appropriate (i.e., idle) Truck will be selected. A truck becomes idle when it finishes its current delivery, i.e., when it arrives to a Store with some cargo. Having taken the next task, that empty Truck will travel to a chosen Warehouse, pick cargo, bring it to the destination Store, and become idle again. So, we see that idle trucks always reside at some Store, not at a Warehouse. Truck’s delivery path has two parts: store-to-warehouse and warehouse-to-store. To calculate the distance that each Truck needs to travel to fulfill its delivery task, we will need to know distances between each Warehouse and each Store. Note that we do not need our Trucks to travel among Warehouses or among Stores, so store-to-store and warehouse-to-warehouse distances are not required.
This distance information will be stored in a special RouteLengthContainer
object.
We will assume that this distance remains the same during simulation: a delivery path, not set explicitly,
has been chosen once, and we only know its length.
We will also assume symmetry: for any Warehouse and any Store, travel distance is the same in both directions.
During simulation the interactions of elements from our subject area can become quite complicated.
To keep this logic maintainable we will create a separate object to start new Tasks.
We will call it Dispatcher
.
This object will analyze the current state of the simulation (the Model
) and create and start new Tasks as needed.
RequestGenerator
, Dispatcher
, and Statistics
will all be singleton objects owned by a specific Model
.
To sum up, here is what each part of our future system will do:
Object | Responsibility |
---|---|
Scenario |
Store information about the initial state of the modeled system |
Asset |
Base class for Warehouse and Store classes |
Warehouse |
Cargo source |
Store |
Cargo delivery destination |
RouteLengthContainer |
Hold distance information between any Warehouse and any Store |
TransportationRequest |
Hold information about the needed transportation |
Truck |
Represent a real truck’s state |
RequestGenerator |
Generate random TransportationRequests |
Task |
Do some useful actions with Trucks to fulfill `TransportationRequest`s |
Dispatcher |
Create and start new Tasks when appropriate |
Statistics |
Calculate service level and other simulation output data |
Model |
Keep track of the system state; create and coordinate |
Data flow of an experiment
We will use a separate Experiment
entity to denote the process of
passing some input data
to the model, running the simulation model, waiting until the simulation is complete,
and then getting the output data from the model:
In this setting, the simulation model acts as a mapping, effectively transforming the input data to the output data.
The 'Engine' object is responsible for running the simulation, it acts as the 'source' of model time. The other three objects inside the 'Model object' get created and coordinated by the Model object.
Several different input objects (scenarios) can be prepared to conduct multiple experiments. This can help to find out how a change in an input parameter affects the output.