MultiContainer + 2 ShiftAmounts#

In certain scenarios it is important to distinguish which types of objects are available in a container. HasContainer does not provide this capability, while MultiContainer does.

A MultiContainer represents a named set of containers. The number of allowed containers is limited by parameter store_capacity.

For each container a name (id_ property), a capacity and level is specified.

A MultiContainer can be initialized with parameter initials as provided in the example for site and vessel configuration below.

In this example two ShiftAmountActivities are packed in a SequentialActivity, loading one piece of Cargo type 1 and one piece of Cargo type 2 from from_site to vessel01.

0. Import libraries#

import datetime, time
import simpy

import shapely.geometry
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#

# create a Site object based on desired mixin classes
Site = type(
    "Site",
    (
        core.Identifiable,
        core.Log,
        core.Locatable,
        core.HasMultiContainer,
        core.HasResource,
    ),
    {},
)

# create a TransportProcessingResource object based on desired mixin classes
TransportProcessingResource = type(
    "TransportProcessingResource",
    (
        core.MultiContainerDependentMovable,
        core.Processor,
        core.HasResource,
        core.LoadingFunction,
        core.UnloadingFunction,
        core.Identifiable,
        core.Log,
    ),
    {},
)

3. Create objects#

3.1. Create site object(s)#

The clas HasContainer has now been replaced with HasMultiContainer. The from_site can contain four different container types, but right now contains only two: One called ‘Cargo type 1’ with a capacity of 10 and a level of 2 and one called ‘Cargo type 2’ with a capacity of 2 and a level of 0.

# prepare input data for from_site
location_from_site = shapely.geometry.Point(4.18055556, 52.18664444)
data_from_site = {"env": my_env,
                  "name": "from_site",
                  "geometry": location_from_site,
                  "store_capacity": 4,
                  "initials": [
                                  {"id": "Cargo type 1", "level": 10, "capacity": 10},
                                  {"id": "Cargo type 2", "level":  5, "capacity":  5},
                              ],
                 }
# instantiate from_site 
from_site = Site(**data_from_site)

3.2. Create vessel object(s)#

The class ContainerDependentMovable has been replaced with MultiContainerDependentMovable. The vessel has two containers, one for ‘Cargo type 1’ and one for ‘Cargo type 2’, each with a capacity of two and a current level of zero.

# prepare input data for vessel_01
data_vessel01 = {"env": my_env,
                 "name": "vessel01",
                 "geometry": location_from_site, 
                 "loading_rate": 1,
                 "unloading_rate": 1,
                 "store_capacity": 4,
                 "initials": [
                                {"id": "Cargo type 1", "level": 0, "capacity": 2},
                                {"id": "Cargo type 2", "level": 0, "capacity": 2},
                             ],
                 "compute_v": lambda x: 10
               }
# instantiate vessel_01 
vessel01 = TransportProcessingResource(**data_vessel01)

ShiftAmount Activity for MultiContainer#

The amount specifies the objects to be transfered and the id_ parameter specifies to which container this activity relates.

# initialise registry
registry = {}
# create a list of the sub processes and include reporting_activity
sub_processes = [
    model.ShiftAmountActivity(
        env=my_env,
        name="Transfer cargo type 1",
        registry=registry,
        processor=vessel01,
        origin=from_site,
        destination=vessel01,
        amount=1,
        id_="Cargo type 1",
        duration=20,
    ),
    model.ShiftAmountActivity(
        env=my_env,
        name="Transfer cargo type 2",
        registry=registry,
        processor=vessel01,
        origin=from_site,
        destination=vessel01,
        amount=1,
        id_="Cargo type 2",
        duration=40,
    )
]

# create a 'sequential activity' that is made up of the 'sub_processes'
sequential_activity = model.SequentialActivity(
    env=my_env,
    name="Sequential activity of basic activities",
    registry=registry,
    sub_processes=sub_processes,
)

4. Register processes and run simpy#

# initate the simpy processes defined in the 'sub processes' and run simpy
model.register_processes(sequential_activity)
my_env.run()

5. Inspect results#

5.1 Inspect logs#

display(plot.get_log_dataframe(vessel01, [*sub_processes]))
Activity Timestamp ActivityState container level geometry
0 Transfer cargo type 1 1970-01-01 00:00:00 START {'Cargo type 1': 0, 'Cargo type 2': 0} POINT (4.18055556 52.18664444)
1 Transfer cargo type 1 1970-01-01 00:00:20 STOP {'Cargo type 2': 0, 'Cargo type 1': 1} POINT (4.18055556 52.18664444)
2 Transfer cargo type 2 1970-01-01 00:00:20 START {'Cargo type 2': 0, 'Cargo type 1': 1} POINT (4.18055556 52.18664444)
3 Transfer cargo type 2 1970-01-01 00:01:00 STOP {'Cargo type 1': 1, 'Cargo type 2': 1} POINT (4.18055556 52.18664444)
display(plot.get_log_dataframe(from_site, [*sub_processes]))
Activity Timestamp ActivityState container level geometry
0 Transfer cargo type 1 1970-01-01 00:00:00 START {'Cargo type 1': 10, 'Cargo type 2': 5} POINT (4.18055556 52.18664444)
1 Transfer cargo type 1 1970-01-01 00:00:20 STOP {'Cargo type 2': 5, 'Cargo type 1': 9} POINT (4.18055556 52.18664444)
2 Transfer cargo type 2 1970-01-01 00:00:20 START {'Cargo type 2': 5, 'Cargo type 1': 9} POINT (4.18055556 52.18664444)
3 Transfer cargo type 2 1970-01-01 00:01:00 STOP {'Cargo type 1': 9, 'Cargo type 2': 4} POINT (4.18055556 52.18664444)

5.2 Visualise gantt charts#

plot.get_gantt_chart([sequential_activity, vessel01, *sub_processes])

5.3 Visualise step charts#

fig = plot.get_step_chart([vessel01])
../_images/63c9ed7c081aeda2c55dcf0a7cfecc27da1f402d24aed2b8aa9f68ab9c79f4e6.png
fig = plot.get_step_chart([from_site])
../_images/57bba732a6fe94692f8bcc1eaef160cb1e39630dd21f6a3f850492f04e26580e.png

Observe that first 1 piece of ‘Cargo type 1’ is loaded from ‘from_site’ to ‘vessel01’. This takes 20 time units. Next 1 piece of ‘Cargo type 2’ is loaded from ‘from_site’ to ‘vessel01’. This takes 20 time units.

The original stock of ‘Cargo type 1’ was 10, and it is reduced to 9 after 20 time units. The original stock of ‘Cargo type 2’ was 5, and it is reduced to 4 after 60 time units.