A Python-based tool for symbolic power system modeling and numerical analysis.
ANDES is by far easier to use for modeling power system devices than other simulation tools such as PSAT, Dome and PST, while maintaining high numerical efficiency.
ANDES produces accurate simulation results. For the Kundur's two-area system with GENROU, TGOV1 and EXDC2, ANDES produces almost identical (<1% discrepancy) results to that from DSATools TSATβ’.
Generator Speed | Excitation Voltage |
---|---|
ANDES provides a descriptive modeling framework in a scripting environment. Modeling DAE-based devices is as simple as describing the mathematical equations.
Controller Model and Equation | ANDES Code |
---|---|
Diagram: DAE: |
In ANDES, what you simulate is what you document. ANDES automatically generates model documentation, and the docs always stay up to date. The screenshot below is the generated documentation for the implemented TGOV1 model.
In addition, ANDES features
- Power flow, trapezoidal method-based time domain simulation, and full eigenvalue analysis.
- Support PSS/E raw and dyr inputs among other formats.
- Symbolic DAE modeling and automated code generation for numerical simulation.
- Numerical DAE modeling for cases when symbolic implementations are difficult.
- Modeling library with common transfer functions and discontinuous blocks.
- Automatic sequential and iterative initialization (experimental) for dynamic models.
- Full equation documentation of supported DAE models.
ANDES is currently under active development. Visit the documentation to learn more about ANDES. Technical details can be referred to our paper on arXiv.
- Get Started with ANDES
- Run Simulations
- Configure ANDES
- Format Converter
- Model Development
- API Reference
- Get Involved
- Who is Using ANDES?
ANDES is a Python package and needs to be installed. We recommend Miniconda if you don't insist on an existing Python environment. Downloaded and install the latest 64-bit Miniconda3 for your platform from https://conda.io/miniconda.html.
Step 1: (Optional) Open the Anaconda Prompt (shell on Linux and macOS) and create a new environment.
Use the following command in the Anaconda Prompt:
conda create --name andes python=3.7
Step 2: Add the conda-forge
channel and set it to default. Do
conda config --add channels conda-forge
conda config --set channel_priority flexible
Step 3: Activate the new environment
This step needs to be executed every time a new Anaconda Prompt or shell is open. At the prompt, do
conda activate andes
Step 4: Download and Install ANDES
-
Download the latest ANDES source code from https://github.com/cuihantao/andes/releases.
-
Extract the package to a folder where source code resides. Try to avoid spaces in any folder name.
-
Change directory to the ANDES root directory, which contains
setup.py
. In the prompt, run
conda install --file requirements.txt --yes
conda install --file requirements-dev.txt --yes
pip install -e .
Observe if any error is thrown. If not, ANDES is successfully installed in the development mode.
Step 5: Test ANDES
After the installation, run andes selftest
and check if all tests pass.
ANDES can be used as a command-line tool or a library. The following explains the command-line usage, which comes handy to run studies.
For a tutorial to use ANDES as a library, visit the interactive tutorial.
ANDES is invoked from the command line using the command andes
.
Running andes
without any input is equal to andes -h
or andes --help
,
which prints out a preamble and help commands:
ANDES 0.6.8 (Git commit id 0ace2bc0, Python 3.7.6 on Darwin)
Session: hcui7, 02/09/2020 08:34:35 PM
usage: andes [-h] [-v {10,20,30,40,50}] {run,plot,misc,prepare,selftest} ...
positional arguments:
{run,plot,misc,prepare,selftest}
[run]: run simulation routine; [plot]: plot simulation
results; [prepare]: run the symbolic-to-numeric
preparation; [misc]: miscellaneous functions.
optional arguments:
-h, --help show this help message and exit
-v {10,20,30,40,50}, --verbose {10,20,30,40,50}
Program logging level. Available levels are 10-DEBUG,
20-INFO, 30-WARNING, 40-ERROR or 50-CRITICAL. The
default level is 20-INFO.
The first level of commands are chosen from {run,plot,misc,prepare,selftest}
.
Each command contains a group of subcommands, which can be looked up by appending -h
to the first-level command.
For example, use andes run -h
to look up the subcommands in run
.
andes
has an option for the program verbosity level, controlled by -v
or --verbose
.
Accpeted levels are the same as in the logging
module:
10 - DEBUG, 20 - INFO, 30 - WARNING, 40 - ERROR, 50 - CRITICAL.
To show debugging outputs, use -v 10
.
Pass the path to the case file to andes run
to perform power flow calculation.
It is recommended to change directory to the folder containing the test case before running.
The Kundur's two-area system can be located under cases/kundur
with the namekundur_full.xlsx
.
To run power flow calculation, do
andes run kundur_full.xlsx
Power flow reports will be saved to the directory where andes is called.
The power flow report, named kundur_full_out.txt
, contains four sections:
- system statistics,
- ac bus and dc node data,
- ac line data,
- the initialized values of algebraic variables and state variables.
ANDES comes with two dynamic analysis routines: time-domain simulation and eigenvalue analysis.
Option -r
or -routine
is used to specify the routine,
followed by the routine name.
Available routine names include pflow, tds, eig
.
pflow
is the default power flow calculation and can be omitted.tds
is for time domain simulation.eig
is for for eigenvalue analysis.
To run time-domain simulation for kundur_full.xlsx
in the current directory, do
andes run kundur_full.xlsx -r tds
Two output files, kundur_full_out.lst
and kundur_full_out.npy
will be created for variable names
and values, respectively.
Likewise, to run eigenvalue analysis for kundur_full.xlsx
, use
andes run kundur_full.xlsx -r eig
The eigenvalue report will be written in a text file named kundur_full_eig.txt
.
andes plot
is the command-line tool for plotting.
Currently, it only supports time-domain simulation data.
Three arguments are needed: file name, x-axis variable index, and y-axis variable index (or indices).
Variable indices can be looked up by opening the kundur_full_out.lst
file as plain text.
Index 0 is always the simulation time.
Multiple y-axis variable indices can be provided in eithers space-separated format or the Pythonic comma-separated style.
To plot speed (omega) for all generators with indices 2, 8, 14, 20, either do
andes plot kundur_full_out.npy 0 2 8 14 20
or
andes plot kundur_full_out.npy 0 2:21:6
ANDES uses a config file to set runtime configs for system, routines and models. The config file is loaded at the time when ANDES is invoked or imported.
At the command-line prompt,
andes misc --save
saves all configs to a file. By default, it goes to~/.andes/andes.conf
.andes misc --edit
is a shortcut for editing the config file. It takes an optional editor name.
Without an editor name, the following default editor is used:
- On Microsoft Windows, it will open up a notepad.
- On Linux, it will use the
$EDITOR
environment variable or usegedit
by default. - On macOS, the default is vim.
ANDES recognizes a few input formats (MATPOWER, PSS/E and ANDES xlsx) and can convert input to the xlsx
format.
This function is useful when one wants to use models that are unique in ANDES.
andes run <CaseName> --convert
performs the conversion toxlsx
.andes run <CaseName> --convertall
performs the conversion and create empty sheets for all supported models.
The output converter is used to convert .npy
output to a comma-separated (csv) file.
To convert, do andes plot <OutputName.npy> -c
The steps to develop new models are outlined.
New models will need to be written in Python and incorporated in the ANDES source code.
Models are placed under andes/models
with a descriptive file name for the model type.
If a new file is created, import the building block classes at the top of the file
from andes.core.model import ModelData, Model
from andes.core.param import IdxParam, NumParam, ExtParam
from andes.core.var import Algeb, State, ExtAlgeb, ExtState
from andes.core.service import ConstService, ExtService
from andes.core.discrete import AntiWindupLimiter
The TGOV1 model will be used to illustrate the model development process.
Create a class to hold parameters that will be loaded from the data file.
The class inherits from ModelData
class TGOV1Data(ModelData):
def __init__(self):
self.syn = IdxParam(model='SynGen',
info='Synchronous generator idx',
mandatory=True,
)
self.R = NumParam(info='Speed regulation gain under machine base',
tex_name='R',
default=0.05,
unit='p.u.',
ipower=True,
)
self.wref0 = NumParam(info='Base speed reference',
tex_name=r'\omega_{ref0}',
default=1.0,
unit='p.u.',
)
self.VMAX = NumParam(info='Maximum valve position',
tex_name='V_{max}',
unit='p.u.',
default=1.2,
power=True,
)
self.VMIN = NumParam(info='Minimum valve position',
tex_name='V_{min}',
unit='p.u.',
default=0.0,
power=True,
)
self.T1 = NumParam(info='Valve time constant',
default=0.1,
tex_name='T_1')
self.T2 = NumParam(info='Lead-lag lead time constant',
default=0.2,
tex_name='T_2')
self.T3 = NumParam(info='Lead-lag lag time constant',
default=10.0,
tex_name='T_3')
self.Dt = NumParam(info='Turbine damping coefficient',
default=0.0,
tex_name='D_t',
power=True,
)
Note that the example above has all the parameters loaded in one class.
In practice, it is recommended to create a base class for common parameters and let TGOV2Data
inherit from it.
See the code in andes/models/governor.py
for the example.
Next, another class to hold the non-parameter instances is created.
The class inherits from Model
and takes three positional arguments by the constructor.
The code below defines parameters, variables and services retrieved from external models (specifically , generators).
class TGOV1Model(Model):
def __init__(self, system, config):
self.Sn = ExtParam(src='Sn',
model='SynGen',
indexer=self.syn,
tex_name='S_m',
info='Rated power from generator',
unit='MVA',
export=False,
)
self.Vn = ExtParam(src='Vn',
model='SynGen',
indexer=self.syn,
tex_name='V_m',
info='Rated voltage from generator',
unit='kV',
export=False,
)
self.tm0 = ExtService(src='tm',
model='SynGen',
indexer=self.syn,
tex_name=r'\tau_{m0}',
info='Initial mechanical input')
self.omega = ExtState(src='omega',
model='SynGen',
indexer=self.syn,
tex_name=r'\omega',
info='Generator speed',
unit='p.u.'
)
In addition, a service can be defined for the inverse of the gain
self.gain = ConstService(v_str='u / R',
tex_name='G',
)
First of all, the turbine governor output modifies the generator power input. Therefore, the generator input variable should be retrieved by the governor. Next, internal variables can be defined.
# mechanical torque input of generators
self.tm = ExtAlgeb(src='tm',
model='SynGen',
indexer=self.syn,
tex_name=r'\tau_m',
info='Mechanical power to generator',
)
self.pout = Algeb(info='Turbine final output power',
tex_name='P_{out}',
)
self.wref = Algeb(info='Speed reference variable',
tex_name=r'\omega_{ref}',
)
self.pref = Algeb(info='Reference power input',
tex_name='P_{ref}',
)
self.wd = Algeb(info='Generator under speed',
unit='p.u.',
tex_name=r'\omega_{dev}',
)
self.pd = Algeb(info='Pref plus under speed times gain',
unit='p.u.',
tex_name="P_d",
)
self.LAG_x = State(info='State in lag transfer function',
tex_name=r"x'_{LAG}",
)
self.LAG_lim = AntiWindupLimiter(u=self.LAG_x,
lower=self.VMIN,
upper=self.VMAX,
tex_name='lim_{lag}',
)
self.LL_x = State(info='State in lead-lag transfer function',
tex_name="x'_{LL}",
)
self.LL_y = Algeb(info='Lead-lag Output',
tex_name='y_{LL}',
)
Set up the equation associated with each variable.
Algebraic equations are in the form of g(x, y) = 0
.
Differential equations are in the form of f(x, y) = \dot{x}
.
self.tm.e_str = 'u*(pout - tm0)'
self.wref.e_str = 'wref0 - wref'
self.pref.e_str = 'tm0 * R - pref'
self.wd.e_str = '(wref - omega) - wd'
self.pd.e_str='(wd + pref) * gain - pd'
self.LAG_x.e_str = 'LAG_lim_zi * (1 * pd - LAG_x) / T1'
self.LL_x.e_str = '(LAG_x - LL_x) / T3'
self.LL_y.e_str='T2 / T3 * (LAG_x - LL_x) + LL_x - LL_y'
self.pout.e_str = '(LL_y + Dt * wd) - pout'
Initializers are used to set up initial values for variables.
Initializers are evaluated in the same sequence as the declaration of variables.
Initializer evaluation results are set to the corresponding variable.
Usually, only internal variables (Algeb
and State
) require initializers.
self.wref.v_str = 'wref0'
self.pout.v_str = 'tm0'
self.LL_y.v_str = 'LAG_x'
self.LL_x.v_str = 'LAG_x'
self.LAG_x.v_str = 'pd'
self.pd.v_str = 'tm0'
self.wd.v_str = '0'
self.pref.v_str = 'tm0 * R'
Alternatively, equations and initializers can be passed to keyword arguments e_str
and v_str
, respectively
, of the corresponding instance.
This step provides additional information on the model. The group to which the device belongs need to be specified, and the routine this model supports need to updated.
For example, TGOV1 belongs to the TurbineGov
group, which is defined in andes/models/group.py
.
TGOV1 participates in the time-domain simulation and is not involved in power flow.
The snipet below is added to the constructor of class TGOV1Model
.
self.group = 'TurbineGov'
self.flags.update({'tds': True})
Next, a TGOV1
class need to be created as the final class. It is a bit boilerplate as of the current
implementation.
class TGOV1(TGOV1Data, TGOV1Model):
def __init__(self, system, config):
TGOV1Data.__init__(self)
TGOV1Model.__init__(self, system, config)
One more step, the class needs to be added to the package __init__.py
file to be loaded.
Edit andes/models/__init__.py
and add to non_jit
whose keys are the file names and values are the classes in
the file.
To add TGOV1
, locate the line with key governor
and add TGOV1
to the value list so that it looks like
non_jit = OrderedDict([
# ...
('governor', ['TG2', 'TGOV1']),
# ...
])
Finally, run andes prepare
from the command-line to re-generate code for the new model.
The official documentation explains the complete list of modeling components. The most commonly used ones are highlighted in the following.
- Learn more about ANDES by reading the documentation
- Report bugs or issues by submitting a GitHub issue
- Submit contributions using pull requests
- Read release notes highlighted here
- Check out and and cite our paper
Please let us know if you are using ANDES for research or projects. We kindly request you to cite our paper if you find ANDES useful.
This work was supported in part by the Engineering Research Center Program of the National Science Foundation and the Department of Energy under NSF Award Number EEC-1041877 and the CURENT Industry Partnership Program.
This project was originally inspired by the book Power System Modelling and Scripting by Prof. Federico Milano.
ANDES is licensed under the GPL v3 License.