Skip to content

This toolbox provides a high-level API for the Mosaik co-simulation framework (https://mosaik.offis.de/) in Matlab

License

Notifications You must be signed in to change notification settings

hesstobi/matlab-mosaik-toolbox

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Mosaik Toolbox

This toolbox provides a high-level API for the Mosaik co-simulation framework (https://mosaik.offis.de/) in Matlab.

Quickstart

It is recommended for Mosaik toolbox to run MATLab 2015a or later. Earlier versions have not been tested.

For socket communication you need JSONLab Version 1.2 or greater. Just install the toolbox or put the .m files somewhere in your MATLAB path.

Then you can just download and install the Mosaik Toolbox (Mosaik-Toolbox.mltbx).

Scenario Definition

For detailed scenario definition please refer to the official mosaik documentation first.

To initiate a simulator, add the following to your sim_config:

sim_config = {
	'Matlab': {
		'cwd': os.path.dirname(os.path.realpath(__file__)),
		'cmd': 'matlab.exe -minimize -nosplash -r "Simulator(\'%(addr)s\')"'
	}
}

Where Simulator is the name of your simulator and has to be in the same folder as your demo.py. If you need to use a simulator which is not in the same folder, use the following syntax:

sim_config = {
	'Matlab': {
		'cwd': os.path.dirname(os.path.realpath(__file__)),
		'cmd': 'matlab.exe -minimize -nosplash -r "Package.Simulator(\'%(addr)s\')"'
	}
}

Where Package is your MATLab package containing the Simulator. In this example, the folder structure would be \+Package\Simulator.m.

To start your simulator in verbose mode (socket messages are displayed and simulator does not exit at the end) add ,\'verbose\',true to your simulator parameters:

sim_config = {
	'Matlab': {
		'cwd': os.path.dirname(os.path.realpath(__file__)),
		'cmd': 'matlab.exe -minimize -nosplash -r "Simulator(\'%(addr)s\',\'verbose\',true)"'
	}
}

It is also recommended to increase the timeout since MATLab can take a little time to load. This can be done by changing the mosaik_config:

mosaik_config = {
	'start_timeout': 600,  # seconds
	'stop_timeout': 10,  # seconds
}

Then, start the world using your just created sim_config and mosaik_config:

world = mosaik.World(sim_config, mosaik_config)

You can now start your simulators and instantiate models as described in the official documentation:

matlab = world.start('Matlab', step_size=10)
model = matlab.Model(parameter=x)

There are is a Mosaik toolbox specific utilities which is very practical but its scenario definition is not explained in the official documentation. Please note that you have to read the official part explaining scenario definition first, before using those utilites:

Collector

The collector utility is used to create tables or graphical plots from the simulation data.

First, change the sim_config:

sim_config = {
	'Matlab': {
		'cwd': os.path.dirname(os.path.realpath(__file__)),
		'cmd': 'matlab.exe -minimize -nosplash -r "MosaikUtilites.Collector(\'%(addr)s\')"'
	}
}

Start the simulator defining the accuray via its step_size:

monitor = world.start('Monitor', step_size=10)

The collectors only model is Collector:

collector = monitor.Collector(graphical_output=True)

Set graphical_output=True if you wish to obtain a graphical plot of the simulated data. You can also set save_path=filename.m if you wish to save your data.

To incorporate an attribute from another model into the collector just it to collector:

world.connect(model, collector, 'attribute')

The collector plots all attributes with the same name in one figure.

Developer's Guide

API Reference

MosaikAPI

class MosaikAPI.Simulator

This is the base that you need to inherit from when developing simulators.

meta()

Description: Creates meta information struct.

Return:
Name:
value
Type:
Struct
Description:
Meta information in the form attribute = value.
Required attributes: api_version, extra_methods, models

create(num,model,varargin)

Description:
Creates models of specified amount, type and initial parameters.

Parameters:
Name:
num
Type:
Double
Description:
Amount of models to be created.
Name:
model
Type:
String
Description:
Type of models to be created.
Name:
model_params
Type:
Keyword arguments
Description:
Model creation parameters.

Return:
Name:
entity_list
Type:
Cell
Description:
Structs with information about created models in the form attribute = value.
Required attributes: eid, type
Optional attributes: rel, children

step(time,varargin)

Description:
Performs simulation step.

Parameters:
Name:
time
Type:
Double
Description:
Time of this simulation step.
Name:
inputs
Type:
Keyword arguments
Description:
Input values in the form destination_full_id.attributes.source_full_id = value.

Return:
Name:
time_next_step
Type:
Double
Description:
Time of next simulation step.

get_data(outputs)

Description:
Receives data for requested attributes.

Parameters:
Name:
outputs
Type:

Struct
Description:
Requested attributes in the form eid = {attribute}.

Return:
Name:
data

Type:
Struct
Description:
Requested values in the form eid.attribute = value.

MosaikUtilities

class MosaikAPI.Model

This is the base that you need to inherit from when just defining models. The simulator used in this case is MosaikAPI.ModelSimulator.

meta()

Description: Creates meta information struct.

Return:
Name:
value
Type:
Struct
Description:
Meta information in the form attribute = value.
Required attributes: public, attrs, params
Optional attributes: any_inputs

step(varargin)

Description:
Creates models of specified amount, type and initial parameters. Returns information about created models.

Parameters:
Name:
num
Type:
Double
Description:
Amount of models to be created.
Name:
model
Type:
String
Description:
Type of model to be created.
Name:
varargin
Type:
Keyword arguments
Description:
Various arguments regarding model creation.

Return:
Name:
entity_list
Type:
Cell
Description: Contains information structs about created models in the form attribute = value.
Required attributes: eid, type
Optional attributes: rel, children

class MosaikAPI.Controller

This is the base that you need to inherit from when developing controllers.

makeSchedule(inputs)

Description:
Creates output values for controlled models based on input values and controller function.

Parameters:
Name:
inputs
Type:
Struct
Description:
Input values in the form destination_full_id.attributes.source_full_id = value.

Return:
Name:
schedule
Type:
Struct
Description:
Output values in the form source_full_id._destination_full_id.attribute = value.

Example Demos

To understand the basic functionality there are example demos provided. It is also recommended to use the examples as reference when reading the developer's guide.

All simulators used in the example demos are implementations of MosaikAPI.ModelSimulator.

ExampleSim

ExampleSim demonstrates the APIs basic functionality.
It only has the model 'Model':

properties

	providedModels = {'Model'}

end

This model has a defined delta and current value:

properties

	delta = 1
	val

end

In every step the model adds the delta value to its current value:

function step(this,~,varargin)

	this.val = this.val + this.delta;

end

While instantiating the model the initial value has to be defined:

function this = Model(sim,eid,varargin)

	this = this@MosaikAPI.Model(sim,eid);

	p = inputParser;
	addOptional(p,'init_value',0,@(x)validateattributes(x,{'numeric'},{'scalar'}));
	parse(p,varargin{:});

	this.val = p.Results.init_value;   

end

ExampleMas

ExampleMas demonstrates the APIs advanced functionality.
It provides the model 'Agent' which can control ExampleSims 'Model' model via asynchronous requests:

properties

	providedModels = {'Agent'}

end
properties

	rel
	val
	link

end

Before executing MosaikAPIs.ModelSimulators step ExampleMas requests and displays the progress:

function time_next_step = step(this,time,varargin)

	progress = this.mosaik.get_progress;
	disp(strcat('Progress: ',num2str(progress,2)));

	time_next_step = step@MosaikAPI.ModelSimulator(this,time,varargin{1});

end

Then it performs a model step in which the 'Agent' models control their related 'Model' models.
First it obtains all related models:

if eq(time,0)
	this.rel = this.sim.mosaik.get_related_entities(this.eid);
	disp(savejson('',this.rel));
end

Then it gets their current data:

for i = 1:numel(rels)
	full_id = rels{i};
	outputs.(full_id) = {'val'};					
end
data = this.sim.mosaik.get_data(outputs);
disp(savejson('',data));

At last it sets a predefined value as the 'Model' models new input:

for i = 1:numel(rels)
	full_id = rels{i};
	inputs.(src_full_id).(full_id).val = this.val;				
end
this.sim.mosaik.set_data(inputs);

There are three scenarios to test:
Three models connected to three agents

ExampleBatteryLoadSim

ExampleBatteryLoadSim simulates a battery to which various loads can be connected. The load consumes capacitance every step and feeds it back to the battery:

rels = this.sim.mosaik.get_related_entities(this.eid);
fn_src_full_id = fieldnames(rels);
l = struct;
for j = 1:numel(fn_src_full_id)
	if strcmp(rels.(fn_src_full_id{j}).type, 'Battery')
		l.(fn_src_full_id{j}) = struct('consumed_capacitance', this.consumed_capacitance);
	end			
end
output = struct;
output.([strrep(this.sim.sid, '-', '_0x2D_'), '_0x2E_', this.eid]) = l;
this.sim.mosaik.set_data(output);

Based on the consumed capacitance the battery voltage decreases:

this.capacitance = this.capacitance - this.consumed_capacitance;

this.voltage = (((this.capacitance / this.init_capacitance) ^ 0.5) * this.init_voltage);

There is a predefined voltage tolerance for every load and if the the voltage falls below that tolerance the load switches off for the rest of the simulation duration:

if ge(this.voltage_in,(this.voltage*(1-this.tolerance))) && le (this.voltage_in,(this.voltage*(1+this.tolerance)))
	this.consumed_capacitance = ((this.voltage_in/this.resistance)*this.sim.step_size);
end

ExampleBatteryLoadSimControlled

ExampleBatteryLoadSimControlled is basically the same simulation as ExampleBatteryLoadSim, but the voltage calculation is done in a battery controller:

capacitance = inputs.(this.eid).capacitance.(batteries{i});
init_capacitance = this.getValue(batteries{i},'init_capacitance');
this.voltage = (capacitance / init_capacitance)^2 * this.init_voltage;

The controller then decides wether the current voltage is above the shutdown voltage and calaculates consumed capacitance by the connected loads:

if ge(this.voltage,this.shutdown_voltage)

	total_consumed_cap = 0;

	for j= 1:numel(loads)

		resistance = this.getValue(loads{j},'resistance');
		consumed_capacitance = (this.voltage / resistance) * this.step_size;

		outputs.(loads{j}).consumed_capacitance = consumed_capacitance;
		total_consumed_cap = total_consumed_cap + consumed_capacitance;

	end

	outputs.(batteries{i}).voltage = this.voltage;
	outputs.(batteries{i}).consumed_capacitance = total_consumed_cap;

end

About

This toolbox provides a high-level API for the Mosaik co-simulation framework (https://mosaik.offis.de/) in Matlab

Resources

License

Stars

Watchers

Forks

Packages

No packages published