Integrating a control mechanism

The scenario that we’re going to create in this part of the tutorial will be similar to the one we created before but incorporate the control mechanism that we just created.

Again, we start by setting some configuration values and creating a simulation world:

# demo_2.py
import mosaik
import mosaik.util


# Sim config
SIM_CONFIG = {
    'ExampleSim': {
        'python': 'simulator_mosaik:ExampleSim',
    },
    'ExampleCtrl': {
        'python': 'controller:Controller',
    },
    'Collector': {
        'cmd': '%(python)s collector.py %(addr)s',
    },
}
END = 10  # 10 seconds

# Create World
world = mosaik.World(SIM_CONFIG)

We added ExampleCtrl to the sim config and let it be executed in-process with mosaik.

We can now start one instance of each simulator:

# Start simulators
with world.group():
    examplesim = world.start('ExampleSim', eid_prefix='Model_')
    examplectrl = world.start('ExampleCtrl')
collector = world.start('Collector')

We’ll create three model instances, the same number of agents, and one database:

# Instantiate models
models = [examplesim.ExampleModel(init_val=i) for i in range(-2, 3, 2)]
agents = examplectrl.Agent.create(len(models))
monitor = collector.Monitor()

We use a list comprehension to create three model instances with individual initial values (-2, 0 and 2). For instantiating the same number of agent instances we use create() which does the same as a list comprehension but is a bit shorter.

Finally we establish pairwise bi-directional connections between the models and the agents:

# Connect entities
for model, agent in zip(models, agents):
    world.connect(model, agent, ('val', 'val_in'))
    world.connect(agent, model, 'delta', weak=True)

The important thing here is the weak=True argument that we pass to the second connection. This tells mosaik how to resolve the cyclic dependency, i.e. which simulator should be stepped first in case that both simulators have a scheduled step at the same time. (In our example this will not happen, as the agents are only stepped by the models’ outputs.)

Finally, we can connect the models and the agents to the monitor and run the simulation:

# Connect to monitor
mosaik.util.connect_many_to_one(world, models, monitor, 'val', 'delta')
mosaik.util.connect_many_to_one(world, agents, monitor, 'delta')

# Run simulation
world.run(until=END)

In the printed output of the collector, you can see two important things: The first is that the agents only provide output when the delta of the controlled model is to be changed. And second, that the new delta is set at the models’ subsequent step after it has been derived by the agents.

Collected data:
- ExampleCtrl-0.Agent_0:
  - delta: {2: -1, 5: 1, 8: -1}
- ExampleCtrl-0.Agent_1:
  - delta: {1: -1, 4: 1, 7: -1}
- ExampleCtrl-0.Agent_2:
  - delta: {0: -1, 3: 1, 6: -1, 9: 1}
- ExampleSim-0.Model_0:
  - delta: {0: 1, 1: 1, 2: -1, 3: -1, 4: -1, 5: 1, 6: 1, 7: 1, 8: -1, 9: -1}
  - val: {0: 0, 1: 2, 2: 2, 3: 0, 4: -2, 5: -2, 6: 0, 7: 2, 8: 2, 9: 0}
- ExampleSim-0.Model_1:
  - delta: {0: 1, 1: -1, 2: -1, 3: -1, 4: 1, 5: 1, 6: 1, 7: -1, 8: -1, 9: -1}
  - val: {0: 2, 1: 2, 2: 0, 3: -2, 4: -2, 5: 0, 6: 2, 7: 2, 8: 0, 9: -2}
- ExampleSim-0.Model_2:
  - delta: {0: -1, 1: -1, 2: -1, 3: 1, 4: 1, 5: 1, 6: -1, 7: -1, 8: -1, 9: 1}
  - val: {0: 2, 1: 0, 2: -2, 3: -2, 4: 0, 5: 2, 6: 2, 7: 0, 8: -2, 9: -2}

This is the complete scenario:

# demo_2.py
import mosaik
import mosaik.util


# Sim config
SIM_CONFIG = {
    'ExampleSim': {
        'python': 'simulator_mosaik:ExampleSim',
    },
    'ExampleCtrl': {
        'python': 'controller:Controller',
    },
    'Collector': {
        'cmd': '%(python)s collector.py %(addr)s',
    },
}
END = 10  # 10 seconds

# Create World
world = mosaik.World(SIM_CONFIG)
# End: Create World

# Start simulators
with world.group():
    examplesim = world.start('ExampleSim', eid_prefix='Model_')
    examplectrl = world.start('ExampleCtrl')
collector = world.start('Collector')
# End: Start simulators

# Instantiate models
models = [examplesim.ExampleModel(init_val=i) for i in range(-2, 3, 2)]
agents = examplectrl.Agent.create(len(models))
monitor = collector.Monitor()
# End: Instantiate models

# Connect entities
for model, agent in zip(models, agents):
    world.connect(model, agent, ('val', 'val_in'))
    world.connect(agent, model, 'delta', weak=True)
# End: Connect entities

# Connect to monitor
mosaik.util.connect_many_to_one(world, models, monitor, 'val', 'delta')
mosaik.util.connect_many_to_one(world, agents, monitor, 'delta')

# Run simulation
world.run(until=END)

Congratulations, you have mastered the mosaik tutorial. The following sections provide a more detailed description of everything you learned so far.