Open source Complex Logistics Simulation

OpenCLSim is a python package for rule driven scheduling of cyclic activities for in-depth comparison of alternative operating strategies

Welcome to OpenCLSim documentation! Please check the contents below for information on installation, getting started and actual example code. If you want to dive straight into the code you can check out our GitHub page or the working examples presented in Jupyter Notebooks.

Installation

Stable release

To install OpenCLSim, run this command in your terminal:

# Use pip to install OpenCLSim
pip install openclsim

This is the preferred method to install OpenCLSim, as it will always install the most recent stable release.

If you do not pip installed, this Python installation guide can guide you through the process.

From sources

The sources for OpenCLSim can be downloaded from the Github repo.

You can either clone the public repository:

# Use git to clone OpenCLSim
git clone git://github.com/TUDelft-CITG/OpenCLSim

Or download the tarball:

# Use curl to obtain the tarball
curl  -OL https://github.com/TUDelft-CITG/OpenCLSim/tarball/master

Once you have a copy of the source, you can install it with:

# Use python to install
python setup.py install

Usage

Import required components

To use OpenCLSim in a project you have to import the following three components:

# Import openclsim for the logistical components
import openclsim.model as model
import openclsim.core as core

# Import simpy for the simulation environment
import simpy

Using Mixins and Metaclasses

The Open Complex Logistics Simulation package is developed with the goal of reusable and generic components in mind. A new class can be instatiated by combining mixins from the openclsim.core, such as presented below. The following lines of code demonstrate how a containervessel can be defined:

# Define the core components
# A generic class for an object that can move and transport material
ContainerVessel = type('ContainerVessel',
                    (core.Identifiable,             # Give it a name and unique UUID
                     core.Log,                      # Allow logging of all discrete events
                     core.ContainerDependentMovable,# It can transport an amount
                     core.HasResource,              # Add information on serving equipment
                     ),
                    {})

# The next step is to define all the required parameters for the defined metaclass
# For more realistic simulation you might want to have speed dependent on the filling degree
v_full  = 8     # meters per second
v_empty = 5     # meters per second

def variable_speed(v_empty, v_full):
    return lambda x: x * (v_full - v_empty) + v_empty

# Other variables
data_vessel = {
           "env": simpy.Environment(),                   # The simpy environment
           "name": "Vessel 01",                          # Name
           "geometry": shapely.geometry.Point(0, 0),     # The lat, lon coordinates
           "capacity": 5_000,                            # Capacity of the vessel
           "compute_v": variable_speed(v_empty, v_full), # Variable speed
           }

# Create an object based on the metaclass and vessel data
vessel_01 = ContainerVessel(**data_vessel)

For more elaboration and examples please check the examples documentation. In-depth Jupyter Notebooks can also be used.

Examples

This small example guide will cover the basic start-up and the three main elements of the OpenClSim package:

  • Start-Up (Minimal set-up)
  • Locations (Sites or stockpiles)
  • Resources (Processors and transporters)
  • Activities (Rule-based operations)

Once the elements above are explained some small simulations examples are presented.

Start-Up

The first part of every OpenClSim simulation is to import the required libraries and to initiate the simulation environment.

Required Libraries

Depending on the simulation it might be required to import additional libraries. The minimal set-up of an OpenCLSim project has the following import statements:

# Import openclsim for the logistical components
import openclsim.model as model
import openclsim.core as core

# Import simpy for the simulation environment
import simpy

Simulation Environment

OpenClSim continues on the SimPy discrete event simulation package. Some components are modified, such as the resources and container objects, but the simulation environment is pure SimPy. Starting the simulation environment can be done with the following line of code. For more information in SimPy environment please refer to the SimPy documentation.

# Start the SimPy environment
env = simpy.Environment()

Locations

Basic processes do not require a location but more comprehensive simulations do. Locations are added to an OpenCLSim environment so that it becomes possible to track all events in both time and space. If a site is initiated with a container it can store materials as well. Adding a processor allows the location to load or unload as well.

Basic Location

The code below illustrates how a basic location can be created using OpenClSim. Such a location can be used to add information on events in space, such as tracking movement events or creating paths to follow.

# Import the library required to add coordinates
import shapely.geometry

# Create a location class
Location = type(
    "Location",
    (
        core.Identifiable,  # Give it a name and unique UUID
        core.Log,           # To keep track of all events
        core.HasResource,   # Add information on the number of resources
        core.Locatable,     # Add coordinates to extract distance information
    ),
    {},
)

location_data = {
    "env": env,                               # The SimPy environment
    "name": "Location 01",                    # Name of the location
    "geometry": shapely.geometry.Point(0, 0), # The lat, lon coordinates
}

