Configuration of a Model

A Model represents a real process or component in form of the code. It follows the general structure of Input - internal calculation - output, triggered by its corresponding simulator that needs to exist for using the model in simulations. The model just represents the calculation steps, and an entity (see the glossary) is the instantiation of a model. So within a scenario it is possible to have multiple entities (like battery storages) for one model type (battery storage model).

This page explains the structure of a _model.py to enable you to create your own model. As there can be different implementations of a device type (like storage system), the can be more than one class within a _model.py (like a very detailled model and a generic model).

Note

All code-blocks derive from charging_station_model.py as of (01/24) if not stated otherwise.

Imports

Import relevant packages for the model calculation
1import warnings
2import math
3from eelib.utils import cos_phi_fix

Class definition

Short explanation, listing of all model parameters with their allowed values for initialization (+ method to return these).
13class ChargingStation:
14    """Models a charging station for electric vehicles of different types."""
15
16    # Valid values and types for each parameter
17    _VALID_PARAMETERS = {
18        "p_rated": {"types": [float], "values": (0, math.inf)},
19        "output_type": {"types": [str], "values": ["AC", "DC"]},
20        "charge_efficiency": {"types": [float, int], "values": (0, 1)},
21        "discharge_efficiency": {"types": [float, int], "values": (0, 1)},
22        "cos_phi": {"types": [float, int], "values": (0, 1)},
23    }
24
25    @classmethod
26    def get_valid_parameters(cls):
27        """Returns dictionary containing valid parameter types and values.
28
29        Returns:
30            dict: valid parameters for this model
31        """
32        return cls._VALID_PARAMETERS

E.g., the rated power p_rated of a charging station has to be a floating point value that is non-negative. The efficiency should have a value between zero and one (100%). These values are used when creating a model in simulations to check for correct values.


Initialization method __init__()

takes parameter values as inputs
55def __init__(
56    self,
57    eid: str,
58    p_rated: int,
59    output_type: str = "AC",
60    charge_efficiency: float = 0.99,
61    discharge_efficiency: float = 0.99,
62    cos_phi: float = 1.0,
63    step_size=60 * 15,  # step size in seconds
64):
creates entity of this model by setting the properties and initializing attributes
78     # Set attributes of init_vals to static properties
79     self.eid = eid
80     self.p_rated = p_rated  # rated active power (AC/DC) [W]
81     self.output_type = output_type  # source AC/DC [-]
82     self.discharge_efficiency = discharge_efficiency  # discharging efficiency [-]
83     self.charge_efficiency = charge_efficiency  # charging efficiency [-]
84     self.cos_phi = cos_phi
85
86     # initialize dynamic output properties
87     self.p = 0  # Active Power (after control) [W]
88     self.q = 0  # Reactive Power (after control) [W]
89     self.p_device = {}  # active power for every vehicle [W]
90     self.p_min = 0  # Minimal active Power [W]
91     self.p_max = 0  # Maximal active Power [W]
92
93     ...
94
95     # save time step length and current time step
96     self.step_size = step_size
97     self.timestep = 0

Model methods

Type-specific function like calculation of power limits, aging, efficiency, adaption of stored energy etc.
 99 def _calc_power_limits(self):
100     """Calculate the power limits for the charging station with the input thats coming from the
101     electric vehicles.
102
103     Raises:
104         ValueError: If the power limits of at least one connected ev do not work together.
105     """
106
107     # calculate current efficiency depending on the direction of power flow
108     self._calc_current_efficiency()
109
110     # in case no ev is connected to cs - no active power flexibility
111     self.p_min = 0
112     self.p_max = 0
113     for ev_id, ev_data in self.ev_data.items():
114         # check for each ev if connected - consider their limits, efficiency and nominal power
115         if ev_data.appearance:
116             # check if min. and max. power are correct
117             if ev_data.p_min > ev_data.p_max:
118                 raise ValueError(f"Min. and max. power of ev {ev_id} do not comply.")
119             # handle the power limits
120             self.p_min = max(
121                 self.p_min + ev_data.p_min / self.efficiency,
122                 -self.p_rated,
123             )
124             self.p_max = min(
125                 self.p_max + ev_data.p_max / self.efficiency,
126                 self.p_rated,
127             )
128
129 ...

