Skip to content

Commit

Permalink
Merge pull request #500 from CURENT/develop
Browse files Browse the repository at this point in the history
Prepare for new release
  • Loading branch information
cuihantao committed Feb 2, 2024
2 parents 6203731 + 2578d0e commit 00e2675
Show file tree
Hide file tree
Showing 33 changed files with 4,544 additions and 4,229 deletions.
4 changes: 2 additions & 2 deletions .azure-pipelines/azure-pipelines-linux.yml
Expand Up @@ -10,8 +10,8 @@ jobs:
timeoutInMinutes: 360
strategy:
matrix:
linux_python3.9:
python.version: '3.9'
linux_python3.11:
python.version: '3.11'

steps:
- task: UsePythonVersion@0
Expand Down
22 changes: 13 additions & 9 deletions .github/ISSUE_TEMPLATE/bug_report.md
Expand Up @@ -8,28 +8,32 @@ assignees: cuihantao
---

**Describe the bug**

A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Run '....'
3. Scroll down to '....'
4. See error

Update data files and code for reproducing the error.

If you need to insert code inline, use a pair of triple backticks to format:

```python
Code goes here
```

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.
A clear and concise description of what you expected to happen.

**Desktop (please complete the following information):**

- OS: [e.g. Windows]
- ANDES version (please paste the preamble from `andes`)
- ANDES version (please paste the output from `andes misc --version`)


**pip packages (please paste the output from `pip list`)


**Additional context**

Add any other context about the problem here.
44 changes: 25 additions & 19 deletions .github/workflows/pythonapp.yml
Expand Up @@ -4,34 +4,40 @@ on: [push, pull_request]

jobs:
build:

name: ANDES Tests
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
- name: Set up Python 3.9
uses: actions/setup-python@v1
- uses: actions/checkout@v4
- uses: conda-incubator/setup-miniconda@v3
with:
python-version: 3.9
- name: Install dependencies
python-version: 3.11
mamba-version: "*"
miniforge-version: "latest"
channels: conda-forge,defaults
channel-priority: true
activate-environment: anaconda-client-env
- shell: bash -el {0}
name: Install dependencies
run: |
# work around a pip issue with extras_require: https://github.com/pypa/pip/issues/8323
python -m pip install -U "pip @ git+https://github.com/pypa/pip.git"
python -m pip install --upgrade pip
python -m pip install .[all]
python -m pip install nbmake==0.10 pytest-xdist line_profiler # add'l packages for notebook tests.
- name: Lint with flake8 for pull requests
mamba install -y nbmake pytest-xdist line_profiler # add'l packages for notebook tests.
mamba install --file requirements.txt --file requirements-extra.txt
python -m pip install -e .
- shell: bash -el {0}
name: Lint with flake8 for pull requests
if: github.event_name == 'pull_request'
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 .
- name: Test with pytest
- shell: bash -el {0}
name: Test with pytest
run: |
pytest --log-cli-level=10
- name: Test notebooks.
pytest
- shell: bash -el {0}
name: Test notebooks.
run: |
pytest --log-cli-level=10 --nbmake examples --ignore=examples/verification
- name: Build a distribution if tagged
pytest --nbmake examples --ignore=examples/verification
- shell: bash -el {0}
name: Build a distribution if tagged
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
run: |
python setup.py sdist
Expand All @@ -40,4 +46,4 @@ jobs:
uses: pypa/gh-action-pypi-publish@master
with:
user: __token__
password: ${{ secrets.pypi_password }}
password: ${{ secrets.pypi_password }}
7 changes: 5 additions & 2 deletions .readthedocs.yml
Expand Up @@ -3,12 +3,16 @@ version: 2
formats:
- pdf

build:
os: ubuntu-22.04
tools:
python: "3.11"

# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/source/conf.py

python:
version: 3.8
install:
- requirements: requirements.txt
- method: pip
Expand All @@ -17,4 +21,3 @@ python:
- doc
- method: setuptools
path: .
system_packages: true
7 changes: 6 additions & 1 deletion andes/core/discrete.py
Expand Up @@ -1633,8 +1633,13 @@ def check_var(self, dae_t, *args, niter=None, err=None, **kwargs):
return

# allow unlimited switching in power flow
if dae_t == 0.0:
if dae_t < 0.0:
self.t_enable[:] = 1.0

# reset `t_last` upon TDS initialization
elif dae_t == 0.0:
self.t_last = np.zeros_like(self.v.v)

