Skip to content

Commit

Permalink
Merge branch 'develop' into DPLine
Browse files Browse the repository at this point in the history
  • Loading branch information
cuihantao committed Mar 31, 2024
2 parents d409483 + 7cc8daf commit 000825b
Show file tree
Hide file tree
Showing 22 changed files with 283 additions and 41 deletions.
8 changes: 4 additions & 4 deletions CONTRIBUTING.rst
Expand Up @@ -13,7 +13,7 @@ Types of Contributions
Report Bugs
~~~~~~~~~~~

Report bugs at https://github.com/cuihantao/andes/issues.
Report bugs at https://github.com/curent/andes/issues.

If you are reporting a bug, please include:

Expand Down Expand Up @@ -42,7 +42,7 @@ or even on the web in blog posts, articles, and such.
Submit Feedback
~~~~~~~~~~~~~~~

The best way to send feedback is to file an issue at https://github.com/cuihantao/andes/issues.
The best way to send feedback is to file an issue at https://github.com/curent/andes/issues.

If you are proposing a feature:

Expand Down Expand Up @@ -97,8 +97,8 @@ Before you submit a pull request, check that it meets these guidelines:
2. If the pull request adds functionality, the docs should be updated. Put
your new functionality into a function with a docstring, and add the
feature to the list in README.rst.
3. The pull request should work for Python 3.6 and up. Check
https://github.com/cuihantao/andes/actions
3. The pull request should work for Python 3.8 and up. Check
https://github.com/curent/andes/actions
and make sure that the tests pass for all supported Python versions.

============
Expand Down
11 changes: 9 additions & 2 deletions andes/core/symprocessor.py
Expand Up @@ -118,19 +118,26 @@ def generate_symbols(self):
self.tex_names[name] = sp.Symbol(tex_name)
# -----------------------------------------------------------

# `all_params_names` include parameters, services, exports from blocks, etc.
for var in self.cache.all_params_names:
is_real = True

if var in self.parent.services:
if self.parent.services[var].vtype == complex:
is_real = False

self.inputs_dict[var] = sp.Symbol(var)

for var in self.cache.all_vars_names:
tmp = sp.Symbol(var)
tmp = sp.Symbol(var, real=True) # all DAE variables are real
self.vars_dict[var] = tmp
self.inputs_dict[var] = tmp
if var in self.cache.vars_int:
self.vars_int_dict[var] = tmp

# store tex names defined in `self.config`
for key in self.config.as_dict():
tmp = sp.Symbol(key)
tmp = sp.Symbol(key, real=True) # not expecting complex numbers in config
self.inputs_dict[key] = tmp
if key in self.config.tex_names:
self.tex_names[tmp] = sp.Symbol(self.config.tex_names[key])
Expand Down
10 changes: 5 additions & 5 deletions andes/io/matpower.py
Expand Up @@ -272,20 +272,20 @@ def mpc2system(mpc: dict, system) -> bool:
if (data[8] == 0.0) or (data[8] == 1.0 and data[9] == 0.0):
# not a transformer
tf = False
ratio = 1
angle = 0
tap_raio = 1
phase_shift = 0
else:
tf = True
ratio = data[8]
angle = data[9] * deg2rad
tap_raio = data[8]
phase_shift = data[9] * deg2rad

vf = system.Bus.Vn.v[system.Bus.idx2uid(fbus)]
vt = system.Bus.Vn.v[system.Bus.idx2uid(tbus)]
system.add('Line', u=status, name=f'Line {fbus:.0f}-{tbus:.0f}',
Vn1=vf, Vn2=vt,
bus1=fbus, bus2=tbus,
r=r, x=x, b=b,
trans=tf, tap=ratio, phi=angle,
trans=tf, tap=tap_raio, phi=phase_shift,
rate_a=rate_a, rate_b=rate_b, rate_c=rate_c)

if ('bus_name' in mpc) and (len(mpc['bus_name']) == len(system.Bus.name.v)):
Expand Down
16 changes: 14 additions & 2 deletions andes/io/psse.py
Expand Up @@ -402,8 +402,16 @@ def _parse_fshunt_v33(raw, system):