location_01 = Location(**location_data)

Storage Location

The code below illustrates how a location can be created that is capable of storing an amount. Such a location can be used by the OpenClSim.model activities as origin or destination.

# Import the library required to add coordinates
import shapely.geometry

# Create a location class
StorageLocation = type(
    "StorageLocation",
    (
        core.Identifiable,  # Give it a name and unique UUID
        core.Log,           # To keep track of all events
        core.HasResource,   # Add information on the number of resources
        core.Locatable,     # Add coordinates to extract distance information
        core.HasContainer,  # Add information on storage capacity
    ),
    {},
)

location_data = {
    "env": env,                               # The SimPy environment
    "name": "Location 02",                    # Name of the location
    "geometry": shapely.geometry.Point(0, 0), # The lat, lon coordinates
    "capacity": 10_000,                       # The maximum number of units
    "level": 10_000,                          # The number of units in the location
}

location_02 = StorageLocation(**location_data)

Processing Storage Location

The code below illustrates how a location can be created that is capable of storing an amount. Additional to the storage location, a processing- and storage location can be used as both the origin and loader or destination and unloader in a OpenClSim.model activity.

# Import the library required to add coordinates
import shapely.geometry

# Create a location class
ProcessingStorageLocation = type(
    "ProcessingStorageLocation",
    (
        core.Identifiable,  # Give it a name and unique UUID
        core.Log,           # To keep track of all events
        core.HasResource,   # Add information on the number of resources
        core.Locatable,     # Add coordinates to extract distance information
        core.HasContainer,  # Add information on storage capacity
        core.Processor,     # Add information on processing
    ),
    {},
)

# Create a processing function
processing_rate = lambda x: x

location_data = {
    "env": env,                               # The SimPy environment
    "name": "Location 03",                    # Name of the location
    "geometry": shapely.geometry.Point(0, 1), # The lat, lon coordinates
    "capacity": 10_000,                       # The maximum number of units
    "level": 0,                               # The number of units in the location
    "loading_func": processing_rate,          # Loading rate of 1 unit per 1 unit time
    "unloading_func": processing_rate,        # Unloading rate of 1 unit per 1 unit time
}

location_03 = ProcessingStorageLocation(**location_data)

Optionally a OpenCLSim.core.Log mixin can be added to all locations to keep track of all the events that are taking place.

Resources

OpenCLSim resources can be used to process and transport units. The OpenCLSim.model activity class requires a loader, an unloader and a mover, this are examples of resources. A resource will always interact with another resource in an OpenClSim.model activity, but it is possible to initiate a simpy process to keep track of a single resource.

Processing Resource

An example of a processing resource is a harbour crane, it processes units from a storage location to a transporting resource or vice versa. In the OpenClSim.model activity such a processing resource could be selected as the loader or unloader. The example code is presented below.

# Create a resource
ProcessingResource = type(
    "ProcessingResource",
    (
        core.Identifiable,  # Give it a name and unique UUID
        core.Log,           # To keep track of all events
        core.HasResource,   # Add information on the number of resources
        core.Locatable,     # Add coordinates to extract distance information
        core.Processor,     # Add information on processing
    ),
    {},
)

# The next step is to define all the required parameters for the defined metaclass
# Create a processing function
processing_rate = lambda x: x

resource_data = {
    "env": env,                         # The SimPy environment
    "name": "Resource 01",              # Name of the location
    "geometry": location_01.geometry,   # The lat, lon coordinates
    "loading_func": processing_rate,    # Loading rate of 1 unit per 1 unit time
    "unloading_func": processing_rate,  # Unloading rate of 1 unit per 1 unit time
}

# Create an object based on the metaclass and vessel data
resource_01 = ProcessingResource(**resource_data)

Transporting Resource

A harbour crane will service transporting resources. To continue with the harbour crane example, basically any vessel is a transporting resource because it is capable of moving units from location A to location B. In the OpenClSim.model activity such a processing resource could be selected as the mover.

# Create a resource
TransportingResource = type(
    "TransportingResource",
    (
        core.Identifiable,              # Give it a name and unique UUID
        core.Log,                       # To keep track of all events
        core.HasResource,               # Add information on the number of resources
        core.ContainerDependentMovable, # It can transport an amount
    ),
    {},
)

# The next step is to define all the required parameters for the defined metaclass
# For more realistic simulation you might want to have speed dependent on the filling degree
v_full = 8  # meters per second
v_empty = 5  # meters per second

def variable_speed(v_empty, v_full):
    return lambda x: x * (v_full - v_empty) + v_empty

