The high-level API

Currently, there are high-level API for Python and Java. Implementations for C# and MatLab will be available soon.

Installation

The Python implementation of the mosaik API is available as a separate package an can easily be installed via pip:

pip install mosaik-api

It supports Python 2.7, >= 3.3 and PyPy.

For the use of the Java-API please refer to the Java-tutorial.

Usage

You create a subclass of mosaik_api.Simulator which implements the four API calls init(), create(), step() and get_data(). You can optionally override configure() and finalize(). The former allows you to handle additional command line arguments that your simulator may need. The latter is called just before the simulator terminates and allows you to perform some clean-up.

You then call mosaik_api.start_simulation() from your main() function to get everything set-up and running. That function handles the networking as well as serialization and de-serialization of messages. Commands from the low-level API are translated to simple function calls. The return value of these functions is used for the reply.

For example, the message

["create", [2, "Model", {"param1": 15, "param2": "spam"}]

will result in a call

create(2, 'Model', param1=15, param2='spam')

API calls

class mosaik_api.Simulator(meta)

This is the base class that you need to inherit from and implement the API calls.

meta

Meta data describing the simulator (the same that is returned by init()).

{
    'api_version': 'x.y',
    'models': {
        'ModelName': {
            'public': True|False,
            'params': ['param_1', ...],
            'attrs': ['attr_1', ...],
            'any_inputs': True|False,
        },
        ...
    },
    'extra_methods': [
        'do_cool_stuff',
        'set_static_data'
    ]
}

The api_version is a string that defines which version of the mosaik API the simulator implements. Since mosaik API version 2.3, the simulator’s major version (“x”, in the snippet above) has to be equal to mosaik’s. Mosaik will cancel the simulation if a version mismatch occurs.

models is a dictionary describing the models provided by this simulator. The entry public determines whether a model can be instantiated by a user (True) or if it is a sub-model that cannot be created directly (False). params is a list of parameter names that can be passed to the model when creating it. attrs is a list of attribute names that can be accessed (reading or writing). If the optional any_inputs flag is set to true, any attributes can be connected to the model, even if they are not attrs. This may, for example, be useful for databases that don’t know in advance which attributes of an entity they’ll receive.

extra_methods is an optional list of methods that a simulator provides in addition to the standard API calls (init(), create() and so on). These methods can be called while the scenario is being created and can be used for operations that don’t really belong into init() or create().

mosaik

An RPC proxy to mosaik.

init(sid, **sim_params)

Initialize the simulator with the ID sid and apply additional parameters (sim_params) sent by mosaik. Return the meta data meta.

If your simulator has no sim_params, you don’t need to override this method.

create(num, model, **model_params)

Create num instances of model using the provided model_params.

num is an integer for the number of model instances to create.

model needs to be a public entry in the simulator’s meta['models'].

model_params is a dictionary mapping parameters (from meta['models'][model]['params']) to their values.

Return a (nested) list of dictionaries describing the created model instances (entities). The root list must contain exactly num elements. The number of objects in sub-lists is not constrained:

[
    {
        'eid': 'eid_1',
        'type': 'model_name',
        'rel': ['eid_2', ...],
        'children': [
            {'eid': 'child_1', 'type': 'child'},
            ...
        ],
    },
    ...
]

The entity ID (eid) of an object must be unique within a simulator instance. For entities in the root list, type must be the same as the model parameter. The type for objects in sub-lists may be anything that can be found in meta['models']. rel is an optional list of related entities; “related” means that two entities are somehow connect within the simulator, either logically or via a real data-flow (e.g., grid nodes are related to their adjacent branches). The children entry is optional and may contain a sub-list of entities.

setup_done()

Callback that indicates that the scenario setup is done and the actual simulation is about to start.

At this point, all entities and all connections between them are know but no simulator has been stepped yet.

Implementing this method is optional.

Added in mosaik API version 2.3

step(time, inputs)

Perform the next simulation step from time time using input values from inputs and return the new simulation time (the time at which step() should be called again).

time and the time returned are integers. Their unit has to agree to that used in the specific mosaik scenario, e.g. seconds (from simulation start).

inputs is a dict of dicts mapping entity IDs to attributes and dicts of values (each simulator has do decide on its own how to reduce the values (e.g., as its sum, average or maximum):

{
    'dest_eid': {
        'attr': {'src_fullid': val, ...},
        ...
    },
    ...
}
get_data(outputs)

Return the data for the requested attributes in outputs

outputs is a dict mapping entity IDs to lists of attribute names whose values are requested:

{
    'eid_1': ['attr_1', 'attr_2', ...],
    ...
}

The return value needs to be a dict of dicts mapping entity IDs and attribute names to their values:

{
    'eid_1: {
        'attr_1': 'val_1',
        'attr_2': 'val_2',
        ...
    },
    ...
}
configure(args, backend, env)

This method can be overridden to configure the simulation with the command line args as created by docopt.

backend and env are the simpy.io backend and environment used for networking. You can use them to start extra processes (e.g., a web server).

The default implementation simply ignores them.

finalize()

This method can be overridden to do some clean-up operations after the simulation finished (e.g., shutting down external processes).

The mosaik-api package provides an example simulator that demonstrates how the API can be implemented.

Asynchronous requests

The asynchronous requests can be called via the MosaikRemote proxy self.mosaik from within step(). They don’t return the actual results but an event (similar to a future of deferred). The event will eventually hold the actual result. To wait for that result to arrive, you simply yield the event, e.g.:

def step(self, time, inputs):
    progress = yield self.mosaik.get_progress()
    # ...
MosaikRemote.get_progress()

Return the current simulation progress from sim_progress.

MosaikRemote.get_related_entities(entities=None)

Return information about the related entities of entities.

If entities omitted (or None), return the complete entity graph, e.g.:

{
    'nodes': {
        'sid_0.eid_0': {'type': 'A'},
        'sid_0.eid_1': {'type': 'B'},
        'sid_1.eid_0': {'type': 'C'},
    },
    'edges': [
        ['sid_0.eid_0', 'sid_1.eid0', {}],
        ['sid_0.eid_1', 'sid_1.eid0', {}],
    ],
}

If entities is a single string (e.g., sid_1.eid_0), return a dict containing all entities related to that entity:

{
    'sid_0.eid_0': {'type': 'A'},
    'sid_0.eid_1': {'type': 'B'},
}

If entities is a list of entity IDs (e.g., ['sid_0.eid_0', 'sid_0.eid_1']), return a dict mapping each entity to a dict of related entities:

{
    'sid_0.eid_0': {
        'sid_1.eid_0': {'type': 'B'},
    },
    'sid_0.eid_1': {
        'sid_1.eid_1': {'type': 'B'},
    },
}
MosaikRemote.get_data(attrs)

Return the data for the requested attributes attrs.

attrs is a dict of (fully qualified) entity IDs mapping to lists of attribute names ({'sid/eid': ['attr1', 'attr2']}).

The return value is a dictionary, which maps the input entity IDs to data dictionaries, which in turn map attribute names to their respective values: ({'sid/eid': {'attr1': val1, 'attr2': val2}}).

MosaikRemote.set_data(data)

Set data as input data for all affected simulators.

data is a dictionary mapping source entity IDs to destination entity IDs with dictionaries of attributes and values ({'src_full_id': {'dest_full_id': {'attr1': 'val1', 'attr2': 'val2'}}}).

The mosaik-api package provides an example “multi-agent system” that demonstrates how to perform asynchronous requests can be implemented.

Starting the simulator

To start your simulator, you just need to create an instance of your Simulator sub-class and pass it to start_simulation():

mosaik_api.start_simulation(simulator, description='', extra_options=None)

Start the simulation process for simulation.

simulation is the instance of your API implementation (see Simulator).

description may override the default description printed with the help on the command line.

extra_option may be a list of options for docopt (example: ['-e, --example     Enable example mode']). Commandline arguments are passed to Simulator.configure() so that your API implementation can handle them.

Here is an example with a bit more context:

import mosaik_api


example_meta = {
    'models' {
        'A': {
              'public': True,
              'params': ['init_val'],
              'attrs': ['val_out', 'dummy_out'],
        },
    }
}


class ExampleSim(mosaik_api.Simulator):
    def __init__(self):
        super().__init__(example_meta)

    sim_name = 'ExampleSimulation'

    def configure(self, args, backend, env):
        # Here you could handle additional command line arguments

    def init(self, sid):
        # Initialize the simulator
        return self.meta

    # Implement the remaining methods (create, step, get_data, ...)


def main():
    import sys

    description = 'A simple example simulation for mosaik.'
    extra_options = [
       '--foo       Enable foo',
       '--bar BAR   The bar parameter',
    ]

    return mosaik_api.start_simulation(ExampleSim(), description, extra_options)


if __name__ == '__main__':
    sys.exit(main())