# consider delay for time-domain simulation
else:
self.t_enable[:] = (dae_t - self.t_last - self.dt.v) >= 0
Expand Down
7 changes: 7 additions & 0 deletions andes/core/model/model.py
Expand Up @@ -514,6 +514,13 @@ def set(self, src, idx, attr, value):
for state in self.states.values():
if (state.t_const is instance) and len(state.a) > 0:
self.system.dae.Tf[state.a[uid]] = instance.v[uid]
# convert scalar `uid` to list
if isinstance(uid, (float, int, str, np.integer, np.floating)):
uid = [uid]
# set diagonal elements of `Teye` for each element in `uid``
uid_int = state.a.tolist()
for ii in uid:
self.system.TDS.Teye[uid_int[ii], uid_int[ii]] = instance.v[ii]

return True

Expand Down
14 changes: 12 additions & 2 deletions andes/core/model/modelcall.py
Expand Up @@ -16,6 +16,8 @@ def __init__(self):
self.md5 = ''

# `f` and `g` are callables generated by lambdify that take positional args
# `s` is a dict of callables for services needing sequential updates
# `sns` is a callable for updating non-sequential services
self.f = None
self.g = None
self.j = dict()
Expand All @@ -26,11 +28,15 @@ def __init__(self):
self.f_args = list()
self.g_args = list()
self.j_args = dict()
self.s_args = OrderedDict()
self.sns_args = list()

# when saving to pycode, dict of functions are stored as individual functions.
# these include `j`, `ia`, `ii`, `ij`, and `s`.
self.ia = OrderedDict()
self.ii = OrderedDict()
self.ij = OrderedDict()
self.s_args = OrderedDict()
self.sns_args = list()

self.ia_args = OrderedDict() # assignment initialization
self.ii_args = OrderedDict() # iterative initialization
self.ij_args = OrderedDict()
Expand All @@ -39,6 +45,10 @@ def __init__(self):
self.jjac = OrderedDict()
self.vjac = OrderedDict()

self.j_names = list() # existing jacobian names for this model
self.init_seq = list() # initialization sequence
self.need_diag_eps = list() # id of algeb variables needing diag_eps

def clear_ijv(self):
for jname in jac_names:
for jtype in jac_types:
Expand Down
3 changes: 2 additions & 1 deletion andes/core/symprocessor.py
Expand Up @@ -409,6 +409,7 @@ def generate_jacobians(self, diag_eps=1e-8):
v_idx = vars_syms_list.index(var.name)

self.calls.append_ijv(f'{var.e_code}{var.v_code}c', e_idx, v_idx, eps)
self.calls.need_diag_eps.append(var.name)