# Other variables
resource_data = {
    "env": env,                                   # The SimPy environment
    "name": "Resource 02",                        # Name of the location
    "geometry": location_01.geometry,             # The lat, lon coordinates
    "capacity": 5_000,                            # Capacity of the vessel
    "compute_v": variable_speed(v_empty, v_full), # Variable speed
}

# Create an object based on the metaclass and vessel data
resource_02 = TransportingResource(**resource_data)

Transporting Processing Resource

Finally, some resources are capable of both processing and moving units. Examples are dredging vessels or container vessels with deck cranes. These specific vessels have the unique property that they can act as the loader, unloader and mover in the OpenClSim.model activity.

# Create a resource
TransportingProcessingResource = type(
    "TransportingProcessingResource",
    (
        core.Identifiable,              # Give it a name and unique UUID
        core.Log,                       # To keep track of all events
        core.HasResource,               # Add information on the number of resources
        core.ContainerDependentMovable, # It can transport an amount
        core.Processor,                 # Add information on processing
    ),
    {},
)

# The next step is to define all the required parameters for the defined metaclass
# For more realistic simulation you might want to have speed dependent on the filling degree
v_full = 8  # meters per second
v_empty = 5  # meters per second

def variable_speed(v_empty, v_full):
    return lambda x: x * (v_full - v_empty) + v_empty

# Create a processing function
processing_rate = lambda x: x

# Other variables
resource_data = {
    "env": env,                                   # The SimPy environment
    "name": "Resource 03",                        # Name of the location
    "geometry": location_01.geometry,             # The lat, lon coordinates
    "capacity": 5_000,                            # Capacity of the vessel
    "compute_v": variable_speed(v_empty, v_full), # Variable speed
    "loading_func": processing_rate,              # Loading rate of 1 unit per 1 unit time
    "unloading_func": processing_rate,            # Unloading rate of 1 unit per 1 unit time
}

# Create an object based on the metaclass and vessel data
resource_03 = TransportingProcessingResource(**resource_data)

Simulations

The code below will start the simulation if SimPy processes are added to the environment. These SimPy processes can be added using a combination of SimPy and OpenCLSim, or by using OpenCLSim activities.

env.run()

SimPy processes

A SimPy process can be initiated using the code below. The code below will instruct Resource 02, which was a TransportingResource, to sail from Location 01 (at Lat, Long (0, 0)) to Location 02 (at Lat, Long (0, 1)). The simulation will stop as soon as Resource 02 is at Location 02.

# Create the process function
def move_resource(mover, destination):

    # the is_at function is part of core.Movable
    while not mover.is_at(destination):

      # the move function is part of core.Movable
      yield from mover.move(destination)

# Add to the SimPy environment
env.process(move_resource(resource_02, location_03))

# Run the simulation
env.run()

Unconditional Activities

Activities are at the core of what OpenCLSim adds to SimPy, an activity is a collection of SimPy Processes. These activities schedule cyclic events, which could be production or logistical processes and, but the current OpenCLSim.model.activity assumes the following cycle:

  • Loading
  • Transporting
  • Unloading
  • Transporting

This cycle is repeated until a certain condition is met. Between the individual components of the cycle waiting events can occur due to arising queues, equipment failure or weather events. The minimal input for an activity is listed below.

  • Origin
  • Destination
  • Loader
  • Mover
  • Unloader

If no additional input is provided, the cyclic process will be repeated until either the origin is empty or the destination is full. The example activity below will stop after two cycles because the origin will be empty and the destination will be full.

# Define the activity
activity_01 = model.Activity(
    env=env,                  # The simpy environment defined in the first cel
    name="Activity 01",       # Name of the activity
    origin=location_02,       # Location 02 was filled with 10_000 units
    destination=location_03,  # Location 03 was empty
    loader=resource_03,       # Resource 03 could load
    mover=resource_03,        # Resource 03 could move
    unloader=resource_03,     # Resource 03 could unload
)

# Run the simulation
env.run()

Conditional Activities

Additionally, start and stop events can be added to the activity. The process will only start as soon as a start event (or a list of start events) is completed and it will stop as soon as the stop event (or a list of stop events) are completed. These can be any SimPy event, such as a time-out, but OpenClSim provides some additional events as well, such as empty- or full events. The activity in the example below will start as soon as the previous activity is finished, but not sooner than 2 days after the simulation is started.

# Activity starts after both
#  - Activity 01 is finished
#  - A minimum of 2 days after the simulation starts
start_event = [activity_01.main_process, env.timeout(2 * 24 * 3600)]

