-
Notifications
You must be signed in to change notification settings - Fork 1
Home
The simulator Minimalistic-Discrete-Energy-Simulator (DES) is a lightweight framework to run resource objects in lockstep on. It allows for synchronized time progress on arbitrary resource sets and incorporates queries to ease control and post-processing.
Communities are built bottom up as a collection of resource sets/subsets to enable modular and customizable simulation setups. The entire framework resides within a jupyter notebook, offering a template to start modeling right away.
The core simulator consists of 300 lines python incorporating the base class to derive custom resources from. The structure of the framework can be outlined as follows:
+-----------------------------------------------------------------------------+
| __ ____ _ ___ ________ |
| / |/ (_)__ (_) _ \/ __/ __/ |
| / /|_/ / / _ \/ / // / _/_\ \ |
| /_/ /_/_/_//_/_/____/___/___/ |
+-----------------------------------------------------------------------------+
| generate test data |
| sample_recs generate rectangular pattern/profile |
| sample_sinus generate waveform pattern/profile |
| query |
| get_rss(_r) access helper to return resource objects |
| get_power access helper to aggregate resource power |
| get_power_grp aggregates the power of a peer/collection |
| parameter |
| get_param calculatesht chosen parameter |
| get_params calculatetime+series parameter |
| time rollbacks |
| jump_back rollback time/logs to previous state |
| general |
| simulation_init generates data model / init. resources |
| simulation_run start the simulation |
| store and load |
| simulation_store store the simulation results (mdata) |
| simulation_load load the simulation results into the model |
| plot |
| plot_cords plots resources by their coordinates |
| plot_logs plots one log per given resource |
| plot_bar plots multiple data series (pyplot wrap) |
| conversion helpers |
| to_kWh convert Ws to kWh |
| to_Ws convert kWh to Ws |
| arc_to_deg convert radians to great circle distance |
| deg_to_arc convert the reverse |
| latlon_to_xyz convert angluar to cartesian |
| xyz_to_latlon convert reverse |
| gen_coords generate random coordinate batch |
| center_point calculate median center of point cloud |
+-----------------------------------------------------------------------------+
There are no installation requirements beside a functional jupyter notebook based on python3.
Then start juptyer and eventually open the template or review the examples.
The general workflow is to build the community and parametrize the simulation setup.
- Write your basic building blocks, the resources, or use/extend the ones available.
- Build the community by forming sets.
- Setup the simulation by
K
andkclock
. - Call
simulation_init
to initialize the data model and perform data import, e.g. load profiles. - Run the simulation
simulation_run
. Results can then be stored or the JPN exported to html
The details are part of the following sections.
The notation helps with precise reasoning. The main notation symbol is followed by non-ordered subscripts (if at all). There is no prefix or superscript emphasizing readability, especially important in plain text, e.g. source code.
script | explanation |
---|---|
K | := total time steps of the simulation |
k | := the current time step |
kclock | := time increment per step k |
r | := resource index/identifier |
R | := total number of resources |
j | := entity index inside a collection |
J | := size of a collection |
The following examples use common abbreviations:
examples | explanation |
---|---|
k2 | simulation time after two steps: k0 to k1 to k2 |
K · kclock | total simulated time in ctime |
P_k_res | residual power at any k ktime |
P_k0_res | residual power at 0 ktime |
P_K_res, P_res | residual power for K kspan, (can be omitted) |
P_j_res | residual power of any peer j |
P_J_res | residual power all peers summed |
P_k10,j0,load | load power of peer j0 at 10 ktime |
P_j0,k1,pv | photovoltaic power of peer j0 at 1 ktime |
P_j0:9,res | total residual power of peers [0;8] |
P_r0,k1 | power of resource r0 at 1 ktime |
P_R | total power of all devices |
E_k10:20 | energy sum over 10 kspan from k [10;19] |
The sign convention for flow is uniform throughout the system. The default units are watt, seconds or wattseconds (if not stated otherwise).
![sign_conv](https://github.com/cs7org/MiniDES/raw/master/wiki_images/sign_conv.png)
The figure indicates the sign for the two flow directions and stays the same regardless of external or internal flow. Energy flow into the system is counted positive and energy flow reducing the energy amount is counted negative. Loss is always negative, since it reduces the system’s energy.
The simulation time is discrete and progresses in K
steps at a kclock
step size, which multiplied results in K · kclock
simulated time. The integer counter k ∈ [0; K − 1] has the current time and is used in formulas to indicate the time at k ktime. The float kclock
stores the seconds (or any other unit m/h/d) to connect the simulation time to clock time. Increasing it reduces the time resolution, which is described in the figure.
![time_line](https://github.com/cs7org/MiniDES/raw/master/wiki_images/time_line.png)
The values K
and kclock
are to be set before calling simulation_init
.
The resources form the basic building block of the community and can be aggregated into sets to query for common parameters, either after or during simulation control.
![sets](https://github.com/cs7org/MiniDES/raw/master/wiki_images/sets.png)
The magg
aggregates the resources into collections with j being the positional index of the subset. In this case there are two sets (yellow and red). While the yellow set has only one subset, the red set has two subsets, namely j0 and j1 of this respective set, eg households hh
.
There are currently 4 resource types available, sufficient to build photovoltaic households and their battery system.
- TimeSeries
- Control
- Battery
- Inverter
The functionality is annotated in the template (no code duplication). The constructors are called as follwed:
TimeSeries(r=1, ct='ld', cord1=10, cord2=11)
# | | | |y-coordinate
# | | |x-coordinate
# | |resource category this object specializes in
# |resource identifier, arbitrary int (but unique across R)
Control(r=0, ct='ctrl', cord1=0, cord2=510)
# | | | |y-coordinate
# | | |x-coordinate
# | |resource category this time control alias 'ctrl'
# |resource identifier, arbitrary int (but unique across R)
Battery(r=2, ct='es', cord1=x, cord2=y,\
# |resource category of type battery
pivot_soc_dch=0.1,\
# |if the SOC undercuts 10% of maximum capacity, the battery refuses discharge.
# | this is helpful for e.g. SOC reservation
E_soc=piv*E_soc_max, E_soc_max=E_soc_max*(1-piv),\
# |initial SOC |capacity maximum
P_ch_const=ch, P_dch_const=dch, loss=tmp, loss_pct=False, eff_rt=0.95)
# | | | | |round-trip efficiency
# | | | |const (False) or pct. idle losses
# | | |idle loss in percent or constant**
# | |maximum discharge power
# |maximum charging power
Inverter(r=3, ct='inv', cord1=50, cord2=60, P_nom=3500, CC=popt, nom_cap=2, P_idle=0)
# | | | | | | |limit inflow at 2*P_nom
# | | |x |y | |efficiency curve parameters***
# | | |optional nominal inverter efficiency
# | | |if enforced the inverter throws if higher
# | |category 'inv' indicating inverter
# |unique resource index
** The constructor signature has the doc. The battery supports constant discharge interpreting tmp
as [W] on loss_pct=False
. When set to loss_pct=True
the value tmp
is the percentage of capacity lost over the simulated time. The battery supports idle losses on low charge/discharge powers, added when undercutting the hardcoded value pivot_P_idle
in [W].
*** The three parameters can be curve-fit directly from the efficiency values of your inverter by. The efficiency curve looks like depicted in the figure below. The inverter supports the inverse calculation to get the inflow for the desired outflow (non-trivial since non-linear curve), if by_out
flag is set, when setting the power flow with set_P
. The constant self-consumption is set by P_idle
[W].
def func(x, a, b, c):
return a*x / (b-x) + c*x
xdata = np.array([0, 10, 100, 1000, 2000, 5000, 1e4])
ydata = np.array([0, 0.1, 0.45, 0.97, 0.94, 0.92, 0.8])
popt, pcov = curve_fit(func, xdata, ydata)
#results in: popt = np.array([-1.09363625e+00, -1.34235172e+02, -2.83214366e-05])
![inv_c](https://github.com/cs7org/MiniDES/raw/master/wiki_images/inv_c.png)
Inherit the base class Resource
and add the custom functionality to the step
method (or call methods from there). If the resource is prosuming power set the log noting the power in/outflow by the index log_index_P
, according tot he position in log_titles
. If not, set it to None
.
To request a log, add your title to the log_titles
tuple and call it by index, e.g. self.view[1,k]
for the second log in the tupel accessing the value at k to read/write.
Each time step, the current simulation time k is handed to the resource to update the local state to k+1, corresponding to a time progress of value kclock
clock time. Beside the two class attributes to allocate logs and to specify the log index associated with eventual power flow, it must overwrite the step function and call the super constructor. Overwriting init(), end() and jump() is optional. Minimum working example:
class Custom(Resource):
log_titles = ( #log allocation
'P_pros [W]',)#0:=prosumage
log_index_P = 0 #index for power
def init(self): #opt.
"""Called at simulation start"""
def end(self, k): #opt.
"""Called at simulation end"""
def jump(self, k):#opt.
"""Restore state of k ktime"""
self.view[:,k:] = 0#rollback log
self.set_k(k)
def step(self,k):
"""Custom functionality here"""
The following examples are common scenarios and solved in the template.
The energy residual of one household is logged over one day. The household has only one resource, the load profile. It shows how to query within the control and ensure energy conservation. The resulting log is visualized. Layout:
+----------+
| GRID |
| ^ |
| | |
| v |
| LD |
+----------+
One household self-charges his battery to achieve maximum self-consumption on its PV system. It also examples a rollback from k800 to k400. Layout:
+--------------------+
| GRID |
| | |
|PV+-->INV+--->x<->ES|
| v |
| LD |
+--------------------+
Battery array discharges always the max soc battery to cover the exact load (reverse inverter calculation). When the array depletes the DCGRID starts to feed in:
+------------------------+
| |
| +----------+INV+--+ |
| DCGRID ES| | |
| ES| + |
| ES+ LD |
| |
+------------------------+
Two photovoltaic-installed households have read the benefits of complementing residual energy profiles. They agree to share the photovoltaic output and hope to be self-sufficient on just one battery shared for collective use over the grid. They try to balance out supply and demand for maximum SSR and SCR. Layout:
+-----------------------------------------+
| |
| +----------------+GRID |
| | | |
| PV+-->INV+----+-+ PV+-->INV+--->x<->ES |
| v v |
| LD LD |
+-----------------------------------------+
The IPython.core.debugger
supports the standard interactive python debugger commands (breakpoints etc.). Place set_trace()
in the executed code and the simulator drops to shell.
Set the VERBOSE
flag and the JPN prints resource internals depending on the level.
VERBOSE = 0 # control verbose level of simulation_load/store/init/run()
# 0:= silent
# 1:= stringify each resource each step
# 2:= 1 with repr() instead of str()
The input data of the case study is available on request.
MIT License
Copyright (c) 2019
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtAgeP1hhQHBHPICTc2ho
vJFNXA2qtf0HjuXXV7i+imaN7RI4hUPQMo4nNCYjeiD3vzAdBTtWRQrI2ONmiFTk
ntAuD0Mg03q+mj/88aawnZbtXBF4QM5sYClInIuW23uhSq17SseWCXtEhmHtz155
4LllN4FBC11/R0shrAvFH4dAn2sM8PBg+FGze2wUaJbEl2rLe+qoek10krbSrpUP
VXCsyVyicR1IaOhldH4I8zpvB6CSPzOkzhQhbxRhxvKwN7kaVlzVGg2u3ccgffHP
dldIk2D14rz0hJ0Ix1qheAQW/+2haBP/lbwW2iLtiyC47sVeDbCpd66Zi9lKDUe4
nwIDAQAB
-----END PUBLIC KEY-----
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
- Architecture Overview
- Install
- Workflow
- Notation
- Sign convention
- Time model
-
Resource models
- TimeSeries
- Control
- Battery
- Inverter
- Building new model
- Examples
- Debug
- License