An adapter to use PYPOWER with mosaik.
Project description
mosaik-pypower
This package contains the adapter to connect PYPOWER to mosaik.
Installation and starting
You can install mosaik-pypower via pip:
$ pip install mosaik-pypower
You can run the tests with:
$ git clone https://gitlab.com/mosaik/mosaik-pypower.git
$ cd mosaik-pypower
$ pip install -r requirements.txt
$ pytest # Run tests on current Python
$ tox # Run tests on all supported Python versions
You can either start mosaik-pypower as normal process or use it as a library. If you run it via the command line, you need to pass the address that mosaik is listening on:
$ mosaik-pypower HOST:PORT
You can run mosaik-pypower --help to get more help.
In order to use it as a library, you have to import the module mosaik_pypower.mosaik and instantiate the class PyPower:
>>> import mosaik_pypower.mosaik
>>> pp = mosaik_pyower.mosaik.PyPower()
Input file format
Mosaik-pypower uses, like PYPOWER, the bus-branch model to represent power grids.
This model is fairly simple: You have a number of buses / nodes that are connected via branches / lines. Transformers are just a special kind of branch. Buses are divided into three sub-types: the reference bus (also known as swing or slack bus), PQ buses and PU buses.
For PQ buses, the (re)active power P and Q are given, PYPOWER will calculate the voltage magnitude and angle for these nodes. PU buses provide active power and a constant voltage, thus PYPOWER computes the reactive power for these buses. Mosaik-pypower currently only supports PQ buses, because they are more general and if you model your power grid for mosaik, you do not know in advance what kind of unit (consumer, producer, …) will be connected to a bus.
Every grid needs to have exactly one reference bus. It has a constant voltage magnitude and angle (usually 1 p.u. and 0°). PYPOWER computes the residual (re)active power for that node. Usually it is placed on the primary or secondary side of the grid’s transformer station (or where it would be):
With transformer Without transformer REF REF | | 8 | | PQ0 PQ0 / \ / \ | | | | PQ1 PQ2 PQ1 PQ2
PYPOWER’s original input format is relatively hard to read/write and also contains more information than we need to model power grids for the use with mosaik. For example, it contains loads and feed-in for each bus which we don’t need here because mosaik will provide it from other simulators.
Thus, mosaik-pypower provides simpler input file formats which it will convert to the format used by PYPOWER. Currently, it can read Excel (xlsx) and JSON files (in an old an a new variant).
The Excel and new JSON formats are structured in a similar way. The difference of the old JSON format are larger. You can find example files in our source code repository. Below, you’ll find a detailed description of them.
Excel
Excel files need to have the suffix *.xlsx and need to provide at least to sheets: one for buses and one for branches. They should usually be named Nodes and Lines, but you can also use any name you want (more on that later).
In every sheet, the first row is reserved for headings. From row two, you list the buses and branches (one per row). If the first cell of a row starts with a # this row is treated as a comment.
Bus sheet
The bus sheet contains at least three columns (you can use the other columns for comments):
The bus name (string)
The bus type (either REF or PQ)
The bus’ base voltage (integer in kV line-to-line, e.g. 400 for a European LV node).
Example:
Node name Node type Base voltage [kV] Notes Grid REF 110 Slack bus # This is a comment Bus0 PQ 20 Bus1 PQ 20 Bus2 PQ 20 Bus3 PQ 20
There must be exactly one REF bus in the list and it must be first in the list.
Branch sheet
The branch sheet contains at least seven columns (again, you can use additional columns for notes):
The branch name (string)
One end of the branch (a valid bus name)
The other end of the branch (a valid bus name)
The transformer or line type (see here for a list of built-in types. You can also add your own)
The branch length (float in km)
A flag indicating whether the branch is online / active (1) or offline / inactive (0)
The default tap turn for a transformer (usually 0)
Example:
Name From To Type Length [km] Online Tap # Transformer Trafo1 Grid Bus0 TRAFO_40 1.0 1 0 # Lines B_0 Bus0 Bus1 NA2XS2Y_185 5.0 1 B_1 Bus0 Bus2 NA2XS2Y_185 3.0 1 B_2 Bus1 Bus3 NA2XS2Y_185 2.0 1 B_3 Bus2 Bus3 NA2XS2Y_185 0.3 1
Additional branch and transformer types
You can add more line and transformer types via two additional sheets, Line types and Transformer types (you can give them other names if you want to).
The headings should be pretty self-explanatory:
Example line types:
Type name R' [Ω/km] X' [Ω/km] C' [nF/km] I_max [A] SPAM_200 0.1337 0.0815 0 404
Example transformer types:
Transformer Type S_r [MVA] I_max_prim [A] I_max_sec [A] P_loss [kW] R [Ω] X [Ω] taps TRAFO_23 23 100 800 100 0.0123 1.234 {-1: 0.9, 0: 1.0, 1: 1.1}
JSON (new format)
The new JSON format (use *.json as a suffix) is very similar to the Excel format, but instead of separate sheets, its just different entries in a JSON object. There is one entry for the buses, one for branches and one for transformers. Each entry contains is a list of lists which are structured like the columns in the Excel format:
{
"bus": [
["Grid", "REF", 110.0],
["Bus0", "PQ", 20.0],
["Bus1", "PQ", 20.0],
["Bus2", "PQ", 20.0],
["Bus3", "PQ", 20.0]
],
"trafo": [
["Trafo1", "Grid", "Bus0", "TRAFO_40", true, 0]
],
"branch": [
["B_0", "Bus0", "Bus1", "NA2XS2Y_185", 5.0, true],
["B_1", "Bus0", "Bus2", "NA2XS2Y_185", 3.0, true],
["B_2", "Bus1", "Bus3", "NA2XS2Y_185", 2.0, true],
["B_3", "Bus2", "Bus3", "NA2XS2Y_185", 0.3, true]
]
}
There must be exactly one REF bus in the list and it must also be the first entry of the list.
If you want to specify additional branch or transformer types, you can add branch_types or trafo_types to the object. They are also structured similarly to the Excel file:
{
"bus": [
["Grid", "REF", 110.0],
["Bus0", "PQ", 20.0],
["Bus1", "PQ", 20.0],
],
"trafo": [
["Trafo1", "Grid", "Bus0", "TRAFO_23", true, 0]
],
"branch": [
["B_0", "Bus0", "Bus1", "SPAM_200", 5.0, true]
],
"branch_types": {
"SPAM_200": [0.1337, 0.0815, 0, 404]
},
"trafo_types": {
"TRAFO_23": [23, 100, 800, 100, 0.0123, 1.234, {"-1": 0.9, "0": 1, "1": 1.1}]
}
}
JSON (old format)
The old JSON format (also with the *.json suffix) differs from the new format by explicitly listing the branch and transformer parameters for each branch and transformer. It must also have a base_mva entry which is used for the p.u. conversion. It can usually just be set to 1:
{ "base_mva": <global_base_mva>, "bus": [ ["<bus_id>", "REF|PQ", <base_kv>], ... ], "trafo": [ ["<trafo_id>", "<from_bus_id>", "<to_bus_id>", <Sr_MVA>, <v1_%>, <P1_MW>, <Imax_p_A>, <Imax_s_A>], ... ], "branch": [ ["<branch_id>", "<from_bus_id>", "<to_bus_id>", <length_km>, <R'_ohm/km>, <X'_ohm/km>, <C'_nF/km>, <I_max_A>], ... ] }
Again, there may only be one REF bus and it must be the first in the list
Usage in mosaik
As pointed out above, you can run mosaik-pypower in-process or as a sub-process. Every instance of mosaik-pypower can handle multiple power grids at once. This is very handy for scenarios with a lot of separate grids. You can start one instance of mosaik-pypower for every CPU core of your machine and distribute all grids over these instance. More instances would mean more overhead, less instances would mean less parallelization.
Instantiation and initialization
Here is an example sim config for mosaik:
sim_config = {
'PyPower-inproc': {
'python': 'mosaik_pypower.mosaik:PyPower',
},
'PyPower-subproc': {
'cmd': 'mosaik-pypower %(addr)s',
},
}
When you create an instance of mosaik-pypower, you can pass three parameters:
step_size is an integer in seconds (of simulation time) and defines how often a power flow analysis should be performend.
pos_load is an optional boolean that lets you specify whether the active power for loads is a positive or a negative number.
The default (True) is to use positive numbers for loads and negative numbers for feed-in of active power.
If you want to use negative values for loads and positive for feed-in, set this flag to False.
converge_exception is an optional boolean that allows to set the behavior in case that the power flow does not converge.
As default (False) all output attributes are set to NaN and the simulation continues. If set to True, an exception is thrown and the simulation stops.
Examples:
# Power-flow every minute:
pp_a = world.start('PyPower', step_size=60)
# Power-flow every 15 minutes, negative values for active power of loads:
pp_b = world.start('PyPower', step_size=300, pos_loads=False)
Models
Mosaik-pypower provides the following models / entity types:
- Grid
public: True
parameters: gridfile [, sheetnames]
This model is used to instantiate a power-grid within mosaik-pypower from the gridfile provided. The Grid instance will have child entities for every element in that grid (buses, branches, …). You can access these entities via the children attribute of the Grid entity. These entities also contain information about how they are related to each other. This allows mosaik to determine the grid topology. You can query it via world.entity_graph[<full_entity_id>.
If you use an Excel file and deviate from the default sheet names, you can optionally pass a sheetnames argument which is a dict with the sheet names to use.
- RefBus / PQBus
public: False
attributes: P, Q, Vl, Vm, Va
Every Grid will contain exactly one RefBus entity and at least one PQBus entity.
P and Q are active / reactive power in [W] / [VAr].
Vl is the nominal voltage in [V] as defined in the grid file. Vm is the current voltage magnitude in [V] and my deviate from Vl. Va is the voltage angle in [°] (degree).
- Branch
public: False
attributes: P_from, Q_from, P_to, Q_to, I_real, I_imag, S_max, I_max, length, R_per_km, X_per_km, onine
A grid consists of an arbitrary amount of branches connecting the PQBus entities with each other.
The attributes (P|Q)_(from|to) denote the (re)active power on both ends of the branch in [W] or [VAr]. I_real and I_imag are the complex current in [A] flowing through the branch.
S_max and I_max denote the maximum acceptable apparent power (in [VA]) and current (in [A]) for that branch.
lengh, R_per_km, X_per_km, C_per_km and online are the respective values for the branch from the input file.
- Transformer
public: False
attributes: P_from, Q_from, P_to, Q_to, S_r, I_max_p, I_max_s, P_loss, U_p, U_s, taps, tap_turn.
A grid may have an arbitrary number of transformers (zero, one or more). Since it is just a special kind of Branch it shares many attributes with it.
The attributes (P|Q)_(from|to) denote the (re)active power on both ends of the branch in [W] or [VAr]. I_real and I_imag are the complex current in [A] flowing through the branch.
S_r is the rated apparent power (in [VA]) of the transformer. I_max_p and I_max_s are the maximum currents in [A] for the primary and secondary side of the transformer. P_loss is the transformer’s nominal power loss in [W].
U_p and U_s are the nominal primary and secondary line-to-line voltages in [V].
taps is a dictionary of the available tap turns of the transformer. tap_turn is the currently active tap turn.
Examples for model instantiation and connection
Here are some examples on how to instantiate a power grid and work with the entities that you get in return.
Basic examples:
pp = world.start('PyPower', step_size=60)
# Create a grid from a JSON file:
grid = pp.Grid(gridfile='path/to/grid.json')
# Get a list of RefBus, PQBus, Transformer and Branch entities:
grid = grid.children
# Create some PV entities and randomly connect them to a PQBus:
pvs = make_pvs(...) # Returns a list of PV entities
pq_buses = [e for e in grid if e.type == 'PQBus']
mosaik.util.connect_randomly(world, pvs, pq_buses, ('P_out', 'P'), ('Q_out', 'Q'))
Advanced stuff:
pp = world.start('PyPower', step_size=60)
# Create a grid from an Excel file and overwrite default sheet names.
# Directly grab the list of child entities
grid = pp.Grid(gridfile='path/to/grid.xlsx', sheetnames={
'bus': 'Nodes',
'branch': 'Lines',
'branch_types': 'Line types',
'trafo_types': 'Transformer types',
}).children
# Get the RefBus
ref_bus = [e for e in grid if e.type == 'RefBus']
# Get all nodes starting with "ConnectionPoint_". Note that every entity ID
# is prefixed with a grid index (e.g., "1-"). We can use regular expressions
# to handle this:
import re
regex_cn = re.compile(r'\d+-ConnectionPoint_.*')
conpoints = [e for e in grid if regex_cn.match(e.eid)]
# Create a dictionary mapping node names to the respective entities. Again,
# we have to strip the grid index:
conpoints = {e.eid.split('-', 1)[1]: n
for n in grid if regex_cn.match(n.eid)}
# Connected loads to defined connection points:
houses = create_houses(...) # Returns a list of (house, node-id) tuples
for house, node_id in houses:
mosaik.connect(house, conpoints[house_id], ('P_out', 'P'), ('Q_out', 'Q'))
# Get "secondary side" buses of all transformers
trafo_nodes = []
# 1. Get all transformers:
trafos = [e for e in grid if e.type == 'Transformer']
# 2. Group PQBuses by full ID for easier access:
nodes = {e.full_id: e for e in grid if e.type == 'PQBus'}
# 3. Iterate transformer relations (assuming that they are connected to the
# RefBus on their primary side):
for trafo in trafos:
rels = world.entity_graph[trafo.full_id]
assert len(rels) == 2
for rel in rels:
if rel in nodes:
trafo_nodes.append(nodes[rel])
break
else:
raise ValueError('No PQBus found at trafo.')
Getting help
If you need, please visit either the mosaik-users mailing list (or the PYPOWER Google group if your issue is very PYPOWER specific).
Changelog
0.8.2 – 2022-09-27
[BUGFIX] Added restriction to numpy <1.23, because newer versions are incompatible with current PYPOWER version
0.8.1 – 2022-03-23
[BUGFIX] Removed limitation for scypi and increased python compatibility to 3.10.
0.8.0 – 2021-05-21
[CHANGE] Updated to mosaik-api 3.0.
[NEW] converge_exception can be set to throw an exception if load flow doesn’t converge
0.7.3 - 2021-03-04
[BUGFIX] Require version <1.6.0 of scipy and <2.0 of xlrd, because newer versions are incompatible
[BUGFIX] Set I_real and I_imag in case of failure of power flow
0.7.2 – 2017-07-19
[BUGFIX] –> Issue #5: https://bitbucket.org/mosaik/mosaik-pypower/issues/5
[BUGFIX] –> Issue #6: https://bitbucket.org/mosaik/mosaik-pypower/issues/6
[LIB-UPDATE] –> scipy update from version 0.17.0 to version 0.19.1
0.7.1 – 2016-02-15
[FIX] Fixed a typo in model.py. “Tap” can now be read from Excel files.
0.7 – 2014-10-28
0.6.3 – 2014-09-22
[CHANGE] Updated to mosaik-api 2.0.
0.6.2 – 2014-07-31
[CHANGE] Cache xlsx files to improve performance
[CHANGE] Updated to mosaik-api 2.0a4.
0.6.1 – 2014-06-30
[NEW] Added I_max_p [A] and I_max_s [A] to the transformer data.
0.6 – 2014-06-26
[NEW] Can import grids from Excel files (xlsx)
[NEW] New import format for JSON files (the old format is still supported)
[CHANGE] Massive internal refactoring.
[CHANGE] Updated to mosaik-api 2.0a3.
0.5 – 2014-03-26
Initial release
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file mosaik-pypower-0.8.3.tar.gz
.
File metadata
- Download URL: mosaik-pypower-0.8.3.tar.gz
- Upload date:
- Size: 27.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.8.10
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 66c4993edad017df90768ee0b79095b3d89842213b7bc4a838f0900d4958fc9b |
|
MD5 | 7a577d80449b815aecfbf9bd192f14a1 |
|
BLAKE2b-256 | 9b259b70cd9c9ba94ca32455ce3d15a8bff98d966594abc890dff85aaed00d78 |
File details
Details for the file mosaik_pypower-0.8.3-py3-none-any.whl
.
File metadata
- Download URL: mosaik_pypower-0.8.3-py3-none-any.whl
- Upload date:
- Size: 22.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/4.0.1 CPython/3.8.10
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 29ae0a0d3d133ac17249f294c861268b90ca98dfe6ad0aaea3d5b64b3c88f33d |
|
MD5 | c4b6a91546f2e4c24ac5663c3d8a46cd |
|
BLAKE2b-256 | 7b6b3cd8781425e9ed4f3c6952adf0a266fb9ab8d8c4d0a4fd6fca8e1e29f34e |