# Define the activity
activity_02 = model.Activity(
    env=env,                  # The simpy environment defined in the first cel
    name="Activity 02",       # Name of the activity
    origin=location_03,       # Location 03 will be filled
    destination=location_02,  # Location 02 will be empty
    loader=resource_03,       # Resource 03 could load
    mover=resource_03,        # Resource 03 could move
    unloader=resource_03,     # Resource 03 could unload
    start_event=start_event,  # Start Event
)

# Run the simulation
env.run()

OpenCLSim

This page lists all functions and classes available in the OpenCLSim.model and OpenCLSim.core modules. For examples on how to use these submodules please check out the Examples page, information on installing OpenCLSim can be found on the Installation page.

Submodules

The main components are the Model module and the Core module. All of their components are listed below.

openclsim.model module

Directory for the simulation activities.

class openclsim.model.AbstractPluginClass[source]

Bases: abc.ABC

Abstract class used as the basis for all Classes implementing a plugin for a specific Activity.

Instance checks will be performed on this class level.

post_process(env, activity_log, activity, start_preprocessing, start_activity, *args, **kwargs)[source]
pre_process(env, activity_log, activity, *args, **kwargs)[source]
validate()[source]
class openclsim.model.PluginActivity(*args, **kwargs)[source]

Bases: openclsim.core.identifiable.Identifiable, openclsim.core.log.Log

Base class for all activities which will provide a plugin mechanism.

The plugin mechanism foresees that the plugin function pre_process is called before the activity is executed, while the function post_process is called after the activity has been executed.

delay_processing(env, activity_label, activity_log, waiting)[source]
post_process(*args, **kwargs)[source]
pre_process(args_data)[source]
register_plugin(plugin, priority=0)[source]
class openclsim.model.GenericActivity(registry, start_event=None, requested_resources={}, keep_resources=[], *args, **kwargs)[source]

Bases: openclsim.model.base_activities.PluginActivity

The GenericActivity Class forms a generic class which sets up all activites.

delayed_process(activity_log, env)[source]

Return a generator which can be added as a process to a simpy environment.

parse_expression(expr)[source]
register_process()[source]
class openclsim.model.MoveActivity(mover, destination, duration=None, show=False, engine_order=1, *args, **kwargs)[source]

Bases: openclsim.model.base_activities.GenericActivity

MoveActivity Class forms a specific class for a single move activity within a simulation.

It deals with a single origin container, destination container and a single combination of equipment to move substances from the origin to the destination. It will initiate and suspend processes according to a number of specified conditions. To run an activity after it has been initialized call env.run() on the Simpy environment with which it was initialized.

To check when a transportation of substances can take place, the Activity class uses three different condition arguments: start_condition, stop_condition and condition. These condition arguments should all be given a condition object which has a satisfied method returning a boolean value. True if the condition is satisfied, False otherwise.

destination: object inheriting from HasContainer, HasResource, Locatable, Identifiable and Log mover: moves to ‘origin’ if it is not already there, is loaded, then moves to ‘destination’ and is unloaded

should inherit from Movable, HasContainer, HasResource, Identifiable and Log after the simulation is complete, its log will contain entries for each time it started moving, stopped moving, started loading / unloading and stopped loading / unloading
start_event: the activity will start as soon as this event is triggered
by default will be to start immediately
main_process_function(activity_log, env)[source]

Return a generator which can be added as a process to a simpy.Environment.

In the process, a move will be made by the mover, moving it to the destination.

activity_log: the core.Log object in which log_entries about the activities progress will be added. env: the simpy.Environment in which the process will be run mover: moves from its current position to the destination

should inherit from core.Movable
destination: the location the mover will move to
should inherit from core.Locatable
engine_order: optional parameter specifying at what percentage of the maximum speed the mover should sail.
for example, engine_order=0.5 corresponds to sailing at 50% of max speed
class openclsim.model.BasicActivity(duration, additional_logs=None, show=False, *args, **kwargs)[source]

Bases: openclsim.model.base_activities.GenericActivity

BasicActivity Class is a generic class to describe an activity, which does not require any specific resource, but has a specific duration.

duration: time required to perform the described activity. additional_logs: list of other concepts, where the start and the stop of the basic activity should be recorded. start_event: the activity will start as soon as this event is triggered

by default will be to start immediately
main_process_function(activity_log, env)[source]

Return a generator which can be added as a process to a simpy.Environment.

The process will report the start of the activity, delay the execution for the provided duration, and finally report the completion of the activiy.

activity_log: the core.Log object in which log_entries about the activities progress will be added. env: the simpy.Environment in which the process will be run stop_event: a simpy.Event object, when this event occurs, the conditional process will finish executing its current