def _parse_gen_v33(raw, system, sw):
# 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11, 12, 13, 14, 15, 16,17,18,19
# I,ID,PG,QG,QT,QB,VS,IREG,MBASE,ZR,ZX,RT,XT,GTAP,STAT,RMPCT,PT,PB,O1,F1
"""
Helper function for parsing static generator section.
"""

# 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
# I, ID, PG, QG, QT, QB, VS, IREG, MBASE, ZR, ZX, RT, XT, GTAP, STAT,
# 15, 16, 17, 18, 19, ..., 26, 27
# RMPCT, PT, PB, O1, F1, ..., O4, F4, WMOD, WPF
# The columns above for v33 is different from the manual of v34.5, which includes two new columns:
# `NREG`` at 8 and `BSLOD` before `O1`

mva = system.config.mva
out = defaultdict(list)
Expand All @@ -417,6 +425,7 @@ def _parse_gen_v33(raw, system, sw):
gen_mva = data[8]
gen_idx += 1
status = data[14]
wmod = data[26] if len(data) >= 26 else 0

param = {'Sn': gen_mva, 'Vn': vn, 'u': status,
'bus': bus, 'subidx': subidx,
Expand All @@ -428,6 +437,7 @@ def _parse_gen_v33(raw, system, sw):
'v0': data[6],
'ra': data[9], # ra - armature resistance
'xs': data[10], # xs - synchronous reactance
'wmod': wmod, # generator control mode
}

if data[0] in sw.keys():
Expand All @@ -443,12 +453,14 @@ def _parse_gen_v33(raw, system, sw):

def _parse_line_v33(raw, system):
#
# 0,1, 2,3,4,5, 6, 7, 8, 9,10,11,12,13, 14,15,16
# I,J,CKT,R,X,B,RATEA,RATEB,RATEC,GI,BI,GJ,BJ,ST,LEN,O1,F1,...,O4,F4
#

