Practical stuff
- Due Date: Sunday 5 January 2025, before 23:59 (Blackboard's clock).
- Team Size: 2 (pair design/programming)!
Note that as of the 2017-2018 Academic Year, each International student should team up with "local"
(i.e., whose Bachelor degree was obtained at the University of Antwerp).
- Submitting your solution:
-
Only one member of each team submits a full solution (ZIP file).
-
The other team member must submit a single (plain text or HTML) file containing only the names of both team members. This will allow us to put in grades for both team members in BlackBoard.
- Submission Medium:
BlackBoard.
- Contact / TA:
Joeri Exelmans.
Introduction
You will use (classic) DEVS to model a queueing and load balancing system for a set of waterway locks. A conceptual view of the system is shown here:
Ships move in the direction of the arrows. A generator generates ships at pseudo-random time intervals, which are added to a queue. Whenever the queue has a ship available, and one of the locks has enough remaining capacity for that ship, the load balancer pulls a ship from the queue and sends it to that lock. A lock may fit more than one ship, so as long as it is not filled up to full capacity, it may wait for more ships to arrive before the lock doors close and the ships can pass through to the other side of the lock. At the end of the system, we have a Sink, where all ships are collected, so we can extract statistics to analyse performance.
Ships can have different sizes. For simplicity, the size of a ship is a small integer (e.g., 1 or 2). Locks can have different capacities: for instance, a lock of capacity 3 will fit either:
- 3 ships of size 1
- 1 ship of size 2 + 1 ship of size 1
Specification
We now give an overview of the different DEVS components, and their behavior, and their parameters. Although many of the parameters are fixed, your solution must work with different parameters as well. In other words, don't hardcode the parameter values in your DEVS blocks!
Atomic DEVS blocks
The specification of the semantics of the Atomic DEVS blocks is entirely deterministic. If you implement everything correctly, the system as-a-whole will behave 100% identical to the teacher's solution.
Coupled DEVS
The system as a whole is modeled as a Coupled DEVS block. Its parameters are mostly passed as-is to the underlying Atomic DEVS blocks. They are:
- gen_num (int) -> Generator.
- gen_rate (float) -> Generator.
- gen_types (list of int) -> Generator and Queue. The Queue only needs this parameter to know the different ship sizes.
- lock_capacities (list of int) -> LoadBalancer and each Lock its respective capacity.
- priority (enum) -> LoadBalancer.
- max_wait_duration (float) -> each Lock (same value for all locks).
- passthrough_duration (float) -> each Lock (same value for all locks).
What is expected
First of all, you are given an implementation of the following AtomicDEVS blocks, which you must not edit:
- Generator
- out_ship (Ship): output port on which an event is sent when a ship is generated.
- Sink
- in_ships (list of Ship): input port on which an event is sent when ships leave a lock. For each ship, the time duration spent in the system is computed and stored.
You will:
- Implement the AtomicDEVS blocks for Queue, RoundRobinLoadBalancer, FillErUpLoadBalancer and Lock.
- Think of the interfaces of these blocks (input/output ports and their events) and the protocol spoken by them.
- Finally, in the CoupledDEVS block representing the entire system, you will have to make the right connections (which of course depends on the input/output ports that you have defined in your AtomicDEVS).
An indication of the complexity: my own solution of the AtomicDEVS blocks is about 300 lines of code (including comments).
Goal: Performance Analysis
Once you have implemented the system, you will do performance analysis, comparing combinations of the following parameter values:
- max_wait_duration (float): we will try 0, 2, 4, 6, 8 minutes.
- priority (enum): we will try giving priority to bigger and smaller ships.
- strategy (enum): we will try "round-robin" and "fill-er-up".
More specifically, we would like to know under which (combinations of) parameter values the (avg/min/max) duration that ships spend in the system is minimized. Also, we'd like to know if one choice (e.g., prioritize bigger) always better than another choice (e.g., prioritize smaller), or does it depend on the choices made for the other parameters?
Getting Started
- Clone the mosis24 branch of this git repository.
- Under the assignment directory, you'll find the following files:
- Write a report, where you:
- explain and motivate the interfaces/protocol of the AtomicDEVS blocks
- show the plotted results
- interpret the plotted results
- Submit via BlackBoard, a ZIP file, containing:
- Your code (only the assignment directory)
- Your report (PDF)
- The generated CSV- and SVG-files
- Deadline: Sunday 5 December 2025, 23:59
Attention!
You must stick to the rules of DEVS:
- The functions timeAdvance and outputFnc are purely getters! They must not mutate the DEVS state, or any variable, anywhere.
- The functions extTransition and intTransition may mutate ONLY the DEVS state.
Any violation of these rules results in an incorrect solution. Points will be subtracted.
Coding Conventions
Please follow these coding conventions:
- Write your AtomicDEVS methods always in the following order:
- extTransition
- timeAdvance
- outputFnc
- intTransition
This reflects the order in which the methods are called by the simulator:
- extTransition always has highest priority (can interrupt anything)
- timeAdvance is called before outputFnc
- outputFnc is called right before intTransition
- Input/output port attributes start with 'in_' and 'out_'
Tips
- To observe the changing State in the PyPDVES debug output, write __repr__-methods (or __str__-methods) for your State-classes.
- Work modularly and test modularly: build each component (Atomic DEVS model) in isolation and integrate only when all components have been fully tested.
- Test/debug your integrated solution with only one ship (set num_ships parameter to 1).
- Initially, make components deterministic (i.e., do not sample from a distribution).
-
Even though the pseudo-random number stream may be deterministic and your simulations may (must!) be replicable,
the output trace may not be intuitive. By using deterministic timings, you will be able to predict what the behaviour trace should be.
This makes being the "testing oracle" easier.
Troubleshooting
Common mistakes include:
- did you forget to return self.state from intTransition or extTransition ?
- did you accidentally write to self.x instead of self.state.x ?
- did you modify the state in timeAdvance or outputFnc ? (NOT ALLOWED!!)
Extra Material
|