run of its sub_processes and then finish
sub_processes: an Iterable of methods which will be called with the activity_log and env parameters and should
return a generator which could be added as a process to a simpy.Environment the sub_processes will be executed sequentially, in the order in which they are given as long as the stop_event has not occurred.
class openclsim.model.SequentialActivity(sub_processes, show=False, *args, **kwargs)[source]

Bases: openclsim.model.base_activities.GenericActivity, openclsim.model.base_activities.RegisterSubProcesses

SequenceActivity Class forms a specific class.

This is for executing multiple activities in a dedicated order within a simulation. It is a structural activity, which does not require specific resources.

sub_processes:
a list of activities to be executed in the provided sequence.
start_event:
The activity will start as soon as this event is triggered by default will be to start immediately
main_process_function(activity_log, env)[source]
class openclsim.model.WhileActivity(sub_processes, condition_event, show=False, *args, **kwargs)[source]

Bases: openclsim.model.base_activities.GenericActivity, openclsim.model.while_activity.ConditionProcessMixin, openclsim.model.base_activities.RegisterSubProcesses

WhileActivity Class forms a specific class for executing multiple activities in a dedicated order within a simulation.

The while activity is a structural activity, which does not require specific resources.

sub_processes
the sub_processes which is executed in sequence in every iteration
condition_event
a condition event provided in the expression language which will stop the iteration as soon as the event is fulfilled.
start_event
the activity will start as soon as this event is triggered by default will be to start immediately
class openclsim.model.RepeatActivity(sub_processes, repetitions: int, show=False, *args, **kwargs)[source]

Bases: openclsim.model.base_activities.GenericActivity, openclsim.model.while_activity.ConditionProcessMixin, openclsim.model.base_activities.RegisterSubProcesses

RepeatActivity Class forms a specific class for executing multiple activities in a dedicated order within a simulation.

sub_processes
the sub_processes which is executed in sequence in every iteration
repetitions
Number of times the subprocess is repeated
start_event
the activity will start as soon as this event is triggered by default will be to start immediately
openclsim.model.single_run_process(env, registry, name, origin, destination, mover, loader, unloader, start_event=None, stop_event=[], requested_resources={})[source]

Single run activity for the simulation.

class openclsim.model.ShiftAmountActivity(processor, origin, destination, duration=None, amount=None, id_='default', show=False, phase=None, *args, **kwargs)[source]

Bases: openclsim.model.base_activities.GenericActivity

ShiftAmountActivity Class forms a specific class for shifting material from an origin to a destination.

It deals with a single origin container, destination container and a single processor to move substances from the origin to the destination. It will initiate and suspend processes according to a number of specified conditions. To run an activity after it has been initialized call env.run() on the Simpy environment with which it was initialized.

origin: container where the source objects are located. destination: container, where the objects are assigned to processor: resource responsible to implement the transfer. amount: the maximum amount of objects to be transfered. duration: time specified in seconds on how long it takes to transfer the objects. id_: in case of MultiContainers the id_ of the container, where the objects should be removed from or assiged to respectively. start_event: the activity will start as soon as this event is triggered

by default will be to start immediately
main_process_function(activity_log, env)[source]

Origin and Destination are of type HasContainer.

class openclsim.model.ParallelActivity(sub_processes, show=False, *args, **kwargs)[source]

Bases: openclsim.model.base_activities.GenericActivity, openclsim.model.base_activities.RegisterSubProcesses

ParallelActivity Class forms a specific class.

This is for executing multiple activities in a dedicated order within a simulation. It is a structural activity, which does not require specific resources.

sub_processes:
a list of activities to be executed in Parallel.
start_event:
The activity will start as soon as this event is triggered by default will be to start immediately
main_process_function(activity_log, env)[source]
openclsim.model.register_processes(processes)[source]

Register all the processes iteratively.

openclsim.model.get_subprocesses(items)[source]

Get a list of all the activities an their subprocesses recursively.

openclsim.core module

Core of the simulation Package.

class openclsim.core.HasContainer(capacity: float, store_capacity: int = 1, level: float = 0.0, *args, **kwargs)[source]

Bases: openclsim.core.simpy_object.SimpyObject

A class which can hold information about objects of the same type.

capacity
amount the container can hold
level
Amount the container holds initially
store_capacity
The number of different types of information can be stored. In this class it usually is 1.
get_state()[source]
class openclsim.core.HasMultiContainer(initials, store_capacity=10, *args, **kwargs)[source]

Bases: openclsim.core.container.HasContainer

A class which can represent information of objects of multiple types.

store_capacity: The number of different types of information can be stored. In this calss it is usually >1. initials: a list of dictionaries describing the id_ of the container, the level of the individual container and the capacity of the individual container.

