PyPowSyBl
The PyPowSyBl project gives access PowSyBl Java framework to Python developers. This Python integration relies on GraalVM to compile Java code to a native library.
Installation
PyPowSyBl is released on PyPi.
First, make sure you have an up-to-date version of pip and setuptools:
pip3 install --upgrade setuptools pip --user
Then you can install PyPowSyBl using pip:
pip3 install pypowsybl --user
Build from sources
Requirements:
- Maven >= 3.1
- Cmake >= 3.14
- C++11 compiler
- Python >= 3.7
- GraalVM 20.3.0 with native image
To build from sources and install PyPowSyBl package:
git clone --recursive https://github.com/powsybl/pypowsybl.git
export JAVA_HOME=<path to GraalVM>
pip3 install --upgrade setuptools pip --user
pip3 install . --user
To run unit tests:
python3 -m unittest tests/test.py
Usage
First, we have to import pypowsybl:
import pypowsybl as pp
Then we can display the version of the PowSyBl modules:
pp.print_version()
Powsybl versions:
+-----------------------------+-----------------------+------------+------------------------------------------+-------------------------------+
| Repository name | Maven project version | Git branch | Git version | Build timestamp |
+-----------------------------+-----------------------+------------+------------------------------------------+-------------------------------+
| powsybl-open-loadflow | X.Y.Z | | | |
| powsybl-single-line-diagram | X.Y.Z | | | |
| powsybl-core | X.Y.Z | | | |
+-----------------------------+-----------------------+------------+------------------------------------------+-------------------------------+
We can create an IEEE 14 buses network and run a load flow computation:
n = pp.network.create_ieee14()
results = pp.loadflow.run_ac(n)
for result in results:
print(result)
LoadFlowComponentResult(component_num=0, status=CONVERGED, iteration_count=3, slack_bus_id='VL4_0', slack_bus_active_power_mismatch=-0.006081)
We can re-run the load flow computation in DC mode:
results = pp.loadflow.run_dc(n)
Or with different parameters:
parameters = pp.loadflow.Parameters(distributed_slack=False)
results = pp.loadflow.run_ac(n, parameters)
We can now iterate over buses and print calculated voltage:
for bus in n.buses:
print(f"Bus {bus.id!r}: v_mag={bus.v_magnitude}, v_ang={bus.v_angle}")
Bus 'VL1_0': v_mag=1.06, v_ang=10.313243381060664
Bus 'VL2_0': v_mag=1.045, v_ang=5.330504871947214
Bus 'VL3_0': v_mag=1.01, v_ang=-2.4121176767072106
Bus 'VL4_0': v_mag=1.0176698517255092, v_ang=0.0
Bus 'VL5_0': v_mag=1.019513126069881, v_ang=1.5391224927328597
Bus 'VL6_0': v_mag=1.07, v_ang=-3.908001888907669
Bus 'VL7_0': v_mag=1.0615190502807328, v_ang=-3.0467156954546497
Bus 'VL8_0': v_mag=1.09, v_ang=-3.0467156954546497
Bus 'VL9_0': v_mag=1.0559312123363436, v_ang=-4.625603385486276
Bus 'VL10_0': v_mag=1.0509841969760743, v_ang=-4.784365794405052
Bus 'VL11_0': v_mag=1.0569062925416597, v_ang=-4.477688311883925
Bus 'VL12_0': v_mag=1.0551885297773924, v_ang=-4.762642162506649
Bus 'VL13_0': v_mag=1.0503816324228432, v_ang=-4.843335457191098
Bus 'VL14_0': v_mag=1.0355296164107972, v_ang=-5.720717197261967
We can also get buses data (like any other network elements) as a Pandas dataframe:
df = n.create_buses_data_frame()
print(df)
v_mag v_angle
VL1_0 1.060 0.00
VL2_0 1.045 -4.98
VL3_0 1.010 -12.72
VL4_0 1.019 -10.33
VL5_0 1.020 -8.78
VL6_0 1.070 -14.22
VL7_0 1.062 -13.37
VL8_0 1.090 -13.36
VL9_0 1.056 -14.94
VL10_0 1.051 -15.10
VL11_0 1.057 -14.79
VL12_0 1.055 -15.07
VL13_0 1.050 -15.16
VL14_0 1.036 -16.04
To disconnect or reconnect a line:
n.disconnect('L1-2-1')
n.connect('L1-2-1')
To open or close a switch:
n.open_switch('a_switch')
n.close_switch('a_switch')
To go further, you can also load a case file instead of creating the IEEE 14 buses network:
n = pp.network.load('test.uct')
And dump the network to another format:
n.dump('test.xiidm', 'XIIDM')
We can generate a single line diagram for a voltage level in the SVG format:
n.write_single_line_diagram_svg('VL1', '/tmp/VL1.svg')
To run a security analysis and print results table:
sa = pp.security.create_analysis()
sa.add_single_element_contingency('L1-2-1', 'c1')
sa.add_single_element_contingency('L2-3-1', 'c2')
sa.add_multiple_elements_contingency(['L1-2-1', 'L1-5-1'], 'c3')
sa_result = sa.run_ac(n)
print(sa_result.get_table())
+----------------+-----------+--------------+----------------+------------+-------+------------+---------------------+-----------------+-------+------+
| Contingency ID | Status | Equipment ID | Equipment name | Limit type | Limit | Limit name | Acceptable duration | Limit reduction | Value | Side |
+----------------+-----------+--------------+----------------+------------+-------+------------+---------------------+-----------------+-------+------+
| c3 | CONVERGED | | | | | | | | | |
| c1 | CONVERGED | | | | | | | | | |
| c2 | CONVERGED | | | | | | | | | |
+----------------+-----------+--------------+----------------+------------+-------+------------+---------------------+-----------------+-------+------+
To run a sensitivity analysis and print post contingency sensitivity matrix (Pandas dataframe):
sa = pp.sensitivity.create_dc_analysis()
sa.add_single_element_contingency('L1-2-1')
sa.set_branch_flow_factor_matrix(['L1-5-1', 'L2-3-1'], ['B1-G', 'B2-G', 'B3-G'])
sa_result = sa.run(n)
df = sa_result.get_post_contingency_sensitivity_matrix_flows('L1-2-1')
print(df)
L1-5-1 L2-3-1
B1-G 0.5 -0.084423
B2-G -0.5 0.084423
B3-G -0.5 -0.490385
To run a load flow with hades2 instead of OLF:
Download Hades2
Create a config.yml
under $HOME/.itools/
hades2:
homeDir: <path-to-hades2>
Then specify Hades2 provider:
pp.loadflow.run_ac(n, provider="Hades2")