CriticalPath analysis#

This notebook provides an example of a simulation that takes a number of sub_processes, grouped in a SequentialActivity, that is executed in a WhileActivity while a stop condition is not yet met.

For this example we work with the following sub_processes:

  • sailing empty

  • loading

  • sailing full

  • unloading

After the simulation is run a gantt chart with the critical path is visualised. Note that the simulation is run with an environment within OpenClSim (which is designed for the purpose of extracting the critical path) and not with simpy.Environment.

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
from openclsim.critical_path.dependencies_from_simpy_step import DependenciesFromSimpy, CriticalPathEnvironment

1. Initialise simpy environment#

# setup environment
my_env = CriticalPathEnvironment(initial_time=0)

2. Define object classes#

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

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

3. Create objects#

3.1. Create site object(s)#

# 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,
                  "capacity": 100,
                  "level": 100
                 }
# instantiate from_site 
from_site = Site(**data_from_site)

# prepare input data for to_site
location_to_site = shapely.geometry.Point(4.25222222, 52.11428333)
data_to_site = {"env": my_env,
                "name": "to_site",
                "geometry": location_to_site,
                "capacity": 100,
                "level": 0
               }
# instantiate to_site 
to_site = Site(**data_to_site)

3.2. Create vessel object(s)#

# prepare input data for vessel_01
data_vessel01 = {"env": my_env,
                 "name": "vessel01",
                 "geometry": location_from_site, 
                 "loading_rate": 0.0004,
                 "unloading_rate": 0.0004,
                 "capacity": 4,
                 "compute_v": lambda x: 10
               }
# instantiate vessel_01 
vessel01 = TransportProcessingResource(**data_vessel01)

3.3 Create activity/activities#

# initialise registry
registry = {}
# create a list of the sub processes
sub_processes = [
    model.MoveActivity(
        env=my_env,
        name="sailing empty",
        registry=registry,
        mover=vessel01,
        destination=from_site,
    ),
    model.ShiftAmountActivity(
        env=my_env,
        name="loading",
        registry=registry,
        processor=vessel01,
        origin=from_site,
        destination=vessel01,
        amount=4,
        duration=1000,
    ),
    model.MoveActivity(
        env=my_env,
        name="sailing full",
        registry=registry,
        mover=vessel01,
        destination=to_site,
    ),
    model.ShiftAmountActivity(
        env=my_env,
        name="unloading",
        registry=registry,
        processor=vessel01,
        origin=vessel01,
        destination=to_site,
        amount=4,
        duration=1000,
    ),
    model.BasicActivity(
        env=my_env,
        name="basic activity",
        registry=registry,
        duration=0,
        additional_logs=[vessel01],
    ),
]

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

# create a while activity that executes the 'sequential activity' while the stop condition is not triggered 
while_activity = model.WhileActivity(
    env=my_env,
    name="while",
    registry=registry,
    sub_processes=[sequential_activity],
    condition_event=[{"type": "container", "concept": to_site, "state": "full"}],
)

4. Register processes and run simpy#

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

5. Inspect results#

5.1 Inspect logs#

plot.get_log_dataframe(vessel01, [while_activity])
Activity Timestamp ActivityState container level geometry type ref
0 sailing empty 1970-01-01 00:00:00.000000 START 0.0 POINT (4.18055556 52.18664444) NaN NaN
1 sailing empty 1970-01-01 00:00:00.000000 STOP 0.0 POINT (4.18055556 52.18664444) NaN NaN
2 loading 1970-01-01 00:00:00.000000 START 0.0 POINT (4.18055556 52.18664444) NaN NaN
3 loading 1970-01-01 00:16:40.000000 STOP 4.0 POINT (4.18055556 52.18664444) NaN NaN
4 sailing full 1970-01-01 00:16:40.000000 START 4.0 POINT (4.18055556 52.18664444) NaN NaN
... ... ... ... ... ... ... ...
245 sailing full 1970-01-02 02:26:38.404972 STOP 4.0 POINT (4.25222222 52.11428333) NaN NaN
246 unloading 1970-01-02 02:26:38.404972 START 4.0 POINT (4.25222222 52.11428333) NaN NaN
247 unloading 1970-01-02 02:43:18.404972 STOP 0.0 POINT (4.25222222 52.11428333) NaN NaN
248 basic activity 1970-01-02 02:43:18.404972 START 0.0 POINT (4.25222222 52.11428333) additional log 96f1d46a-a682-44eb-bbbd-5c51c62ea42f
249 basic activity 1970-01-02 02:43:18.404972 STOP 0.0 POINT (4.25222222 52.11428333) additional log 96f1d46a-a682-44eb-bbbd-5c51c62ea42f