out = defaultdict(list)
for data in raw['branch']:
param = {
'u': data[13],
'bus1': data[0], 'bus2': data[1],
'r': data[3], 'x': data[4], 'b': data[5],
'rate_a': data[6], 'rate_b': data[7], 'rate_c': data[8],
Expand Down
167 changes: 167 additions & 0 deletions andes/models/line/line.py
Expand Up @@ -3,8 +3,10 @@
"""

import numpy as np

from andes.core import (ModelData, IdxParam, NumParam, DataParam,
Model, ExtAlgeb, ConstService, Algeb, ExtParam, State)
from andes.shared import spmatrix


class LineData(ModelData):
Expand Down Expand Up @@ -365,3 +367,168 @@ def get_tf_idx(self):
"""

return np.array(self.idx.v)[self.istf]

def build_y(self):
"""
Build bus admittance matrix. Store the matrix in ``self.Y``.
Returns
-------
Y : spmatrix
Bus admittance matrix.
"""

nb = self.system.Bus.n

y1 = self.u.v * (self.g1.v + self.b1.v * 1j)
y2 = self.u.v * (self.g2.v + self.b2.v * 1j)
y12 = self.u.v / (self.r.v + self.x.v * 1j)
m = self.tap.v * np.exp(1j * self.phi.v)
m2 = self.tap.v**2
mconj = np.conj(m)

# build self and mutual admittances into Y
self.Y = spmatrix((y12 + y1 / m2), self.a1.a, self.a1.a, (nb, nb), 'z')
self.Y -= spmatrix(y12 / mconj, self.a1.a, self.a2.a, (nb, nb), 'z')
self.Y -= spmatrix(y12 / m, self.a2.a, self.a1.a, (nb, nb), 'z')
self.Y += spmatrix(y12 + y2, self.a2.a, self.a2.a, (nb, nb), 'z')

return self.Y

def build_b(self, method='fdpf'):
"""
build Bp and Bpp matrices for fast decoupled power flow and DC power flow.
Results are saved to ``self.Bp`` and ``self.Bpp`` without return.
"""
self.build_Bp(method)
self.build_Bpp(method)

def build_Bp(self, method='fdpf'):
"""
Function for building B' matrix.
The result is saved to ``self.Bp`` and returned
Parameters
----------
method : str
Method for building B' matrix. Choose from 'fdpf', 'fdbx', 'dcpf'.
Returns
-------
Bp : spmatrix
B' matrix.
"""
nb = self.system.Bus.n

if method not in ("fdpf", "fdbx", "dcpf"):
raise ValueError(f"Invalid method {method}; choose from 'fdpf', 'fdbx', 'dcpf'")

# Build B prime matrix -- FDPF
# `y1`` neglects line charging shunt, and g1 is usually 0 in HV lines
# `y2`` neglects line charging shunt, and g2 is usually 0 in HV lines
y1 = self.u.v * self.g1.v
y2 = self.u.v * self.g2.v

# `m` neglected tap ratio
m = np.exp(self.phi.v * 1j)
mconj = np.conj(m)
m2 = np.ones(self.n)

if method in ('fdxb', 'dcpf'):
# neglect line resistance in Bp in XB method
y12 = self.u.v / (self.x.v * 1j)
else:
y12 = self.u.v / (self.r.v + self.x.v * 1j)

self.Bdc = spmatrix((y12 + y1) / m2, self.a1.a, self.a1.a, (nb, nb), 'z')
self.Bdc -= spmatrix(y12 / mconj, self.a1.a, self.a2.a, (nb, nb), 'z')
self.Bdc -= spmatrix(y12 / m, self.a2.a, self.a1.a, (nb, nb), 'z')
self.Bdc += spmatrix(y12 + y2, self.a2.a, self.a2.a, (nb, nb), 'z')
self.Bdc = self.Bdc.imag()

for item in range(nb):
if abs(self.Bdc[item, item]) == 0:
self.Bdc[item, item] = 1e-6 + 0j

return self.Bdc

def build_Bpp(self, method='fdpf'):
"""
Function for building B'' matrix.
The result is saved to ``self.Bpp`` and returned
Parameters
----------
method : str
Method for building B'' matrix. Choose from 'fdpf', 'fdbx', 'dcpf'.
Returns
-------
Bpp : spmatrix
B'' matrix.
"""

nb = self.system.Bus.n

if method not in ("fdpf", "fdbx", "dcpf"):
raise ValueError(f"Invalid method {method}; choose from 'fdpf', 'fdbx', 'dcpf'")

# Build B double prime matrix
# y1 neglected line charging shunt, and g1 is usually 0 in HV lines
# y2 neglected line charging shunt, and g2 is usually 0 in HV lines
# m neglected phase shifter
y1 = self.u.v * (self.g1.v + self.b1.v * 1j)
y2 = self.u.v * (self.g2.v + self.b2.v * 1j)

m = self.tap.v
m2 = abs(m)**2

if method in ('fdbx', 'fdpf', 'dcpf'):
# neglect line resistance in Bpp in BX method
y12 = self.u.v / (self.x.v * 1j)
else:
y12 = self.u.v / (self.r.v + self.x.v * 1j)

self.Bpp = spmatrix((y12 + y1) / m2, self.a1.a, self.a1.a, (nb, nb), 'z')
self.Bpp -= spmatrix(y12 / np.conj(m), self.a1.a, self.a2.a, (nb, nb), 'z')
self.Bpp -= spmatrix(y12 / m, self.a2.a, self.a1.a, (nb, nb), 'z')
self.Bpp += spmatrix(y12 + y2, self.a2.a, self.a2.a, (nb, nb), 'z')
self.Bpp = self.Bpp.imag()

for item in range(nb):
if abs(self.Bpp[item, item]) == 0:
self.Bpp[item, item] = 1e-6 + 0j

return self.Bpp

def build_Bdc(self):
"""
The MATPOWER-flavor Bdc matrix for DC power flow. Saves results to `self.Bdc`.
The method neglects line charging and line resistance. It retains tap ratio.
Returns
-------
Bdc : spmatrix
Bdc matrix.
"""

nb = self.system.Bus.n

y12 = self.u.v / (self.x.v * 1j)
y12 = y12 / self.tap.v

self.Bdc = spmatrix(y12, self.a1.a, self.a1.a, (nb, nb), 'z')
self.Bdc -= spmatrix(y12, self.a1.a, self.a2.a, (nb, nb), 'z')
self.Bdc -= spmatrix(y12, self.a2.a, self.a1.a, (nb, nb), 'z')
self.Bdc += spmatrix(y12, self.a2.a, self.a2.a, (nb, nb), 'z')
self.Bdc = self.Bdc.imag()

for item in range(nb):
if abs(self.Bdc[item, item]) == 0:
self.Bdc[item, item] = 1e-6

return self.Bdc
13 changes: 13 additions & 0 deletions andes/models/static/pv.py
Expand Up @@ -36,6 +36,19 @@ def __init__(self):
self.ra = NumParam(default=0.0, info='armature resistance', tex_name='r_a')
self.xs = NumParam(default=0.3, info='armature reactance', tex_name='x_s')

# `wmod`: generator model included in PSS/E file; not yet used in ANDES
# 0: conventional
# 1: renewable standard QT/ QB limits
# 2: renewable +, - Qlimits based on WPF
# 3: renewable, fixed Qlimit based on WPF
# 4: Infeed machine
self.wmod = NumParam(default=0,
info='Machine ctrl. mode [PSS/E]',
tex_name=r'w_{mod}',
unit='int',
export=False,
)


class PVModel(Model):
"""
Expand Down
10 changes: 6 additions & 4 deletions andes/routines/eig.py
Expand Up @@ -103,16 +103,18 @@ def _reduce(self, fx, fy, gx, gy, Tf, dense=True):
spmatrix
The reduced state matrix
"""
gyx = matrix(gx)
self.solver.linsolve(gy, gyx)
self.gyx = matrix(gx)
self.solver.linsolve(gy, self.gyx)

Tfnz = Tf + np.ones_like(Tf) * np.equal(Tf, 0.0)
iTf = spdiag((1 / Tfnz).tolist())

self.fxy = (fx - fy * self.gyx)

if dense:
return iTf * (fx - fy * gyx)
return iTf * self.fxy
else:
return sparse(iTf * (fx - fy * gyx))
return sparse(iTf * self.fxy)

def _reorder(self):
"""
Expand Down
4 changes: 2 additions & 2 deletions andes/system.py
@@ -1,8 +1,8 @@
"""
System class for power system data and methods
System class for power system data and methods.
"""

# [ANDES] (C)2015-2022 Hantao Cui
# [ANDES] (C)2015-2024 Hantao Cui
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
Expand Down
2 changes: 1 addition & 1 deletion andes/variables/dae.py
Expand Up @@ -282,7 +282,7 @@ class DAE:
+===========+=============================================+
| m | The number of algebraic variables/equations |
+-----------+---------------------------------------------+
| n | The number of algebraic variables/equations |
| n | The number of state variables/equations |
+-----------+---------------------------------------------+
| o | The number of limiter state flags |
+-----------+---------------------------------------------+
Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Expand Up @@ -125,7 +125,7 @@

html_context = {
"github_url": "https://github.com",
"github_user": "cuihantao",
"github_user": "curent",
"github_repo": "andes",
"github_version": "master",
"doc_path": "docs/source",
Expand Down
4 changes: 2 additions & 2 deletions docs/source/examples/index.rst
Expand Up @@ -6,9 +6,9 @@ Examples
A collection of examples are presented to supplement the tutorial. The
examples below are identical to the Jupyter Notebook in the ``examples``
folder of the repository
`here <https://github.com/cuihantao/andes/tree/master/examples>`__. You
`here <https://github.com/curent/andes/tree/master/examples>`__. You
can run the examples in a live Jupyter Notebook online using
`Binder <https://mybinder.org/v2/gh/cuihantao/andes/master>`__.
`Binder <https://mybinder.org/v2/gh/curent/andes/master>`__.

.. toctree::
:maxdepth: 2
Expand Down

0 comments on commit 000825b

Please sign in to comment.