complex process synchronization#

In the process synchronization notebook the synchronization using start_events was compared with a sequence of activities. However, the synchronization of two processes e.g. two vessels can not be implemented using sequence, but requires the start_event synchronization.

In this demo two processes each consisting of a sequence of activities representing the processes of two vessels are synchronized, e.g. because they have to make a hand-over of material.

This basic mechanism is also used in the feeder demo that is discussed under the real-world examples.

0. Import libraries#

import datetime, time
import simpy

import pandas as pd

import openclsim.core as core
import openclsim.model as model
import openclsim.plot as plot

1. Initialise simpy environment#

# setup environment
simulation_start = 0
my_env = simpy.Environment(initial_time=simulation_start)

2. Define object classes#

No sites and vessels defined in this example

3. Create objects#

3.1. Create site object(s)#

No site objects defined in this example

3.2. Create vessel object(s)#

No vessel objects defined in this example

3.3 Create activity/activities#

The activity names of the first sequence all start with A Basic activity. Activity A Basic activity3 is constrained such that it can only be executed after B Basic activity 2 has been completed. Thus, in this demo, two processes controlled by a structural control element are synchronized with each other using start_events.

# initialise registry and keep_resources
registry = {}
keep_resources = {}
# create a reporting activity
reporting_activity = model.BasicActivity(
    env=my_env,
    name="Reporting activity",
    registry=registry,
    duration=0,
    keep_resources=keep_resources,
)
# create a list of the sub processes
sub_processes_B = [
    model.BasicActivity(
        env=my_env,
        name="B Basic activity1",
        registry=registry,
        duration=10,
        additional_logs=[reporting_activity],
        keep_resources=keep_resources,
    ),
    model.BasicActivity(
        env=my_env,
        name="B Basic activity2",
        registry=registry,
        duration=500,
        additional_logs=[reporting_activity],
        keep_resources=keep_resources,
    ),
    model.BasicActivity(
        env=my_env,
        name="B Basic activity3",
        registry=registry,
        duration=120,
        additional_logs=[reporting_activity],
        keep_resources=keep_resources,
    ),
]

# create a 'sequential activity' that is made up of the 'sub_processes'
sequential_activity_B = model.SequentialActivity(
    env=my_env,
    name="B Sequential process",
    registry=registry,
    sub_processes=sub_processes_B,
    keep_resources=keep_resources,
)
# create a list of the sub processes
sub_processes_A = [
    model.BasicActivity(
        env=my_env,
        name="A Basic activity1",
        registry=registry,
        duration=14,
        additional_logs=[reporting_activity],
        keep_resources=keep_resources,
    ),
    model.BasicActivity(
        env=my_env,
        name="A Basic activity2",
        registry=registry,
        duration=10,
        additional_logs=[reporting_activity],
        keep_resources=keep_resources,
    ),
    model.BasicActivity(
        env=my_env,
        name="A Basic activity3",
        registry=registry,
        duration=220,
        additional_logs=[reporting_activity],
        keep_resources=keep_resources,
        start_event=[
            {
                "name": "B Basic activity2", 
                "type": "activity", 
                "state": "done"
            }
        ],
    ),
]

# create a 'sequential activity' that is made up of the 'sub_processes'
sequential_activity_A = model.SequentialActivity(
    env=my_env,
    name="A Sequential process",
    registry=registry,
    sub_processes=sub_processes_A,
    keep_resources=keep_resources,
)

4. Register processes and run simpy#

# initate the simpy processes defined in the 'while activity' and run simpy
model.register_processes([sequential_activity_A, sequential_activity_B, reporting_activity])
my_env.run()

5. Inspect results#

5.1 Inspect logs#

display(plot.get_log_dataframe(reporting_activity, [sequential_activity_A, *sub_processes_A, sequential_activity_B, *sub_processes_B, reporting_activity]))
Activity Timestamp ActivityState type ref
0 Reporting activity 1970-01-01 00:00:00 START NaN NaN
1 A Basic activity1 1970-01-01 00:00:00 START additional log e7313420-d7c4-4550-bcfc-9392bc2fdfae
2 B Basic activity1 1970-01-01 00:00:00 START additional log d7c361f6-7b3c-4557-a7b7-3755b5940495
3 Reporting activity 1970-01-01 00:00:00 STOP NaN NaN
4 B Basic activity1 1970-01-01 00:00:10 STOP additional log d7c361f6-7b3c-4557-a7b7-3755b5940495
5 B Basic activity2 1970-01-01 00:00:10 START additional log ca1011ac-8c6a-4b9c-bab7-db24dd363c96
6 A Basic activity1 1970-01-01 00:00:14 STOP additional log e7313420-d7c4-4550-bcfc-9392bc2fdfae
7 A Basic activity2 1970-01-01 00:00:14 START additional log 66989b96-c7e2-486e-9352-2160ef5dfd01
8 A Basic activity2 1970-01-01 00:00:24 STOP additional log 66989b96-c7e2-486e-9352-2160ef5dfd01
11 A Basic activity3 1970-01-01 00:00:24 WAIT_START additional log 730be5db-a1b9-4923-b11f-dbca919b56ef
9 B Basic activity2 1970-01-01 00:08:30 STOP additional log ca1011ac-8c6a-4b9c-bab7-db24dd363c96
10 B Basic activity3 1970-01-01 00:08:30 START additional log 92689d91-090a-45b3-bd27-288a8ed3a036
12 A Basic activity3 1970-01-01 00:08:30 WAIT_STOP additional log 730be5db-a1b9-4923-b11f-dbca919b56ef
13 A Basic activity3 1970-01-01 00:08:30 START additional log 730be5db-a1b9-4923-b11f-dbca919b56ef
14 B Basic activity3 1970-01-01 00:10:30 STOP additional log 92689d91-090a-45b3-bd27-288a8ed3a036
15 A Basic activity3 1970-01-01 00:12:10 STOP additional log 730be5db-a1b9-4923-b11f-dbca919b56ef

5.2 Visualise gantt charts#

plot.get_gantt_chart([*model.get_subprocesses(sequential_activity_A), *model.get_subprocesses(sequential_activity_B)])

Both processes start at the same time. A Basic activity3 enters waiting state since A Basic activity2 has not been completed yet.

Hint: Having a start_event based on the last activity of a sequence is currently not working. If the constraint is made based on the end of the parent sequence, then the intended synchronization can be accomplished.

When changing the duration of A Basic activity2 to 1000 then there will be no waiting since the A Basic activity2 completes after B Basic activity2 has been completed.

5.3 Visualise container volume developments#

Since there are no objects with containers, in this example, there is nothing to visualise.