get_state()[source]
class openclsim.core.EventsContainer(env, store_capacity: int = 1, *args, **kwargs)[source]

Bases: simpy.resources.store.FilterStore

EventsContainer provide a basic class for managing information which has to be stored in an object.

It is a generic container, which has a default behavior, but can be used for storing arbitrary objects.

store_capacity
Number of stores that can be contained by the multicontainer
container_list
empty_event

Properties that are kept for backwards compatibility. mThey are NOT applicable for MultiContainers.

full_event

Properties that are kept for backwards compatibility. mThey are NOT applicable for MultiContainers.

get(amount, id_='default')[source]

Request to get an item from the store matching the filter. The request is triggered once there is such an item available in the store.

filter is a function receiving one item. It should return True for items matching the filter criterion. The default function returns True for all items, which makes the request to behave exactly like StoreGet.

get_available(amount, id_='default')[source]
get_callback(event, id_='default')[source]
get_capacity(id_='default')[source]
get_empty_event(start_event=False, id_='default')[source]
get_full_event(start_event=False, id_='default')[source]
get_level(id_='default')[source]
initialize(init=0, capacity=0)[source]

Initialize method is a convenience method for backwards compatibility reasons.

initialize_container(initials)[source]

Initialize method used for MultiContainers.

put(amount, capacity=0, id_='default')[source]

Request to put item into the store. The request is triggered once there is space for the item in the store.

put_available(amount, id_='default')[source]
put_callback(event, id_='default')[source]
class openclsim.core.Identifiable(name: str, ID: str = None, *args, **kwargs)[source]

Bases: object

OpenCLSim Identifiable with tags and a description.

name
a name
ID : UUID
a unique id generated with uuid
description
Text that can be used to describe a simulation object. Note that this field does not influence the simulation.
tags
List of tags that can be used to identify objects. Note that this field does not influence the simulation.
class openclsim.core.Locatable(geometry, *args, **kwargs)[source]

Bases: object

Something with a geometry (geojson format).

lat : degrees
can be a point as well as a polygon

lon : degrees

get_state()[source]
is_at(locatable, tolerance=100)[source]
class openclsim.core.Log(*args, **kwargs)[source]

Bases: openclsim.core.simpy_object.SimpyObject

Log class to log the object activities.

get_state()[source]

Add an empty instance of the get state function so that it is always available.

log_entry(t, activity_id, activity_state=<LogState.UNKNOWN: -1>, additional_state=None, activity_label={})[source]
class openclsim.core.LogState[source]

Bases: enum.Enum

LogState enumeration of all possible states of a Log object.

Access the name using .name and the integer value using .value

START = 1
STOP = 2
UNKNOWN = -1
WAIT_START = 3
WAIT_STOP = 4
class openclsim.core.Movable(v: float = 1, *args, **kwargs)[source]

Bases: openclsim.core.simpy_object.SimpyObject, openclsim.core.locatable.Locatable

Movable class.

Used for object that can move with a fixed speed geometry: point used to track its current location

v
speed
current_speed
move(destination, engine_order=1.0, duration=None)[source]

Determine distance between origin and destination.

Yield the time it takes to travel based on flow properties and load factor of the flow.

sailing_duration(origin, destination, engine_order, verbose=True)[source]

Determine the sailing duration.

class openclsim.core.ContainerDependentMovable(compute_v, *args, **kwargs)[source]

Bases: openclsim.core.movable.Movable, openclsim.core.container.HasContainer

ContainerDependentMovable class.

Used for objects that move with a speed dependent on the container level compute_v: a function, given the fraction the container is filled (in [0,1]), returns the current speed

v_empty
Velocity of the vessel when empty
v_full
Velocity of the vessel when full
current_speed
class openclsim.core.MultiContainerDependentMovable(compute_v, *args, **kwargs)[source]

Bases: openclsim.core.movable.Movable, openclsim.core.container.HasMultiContainer

MultiContainerDependentMovable class.

Used for objects that move with a speed dependent on the container level. This movable is provided with a MultiContainer, thus can handle container containing different object. compute_v: a function, given the fraction the container is filled (in [0,1]), returns the current speed

current_speed
class openclsim.core.Processor(*args, **kwargs)[source]

Bases: openclsim.core.simpy_object.SimpyObject

Processor class.

Adds the loading and unloading components and checks for possible downtime.

If the processor class is used to allow “loading” or “unloading” the mixins “LoadingFunction” and “UnloadingFunction” should be added as well. If no functions are used a subcycle should be used, which is possible with the mixins “LoadingSubcycle” and “UnloadingSubcycle”.