def generate_pretty_print(self):
"""
Expand Down Expand Up @@ -518,7 +519,7 @@ def generate_pycode(self, pycode_path, yapf_pycode):

# variables
for name in dilled_vars:
out.append(f'{name} = ' + pprint.pformat(self.calls.__dict__[name]))
out.append(f'{name} = ' + pprint.pformat(self.calls.__dict__[name]) + "\n")

out_str = '\n'.join(out)

Expand Down
5 changes: 4 additions & 1 deletion andes/io/matpower.py
Expand Up @@ -151,7 +151,10 @@ def m2mpc(infile: str) -> dict:
elif isinstance(val, list):
if len(val) == 0:
continue
mpc_array[key] = np.array(val)
if "name" in key:
mpc_array[key] = np.array(val, dtype=object)
else:
mpc_array[key] = np.array(val)
else:
raise NotImplementedError("Unkonwn type for mpc, ", type(val))

Expand Down
6 changes: 3 additions & 3 deletions andes/io/streaming.py
Expand Up @@ -198,7 +198,7 @@ def _find_pos(self, model, fkey, src_col=0):
"""Find the positions of foreign keys in the source model index list"""
if isinstance(fkey, ndarray):
fkey = fkey.tolist()
elif type(fkey) in (int, float):
elif isinstance(fkey, (int, float)):
fkey = [fkey]

ret = []
Expand Down Expand Up @@ -254,7 +254,7 @@ def send_init(self, recepient='all'):
)
sleep(0.5)
else:
if type(recepient) != list:
if not isinstance(recepient, list):
recepient = [recepient]
for item in recepient:
self.dimec.send_r(item, Varheader=self.Varheader)
Expand Down Expand Up @@ -292,7 +292,7 @@ def record_module_init(self, name, init_var):

@staticmethod
def transpose_matlab_row(a):
if type(a) is ndarray:
if isinstance(a, ndarray):
if a.shape[0] == 1:
a = a[0]
return a
Expand Down
8 changes: 4 additions & 4 deletions andes/models/static/pq.py
Expand Up @@ -211,12 +211,12 @@ def __init__(self, system=None, config=None):
# To modify P and Q during TDS, use `alter` to set values to `Ppf` and `Qpf`
# after, before simulation, setting `config.p2p=1` and `config.q2q=1`.

self.a.e_str = "u * Indicator(dae_t <= 0) * " \
self.a.e_str = "u * Indicator(dae_t < 0) * " \
"(p0 * vcmp_zi + Rlb * vcmp_zl * v**2 + Rub * vcmp_zu * v**2) + " \
"u * Indicator(dae_t > 0) * " \
"u * Indicator(dae_t >= 0) * " \
"(p2p * Ppf + p2i * Ipeq * v + p2z * Req * v**2)"

self.v.e_str = "u * Indicator(dae_t <= 0) * " \
self.v.e_str = "u * Indicator(dae_t < 0) * " \
"(q0 * vcmp_zi + Xlb * vcmp_zl * v**2 + Xub * vcmp_zu * v**2) + " \
"u * Indicator(dae_t > 0) * " \
"u * Indicator(dae_t >= 0) * " \
"(q2q * Qpf + q2i * Iqeq * v + q2z * Xeq * v**2)"
5 changes: 3 additions & 2 deletions andes/routines/tds.py
Expand Up @@ -197,6 +197,7 @@ def init(self):
# restore power flow solutions
system.dae.x[:len(system.PFlow.x_sol)] = system.PFlow.x_sol
system.dae.y[:len(system.PFlow.y_sol)] = system.PFlow.y_sol
system.dae.t -= system.dae.t # set `dae.t` to zero

# Note:
# calling `set_address` on `system.exist.pflow_tds` will point all variables
Expand Down Expand Up @@ -347,8 +348,8 @@ def run(self, no_summary=False, **kwargs):
if no_summary is False and (system.dae.t == 0):
self.summary()

# only initializing at t=0 allows to continue when `run` is called again.
if system.dae.t == 0:
# only initializing at t<0 allows to continue when `run` is called again.
if system.dae.t < 0:
self.init()
else: # resume simulation
self.init_resume()
Expand Down
7 changes: 3 additions & 4 deletions andes/shared.py
Expand Up @@ -40,11 +40,10 @@
jac_names = ('fx', 'fy', 'gx', 'gy')
jac_types = ('c', '')

dilled_vars = ['f_args', 'g_args', 'j_args',
's_args', 'sns_args',
dilled_vars = ['f_args', 'g_args', 'j_args', 's_args', 'sns_args',
'ia_args', 'ii_args', 'ij_args',
'ijac', 'jjac', 'vjac', 'j_names',
'init_seq']
'ijac', 'jjac', 'vjac',
'j_names', 'init_seq', "need_diag_eps"]

jac_full_names = list()
for jname in jac_names:
Expand Down
47 changes: 24 additions & 23 deletions andes/system.py
Expand Up @@ -13,6 +13,7 @@

import configparser
import importlib
import importlib.util
import inspect
import logging
import os
Expand Down Expand Up @@ -1248,7 +1249,6 @@ def connectivity(self, info=True):
diag = list(matrix(spmatrix(u, to, os, (n, 1), 'd') +
spmatrix(u, fr, os, (n, 1), 'd')))

nib = self.Bus.n_islanded_buses = diag.count(0)
for idx in range(n):
if diag[idx] == 0:
self.Bus.islanded_buses.append(idx)
Expand All @@ -1265,12 +1265,20 @@ def connectivity(self, info=True):
'd')
temp = sparse(temp) # need to drop allocated zero values

cons = temp[0, :]
nelm = len(cons.J)
# Translated find_islanded_areas from goderya.jl into Python
conn = spmatrix([], [], [], (1, n), 'd')
enum = idx = islands = 0
island_sets = []
starting_bus = 0
visit_idx = 0

while True:
if starting_bus in self.Bus.islanded_buses:
starting_bus += 1
continue

cons = temp[starting_bus, :]
nelm = len(cons.J)

while True:
cons = cons * temp
cons = sparse(cons) # remove zero values
Expand All @@ -1279,31 +1287,24 @@ def connectivity(self, info=True):
break
nelm = new_nelm

# started with an islanded bus
if len(conn.J) == 0:
enum += 1
# all buses are interconnected
elif len(cons.J) == n:
break

self.Bus.island_sets.append(list(cons.J))
island_sets.append(list(cons.J))
conn += cons
islands += 1
nconn = len(conn.J)
if nconn >= (n - nib):
self.Bus.island_sets = [i for i in self.Bus.island_sets if len(i) > 0]

if len(conn.J) >= (n - len(self.Bus.islanded_buses)):
break

for element in conn.J[idx:]:
if not diag[idx]:
enum += 1 # skip islanded buses
if element <= enum:
idx += 1
enum += 1
# Increment `starting_bus` until it's not in `conn.J` and
# `self.Bus.islanded_buses`
for i in range(visit_idx, self.Bus.n):
if i in conn.J or i in self.Bus.islanded_buses:
i += 1
else:
visit_idx = i
break

cons = temp[enum, :]
starting_bus = visit_idx

self.Bus.island_sets = island_sets

# --- check if all areas have a slack generator ---
if len(self.Bus.island_sets) > 0:
Expand Down

0 comments on commit 00e2675

Please sign in to comment.