1

I have simplified a version of a problem I am attempting to model where I am using Simpy to describe the movement of travellers along a path.

The path is represented by a collection of Node() objects where each node contains a Simpy.Resource. Each node is connected to the next node in the path by the connected_to attribute. In the example code I have created a list of 10 nodes where each node in the list is connected to the preceding node in the list.

When a traveller (represented by an Occupier() object) is instantiated it is allocated the resource of a node. The traveller then moves along the nodes, only taking a step if the next node is available. My aim is for the traveller to simultaneously be allocated its destination node and release the node where it was previously located.

import simpy


class Node(object):
    def __init__(self, env):
        self.env = env
        self.resource = simpy.Resource(self.env)
        self.up_connection = None
        self.travel_delay = 5


class Occupier(object):
    def __init__(self, env):
        self.env = env
        self.location = None
        self.destination = None
        self.requests = []

    def travel(self, instantiation_loc):
        self.requests.append(instantiation_loc.resource.request())
        yield(self.requests[-1])

        self.location = instantiation_loc
        self.destination = instantiation_loc.up_connection
        yield self.env.timeout(self.location.travel_delay)
        node_occupancy(nodes)

        while self.destination.up_connection != None:
            self.requests.append(self.destination.resource.request())
            yield self.requests[-1]

            self.location.resource.release(self.requests[0])
            self.requests.pop(0)
            self.location = self.destination
            self.destination = self.location.up_connection

            yield self.env.timeout(self.location.travel_delay)
            node_occupancy(nodes)


def node_occupancy(nodes):
    print([node.resource.count for node in nodes])       


env = simpy.Environment()

nodes = [Node(env) for i in range(10)]
for i in range(len(nodes) - 1):
    nodes[i].up_connection = nodes[i + 1]

env.process(Occupier(env).travel(nodes[0]))

env.run()

If I run the above code with one traveller it seems to work fine giving the following output:

[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]

However, if instantiate a second traveller you can see that there are points in time where one traveller is occupying two resources when it should only occupy one:

env.process(Occupier(env).travel(nodes[3]))
env.process(Occupier(env).travel(nodes[0]))

corresponding output:

[1, 0, 0, 1, 0, 0, 0, 0, 0, 0]
[1, 0, 0, 1, 1, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 1, 0, 0, 0, 0, 0]
[0, 1, 0, 0, 1, 1, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 1, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 1, 1, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 1, 0, 0, 0]
[0, 0, 0, 1, 0, 0, 1, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0, 1, 0, 0]
[0, 0, 0, 0, 1, 0, 0, 1, 1, 0]
[0, 0, 0, 0, 0, 1, 0, 0, 1, 0]
[0, 0, 0, 0, 0, 1, 0, 0, 1, 0]
[0, 0, 0, 0, 0, 0, 1, 0, 1, 0]
[0, 0, 0, 0, 0, 0, 0, 1, 1, 0]

It is important for my simulation that a traveller only ever occupies one resource as the attributes of the nodes are amended frequently based on this.

Is there any way to prevent this behaviour where a traveller never occupies more than one resource? i.e. a resource is simultaneously released when the traveller is allocated a new resource

1 Answers1

1

In fact, your model is running correctly.

Try to add to the function node_ocupacy, the current execution time and some markers to identify the current stage of the simulation:

def node_occupancy(nodes, node, case):
    print(env.now, node, case, [node.resource.count for node in nodes])  

Also, I made some changes just to see a better simulation log:

def travel(self, instantiation_loc, loc):
    self.requests.append(instantiation_loc.resource.request())
    yield(self.requests[-1])

    self.location = instantiation_loc
    self.destination = instantiation_loc.up_connection
    yield self.env.timeout(self.location.travel_delay)
    node_occupancy(nodes, loc, 1)

    while self.destination.up_connection != None:
        self.requests.append(self.destination.resource.request())
        node_occupancy(nodes, loc, 2)

        yield self.requests[-1]
        node_occupancy(nodes, loc, 3)
        self.location.resource.release(self.requests[0])
        node_occupancy(nodes, loc, 4)
        self.requests.pop(0)
        self.location = self.destination
        self.destination = self.location.up_connection

        yield self.env.timeout(self.location.travel_delay)
        node_occupancy(nodes, loc, 5)

Now, run the simulation with a marker for the current node:

env.process(Occupier(env).travel(nodes[3], 3))
env.process(Occupier(env).travel(nodes[0], 0))

Look at the results and you will notice that events (request/release) occurs at the same time and the simultaneous resource time occupation is always = 0 (i.e.: the time between stages '3' and '4', for the same entity will be always 0):

5 3 1 [1, 0, 0, 1, 0, 0, 0, 0, 0, 0]
5 3 2 [1, 0, 0, 1, 1, 0, 0, 0, 0, 0]
5 0 1 [1, 0, 0, 1, 1, 0, 0, 0, 0, 0]
5 0 2 [1, 1, 0, 1, 1, 0, 0, 0, 0, 0]
5 3 3 [1, 1, 0, 1, 1, 0, 0, 0, 0, 0]
5 3 4 [1, 1, 0, 0, 1, 0, 0, 0, 0, 0]
5 0 3 [1, 1, 0, 0, 1, 0, 0, 0, 0, 0]
5 0 4 [0, 1, 0, 0, 1, 0, 0, 0, 0, 0]
  • Thanks Afonso - this clarification has helped. My main issue is that I need to modify the attributes of the nodes after each Occupier has moved; the values I change the attributes to are dependent on the occupancy of the node. This is an issue for step `5 3 4 [1, 1, 0, 0, 1, ...]` where if the farther placed Occupier, having completed his move, calls the function to modify the node attributes, it will incorrectly modify them because the Occupier near the start is registered as occupying both the first and second node. I guess this is a paradigm issue of DES though... – MartyFizzle Oct 24 '18 at 14:54