check_possible_shift(origin, destination, amount, activity, id_='default')[source]

Check if all the material is available.

If the amount is not available in the origin or in the destination yield a put or get. Time will move forward until the amount can be retrieved from the origin or placed into the destination.

determine_processor_amount(origin, destination, amount=None, id_='default')[source]

Determine the maximum amount that can be carried.

process(origin, destination, shiftamount_fcn, id_='default')[source]

Move content from ship to the site or from the site to the ship.

This to ensure that the ship’s container reaches the desired level. Yields the time it takes to process.

class openclsim.core.LoadingFunction(loading_rate: float, load_manoeuvring: float = 0, *args, **kwargs)[source]

Bases: object

Create a loading function and add it a processor.

This is a generic and easy to read function, you can create your own LoadingFunction class and add this as a mixin.

loading_rate : amount / second
The rate at which units are loaded per second
load_manoeuvring : seconds
The time it takes to manoeuvring in minutes
loading(origin, destination, amount, id_='default')[source]

Determine the duration based on an amount that is given as input with processing.

The origin an destination are also part of the input, because other functions might be dependent on the location.

class openclsim.core.UnloadingFunction(unloading_rate: float, unload_manoeuvring: float = 0, *args, **kwargs)[source]

Bases: object

Create an unloading function and add it a processor.

This is a generic and easy to read function, you can create your own LoadingFunction class and add this as a mixin.

unloading_rate : volume / second
the rate at which units are loaded per second
unload_manoeuvring : minutes
the time it takes to manoeuvring in minutes
unloading(origin, destination, amount, id_='default')[source]

Determine the duration based on an amount that is given as input with processing.

The origin an destination are also part of the input, because other functions might be dependent on the location.

class openclsim.core.HasResource(nr_resources: int = 1, *args, **kwargs)[source]

Bases: openclsim.core.simpy_object.SimpyObject

HasProcessingLimit class.

Adds a limited Simpy resource which should be requested before the object is used for processing.

nr_resources
Number of rescources of the object
class openclsim.core.SimpyObject(env, *args, **kwargs)[source]

Bases: object

General object which can be extended by any class requiring a simpy environment.

env
A simpy Environment

openclsim.server module

Module contents

Top-level package for OpenCLSim.

OpenCLSim API

A flask server is part of the OpenCLSim package. This allows using the python code from OpenCLSim from a separate front-end.

Starting the Flask Server

The example code below lets you start the Flask server from the windows command line, for other operation systems please check the Flask Documentation.

# Set Flask app
set FLASK_APP=openclsim/server.py

# Set Flask environment
set FLASK_ENV=development

# Run Flask
flask run

Using the Flask Server

You can send json strings to the Flask Server using the methods presented in the server module.

Contributing

Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.

You can contribute in many ways:

Types of Contributions

Report Bugs

Report bugs at https://github.com/TUDelft-CITG/OpenCLSim/issues.

If you are reporting a bug, please include:

  • Your operating system name and version.
  • Any details about your local setup that might be helpful in troubleshooting.
  • Detailed steps to reproduce the bug.

Fix Bugs

Look through the GitHub issues for bugs. Anything tagged with “bug” and “help wanted” is open to whoever wants to implement it.

Implement Features

Look through the GitHub issues for features. Anything tagged with “enhancement” and “help wanted” is open to whoever wants to implement it.

Write Documentation

OpenCLSim could always use more documentation, whether as part of the official OpenCLSim docs, in docstrings, or even on the web in blog posts, articles, and such.

Submit Feedback

The best way to send feedback is to file an issue at https://github.com/TUDelft-CITG/OpenCLSim/issues.

If you are proposing a feature:

  • Explain in detail how it would work.
  • Keep the scope as narrow as possible, to make it easier to implement.
  • Remember that this is a volunteer-driven project, and that contributions are welcome :)

Get Started!

Ready to contribute? Here’s how to set up OpenCLSim for local development.

  1. Fork the OpenCLSim repository on GitHub.

  2. Clone your fork locally:

    $ git clone git@github.com:your_name_here/OpenCLSim.git
    
  3. Install your local copy into a virtualenv. Assuming you have virtualenvwrapper installed, this is how you set up your fork for local development:

    $ mkvirtualenv openclsim
    $ cd openclsim/
    $ python setup.py develop
    
  4. Create a branch for local development:

    $ git checkout -b name-of-your-bugfix-or-feature
    

    Now you can make your changes locally.

  5. When you’re done making changes, check that your changes pass flake8 and the tests, including testing other Python versions with tox:

    $ flake8 openclsim tests
    $ python setup.py test or py.test
    $ tox
    

    To get flake8 and tox, just pip install them into your virtualenv.

  6. The style of OpenCLSim is according to Black. Format your code using Black witht the following lines of code:

    $ black openclsim
    $ black tests
    

    You can install black using pip.

  7. Commit your changes and push your branch to GitHub:

    $ git add .
    $ git commit -m "Your detailed description of your changes."
    $ git push origin name-of-your-bugfix-or-feature
    
  8. Submit a pull request through the GitHub website.

