Set Up and Run a Simulation

A good thing when working with the eELib would be the familiarization with the test scenarios of the eELib:

  • For a single building to see the operation of devices inside the household.

    • One is with just a few devices and explained setup (“minimum example”),

    • One is for more devices and also using helper functions to create entities, connections etc.

  • For a small and simple 8-bus low-voltage grid (2 feeders with six resp. two households) to estimate of the impact of different operating strategies on the local grid.

  • For a multi-family household with an energy management orchestrating the coordination between the various households.

With the following explanations of the setup of simulations, try to understand the given examples. Afterwards, you should know what is needed for a simulation and how you could set it up.

What files are needed for a simulation?

To run a simulation, we need just two files: A scenario script and the scenario input data. The input data consists of the model data, the grid information, grid parameters, and the model connections. Additionally, corresponding data such as files for the input of pv generation or a household baseload, must be provided.

Scenario script

Start the simulators, build the models, create the connections and start mosaik. Exemplary scripts for building, grid etc. can be found in the examples folder. The setup of these scripts is explained below in the Scenario Script Configuration section.

../_images/example_scenario_script.png

examples/test_scenario_building.py (01/24)

Input data file

As mentioned before, the input file combines the configuration and parameterization information for the scenario:

  • Model data: Information on the number of models and their parameterization.

  • Grid: Full details on the given buses and their connections via grid lines and transformer.

  • Model connections: Information on linking the model entities in the simulation.

  • Grid parameterization: Setup to analyze the grid, limits for buses, lines and transformer.

../_images/overview_input_data.png

examples/input/grid.json (02/25)

Model data

Here, the model entites of all model types and their parameterization is stored. E.g. the field “HouseholdCSV” has a dict of all household baseloads (with their naming) that read from a csv file. Have a look at the example below.

examples/input/grid.json (02/25)
 1{
 2   "model_data": {
 3      "PvCSV": {
 4         "PvCSV_0": {
 5               "p_rated": 8000,
 6               "cos_phi": 0.9,
 7               "datafile": "examples/data_input/pv/pv_10kw_15min.csv",
 8               "date_format": "YYYY-MM-DD HH:mm:ss",
 9               "start_time": "2022-01-01 00:00:00",
10               "header_rows": 1
11         },
12         ...
13      },
14      "HouseholdCSV": {
15         "HouseholdCSV_0": {
16               "p_rated": 4500,
17               "datafile": "examples/data_input/load/baseload_htw_12.csv",
18               "date_format": "YYYY-MM-DD HH:mm:ss",
19               "header_rows": 1,
20               "start_time": "2010-01-01 00:00:00"
21         },
22         ...
23      },
24      ...
25      "HEMS_default": {
26         "HEMS_default_0": {
27               "cs_strategy": "max_p",
28               "bss_strategy": "surplus"
29         },
30         ...
31      },
32      ...
33   },
34   ...
35}

Grid

In .json format (possibly created via pandapower). Provides details on the given buses and their connections via grid lines (plus their parameterization). In case you might be using powerfactory this should be just a path to the grid file that can be used in the powerfactory application (.dz format).

examples/input/grid.json (02/25)
 1{
 2   ...
 3   "grid": {
 4      "_module": "pandapower.auxiliary",
 5      "_class": "pandapowerNet",
 6      "_object": {
 7         "bus": {
 8            "_module": "pandas.core.frame",
 9            "_class": "DataFrame",
10            "_object": "{\"columns\":[\"name\",\"vn_kv\",\"type\",\"zone\",\"in_service\"],\"index\":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],\"data\":[[\"Trafostation_OS\",10.0,\"b\",null,true],[\"main_busbar\",0.4,\"b\",null,true],[\"MUF_1_1\",0.4,\"n\",null,true],[\"loadbus_1_1\",0.4,\"b\",null,true],[\"KV_1_2\",0.4,\"b\",null,true],[\"loadbus_1_2\",0.4,\"b\",null,true],[\"MUF_1_3\",0.4,\"n\",null,true],[\"loadbus_1_3\",0.4,\"b\",null,true],[\"KV_1_4\",0.4,\"b\",null,true],[\"loadbus_1_4\",0.4,\"b\",null,true],[\"MUF_1_5\",0.4,\"n\",null,true],[\"loadbus_1_5\",0.4,\"b\",null,true],[\"KV_1_6\",0.4,\"b\",null,true],[\"loadbus_1_6\",0.4,\"b\",null,true],[\"MUF_2_1\",0.4,\"n\",null,true],[\"loadbus_2_1\",0.4,\"b\",null,true],[\"KV_2_2\",0.4,\"b\",null,true],[\"loadbus_2_2\",0.4,\"b\",null,true]]}",
11            "orient": "split",
12            "dtype": {
13            "name": "object",
14            "vn_kv": "float64",
15            "type": "object",
16            "zone": "object",
17            "in_service": "bool"
18            }
19         },
20         "load": {...},
21         ...
22      }
23   },
24   ...
25}

