Skip to content

Commit

Permalink
Merge pull request #87 from CURENT/misc
Browse files Browse the repository at this point in the history
Misc
  • Loading branch information
jinningwang authored Jul 2, 2024
2 parents 9ef8eb2 + ad85c45 commit d61b191
Show file tree
Hide file tree
Showing 20 changed files with 334 additions and 216 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ Use the following resources to get involved.

# Installation

***NOTE:*** ``kvxopt`` is recommended to install via ``conda`` as sometimes ``pip`` struggles to set the correct path for compiled libraries.
***NOTE:***
- ``kvxopt`` is recommended to install via ``conda`` as sometimes ``pip`` struggles to set the correct path for compiled libraries
- `cvxpy` versions **below 1.5** are incompatible with `numpy` versions **2.0 and above**.

AMS is released as ``ltbams`` on PyPI and conda-forge.
Install from PyPI using pip:
Expand Down
2 changes: 1 addition & 1 deletion ams/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ def preamble():

def main():
"""
Entry point of the ANDES command-line interface.
Entry point of the AMS command-line interface.
"""

parser = create_parser()
Expand Down
19 changes: 10 additions & 9 deletions ams/interop/andes.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from andes.system import System as andes_System

from ams.io import input_formats
from ams.shared import nan

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -260,7 +261,7 @@ def parse_addfile(adsys, amsys, addfile):
except KeyError: # set the most frequent string as the model name
split_list = []
for item in df[idxn].values:
if item is None or np.nan:
if item is None or nan:
continue
try:
split_list.append(item.split('_'))
Expand Down Expand Up @@ -295,7 +296,7 @@ def parse_addfile(adsys, amsys, addfile):
# add dynamic models
for name, df in df_models.items():
# drop rows that all nan
df.replace(['', ' '], np.NaN, inplace=True) # replace empty string with nan
df.replace(['', ' '], nan, inplace=True) # replace empty string with nan
df.dropna(axis=0, how='all', inplace=True)
# if the dynamic model also exists in AMS, use AMS parameters for overlap
if (name in amsys.models.keys()) and amsys.models[name].n > 0:
Expand Down Expand Up @@ -684,15 +685,15 @@ def receive(self, adsys=None, routine=None, no_update=False):
cond_ads_stg_u = (mname_ads in ['StaticGen', 'PV', 'Sclak']) and (pname_ads == 'u')
if cond_ads_stg_u and (self.is_tds):
# --- SynGen ---
u_sg = sa.SynGen.get(idx=link['syg_idx'].replace(np.NaN, None).to_list(),
u_sg = sa.SynGen.get(idx=link['syg_idx'].replace(nan, None).to_list(),
src='u', attr='v',
allow_none=True, default=0,)
# --- DG ---
u_dg = sa.DG.get(idx=link['dg_idx'].replace(np.NaN, None).to_list(),
u_dg = sa.DG.get(idx=link['dg_idx'].replace(nan, None).to_list(),
src='u', attr='v',
allow_none=True, default=0,)
# --- RenGen ---
u_rg = sa.RenGen.get(idx=link['rg_idx'].replace(np.NaN, None).to_list(),
u_rg = sa.RenGen.get(idx=link['rg_idx'].replace(nan, None).to_list(),
src='u', attr='v',
allow_none=True, default=0,)
# --- output ---
Expand Down Expand Up @@ -722,19 +723,19 @@ def receive(self, adsys=None, routine=None, no_update=False):
cond_ads_stg_p = (mname_ads in ['StaticGen', 'PV', 'Sclak']) and (pname_ads == 'p')
if cond_ads_stg_p and (self.is_tds):
# --- SynGen ---
p_sg = sa.SynGen.get(idx=link['syg_idx'].replace(np.NaN, None).to_list(),
p_sg = sa.SynGen.get(idx=link['syg_idx'].replace(nan, None).to_list(),
src='Pe', attr='v',
allow_none=True, default=0,)
# --- DG ---
Ie_dg = sa.DG.get(idx=link['dg_idx'].replace(np.NaN, None).to_list(),
Ie_dg = sa.DG.get(idx=link['dg_idx'].replace(nan, None).to_list(),
src='Ipout_y', attr='v',
allow_none=True, default=0,)
v_dg = sa.DG.get(idx=link['dg_idx'].replace(np.NaN, None).to_list(),
v_dg = sa.DG.get(idx=link['dg_idx'].replace(nan, None).to_list(),
src='v', attr='v',
allow_none=True, default=0,)
p_dg = Ie_dg * v_dg
# --- RenGen ---
p_rg = sa.RenGen.get(idx=link['rg_idx'].replace(np.NaN, None).to_list(),
p_rg = sa.RenGen.get(idx=link['rg_idx'].replace(nan, None).to_list(),
src='Pe', attr='v',
allow_none=True, default=0,)
# --- output ---
Expand Down
8 changes: 4 additions & 4 deletions ams/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ def run(filename, input_path='', verbose=20, mp_verbose=30,
ncpu=NCPUS_PHYSICAL, pool=False,
cli=False, shell=False, **kwargs):
"""
Entry point to run ANDES routines.
Entry point to run AMS routines.
Parameters
----------
Expand Down Expand Up @@ -511,7 +511,7 @@ def demo(**kwargs):

def versioninfo():
"""
Print version info for ANDES and dependencies.
Print version info for AMS and dependencies.
"""

import numpy as np
Expand Down Expand Up @@ -563,7 +563,7 @@ def print_license():

def edit_conf(edit_config: Optional[Union[str, bool]] = ''):
"""
Edit the Andes config file which occurs first in the search path.
Edit the AMS config file which occurs first in the search path.
Parameters
----------
Expand Down Expand Up @@ -647,7 +647,7 @@ def save_conf(config_path=None, overwrite=None, **kwargs):
# TODO: list AMS output files here
def remove_output(recursive=False):
"""
Remove the outputs generated by Andes, including power flow reports
Remove the outputs generated by AMS, including power flow reports
``_out.txt``, time-domain list ``_out.lst`` and data ``_out.dat``,
eigenvalue analysis report ``_eig.txt``.
Expand Down
18 changes: 11 additions & 7 deletions ams/opt/omodel.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,10 @@ def v(self):

@v.setter
def v(self, value):
# FIXME: is this safe?
self._v = value
if self.optz is None:
logger.info(f"Variable <{self.name}> is not initialized yet.")
else:
self.optz.value = value

def get_idx(self):
if self.is_group:
Expand Down Expand Up @@ -595,6 +597,10 @@ def v(self):
else:
return self.optz._expr.value

@v.setter
def v(self, value):
raise AttributeError("Cannot set the value of the constraint.")


class Objective(OptzBase):
"""
Expand Down Expand Up @@ -634,7 +640,6 @@ def __init__(self,
OptzBase.__init__(self, name=name, info=info, unit=unit)
self.e_str = e_str
self.sense = sense
self._v = 0
self.code = None

@property
Expand Down Expand Up @@ -673,13 +678,12 @@ def v(self):
"""
if self.optz is None:
return None
out = self.om.obj.value
out = self._v if out is None else out
return out
else:
return self.optz.value

@v.setter
def v(self, value):
self._v = value
raise AttributeError("Cannot set the value of the objective function.")

def parse(self, no_code=True):
"""
Expand Down
76 changes: 39 additions & 37 deletions ams/pypower/core/pips.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,24 @@
"""
Python Interior Point Solver (PIPS).
"""
import logging # NOQA
import logging

import numpy as np # NOQA
from numpy import flatnonzero as find # NOQA
import numpy as np
from numpy import flatnonzero as find

import scipy.sparse as sp # NOQA
from scipy.sparse import csr_matrix as c_sparse # NOQA
import scipy.sparse as sp
from scipy.sparse import csr_matrix as c_sparse

from andes.shared import deg2rad, rad2deg # NOQA
from andes.shared import deg2rad, rad2deg

from ams.pypower.eps import EPS # NOQA
from ams.pypower.idx import IDX # NOQA
from ams.pypower.utils import sub2ind # NOQA
from ams.pypower.make import makeYbus # NOQA
from ams.shared import inf

from ams.pypower.eps import EPS
from ams.pypower.idx import IDX
from ams.pypower.utils import sub2ind
from ams.pypower.make import makeYbus
from ams.pypower.routines.opffcns import (opf_costfcn, opf_consfcn,
opf_hessfcn,) # NOQA
opf_hessfcn,)


logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -205,13 +207,13 @@ def pips(f_fcn, x0=None, A=None, l=None, u=None, xmin=None, xmax=None,

# default argument values
if l is None or len(l) == 0:
l = -np.Inf * np.ones(nA)
l = -inf * np.ones(nA)
if u is None or len(u) == 0:
u = np.Inf * np.ones(nA)
u = inf * np.ones(nA)
if xmin is None or len(xmin) == 0:
xmin = -np.Inf * np.ones(x0.shape[0])
xmin = -inf * np.ones(x0.shape[0])
if xmax is None or len(xmax) == 0:
xmax = np.Inf * np.ones(x0.shape[0])
xmax = inf * np.ones(x0.shape[0])
if gh_fcn is None:
nonlinear = False
gn = np.array([])
Expand Down Expand Up @@ -343,15 +345,15 @@ def pips(f_fcn, x0=None, A=None, l=None, u=None, xmin=None, xmax=None,

maxh = np.zeros(1) if len(h) == 0 else max(h)

gnorm = np.linalg.norm(g, np.Inf) if len(g) else 0.0
lam_norm = np.linalg.norm(lam, np.Inf) if len(lam) else 0.0
mu_norm = np.linalg.norm(mu, np.Inf) if len(mu) else 0.0
znorm = np.linalg.norm(z, np.Inf) if len(z) else 0.0
gnorm = np.linalg.norm(g, inf) if len(g) else 0.0
lam_norm = np.linalg.norm(lam, inf) if len(lam) else 0.0
mu_norm = np.linalg.norm(mu, inf) if len(mu) else 0.0
znorm = np.linalg.norm(z, inf) if len(z) else 0.0
feascond = \
max([gnorm, maxh]) / (1 + max([np.linalg.norm(x, np.Inf), znorm]))
max([gnorm, maxh]) / (1 + max([np.linalg.norm(x, inf), znorm]))
gradcond = \
np.linalg.norm(Lx, np.Inf) / (1 + max([lam_norm, mu_norm]))
compcond = np.dot(z, mu) / (1 + np.linalg.norm(x, np.Inf))
np.linalg.norm(Lx, inf) / (1 + max([lam_norm, mu_norm]))
compcond = np.dot(z, mu) / (1 + np.linalg.norm(x, inf))
costcond = np.abs(f - f0) / (1 + np.abs(f0))

# save history
Expand Down Expand Up @@ -467,14 +469,14 @@ def pips(f_fcn, x0=None, A=None, l=None, u=None, xmin=None, xmax=None,

maxh1 = np.zeros(1) if len(h1) == 0 else max(h1)

g1norm = np.linalg.norm(g1, np.Inf) if len(g1) else 0.0
lam1_norm = np.linalg.norm(lam, np.Inf) if len(lam) else 0.0
mu1_norm = np.linalg.norm(mu, np.Inf) if len(mu) else 0.0
z1norm = np.linalg.norm(z, np.Inf) if len(z) else 0.0
g1norm = np.linalg.norm(g1, inf) if len(g1) else 0.0
lam1_norm = np.linalg.norm(lam, inf) if len(lam) else 0.0
mu1_norm = np.linalg.norm(mu, inf) if len(mu) else 0.0
z1norm = np.linalg.norm(z, inf) if len(z) else 0.0

feascond1 = max([g1norm, maxh1]) / \
(1 + max([np.linalg.norm(x1, np.Inf), z1norm]))
gradcond1 = np.linalg.norm(Lx1, np.Inf) / (1 + max([lam1_norm, mu1_norm]))
(1 + max([np.linalg.norm(x1, inf), z1norm]))
gradcond1 = np.linalg.norm(Lx1, inf) / (1 + max([lam1_norm, mu1_norm]))

if (feascond1 > feascond) and (gradcond1 > gradcond):
sc = True
Expand Down Expand Up @@ -560,15 +562,15 @@ def pips(f_fcn, x0=None, A=None, l=None, u=None, xmin=None, xmax=None,
else:
maxh = max(h)

gnorm = np.linalg.norm(g, np.Inf) if len(g) else 0.0
lam_norm = np.linalg.norm(lam, np.Inf) if len(lam) else 0.0
mu_norm = np.linalg.norm(mu, np.Inf) if len(mu) else 0.0
znorm = np.linalg.norm(z, np.Inf) if len(z) else 0.0
gnorm = np.linalg.norm(g, inf) if len(g) else 0.0
lam_norm = np.linalg.norm(lam, inf) if len(lam) else 0.0
mu_norm = np.linalg.norm(mu, inf) if len(mu) else 0.0
znorm = np.linalg.norm(z, inf) if len(z) else 0.0
feascond = \
max([gnorm, maxh]) / (1 + max([np.linalg.norm(x, np.Inf), znorm]))
max([gnorm, maxh]) / (1 + max([np.linalg.norm(x, inf), znorm]))
gradcond = \
np.linalg.norm(Lx, np.Inf) / (1 + max([lam_norm, mu_norm]))
compcond = np.dot(z, mu) / (1 + np.linalg.norm(x, np.Inf))
np.linalg.norm(Lx, inf) / (1 + max([lam_norm, mu_norm]))
compcond = np.dot(z, mu) / (1 + np.linalg.norm(x, inf))
costcond = float(np.abs(f - f0) / (1 + np.abs(f0)))

hist.append({'feascond': feascond, 'gradcond': gradcond,
Expand Down Expand Up @@ -775,8 +777,8 @@ def pipsopf_solver(om, ppopt, out_opt=None):

# try to select an interior initial point
ll, uu = xmin.copy(), xmax.copy()
ll[xmin == -np.Inf] = -1e10 # replace Inf with numerical proxies
uu[xmax == np.Inf] = 1e10
ll[xmin == -inf] = -1e10 # replace Inf with numerical proxies
uu[xmax == inf] = 1e10
x0 = (ll + uu) / 2
Varefs = bus[bus[:, IDX.bus.BUS_TYPE] == IDX.bus.REF, IDX.bus.VA] * deg2rad
# angles set to first reference angle
Expand Down
4 changes: 3 additions & 1 deletion ams/pypower/core/ppoption.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import numpy as np

from ams.shared import inf


PF_OPTIONS = [
('pf_alg', 1, '''power flow algorithm:
Expand Down Expand Up @@ -184,7 +186,7 @@
3 - concurrent (LP only)
4 - deterministic concurrent (LP only)
'''),
('grb_timelimit', np.Inf, 'maximum time allowed for solver (TimeLimit)'),
('grb_timelimit', inf, 'maximum time allowed for solver (TimeLimit)'),
('grb_threads', 0, '(auto) maximum number of threads to use (Threads)'),
('grb_opt', 0, 'See gurobi_options() for details')
]
Expand Down
Loading

0 comments on commit d61b191

Please sign in to comment.