0% found this document useful (0 votes)
50 views

Lopez 2019 A

This document discusses enhancing PowerFactory dynamic models by calling Python functions from within the DIgSILENT Simulation Language (DSL). The authors present an open-source library that allows PowerFactory users to easily call Python code from DSL models. This overcomes limitations of defining new models in C/C++ and allows leveraging the extensive Python ecosystem. Three example use cases are described that highlight increased modeling capabilities when combining DSL and Python.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
50 views

Lopez 2019 A

This document discusses enhancing PowerFactory dynamic models by calling Python functions from within the DIgSILENT Simulation Language (DSL). The authors present an open-source library that allows PowerFactory users to easily call Python code from DSL models. This overcomes limitations of defining new models in C/C++ and allows leveraging the extensive Python ecosystem. Three example use cases are described that highlight increased modeling capabilities when combining DSL and Python.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

Enhancing PowerFactory Dynamic Models

with Python for Rapid Prototyping


Claudio David López, Miloš Cvetković and Peter Palensky
Department of Electrical Sustainable Energy
Delft University of Technology
The Netherlands

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

pyFunID = 1 ! From CALLABLE_REGISTRY


def square_to_polar(re, im):
"""Converts a phasor from square to polar ! -----------------------------------
coordinates. ! Executed only during initialization
""" ! -----------------------------------
mag = float(np.sqrt(re**2 + im**2)) ! square_to_polar takes 2 arguments
ang = float(np.arctan2(im, re)) inc(dummy0) = LoadPyFun(2, pyFunID)
return mag, ang
! -----------------------------------
! Executed every time step
CALLABLE_REGISTRY = [ ! -----------------------------------
external_function, # pyFunID = 0 ! Set arguments
square_to_polar # pyFunID = 1 dummy1 = SetPyFunArg(re, 0, pyFunID)
] dummy2 = SetPyFunArg(im, 1, pyFunID)

Listing 1. Example of a valid script.py file. ! Call square_to_polar


dummy3 = CallPyFun(pyFunID)

! Get returned values


does not need to be restarted when the Python code is mag = GetPyFunRetVal(0, pyFunID)
modified. All of these characteristics make the library valuable ang = GetPyFunRetVal(1, pyFunID)
for rapid prototyping.
Listing 2. DSL code for calling the Python function square_to_polar.
To illustrate what can be accomplished with our library
we present three example use cases. In the first one we
implement a load model in Python and compare it to its DSL
implementation to highlight how some models are easier to • Each Python function and/or method can return a different
implement in Python, in the second one we present a co- number of values.
simulation interface that shows how the library can be used • Each Python function and/or method takes only floating
to couple PowerFactory with external simulators and models, point arguments.
and in the third one we present a machine learning-based • Each Python function and/or method returns only floating
fault detector that emphasizes how this library can be used to point values.
incorporate models that are beyond classic power engineering. • Modifications to the Python code take effect on the next
Together, these examples show that it becomes straightforward simulation run, without restarting PowerFactory.
to enhance DSL with Python and that sophisticated models
can be produced with reduced effort using popular Python B. Assumptions
libraries. As a consequence, PowerFactory users gain access
The library assumes that the Python functions and methods
to enhanced modeling capabilities and user-friendliness, and
that are to be called from DSL are referenced in a Python
a more speedy workflow, which is beneficial for rapid proto-
list called CALLABLE_REGISTRY, which should be defined
typing.
in a file called script.py located in PowerFactory’s installation
This paper is structured as follows: Section II presents the
folder. Since only references to the functions and methods
library design, Section III describes some key aspects of the
are expected in CALLABLE_REGISTRY, the functions and
library implementation, Section IV introduces the example use
methods themselves may be defined either in script.py or in
cases, and Section V concludes the paper.
an external Python script.
II. L IBRARY D ESIGN Listing 1 shows an example of a valid script.py. This script
defines one function and it imports an external function from
In this section we describe the main aspects of the library and external Python script. Both functions are then referenced
design, namely the requirements it fulfills, the assumptions that in CALLABLE_REGISTRY so they can be called from DSL.
it makes to find and access Python code, and the functions it When called from DSL, these functions are identified by their
provides to the user. pyFunID, which is their index in CALLABLE_REGISTRY.

A. Requirements C. DSL Application Programming Interface


