diff --git a/docs/references/engine-api.rst b/docs/references/engine-api.rst
index 9fd3fcd..8885e5a 100644
--- a/docs/references/engine-api.rst
+++ b/docs/references/engine-api.rst
@@ -1,10 +1,30 @@
-Engine's API
+Engine API
=====================
-.. automodule:: illuminator.engine
+The simulation engine provides a wrapper around Mosaik simulation to simplify the process of creating and running simulations.
+
+Python Interface
+---------------------
+
+.. autoclass:: illuminator.engine.Simulation
:members:
:show-inheritance:
+Utility Functions
+---------------------
+
+.. autofunction:: illuminator.engine.apply_default_values
+
+.. autofunction:: illuminator.engine.build_connections
+
+.. autofunction:: illuminator.engine.compute_mosaik_end_time
+
+.. autofunction:: illuminator.engine.connect_monitor
+
+.. autofunction:: illuminator.engine.create_world
+
+.. autofunction:: illuminator.engine.genarate_mosaik_configuration
+
..
diff --git a/docs/user/config-file.md b/docs/user/config-file.md
index 9ec4afe..91ca97e 100644
--- a/docs/user/config-file.md
+++ b/docs/user/config-file.md
@@ -68,26 +68,26 @@ monitor:
| Keyword | Description | Optional | Default |
|---------|-------------|----------|---------|
-| **scenario:** | a set of global values for a simulation. | | |
-|`name` | A name for the simulation, internally this name will be asssigned to what the Mosaik World created during runtime. | | |
-| `start_time` | start time for the simulation. Must be a timestamp in ISO 8601 format | | |
-| `end_time` | end time for the simulation. Must be a timestamp in ISO 8601 format. | | |
-| `time_resolution` | number of seconds between simulation steps | ☑ | 900 (15 min)
-| **models:** | a list of models for the simulation | | |
-| `name` | a name for the model. Must be unique for each simulation | | |
-| `type` | type of model. This must correspond with the name of the model registered in the Illuminator. | | |
-| `inputs` | a set of input-names and initial values for the model. The model type determines which names and values are applicable to each model, and they must be declared accordingly. Inputs are optional | | If the value is set to `null`, the default value will be used. See the respective model type for details.|
-| `outputs` | a set of output-names and initial values for the model. Similar to *inputs* valid names and values for each model are determined by the model *type*. See the respective model type for details. | | If the value is set to `null`, the default value will be used. |
-| `parameters` | a set of name-value pairs for the model. Parameters declared constants for a model during runtime. | ☑ | If ommited, the default values will be used. See the respective model type for details. |
-| `states` | a set of name-value pairs considered as states for the model. The values modify the internal initial values of a state. | ☑ | If ommited, the default values will be used. See the respective model type for details. |
-| `triggers` | names of inputs, output or states that are use as triggers for a particular model. Triggers can only be declared by models that implement the *event-based paradigm*. See the respective model type to know if it accepts triggers. | ☑ | |
-| `connect` | to declare in which client a model runs when using a Raspberry Pi cluster. | ☑ | |
-| `ip` | Ip of the client manchine that will run the model. Only IP version 4 format. | | |
-| `port` | TCP port to use to connect to the client machine| ☑ | |
+| **scenario:** | a set of global values
for a simulation. | | |
+|`name` | A name for the simulation, internally
this name will be asssigned to what
the Mosaik World created during runtime. | | |
+| `start_time` | start time for the simulation.
Must be a timestamp in ISO 8601 format | | |
+| `end_time` | end time for the simulation.
Must be a timestamp in ISO 8601 format. | | |
+| `time_resolution` | number of seconds between
simulation steps | ☑ | 900 (15 min)
+| **models:** | a list of models for
the simulation | | |
+| `name` | a name for the model. Must
be unique for each simulation | | |
+| `type` | type of model. This must correspond
with the name of the model
registered in the Illuminator. | | |
+| `inputs` | a set of input-names and initial
values for the model. The model
type determines which names and
values are applicable to each
model, and they must be declared accordingly.
Inputs are optional | | If the value is set to `null`, the default value will be used. See the respective model type for details.|
+| `outputs` | a set of output-names and initial
values for the model. Similar to
*inputs* valid names and values
for each model are determined by
the model *type*. See the respective
model type for details. | | If the value is set to `null`, the default value will be used. |
+| `parameters` | a set of name-value pairs for
the model. Parameters declared constants
for a model during runtime. | ☑ | If ommited, the default values will be used. See the respective model type for details. |
+| `states` | a set of name-value pairs considered
as states for the model. The values modify
the internal initial values of a state. | ☑ | If ommited, the default values will be used. See the respective model type for details. |
+| `triggers` | names of inputs, output or states
that are use as triggers for a particular model.
Triggers can only be declared by models
that implement the *event-based paradigm*.
See the respective model type to know if
it accepts triggers. | ☑ | |
+| `connect` | to declare in which client a model runs
when using a Raspberry Pi cluster. | ☑ | |
+| `ip` | Ip of the client manchine that will run
the model. Only IP version 4 format. | | |
+| `port` | TCP port to use to connect to the
client machine| ☑ | |
| **connections:** | how models connect to each other. | | |
-| `from` | origin of the connection declared as `.`. Input names use here must also appear as *inputs* in the models section. | | |
-| `to` | destination of the connection declared as `.`. Output names use here must also appear as *outputs* in the models section. | |
+| `from` | origin of the connection declared as
`.`. Input names
use here must also appear as *inputs* in
the models section. | | |
+| `to` | destination of the connection declared as
`.`. Output names
use here must also appear as *outputs* in
the models section. | |
| **monitor:** |
-| `file` | path to a CSV file to store results of the simulation. File will be created if necessary. | ☑ | a `out.csv` file saved to the current directory |
-|`items` | a list of which inputs, outputs or states of models that most be monitored during runtime. Items must be declared as `.`, where *name* is an input, output or stated clared in the *models* section. No duplicated values are allowed | | |
+| `file` | path to a CSV file to store results of
the simulation. File will be created if
necessary. | ☑ | a `out.csv` file saved to the current directory |
+|`items` | a list of which inputs, outputs or states
of models that most be monitored during
runtime. Items must be declared as
`.`, where *name* is an
input, output or stated clared in the
*models* section. No duplicated values
are allowed | | |
diff --git a/examples/adder.yaml b/examples/adder.yaml
deleted file mode 100644
index 818c3ce..0000000
--- a/examples/adder.yaml
+++ /dev/null
@@ -1,31 +0,0 @@
-scenario:
- name: "AddingNumbers" # in mosaik so called world
- start_time: '2012-01-02 00:00:00' # ISO 8601 start time of the simulation
- end_time: '2012-01-02 00:00:10' # duration in seconds
- time_resolution: 900 # time step in seconds. Defaults to 15 minutes (900 s)
-models: # list of models for the energy network
-- name: Adder1 # name for the model (must be unique)
- type: Adder # most match the class inheriting from IlluminatorConstructor
- inputs:
- in1: 10 # input-name: initial value, default value will be used if not defined
- in2: 20
- outputs:
- out1: 0
- parameters:
- param1: "adding tens"
-- name: Adder2
- type: Adder # models can reuse the same type
- inputs:
- in1: 100 # input-name: initial value, default value will be used if not defined
- in2: 200
- parameters:
- param1: "adding hundreds"
-connections:
-- from: Adder1.out1 # start model, pattern: model_name.output_name/input_name
- to: Adder2.in1 # end model
-monitor: # a list of models, its inputs, output and states to be monitored and logged
-- Adder2.out2 # pattern model_name.state_name
-
-
-
-
diff --git a/examples/adder_copy.yaml b/examples/adder_copy.yaml
index 9b23b85..6468a5d 100644
--- a/examples/adder_copy.yaml
+++ b/examples/adder_copy.yaml
@@ -1,9 +1,8 @@
scenario:
name: "AddingNumbers" # in mosaik so called world
start_time: '2012-01-02 00:00:00' # ISO 8601 start time of the simulation
- end_time: '2012-01-03 00:00:10' # duration in seconds
+ end_time: '2012-01-02 00:45:01' # duration in seconds
time_resolution: 900 # time step in seconds. Defaults to 15 minutes (900 s)
- results: './out.csv'
models: # list of models for the energy network
- name: Adder2
type: Adder # models can reuse the same type
@@ -26,17 +25,17 @@ models: # list of models for the energy network
connections:
- from: Adder1.out1 # start model, pattern: model_name.output_name/input_name
to: Adder2.in1 # end model
- time_shifted: False
+ time_shifted: True
# TODO: Below is what Illuminator looks for, but is not valid. Needs to be fixed
# - from: Adder1-0.hybrid_0.out1
# to: Adder2-0.hybrid_0.in1
-
#monitor: # a list of models, its inputs, output and states to be monitored and logged
#- Adder2.out2 # pattern model_name.state_name
monitor:
file: './out.csv'
items:
- Adder2.out1
+ - Adder1.out1
diff --git a/examples/adder_model.py b/examples/adder_model.py
index be955ba..c7665bf 100644
--- a/examples/adder_model.py
+++ b/examples/adder_model.py
@@ -25,8 +25,6 @@ def step(self, time) -> None:
return time + self._model.time_step_size
- # todo: adopt get_data() as defined by Mosaik
-
if __name__ == '__main__':
import mosaik
diff --git a/examples/python_api.py b/examples/python_api.py
new file mode 100644
index 0000000..19f93ba
--- /dev/null
+++ b/examples/python_api.py
@@ -0,0 +1,5 @@
+from illuminator.engine import Simulation
+
+sim = Simulation('examples/adder_copy.yaml')
+
+sim.run()
\ No newline at end of file
diff --git a/examples/simulation.example.yaml b/examples/simulation.example.yaml
index 48ec508..f2cd0a2 100644
--- a/examples/simulation.example.yaml
+++ b/examples/simulation.example.yaml
@@ -1,10 +1,9 @@
scenario:
+# this is only for illustrative purposes, it won't run scuccessfully
name: "ExampleScenario" # in mosaik so called world
start_time: '2012-01-02 00:00:00' # ISO 8601 start time of the simulation
end_time: '2012-01-02 00:00:10' # duration in seconds
time_resolution: 900 # time step in seconds (optional). Defaults to 15 minutes (900 s)
- results: './out.csv' # optional with default, path to the results file for the scenario. This should be optional
-models: # list of models for the energy network
- name: Battery1 # name for the model (must be unique)
type: Battery # name of the model registered in the Illuminator
inputs:
@@ -55,7 +54,9 @@ connections:
- from: Battery2.output
to: PV1.input2
monitor: # a list of models, its inputs, output and states to be monitored and logged
-- Battery1.input1 # pattern model_name.state_name
-- Battery2.output1 # no duplicates allowed
-- PV1.soc # pattern model_name.state_name
-- PV1.output1
+ file: './out.csv' # optional with default, path to the results file for the scenario. This should be optional
+ items:
+ - Battery1.input1 # pattern model_name.state_name
+ - Battery2.output1 # no duplicates allowed
+ - PV1.soc # pattern model_name.state_name
+ - PV1.output1
diff --git a/simple_test2.yaml b/simple_test2.yaml
new file mode 100644
index 0000000..9283edc
--- /dev/null
+++ b/simple_test2.yaml
@@ -0,0 +1,43 @@
+scenario:
+ name: "SimpleTest" # in mosaik so called world
+ start_time: '2012-01-01 00:00:00' # ISO 8601 start time of the simulation
+ end_time: '2012-01-01 01:00:00' # duration in seconds
+ time_resolution: 900 # time step in seconds (optional). Defaults to 15 minutes (900 s)
+models: # list of models for the energy network
+- name: CSVB # name for the model (must be unique)
+ type: CSV # name of the model registered in the Illuminator
+ parameters: # a CSV model must have a start time and a file as parameters
+ start: '2012-01-01 00:00:00' # ISO 8601 start time of the simulation
+ datafile: './tests/data/solar-sample.csv' # path to the file with the data
+- name: PV
+ type: PvAdapter # models can reuse the same type
+ inputs:
+ G_Gh: null # are meant to ovewrite initial values
+ G_Dh: null
+ G_Bn: null
+ Ta: null
+ hs: null
+ FF: null
+ Az: null
+ outputs:
+ G_Gh: null
+connections:
+- from: CSVB.G_Gh # start model, pattern: model_name.output_name/input_name
+ to: PV.G_Gh # end model
+- from: CSVB.G_Dh
+ to: PV.G_Dh
+- from: CSVB.G_Bn
+ to: PV.G_Bn
+- from: CSVB.Ta
+ to: PV.Ta
+- from: CSVB.hs
+ to: PV.hs
+- from: CSVB.FF
+ to: PV.FF
+- from: CSVB.Az
+ to: PV.Az
+monitor:
+ file: './out_test2.csv' # optional with default, path to the results file for the scenario. This should be optional # a list of models, its inputs, output and states to be monitored and logged
+ items:
+ - PV.pv_gen
+ - PV.G_Dh
\ No newline at end of file
diff --git a/src/illuminator/builder/model.py b/src/illuminator/builder/model.py
index 1591db3..23e0566 100644
--- a/src/illuminator/builder/model.py
+++ b/src/illuminator/builder/model.py
@@ -4,6 +4,7 @@
from enum import Enum
from datetime import datetime
import illuminator.engine as engine
+from mosaik_api_v3 import Simulator
class SimulatorType(Enum):
TIME_BASED = 'time-based'
@@ -44,9 +45,11 @@ class IlluminatorModel():
simulator_type: SimulatorType
The type of simulator that the model belongs to.
time_step_size: int
- The time step size for the simulator.
+ The time of each simulation step in seconds. Default 900.
time: int
The current time of the simulation.
+ model_type: str
+ A name for the class of model that is being defined. Default 'Model'.
"""
parameters: Dict = field(default_factory=dict)
@@ -55,13 +58,16 @@ class IlluminatorModel():
states: Dict = field(default_factory=dict)
triggers: Optional[Dict] = field(default_factory=list)
simulator_type: SimulatorType = SimulatorType.TIME_BASED
- time_step_size: int = 15 # This is closely related to logic in the step method. Currently, all models have the same time step size (15 minutes). This is a global setting for the simulation, not a model-specific setting.
+ time_step_size: int = 900 # This is closely related to logic in the step method.
+ # Currently, all models have the same time step size (15 minutes).
+ # This is a global setting for the simulation, not a model-specific setting.
time: Optional[datetime] = None # Shouldn't be modified by the user.
- model_type: Optional[str] = "Model"
+ model_type: Optional[str] = "Model"
+
def __post_init__(self):
- self.validate_states()
- self.validate_triggers()
+ self._validate_states()
+ self._validate_triggers()
# self.validate_simulator_type()
@property
@@ -71,7 +77,7 @@ def simulator_meta(self) -> dict:
meta = {
'type': self.simulator_type.value,
'models': {
- self.model_type : {
+ self.model_type: { # This must be the name of he model
'public': True,
'params': list(self.parameters.keys()),
'attrs': list(self.inputs.keys()) + list(self.outputs.keys())
@@ -79,7 +85,7 @@ def simulator_meta(self) -> dict:
}}
return meta
- def validate_states(self):
+ def _validate_states(self):
"""Check if items in 'states' are in parameters, inputs or outputs"""
for state in self.states:
if state not in self.parameters and state not in self.inputs and \
@@ -87,14 +93,14 @@ def validate_states(self):
raise ValueError(f"State: {state} must be either a parameter, "
"an input or an output")
- def validate_triggers(self):
+ def _validate_triggers(self):
"""Check if triggers are in inputs or outputs"""
for trigger in self.triggers:
if trigger not in self.inputs and trigger not in self.outputs:
raise ValueError(f"Trigger: {trigger} must be either an input "
"or an output")
- def validate_simulator_type(self):
+ def _validate_simulator_type(self):
"""Check if triggers are defined for time-based and event-based
simulations only
"""
@@ -107,7 +113,10 @@ def validate_simulator_type(self):
raise ValueError("Triggers are required in event-based simulators")
-from mosaik_api_v3 import Simulator
+# COTINUE FROM HERE
+# need to find a convenient an easy way to define new models
+# Ideas:
+# - add model definition as a method of the ModelConstructor class. will it work?
class ModelConstructor(ABC, Simulator):
"""A common interface for constructing models in the Illuminator"""
diff --git a/src/illuminator/engine.py b/src/illuminator/engine.py
index a6195a2..f220fc9 100644
--- a/src/illuminator/engine.py
+++ b/src/illuminator/engine.py
@@ -218,7 +218,8 @@ def start_simulators(world: MosaikWorld, models: list) -> dict:
return model_entities
-def build_connections(world:MosaikWorld, model_entities: dict[MosaikEntity], connections: list[dict], model_config: list[dict]) -> MosaikWorld:
+def build_connections(world:MosaikWorld, model_entities: dict[MosaikEntity], connections: list[dict],
+ models: list[dict]) -> MosaikWorld: # TODO: limit model_config to only models
"""
Connects the model entities in the Mosaik world based on the connections specified in the YAML configuration file.
@@ -230,6 +231,8 @@ def build_connections(world:MosaikWorld, model_entities: dict[MosaikEntity], con
A dictionary of model entities created for the Mosaik world.
connections: list
A list of connections to be established between the model entities.
+ models: list
+ The models involved in the connections based on the configuration file.
Returns
-------
@@ -241,7 +244,8 @@ def build_connections(world:MosaikWorld, model_entities: dict[MosaikEntity], con
for connection in connections:
from_model, from_attr = connection['from'].split('.')
to_model, to_attr = connection['to'].split('.')
- to_model_config = next((m for m in model_config if m['name'] == to_model))
+ # TODO: check if this will work for the cases in which there are not time_shifted connections
+ to_model_config = next((m for m in models if m['name'] == to_model))
time_shifted = connection['time_shifted']
# Establish connections in the Mosaik world
try:
@@ -412,7 +416,7 @@ def run(self):
model_entities = start_simulators(world, config['models'])
# Connect the models based on the connections specified in the configuration
- world = build_connections(world, model_entities, config['connections'])
+ world = build_connections(world, model_entities, config['connections'], config['models'])
# Connect monitor
world = connect_monitor(world, model_entities, monitor, config['monitor'])
diff --git a/src/illuminator/models/Battery/battery_mosaik.py b/src/illuminator/models/Battery/battery_mosaik.py
index c22234e..30c6ead 100644
--- a/src/illuminator/models/Battery/battery_mosaik.py
+++ b/src/illuminator/models/Battery/battery_mosaik.py
@@ -1,12 +1,13 @@
# only can build one battery model
import mosaik.scheduler
-import mosaik_api
-try:
- import Models.Battery.battery_model as batterymodelset
-except ModuleNotFoundError:
- import battery_model as batterymodelset
-else:
- import Models.Battery.battery_model as batterymodelset
+import mosaik_api_v3 as mosaik_api
+# try:
+import illuminator.models.Battery.battery_model as batterymodelset
+# except ModuleNotFoundError:
+# import battery_model as batterymodelset
+# else:
+# import Models.Battery.battery_model as batterymodelset
+
import pandas as pd
#todo: convert this battery model simAPI to a controller api. This becomes a mosaik API to start the battery and the electrolyser.
@@ -332,5 +333,9 @@ def get_data(self, outputs:dict) -> dict:
def main():
mosaik_api.start_simulation(BatteryholdSim(), 'Battery-Simulator')
+ import mosaik_api_v3
+
+ mosaik_api_v3.start_simulation()
+
if __name__ == "__main__":
main()
diff --git a/src/illuminator/models/adder.py b/src/illuminator/models/adder.py
index 9628303..58a1c36 100644
--- a/src/illuminator/models/adder.py
+++ b/src/illuminator/models/adder.py
@@ -7,7 +7,6 @@
from illuminator.builder import IlluminatorModel, ModelConstructor
-
# Define the model parameters, inputs, outputs...
adder = IlluminatorModel(
parameters={"param1": "addition"},
@@ -25,26 +24,25 @@ def step(self, time, inputs, max_advance=900) -> None:
print(f"inputs: {inputs}")
print(f'internal inputs: {self._model.inputs}')
for eid, attrs in inputs.items():
- print(f"eid: {eid}, attrs:{attrs}")
- print(f"self.model_entities: {self.model_entities}")
+ # print(f"eid: {eid}, attrs:{attrs}")
+ # print(f"self.model_entities: {self.model_entities}")
model_instance = self.model_entities[eid]
for inputname, value in inputs[eid].items():
- print(f"inputname: {inputname}, value:{value}")
- print(f"model_instance.inputs[inputname]: {model_instance.inputs[inputname]}")
+ # print(f"inputname: {inputname}, value:{value}")
+ # print(f"model_instance.inputs[inputname]: {model_instance.inputs[inputname]}")
if len(value) > 1:
raise RuntimeError(f"Why are you passing multiple values {value}to a single input? ")
else:
first_key = next(iter(value))
model_instance.inputs[inputname] = value[first_key]
- print(f"The dictionary value: {value[first_key]}")
+ # print(f"The dictionary value: {value[first_key]}")
self._model.outputs["out1"] = self._model.inputs["in1"] + self._model.inputs["in2"] # TODO do we add values internally or based on the current inputs
print("result:", self._model.outputs["out1"])
return time + self._model.time_step_size
- # todo: adopt get_data() as defined by Mosaik
-
+
if __name__ == '__main__':
# Create a model by inheriting from ModelConstructor
# and implementing the step method