Model connections

This includes the connections between grid buses, ems models, and the prosumer devices. It states which devices are located at which grid connection point (by naming the corresponding load bus) and whether an EMS is used. If no EMS is given at a loadbus (see “load_1_3” below), the devices are connected to the grid by a GCP_Aggregator_HEMS that only adds up the power values of listed devices but has no control ability. Hence, this should only be possible for some of the devices, as e.g. a single heat pump with no control makes no sense.

This section also lists the connection from EVs to their charging points, as within one household (especially multi-family houses) there may be more than one CS in a household. If such data is available, this would also allow for public charging points to be included.

examples/input/grid.json (02/25)
 1{
 2   ...
 3   "grid_model_connect_config": {
 4     "load_1_1": {
 5         "ems": "HEMS_default_0",
 6         "entities": [
 7             "HouseholdCSV_0",
 8             "ChargingStation_0",
 9             "EV_0"
10         ],
11         "cs_ev_connection": {
12             "ChargingStation_0": ["EV_0"]
13         }
14     },
15     "load_1_2": {
16         "ems": "HEMS_default_1",
17         "entities": [
18             "HouseholdCSV_1",
19             "HouseholdThermalCSV_0",
20             "Heatpump_0"
21         ],
22         "cs_ev_connection": {}
23     },
24     "load_1_3": {
25         "ems": "",
26         "entities": ["PVLibExact_0"],
27         "cs_ev_connection": {}
28     },
29     ...
30   },
31   ...
32}

Grid parameters

To set limits for the components in the grid (e.g. buses or lines), you can use this. These values are used to compare the grid results to. Hence, they are net directly set to the grid, as a congestion should be allowed for the power flow to calculate. It should then afterwards be found in the analysis of that results.

examples/input/grid.json (02/25)
 1{
 2   ...
 3   "grid_parameter": {
 4     "line_loading_max": 100,
 5     "trafo_loading_max": 100,
 6     "bus_vm_max_lv": 1.1,
 7     "bus_vm_min_lv": 0.9,
 8     "bus_vm_max_mv": 1.1,
 9     "bus_vm_min_mv": 0.9,
10     "bus_vm_max_hv": 1.1,
11     "bus_vm_min_hv": 0.95,
12     "bus_vm_max_ehv": 1.1,
13     "bus_vm_min_ehv": 1.0
14 }
15}

Configuration of a Scenario Script

Tip

The following explanations are just to show and explain what is done to set up a simulation scenario, within the script that you might execute. Although you can configure you scenario script by setting it up with these instructions, it can be a lot easier to use our Scenario Configurator that is introduced in the upcoming sections.

Note

All of these code-blocks derive from examples/test_scenario_building.py as of (02/25) if not stated otherwise.

Setup

import of used packages (examples, like mosaik or logging during the simulation)
 8import os
 9import mosaik