The library fulfills the following requirements: The Application Programming Interface (API) is com-
• Multiple Python functions and/or methods may be called posed of four DSL functions. Together they make it
from a DSL model. possible to call the Python functions and methods in
• Each Python function and/or method can take a different CALLABLE_REGISTRY. These four functions are defined as
number of arguments. follows:
class ElectronicLoadWECC:
def __init__(self, Vd1, Vd2, frcel,
Pel0, Qel0, Vmin0=1.0):
self.Vd1 = Vd1
self.Vd2 = Vd2
self.frcel = frcel
self.Pel0 = Pel0 Fig. 1. PowerFactory Composite Frame of the WECC electronic load model,
self.Qel0 = Qel0 where V is the voltage magnitude measured at the load terminals, and Pext
self.Vmin = Vmin0 and Qext are the active and reactive power consumption.

def calc_pq(self, V):


if V < self.Vmin: B1 B2 B3 B4 B5 B6
T1 T2 RLC
self.Vmin = V L2-3 L3-4 LS
G1 LE
if self.Vmin < self.Vd2:
self.Vmin = self.Vd2
L1 L2 SF1 SF2
if V < self.Vd2:
Fvl = 0.0 Fig. 2. Test grid from [6]. The WECC Composite load model is enclosed
elif V < self.Vd1: in the dashed rectangle. The motor loads were removed. The electronic load
if V <= self.Vmin: is marked as LE. The loads L1, L2 and LS, and the filters SF1 and SF2 are
Fvl = (V - self.Vd2)/(self.Vd1 static.
- self.Vd2)
else:
Fvl = (self.Vmin - self.Vd2 LoadPyFun(argNum, pyFunID)
+ self.frcel*(V Loads a Python function into PowerFactoy, where
- self.Vmin))/(self.Vd1 • argNum is the number of expected arguments, and
- self.Vd2)
• pyFunID specifies which function.
else:
if self.Vmin >= self.Vd1: SetPyFunArg(argVal, argID, pyFunID)
Fvl = 1.0
else: Sets the value of one of the arguments of a Python function,
Fvl = (self.Vmin - self.Vd2 where
+ self.frcel*(self.Vd1 • argVal is the value of the argument,
- self.Vmin))/(self.Vd1 • argID specifies which argument, and
- self.Vd2)
• pyFunID specifies which function.

return Fvl*self.Pel0, Fvl*self.Qel0 CallPyFun(pyFunID)


Calls the Python function specified by pyFunID.
eload = ElectronicLoadWECC(0.866, 0.7, 0.7, GetPyFunRetVal(retValID, pyFunID)
20, 10) Gets one of the values returned by a Python function, where
• retValID specifies which argument, and
CALLABLE_REGISTRY = [ • pyFunID specifies which function.
eload.calc_pq # pyFunID = 0
] The reason why we require three different DSL functions
each time we call a Python function or method is that DSL
Listing 3. Python implementetion of the WECC electronic load model.
functions cannot take a variable number of arguments and
return only one value. Since the library needs to accommodate
! Block input: V Python functions and methods that take different numbers of
! Block outputs: Pel, Qel arguments and return different numbers of values, setting argu-
ments and retrieving returned values one by one circumvents
pyFunID = 0 ! From CALLABLE_REGISTRY this limitation.
Listing 2 shows how this API can be used to call the
! Load eload.calc_pq
inc(dummy0) = LoadPyFun(1, pyFunID) function square_to_polar defined in Listing 1. This DSL
listing must be placed inside a PowerFactory Block Definition
! Call eload.calc_pq whose inputs are re and im, and whose outputs are mag and
dummy1 = SetPyFunArg(V, 0, pyFunID) ang. Note that the only API function whose returned value
dummy2 = CallPyFun(pyFunID) matters is GetPyFunRetVal.
P = GetPyFunRetVal(0, pyFunID)
Q = GetPyFunRetVal(1, pyFunID) III. L IBRARY I MPLEMENTATION
Listing 4. DSL code that calls eload.calc_pq. To implement the library we relied on the facts that new
DSL functions can be defined in C++ [1] and that Python
import zmq

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)

P (MW) Q (Mvar) context = zmq.Context()


socket = context.socket(zmq.REP)
20
socket.bind("tcp://*:7000")
Load

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

LDA = skda.LinearDiscriminantAnalysis −200

0.0 0.1 0.2 0.3 0.4 0.5


class ElectronicLoadWECC: Time (s)
0
.
.

Q (Mvar)
.
−50

def random_load(Pnom, Qnom, max_dev): −100


p_dev = random.uniform(-max_dev, max_dev)
q_dev = random.uniform(-max_dev, max_dev) 0.0 0.1 0.2 0.3 0.4 0.5
P = (1 + p_dev)*Pnom Time (s)
Q = (1 + q_dev)*Qnom (a)
return P, Q
B1 B2 B3 B4 B5 B6
1.0
class FaultDetector:
def __init__(self, n_buses):

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

You might also like