Lopez 2019 A
Lopez 2019 A
Abstract—DIgSILENT PowerFactory is among the most widely tentially unlimited expansion of the simulation capabilities of
adopted power system analysis tools in research and industry. It PowerFactory. Additionally, this feature is also useful when
provides a comprehensive library of device models and it allows implementing a model in DSL becomes too cumbersome, for
users to define their own. Models for dynamic simulation can be
defined in the DIgSILENT Simulation Language (DSL). When example, when if/else, while and/or for statements are
the functionality of DSL is insufficient, new DSL functions can be required, none of which are provided in DSL.
defined in C or C++. However, C and C++ can be challenging for However, creating new DSL models in C or C++ poses
inexperienced programmers. Furthermore, every time the C or some challenges. C is a low-level programming language
C++ code is modified, it needs to be recompiled and PowerFactory by today’s standards, so considerable programming effort is
needs to be restarted for the changes to take effect, which
slows down the workflow, model development, and inhibits rapid needed to implement sophisticated functionality. Furthermore,
prototyping. In this paper we present an open source library some of its features have proven troublesome to inexperienced
that allows users to call Python functions and methods from programmers, such as direct memory addressing and dynamic
DSL with minimal effort. Python is a powerful and much easier memory management. On the other hand, C++ is a higher level
to use language than C or C++. Additionally, Python programs language than C, but with a steep learning curve, and most
do not need to be compiled. Furthermore, with this library
PowerFactory does not need to be restarted every time the Python electrical power engineers are not well versed in it. Using these
code is changed. To illustrate what can be accomplished with languages can also slow down the simulation development
our library we present three example use cases related to load workflow; Every time the C or C++ code is modified, the DLL
modeling, co-simulation, and fault detection based on machine must be recompiled and PowerFactory must be restarted for the
learning. The examples show that it becomes straightforward to changes to take effect. Altogether, using C or C++ for creating
enhance DSL with Python and that sophisticated models can be
produced with reduced effort using popular open source Python DSL models is inconvenient when the code is modified often
libraries. As a consequence, PowerFactory users gain access to and/or when the main goal is implementation with minimal
enhanced modeling capabilities and user-friendliness, and a more effort, which is the case during model development and rapid
speedy workflow, which is beneficial for rapid prototyping. prototyping.
Index Terms—Co-simulation, DSL, dynamic simulation,
Python in comparison to C and C++ is easier to learn, read,
machine-learning, PowerFactory, Python
write and debug. It is a high-level language with an extensive
standard library. In addition, Python’s popularity within the
I. I NTRODUCTION
scientific and engineering communities has materialized in
DIgSILENT PowerFactory is among the most widely a vast collection of reliable and user-friendly open source
adopted power system analysis tools in research and industry. libraries, like SciPy [2] for scientific computing, scikit-learn
It is known for its versatility, since it allows users to perform [3] for machine learning, and pandas [4] for manipulation
a wide range of static and dynamic analyses on power system and analysis of large datasets. These characteristics make
models, and provides a comprehensive library of electrical it appealing when the priority is to minimize programming
power system device models that users can customize and effort. Since version 15.1 PowerFactory provides a Python
expand with new models. In the case of dynamic simula- 3 API [5], however, this API cannot be invoked during a
tions (i.e., transient stability and electromagnetic transient dynamic simulation, much less from a DSL model [1], as it
simulation), users can define new models in the DIgSILENT is mainly intended for automating simulation tasks.
Simulation Language (DSL). In this paper we present a small open source library1 that
DSL is a language for modeling continuous linear and addresses these challenges by allowing PowerFactory users
non-linear systems that is well suited for defining control to easily call Python functions and methods from their DSL
structures. When the functionality of DSL is insufficient for models. Our library has the added benefit of a more speedy
a given application, new DSL functions can be defined by simulation development workflow. Unlike C and C++, Python
the user. These functions must be specified in either the C is an interpreted scripting language that does not need to be
or C++ programming language and compiled into a Dynamic compiled. Moreover, the library is designed so PowerFactory
Link Library (DLL) that PowerFactory can access [1]. This
is a powerful feature, as it creates the opportunity for po- 1 https://github.com/claudiodavidlopez/digexfunPyDSL
import numpy as np ! Block inputs: re, im
from external_script import external_function ! Block outputs: mag, ang
1.0
V (p.u.)
class ElectronicLoadWECC:
0.8 .
.
.
0.0 0.1 0.2 0.3 0.4 0.5
Time (s) eload = ElectronicLoadWECC(0.866, 0.7, 0.7,
(a) 20, 10)
10 while True:
sim_ins = socket.recv_json()
V = sim_ins[0]
0 P, Q = eload.calc_pq(V)
0.0 0.1 0.2 0.3 0.4 0.5 sim_outs = [P, Q]
Time (s)
socket.send_json(sim_outs)
(b) Listing 6. External electronic load script for co-simulation (eload.py).
Fig. 3. Response of the electronic load to voltage variations resulting from
a high-impedance fault. (a) Terminal voltage input. (b) Power consumption
output.
A. WECC Electronic Load Model
Fvl = select(V < Vd2, 0.0, select(V < Vd1, The WECC Composite Load Model, developed by the
select(V <= Vmin, (V - Vd2)/(Vd1 - Vd2),
(Vmin - Vd2 + frcel*(V - Vmin))/(Vd1 -
Western Electricity Coordinating Council (WECC), represents
Vd2)), select(Vmin > Vd1, 1.0, (Vmin - the dynamic characteristics of end-use loads [8]. One of the
Vd2 + frcel*(Vd1 - Vmin))/(Vd1 - Vd2)))) components of the WECC Composite Load Model is the
electronic load model. This model relates active and reactive
Listing 5. DSL code that calculates Fvl.
power consumption to the voltage magnitude at the load
terminals.
The ElectronicLoadWECC in Listing 3 is a Python
provides an API for embedding Python code in C++ [7]. implementation of the electronic load model. In this class,
Thus, it is possible to create an interface between DSL and the calc_pq method establishes the relationship between
Python in C++. The role of this C++ interface is twofold: the terminal voltage and power consumption, and the __init__
front end provides the API defined in Section II-C, while the method initializes the model parameters and the Vmin
back end is in charge of loading script.py and manipulating variable. This variable is defined as a member variable
the functions and methods in CALLABLE_REGISTRY, at the because its value must be remembered between calls to
request of the API. This C++ interface must be compiled as a the calc_pq method. Below the class definition is the
DLL, given a name that starts with the digexfun prefix (e.g., class instantiation, that creates the eload object with
digexfunPyDSL.dll), and placed in PowerFactory’s installation the desired set of model parameters. Once eload has
folder. This is because PowerFactory loads at start-up all DLLs been created, the eload.calc_pq method is added to
located in its installation folder that have the digexfun prefix CALLABLE_REGISTRY so it can be called from DSL.
in their names [1]. To ensure that up-to-date Python code is Listing 4 shows the DSL code that calls eload.calc_pq.
always executed without having to restart PowerFactory to This code is embedded in the Load Model block inside of the
force the DLL to be reloaded, the library detects when a new PowerFactory Composite Frame from Fig. 1. In the Composite
simulation is initialized and reloads script.py. Model, the Terminal block represents a bus in the grid model
and it provides the V argument to eload.calc_pq. In turn,
IV. E XAMPLE U SE C ASES the Load Model block provides the active and reactive power
returned by eload.calc_pq to the Load block, which
The possible applications of Python-enhanced DSL models represents a load in the grid model.
are potentially endless. To illustrate some of the possibilities To test the Python implementation of the electronic load
we present three simple use cases that we hope can provide model we used the grid from Fig. 2 as developed in [6], but
the reader a starting point from which to tackle more complex removed the motor loads from the WECC Composite Load
problems. Although we omit some details for the sake of Model and replaced the DSL electronic load with our Python
brevity, these examples are available in full in the library implementation. Fig. 3 shows the response of the electronic
repository.1 load to the voltage variations that result from a high-impedance
fault at bus B3. import zmq
Even though the electronic load model was implemented in
DSL in [6], the Python implementation is easier to create and ADDRESS = "tcp://localhost:7000"
understand. This becomes apparent when comparing the DSL
code used for calculating the Fvl variable, shown in Listing
5, to the Python code from Listing 3. While the Python code class CosimInterface:
is a series of if/else statements, the DSL code is several def __init__(self, address):
self.address = address
nested select functions. The advantage of using Python in self.context = None
this particular case becomes even clearer when comparing the self.socket = None
Python implementation of the electronic load model to the
pseudocode used to define it in [8]; translating the pseudocode def _open_connection(self):
into Python is almost trivial. self.context = zmq.Context()
self.socket = self.context.socket(
B. Co-Simulation Interface zmq.REQ)
self.socket.connect(self.address)
In a co-simulation two or more simulators simulate cooper-
atively by exchanging variables at run time. These simulators def _close_connection(self):
may run on the same computer or separate computers. To self.socket.close()
self.context.term()
exemplify how this can be accomplished with our library,
we removed the electronic load model from the previous def exchange_variables(self, *sim_outs):
example, and placed it in the eload.py script from Listing 6, self._open_connection()
that can be executed independently from PowerFactory. The self.socket.send_json(sim_outs)
objective now is to create an interface to couple the grid model sim_ins = self.socket.recv_json()
self._close_connection()
from Fig. 2 to the electronic load model in eload.py. This return sim_ins
means that at every simulation time step PowerFactory must
send the voltage at bus B6 to eload.py, and eload.py must
reply with its active and reactive power consumption. Listing cs_int = CosimInterface(ADDRESS)
7 presents the co-simulation interface that couples Power-
Factory and eload.py. The communication is implemented CALLABLE_REGISTRY = [
with PyZMQ, which provides a Python API to the ØMQ cs_int.exchange_variables
messaging library [9], and JSON-encoded messages. The ]
interface is tasked with sending outputs from PowerFactory
Listing 7. Simple co-simulation interface for PowerFactory.
to eload.py, and receiving inputs from eload.py. Note that
the cs_int.exchange_variables method can take a
variable number of arguments, so it can be used to exchange
as many variables as needed, provided the number is specified Using this grid we created a training dataset by applying
in the argNum argument to the LoadPyFun function. Using faults to every bus, each time varying the fault impedance
PyZMQ much more sophisticated co-simulations settings can between 0 and 40 Ω in steps of 10 Ω, and measuring the
be implemented, such as those in [10]. active and reactive power flowing through every branch. This
produced a dataset where the samples are the results of each
C. Machine Learning for Fault Detection time step and the features are the power flows. To train LDA
DSL on its own is certainly inadequate for simulations we labeled each sample in the dataset as normal (label = −2),
that require machine learning functionality, but Python is a post fault (label = −1) or fault, in which case the label is the
common choice for these applications. In this example we take number of the bus where the fault is happening.
a naive approach to fault detection using Linear Discriminant Listing 8 shows the Python implementation of the random
Analysis (LDA) for classification [11]. The objective is to train load (random_load function) and the LDA-based fault
LDA to detect when and where in the grid a fault happens, detector (FaultDetector class). The fault detector relies
and to use this information to clear the fault. We assume on the LDA implementation the scikit-learn library provides
that faults occur only at buses and that they can have an [3]. We train LDA in the __init__ method and detect faults
impedance between 0 and 40 Ω. Thus, LDA must classify a with the detect method, which takes the active and reactive
set of measurements obtained from the grid as characteristic power measurements from the grid and returns six flags (one
of normal operation or a fault at a specific bus. per bus) that are set when a fault in the corresponding bus
To add more variance to the operating conditions we mod- occurs.
ified the grid from Fig. 2 so all loads are random, except Fig. 4 shows the inputs (branch power flows) and outputs
for the electronic load. The random loads vary their power (flags) of the LDA-based fault detector during a simulation
consumption within ±3% of the nominal value following a where a fault with an impedance of 33 Ω is applied to bus B3
uniform distribution, and are implemented in Python. at 0.1 s and a fault with an impedance of 13 Ω is applied to
import random L2-3 L3-4 T1 T2 RLC
import pandas as pd 0
import numpy as np
import sklearn.discriminant_analysis as skda
P (MW)
−100
Q (Mvar)
.
−50
Flags
self.n_buses = n_buses 0.5
data, labels = load_training_data()
self.lda = LDA()
self.lda = self.lda.fit(data, labels) 0.0
0.0 0.1 0.2 0.3 0.4 0.5
def detect(self, *args): Time (s)
fault_flags = [0.0]*self.n_buses
label = int(self.lda.predict([args])) (b)
if label > 0: Fig. 4. Inputs and outputs of the LDA-based fault detector during faults at
fault_flags[label-1] = 1.0 bus B3 and B5. (a) Input branch power flows. (b) Output fault flags.
return fault_flags
using the same pyFunID to prevent inadvertently overriding
fdetector = FaultDetector(n_buses=6) these arguments.
During a dynamic simulation, PowerFactory might call the
DSL functions, and therefore the Python functions or methods,
CALLABLE_REGISTRY = [
more than once per time step. In the previous examples we
eload.calc_pq, # pyFunID = 0
random_load, # pyFunID = 1 do not deal with this problem, but we advise the reader to
random_load, # pyFunID = 2 consider this behavior while designing a Python function or
random_load, # pyFunID = 3 class for use with PowerFactory.
fdetector.detect, # pyFunID = 4
]
Listing 8. Extended script.py with the implementation of the random load V. C ONCLUSION
and the LDA-based fault detector.
This paper presented a small open source library for enhanc-
ing DSL models with Python, and three example use cases
bus B5 at 0.3 s. Both faults are cleared with a delay of 0.1 s. related to load modeling, co-simulation, and fault detection
As Fig. 4 (b) shows, the flag that corresponds to the bus where based on machine learning. The example use cases show that
the fault occurs is set as soon as the fault starts, and is reset with our library it is simple to embed Python code in DSL
once the fault is cleared. models, and that with the help of popular open source Python
libraries it is possible to easily create sophisticated models that
are beyond the boundaries of traditional power engineering.
D. Caveats
The library is especially well suited for situations where the
Note that in Listing 8 we register the random_load model code is modified often and/or when the main goal is
function three times in CALL_REGISTRY, once for each implementation with minimal effort. As a consequence, Pow-
random load in the test system. This means that we can access erFactory users gain access to enhanced modeling capabilities
it with three different pyFunIDs. We avoid calling the same and user-friendliness, and a more speedy workflow, which is
function or method multiple times with different arguments beneficial for model development and rapid prototyping.
R EFERENCES and analysis of large datasets,” in Advanced Smart Grid Functionalities
Based on PowerFactory, F. M. Gonzalez-Longattand and J. L. Rueda-
[1] M. Stifter, F. Andrén, R. Schwalbe, and W. Tremmel, “Interfacing Pow-
Torres, Eds. Springer, 2018, pp. 19–48.
erFactory: Co-simulation, real-time simulation and controller hardware-
[6] A. Joseph, M. Cvetković, and P. Palensky, “Predictive mitigation of short
in-the-loop applications,” in PowerFactory Applications for Power Sys-
term voltage instability using a faster than real-time digital replica,” in
tem Analysis, F. M. Gonzalez-Longatt and J. L. Rueda, Eds. Springer
Proceedings of the 2018 IEEE PES Innovative Smart Grid Technologies
International Publishing, 2014, pp. 343–366.
Conference Europe (ISGT-Europe), Oct. 2018.
[2] T. E. Oliphant, “Python for scientific computing,” Computing in Science
[7] Python Software Foundation. Python/C API Reference Manual.
& Engineering, vol. 9, no. 3, pp. 10–20, May 2007.
[Online]. Available: docs.python.org/3/c-api/index.html
[3] F. Pedregosa, G. Varoquaux, A. Gramfort, V. Michel, B. Thirion,
[8] Western Electricity Coordinating Council, “WECC dynamic composite
O. Grisel, M. Blondel, P. Prettenhofer, R. Weiss, V. Dubourg, J. Vander-
load model (CMPLDW) specifications,” WECC, Tech. Rep., Jan. 2015.
plas, A. Passos, D. Cournapeau, M. Brucher, M. Perrot, and E. Duch-
[9] iMatix. ØMQ. [Online]. Available: zeromq.org
esnay, “Scikit-learn: Machine learning in Python,” Journal of Machine
[10] C. D. López, M. Cvetković, and P. Palensky, “Distributed co-simulation
Learning Research, vol. 12, pp. 2825–2830, 2011.
for collaborative analysis of power system dynamic behavior,” in Pro-
[4] W. McKinney, “Data structures for statistical computing in Python,” in
ceedings of the MEDPOWER 2018 Conference, Nov. 2018.
Proceedings of the 9th Python in Science Conference, S. van der Walt
[11] J. Friedman, T. Hastie, and R. Tibshirani, The elements of statistical
and J. Millman, Eds., 2010, pp. 51–56.
learning, 2nd ed. Springer, 2001.
[5] C. D. López and J. L. Rueda-Torres, “Python scripting for DIgSILENT
PowerFactory: Leveraging the Python API for scenario manipulation