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 RequestGenerator, Dispatcher, and Statistics singleton objects

Objects diagram

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:

Experiment data flow

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.