Skip to content

Commit

Permalink
Merge pull request #91 from manuGil/dev+models
Browse files Browse the repository at this point in the history
Models with time shifts
  • Loading branch information
manuGil authored Nov 28, 2024
2 parents 29e6553 + 9e6a964 commit f17e57e
Show file tree
Hide file tree
Showing 12 changed files with 144 additions and 93 deletions.
24 changes: 22 additions & 2 deletions docs/references/engine-api.rst
Original file line number Diff line number Diff line change
@@ -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



..
Expand Down
40 changes: 20 additions & 20 deletions docs/user/config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <br>for a simulation. | | |
|`name` | A name for the simulation, internally <br>this name will be asssigned to what <br>the Mosaik World created during runtime. | | |
| `start_time` | start time for the simulation.<br>Must be a timestamp in ISO 8601 format | | |
| `end_time` | end time for the simulation. <br>Must be a timestamp in ISO 8601 format. | | |
| `time_resolution` | number of seconds between <br>simulation steps | &#9745; | 900 (15 min)
| **models:** | a list of models for <br>the simulation | | |
| `name` | a name for the model. Must <br>be unique for each simulation | | |
| `type` | type of model. This must correspond <br>with the name of the model <br>registered in the Illuminator. | | |
| `inputs` | a set of input-names and initial <br>values for the model. The model <br>type determines which names and <br>values are applicable to each <br>model, and they must be declared accordingly. <br>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 <br>values for the model. Similar to <br>*inputs* valid names and values <br>for each model are determined by <br>the model *type*. See the respective <br>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 <br>the model. Parameters declared constants <br>for a model during runtime. | &#9745; | If ommited, the default values will be used. See the respective model type for details. |
| `states` | a set of name-value pairs considered <br>as states for the model. The values modify <br>the internal initial values of a state. | &#9745; | If ommited, the default values will be used. See the respective model type for details. |
| `triggers` | names of inputs, output or states <br>that are use as triggers for a particular model. <br>Triggers can only be declared by models <br>that implement the *event-based paradigm*. <br>See the respective model type to know if <br>it accepts triggers. | &#9745; | |
| `connect` | to declare in which client a model runs <br>when using a Raspberry Pi cluster. | &#9745; | |
| `ip` | Ip of the client manchine that will run <br>the model. Only IP version 4 format. | | |
| `port` | TCP port to use to connect to the <br>client machine| &#9745; | |
| **connections:** | how models connect to each other. | | |
| `from` | origin of the connection declared as `<model-name>.<output-name>`. Input names use here must also appear as *inputs* in the models section. | | |
| `to` | destination of the connection declared as `<model-name>.<input-name>`. Output names use here must also appear as *outputs* in the models section. | |
| `from` | origin of the connection declared as <br>`<model-name>.<output-name>`. Input names <br>use here must also appear as *inputs* in<br>the models section. | | |
| `to` | destination of the connection declared as <br>`<model-name>.<input-name>`. Output names <br>use here must also appear as *outputs* in <br>the models section. | |
| **monitor:** |
| `file` | path to a CSV file to store results of the simulation. File will be created if necessary. | &#9745; | 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 `<model-name>.<name>`, 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 <br>the simulation. File will be created if <br>necessary. | &#9745; | a `out.csv` file saved to the current directory |
|`items` | a list of which inputs, outputs or states <br>of models that most be monitored during <br>runtime. Items must be declared as <br>`<model-name>.<name>`, where *name* is an <br>input, output or stated clared in the <br>*models* section. No duplicated values <br>are allowed | | |

31 changes: 0 additions & 31 deletions examples/adder.yaml

This file was deleted.

7 changes: 3 additions & 4 deletions examples/adder_copy.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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



Expand Down
2 changes: 0 additions & 2 deletions examples/adder_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 5 additions & 0 deletions examples/python_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from illuminator.engine import Simulation

sim = Simulation('examples/adder_copy.yaml')

sim.run()
13 changes: 7 additions & 6 deletions examples/simulation.example.yaml
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -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
43 changes: 43 additions & 0 deletions simple_test2.yaml
Original file line number Diff line number Diff line change
@@ -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
29 changes: 19 additions & 10 deletions src/illuminator/builder/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -71,30 +77,30 @@ 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())
}
}}
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 \
state not in self.outputs:
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
"""
Expand All @@ -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"""
Expand Down
10 changes: 7 additions & 3 deletions src/illuminator/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
-------
Expand All @@ -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:
Expand Down Expand Up @@ -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'])
Expand Down
19 changes: 12 additions & 7 deletions src/illuminator/models/Battery/battery_mosaik.py
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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()
Loading

0 comments on commit f17e57e

Please sign in to comment.