step() method

Within the step() method, the calculation processes are executed for the model entity.

This example is coming from the storage_model.py (01/24).

For handling of the processes of the model (its calculation) within a timestep
226 def step(self, timestep):
227     """Performs simulation step of eELib battery model.
228     Calculates all of the Dynamic Properties based on the Input Properties.
229
230     Args:
231         timestep (int): Current simulation time
232     """
First: Handling of a new timestep (if entity was called for first time, do some processes once, like adapting energy). This is needed for storages etc., but may not be needed for other model types.
233     # handle current timestep
234     if not self.timestep == timestep:
235         self.timestep = timestep
236
237         # adapt energy content from last timestep ( + self-discharge)
238         if self.p >= 0:  # charging
239             self.e_bss_step_volume = (
240                 self.p * self.charge_efficiency * (self.step_size / 3600)
241             )
242         else:  # discharging
243             self.e_bss_step_volume = (
244                 self.p / self.discharge_efficiency * (self.step_size / 3600)
245             )
246         self.e_bat += self.e_bss_step_volume
247
248         # Calculate battery cycles
249         self.bss_cycles += abs(self.e_bss_step_volume / self.e_cycle)
250
251         # Calculate battery state of health and aging properties
252         if self.status_aging:
253             self.__calculate_aging_status()
Call model-specific methods in supposed order
259     # Set active power and energy within limits
260     self.__set_power_within_limit()
261     self.__set_energy_within_limit()
262     self.soc = self.e_bat / self.e_bss_usable
263
264     self.__calc_charging_efficiency()
265
266     self.__calc_power_limits()

Checklist for adding / enhancing a model

General Guidelines

When adding or enhancing a model, try to make use of the existing properties and methods from this or other models

Model File Structure

A model includes the following files in a model folder which can be a subfolder inside eelib/core/***:

  1. an init file, e.g., eelib/core/devices/example/__init__.py (can be empty or with info about the model itself)

  2. a model file, e.g., eelib/core/devices/example/example_model.py (see beginning of the article)

  3. a simulator file, e.g., eelib/core/devices/example/example_simulator.py (see Configuration of a Simulator)

Required changes to eelib/data
  1. if a model dataclass is needed or helpful to communicate information to other models, add or adjust the specific dataclass in eelib/data/dataclass.py

  2. update eelib/data/__init__.py, to collect all dataclasses at that point

Tests & Documentation
  1. Writer proper comments and documentation (e.g. docstrings for every method, but also comments to illustrate the behaviour of the code)

  2. create unit tests for all relevant functionalities eelib/testing/unit/devices/test_example_model.py

  3. add the model to the documentation overview, docs/source/reference_manual/model_overview.rst (you can use eelib/utils/simulation_setup/create_sim_script.ipynb and create this automatically by executing the first two cells)

Other considerations
  1. Add new model attributes to the META of the simulator (see Configuration of a Simulator)

  2. If you add new packages, update requirements.txt

Integrating the model into a Simulation

Strategy

  • If fitting, implement the new model or functionalities into the HEMS strategies to be used in a simulation (see Implementing an operating Strategy). Here you should also add properties for recieving the correct values from the new model to integrate these into the strategies, e.g. by adding the properties to the META listing.

  • If it is a different model, you should integrate it in a different way into the test scenarios, e.g. a market model should just be added and connected to other models In the case these models are so-called architecture models and have to be connected to all other models, you need to add them to the list in eelib/model_connections/architecture_models.json file

Model Connections

  1. define new connections between properties of different models in eelib/model_connections/model_connect_config.json file (simply paste them in the direction of the connection)

  2. for connections in both direction, setup strong (first sent value) and weak (at beginning only done with default value) directions, see FAQ . Use lower-case letters for the model names here!

  3. if the direction of the connection is of importance, there may be a need to adapt the simulation_helper functions connect_entities() and connect_entities_of_two_model_types().

Integration into Test Scenarios

Add the model to…

  1. … the test scenario scripts (in the SIM_CONFIG)

  2. … the model data of the test scenarios (examples/data_input)

  3. … the model inputs of the test scenarios (examples/input)

Then execute the test scenarios and see whether the models works and is correctly integrated into the simulation process.