The high-level API
Currently, there are high-level APIs for Python, Java, C#, and MatLab.
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-v3
It supports Python 2.7, >= 3.3 and PyPy.
Usage
You create a subclass of mosaik_api_v3.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_v3.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_v3.Simulator(meta)[source]
This is the base class that you need to inherit from and implement the API calls.
- Parameters:
meta (Meta)
- meta[source]
Meta data describing the simulator (the same that is returned by
init
).{ 'api_version': 'x.y', 'type': 'time-based'|'event-based'|'hybrid', 'models': { 'ModelName': { 'public': True|False, 'params': ['param_1', ...], 'attrs': ['attr_1', ...], 'any_inputs': True|False, 'trigger': ['attr_1', ...], 'non-persistent': ['attr_2', ...], }, ... }, '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.
The type defines how the simulator is advanced through time and whether its attributes are persistent in time or transient.
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 totrue
, 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. trigger is a list of attribute names that cause the simulator to be stepped when another simulator provides output which is connected to one of those.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 intoinit()
orcreate()
.
- init(sid, time_resolution=1.0, **sim_params)[source]
- Initialize the simulator with the ID sid and pass the
time_resolution and 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)[source]
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()[source]
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
- Return type:
None
- step(time, inputs, max_advance)[source]
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 is arbitrary, e.g. seconds (from simulation start), but has to be consistent among all simulators used in a simulation.
inputs is a dict of dicts mapping entity IDs to attributes and dicts of values (each simulator has to decide on its own how to reduce the values (e.g., as its sum, average or maximum):
{ 'dest_eid': { 'attr': {'src_fullid': val, ...}, ... }, ... }
max_advance tells the simulator how far it can advance its time without risking any causality error, i.e. it is guaranteed that no external step will be triggered before max_advance + 1, unless the simulator activates an output loop earlier than that. For time-based simulators (or hybrid ones without any triggering input) max_advance is always equal to the end of the simulation (until).
- get_data(outputs)[source]
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', ... }, ... 'time': output_time (for event-based sims, optional) }
Time-based simulators have set an entry for all requested attributes, whereas for event-based and hybrid simulators this is optional (e.g. if there’s no new event). Event-based and hybrid simulators can optionally set a timing of their non-persistent output attributes via a time entry, which is valid for all given (non-persistent) attributes. If not given, it defaults to the current time of the step. Thus only one output time is possible per step. For further output times the simulator has to schedule another self-step (via the step’s return value).
- configure(args)[source]
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.
The mosaik-api-v3 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
, except for set_data()
which has to
be called from another thread/process (see below). 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, max_advance):
progress = yield self.mosaik.get_progress()
# ...
- async MosaikRemote.get_progress()[source]
Return the current simulation progress from
sim_progress
.- Return type:
- async MosaikRemote.get_related_entities(entities=None)[source]
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'}, }, }
- async MosaikRemote.get_data(attrs)[source]
Warning
This method is deprecated and will be removed in a future release. Implement cyclic data flow using time-shifted and weak connections instead.
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}}
).
- async MosaikRemote.set_data(data)[source]
Warning
This method is deprecated and will be removed in a future release. Implement cyclic data flow using time-shifted and weak connections instead.
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'}}}
).
- async MosaikRemote.set_event(event_time)[source]
Schedules an event/step at simulation time event_time.
- Parameters:
event_time (int)
The mosaik-api-v3 package provides an example “multi-agent system” that demonstrates how 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_v3.start_simulation(simulator, description='', extra_options=None, configure_logging=True)[source]
Start the simulation process for
simulator
.- simulator is the instance of your API implementation (see
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 toSimulator.configure
so that your API implementation can handle them.configure_logging determines whether the API sets up the loguru logger. If the user specifies the log level using the –log-level command line flag, logging will always be configured.
Here is an example with a bit more context:
import mosaik_api_v3
example_meta = {
'type': 'time-based',
'models' {
'A': {
'public': True,
'params': ['init_val'],
'attrs': ['val_out', 'dummy_out'],
},
}
}
class ExampleSim(mosaik_api_v3.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_v3.start_simulation(ExampleSim(), description, extra_options)
if __name__ == '__main__':
sys.exit(main())