250 rows × 7 columns

5.2 Visualise gantt charts#

plot.get_gantt_chart([while_activity])
plot.get_gantt_chart([vessel01, from_site, to_site],id_map=[while_activity])

5.3 Visualise container volume developments#

fig = plot.get_step_chart([vessel01, from_site, to_site])
../_images/2bec3873e8128b0d1390488968d4d5cf054ee4b7115c76c6ace0e99daf808b50.png

5.4 Visualise critical path#

Determine critical path with method ‘from simpy’. One can investigate the activities on the critical path through inspection of the dataframe with recorded activities or through a gannt chart.

my_cp_dependencies_from_simpy = DependenciesFromSimpy(env=my_env, 
                                                      object_list=[vessel01, from_site, to_site],
                                                      activity_list=[while_activity])
critical_df = my_cp_dependencies_from_simpy.get_critical_path_df()
print("An overview of recorded activities, with an indication if activity is on critical path (column 'is_critical')")
critical_df
An overview of recorded activities, with an indication if activity is on critical path (column 'is_critical')
ActivityID Activity SimulationObject start_time end_time duration state cp_activity_id is_critical
1 f22afd72-13aa-4b38-8d1d-28c93acdc36f loading from_site 1970-01-01 00:00:00.000000 1970-01-01 00:16:40.000000 0 days 00:16:40 ACTIVE 79239fec-36f8-4a3b-9069-67d099c1cabb True
2 f22afd72-13aa-4b38-8d1d-28c93acdc36f loading vessel01 1970-01-01 00:00:00.000000 1970-01-01 00:16:40.000000 0 days 00:16:40 ACTIVE 79239fec-36f8-4a3b-9069-67d099c1cabb True
4 49eafd9c-23fe-43c3-8bbb-40923f84d8cd sailing empty vessel01 1970-01-01 00:00:00.000000 1970-01-01 00:00:00.000000 0 days 00:00:00 ACTIVE 0a37f983-626f-4cd1-9f96-1dbeb601d7eb False
11 16f6278f-7bb3-4299-b4b2-3742326cb0ed sailing full vessel01 1970-01-01 00:16:40.000000 1970-01-01 00:32:22.824591 0 days 00:15:42.824591 ACTIVE 056fc0f7-233b-4bf8-89ad-95b1944d09e6 True
15 8ad97efb-f073-4368-a54b-c4db7144e147 unloading to_site 1970-01-01 00:32:22.824591 1970-01-01 00:49:02.824591 0 days 00:16:40 ACTIVE ea4ee3ce-26ef-4190-bac6-4d1603fcbd27 True
... ... ... ... ... ... ... ... ... ...
464 f22afd72-13aa-4b38-8d1d-28c93acdc36f loading vessel01 1970-01-02 01:54:15.580381 1970-01-02 02:10:55.580381 0 days 00:16:40 ACTIVE a13272f8-88ff-46ea-b60f-9cad1781ab71 True
467 16f6278f-7bb3-4299-b4b2-3742326cb0ed sailing full vessel01 1970-01-02 02:10:55.580381 1970-01-02 02:26:38.404972 0 days 00:15:42.824591 ACTIVE e8bb1673-2972-4284-a13b-97191c4baa24 True
471 8ad97efb-f073-4368-a54b-c4db7144e147 unloading to_site 1970-01-02 02:26:38.404972 1970-01-02 02:43:18.404972 0 days 00:16:40 ACTIVE e077283c-f164-42aa-98f1-a3d925954772 True
472 8ad97efb-f073-4368-a54b-c4db7144e147 unloading vessel01 1970-01-02 02:26:38.404972 1970-01-02 02:43:18.404972 0 days 00:16:40 ACTIVE e077283c-f164-42aa-98f1-a3d925954772 True
474 96f1d46a-a682-44eb-bbbd-5c51c62ea42f basic activity vessel01 1970-01-02 02:43:18.404972 1970-01-02 02:43:18.404972 0 days 00:00:00 ACTIVE 52936c37-1578-4642-9426-8b2d88476dc8 False

175 rows × 9 columns

my_cp_dependencies_from_simpy.make_plotly_gantt_chart()