10import eelib.utils.simulation_helper as sim_help
11from eelib.model_connections.connections import get_default_connections
12import logging
Setting of paths for simulation data and used model simulators
27# define paths and filenames
28DIR = sim_help.get_default_dirs(
29    os.path.realpath(os.path.dirname(__file__)), scenario="building", format_db="hdf5"
30)
Define simulators and models for the simulation
37# Sim config.: Simulators and their used model types with the properties to store into DB
38SIM_CONFIG = {
39   # used database, will be left out for model creation and connections
40   "DBSim": {"python": "eelib.data.database.hdf5:Hdf5Database"},
41   ...
42   # all the used simulators and their models for this simulation
43   "HEMSSim": {
44      "python": "eelib.core.control.hems.hems_simulator:Sim",
45      "models": {
46            "HEMS_default": ["p_balance", "p_demand", "p_generation"]
47      },
48   },
49   "CSVSim": {
50      "python": "eelib.data.csv_reader.csv_reader_simulator:Sim",
51      "models": {
52            "HouseholdCSV": ["p", "q"],
53            "PvCSV": ["p", "q"],
54            ...
55      },
56   },
57   ...

This is done in a format that fits both mosaik for orchestrating the simulation and the handling of data and the simulation setup. So in the “SIM_CONFIG” dict we first need to give all used simulators as the keys (e.g., “CSVSim” for all used csv readers). With the sub-key “python” , we then provide the path to this simulator in our library (e.g., “eelib.data.csv_reader.csv_reader_simulator:Sim”). Under “models” we list all of the used model types within the simulation for this simulator (e.g., “HouseholdCSV”, “PvCSV”) and the attributes of them that should be stored within the database (e.g., [“p”, “q”] for active and reactive power of the PV system).

Configure start and end time of the simulation, step length, and whether to use forecasts. Model data and connections are read and SIM_CONFIG is handed to mosaik.
 82# Configuration of scenario: time and granularity
 83START = "2020-01-01 00:00:00"
 84END = "2020-01-04 00:00:00"
 85USE_FORECAST = False
 86STEP_SIZE_IN_SECONDS = 900  # 1=sec-steps, 3600=hour-steps, 900=15min-steps, 600=10min-steps
 87N_SECONDS = int(
 88    (
 89        arrow.get(END, "YYYY-MM-DD HH:mm:ss") - arrow.get(START, "YYYY-MM-DD HH:mm:ss")
 90    ).total_seconds()
 91)
 92N_STEPS = int(N_SECONDS / STEP_SIZE_IN_SECONDS)
 93scenario_config = {
 94    "start": START,  # time of beginning for simulation
 95    "end": END,  # time of ending
 96    "step_size": STEP_SIZE_IN_SECONDS,
 97    "n_steps": N_STEPS,
 98    "use_forcast": USE_FORECAST,
 99    "bool_plot": False,
100}
101
102# Read Scenario file with data for model entities
103with open(DIR["INPUT_DATA"]) as f:
104    scenario_data = json.load(f)
105
106# Read configuration file with data for connections between prosumer devices
107model_connect_config = get_default_connections()
108
109# Create world
110world = mosaik.World(SIM_CONFIG, debug=True)

With mosaik.World, we start the orchestrator mosaik and afterward set up the simulation using mosaik. For this, we mostly use mosaik functionalities within the helper functions of the eelib.utils.simulation_helper folder. Regarding the order, we mostly first handle the exceptions of the models, like the output database, and afterward use the helper functions for all other models. Fore a deeper understanding of what is done with these steps, you should look at the implementation of these helper functions and possibly even the setup of mosaik for creating the simulation.


Start Simulators for all used Models

120# start all simulators/model factories with mosaik for data given in SIM_CONFIG
121dict_simulators = sim_help.start_simulators(
122    sim_config=SIM_CONFIG, world=world, scenario_config=scenario_config
123)

Initiate Model Entities using the created Simulators for each Device Type

133# create all models based on given SIM_CONFIG
134dict_entities = sim_help.create_entities(
135    sim_config=SIM_CONFIG, model_data=scenario_data["model_data"], dict_simulators=dict_simulators
136)

Connect Entities

The connections for each entity are listed in the model_connect_config data. Now tell mosaik about them and let mosaik create graphs for how the calculation procedure of the simulation is to be executed. Additionally, the entities are connected to the database to store the desired resulting property values.

143# connect all models to each other
144sim_help.connect_entities(
145    world=world,
146    dict_entities=dict_entities,
147    model_connect_config=model_connect_config,
148    dict_simulators=dict_simulators,
149)

Run Simulation

161world.run(until=scenario_config["n_steps"], print_progress=True)

Just go Ahead and Run a Simulation

  • You can run one of the test_scenarios in the examples folder

  • While running the simulation, some stuff is logged like creation of simulators, entities, and connections. Additionally, mosaik plots the progress of the whole simulation (like how many time steps are finished calculating).

  • Adapting the parameterization in the simulation files can yield quite different results.

  • Running one of the simulations will create a .hdf5 results data file in the folder /examples/results.

  • You can view the information of this file via the H5Web Extension in Microsoft VSC and plot the profiles (stored under Series) of the used devices.

  • If you want, you can set the parameter bool_plot in the scenario_config dict to true (only do this for small simulations!). mosaik will then present four plots for the connections, the simulation process and the timely duration during the simulation.

Configure a Simulation Scenario

For sure, you may create your own simulation by simply

  • Copying one of the scenario scripts,

  • Deleting all of the redundant stuff (simulators/devices/connections), and

  • Copying and setting up the corresponding input file.

ALTERNATIVELY, you can (or should!) use our easy …

Step-by-Step Scenario Configurator

To create all the needed files for your own scenario, you can use the jupyter notebook script_sim_setup.ipynb in the subfolder eelib\\utils\\simulation_setup. In this notebook you will be guided through all the steps of setting up your simulation scenario. Those steps will be shortly explained here, for more information just have a look at the notebook, which aims to be self-explanatory.

  1. Generate model overview: You can create an .rst file that displays all the models of the eELib and their parameters.

  2. You can create your local power grid with different options:

    • Use a pre-defined Pandapower or SimBench grid.

    • Create an easy radial LV grid.

    • For using PowerFactory, you should just provide the path to your grid file.

  3. Set parameters for the grid including limits for buses, lines and transformer.

  4. Generate the model data: You can create and set all your model entities including their parameterization within excel files, again using different options:

    1. All models should have the same parameterization, then you have to give that configuration once and afterwards list, at which grid loads these models should be connected.

    2. The models can have different parameterization, then you have to list them specifically.

  5. Create the full input file for your scenario.

  6. Finally, if you set the last configuration parameters, you can also create the script to execute your scenario. Optionally, you can shift the scenario files to the examples folder so you just have to press play to execute your own scenario.