Pull Request Guidelines

Before you submit a pull request, check that it meets these guidelines:

  1. The pull request should include tests.
  2. If the pull request adds functionality, the docs should be updated. Put your new functionality into a function with a docstring, and add the feature to the list in README.rst.
  3. The pull request should work for Python 3.4, 3.5 and 3.6, and for PyPy. Check CircleCI and make sure that the tests pass for all supported Python versions.

Tips

To run a subset of tests:

$ py.test tests.test_openclsim

To make the documentation pages:

$ make docs # for linux/osx

For windows:

$ del docs\openclsim.rst
$ del docs\modules.rst
$ sphinx-apidoc -o docs/ openclsim
$ cd docs
$ make html
$ start explorer _build\html\index.html

Deploying

A reminder for the maintainers on how to deploy. Make sure all your changes are committed (including an entry in HISTORY.rst). Then run:

$ bumpversion patch # possible: major / minor / patch
$ git push
$ git push --tags

Travis will then deploy to PyPI if tests pass.

Credits

Contributors

Various MSc projects

  • Joris den Uijl, 2018. Integrating engineering knowledge in logistical optimisation: development of a concept evaluation tool. MSc thesis. Delft University of Technology, Civil Engineering and Geosciences, Hydraulic Engineering. Delft, the Netherlands.
  • Vibeke van der Bilt, 2019. Assessing emission performance of dredging projects. MSc thesis. Delft University of Technology, Civil Engineering and Geosciences, Hydraulic Engineering - Ports and Waterways. Delft, the Netherlands.
  • Pieter van Halem, 2019. Route optimization in dynamic currents. Navigation system for the North Sea and Wadden Sea. MSc thesis. Delft University of Technology, Civil Engineering and Geosciences, Environmental Fluid Mechanics. Delft, the Netherlands.
  • Servaas Kievits, 2019. A framework for the impact assessment of low discharges on the performance of inland waterway transport. MSc thesis. Delft University of Technology, Civil Engineering and Geosciences, Hydraulic Engineering - Ports and Waterways. Delft, the Netherlands.

Ongoing PhD work

  • Frederik Vinke, 2019. Climate proofing the inland water transport system in the Netherlands. PhD thesis. Delft University of Technology, Civil Engineering and Geosciences, Hydraulic Engineering - Ports and Waterways. Delft, the Netherlands.

History

1.2.3 (2020-05-07)

Improved documentation and readme.

1.2.2 (2020-04-10)

Fixed a bug raised in GitHub issue #89.

1.2.1 (2020-03-27)

Minor bug fixes.

1.2.0 (2020-01-27)

  • Major updates to the Movable class
  • You can now enter multiple origins and destinations in one activity
  • Optimisation of the schedule is possible by enhanding the Movable

1.1.1 (2019-12-11)

  • Minor bug fixes

1.1.0 (2019-08-30)

  • More generic Movable class
  • More generic Routeable class
  • Easier to implement own functions and adjustments

1.0.1 (2019-07-26)

  • Small bug fixes

1.0.0 (2019-07-10)

  • First formal release

0.3.0 (2019-06-20)

  • First release to PyPI and rename to OpenCLSim

v0.2.0 (2019-02-14)

  • Second tag on GitHub

v0.1.0 (2018-08-01)

  • First tag on GitHub

Version conventions

This package is being developed continuously. Branch protection is turned on for the master branch. Useful new features and bugfixes can be developed in a separate branch or fork. Pull requests can be made to integrate updates into the master branch. To keep track of versions, every change to the master branch will receive a version tag. This page outlines the version tags’ naming convention.

Each change to the master branch is stamped with a unique version identifier. We use sequence based version identifiers, that consist of a sequence of three numbers: the first number is a major change identifier, followed by a minor change idenfier and finally a maintenance identifier. This leads to version identifiers of the form:

major.minor.maintenance (example: 1.2.2)

The following guideline gives an idea what types of changes are considered major changes, minor changes and maintenance:

  • Major changes (typically breaking changes) -> major + 1
  • Minor changes (typically adding of new features) -> minor + 1
  • Maintenance (typically bug fixes and updates in documentation -> maintenance + 1

Indices and tables