Skip to content

Commit 9281c39

Browse files
authored
Merge pull request #259 from cuihantao/develop
Develop
2 parents aa4567c + 40c7bc2 commit 9281c39

26 files changed

+2181
-1205
lines changed

andes/cases/ieee14/ieee14_exac1.json

+1,405
Large diffs are not rendered by default.

andes/cases/ieee14/ieee14_pll1.xlsx

19.1 KB
Binary file not shown.

andes/cli.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
import argparse
66
import importlib
77
import logging
8-
import os
98
import platform
109
import sys
1110
from time import strftime
1211

12+
from andes.shared import NCPUS_PHYSICAL
1313
from ._version import get_versions
1414

1515
from andes.main import config_logger, find_log_path
@@ -59,7 +59,7 @@ def create_parser():
5959
run.add_argument('-P', '--pert', help='Perturbation file path', default='')
6060
run.add_argument('-o', '--output-path', help='Output path prefix', type=str, default='')
6161
run.add_argument('-n', '--no-output', help='Force no output of any kind', action='store_true')
62-
run.add_argument('--ncpu', help='Number of parallel processes', type=int, default=os.cpu_count())
62+
run.add_argument('--ncpu', help='Number of parallel processes', type=int, default=NCPUS_PHYSICAL)
6363
run.add_argument('--dime-address', help='DiME streaming server protocol, address and port,'
6464
'e.g., tcp://127.0.0.1:5000 or ipc:///tmp/dime2', type=str)
6565
run.add_argument('--tf', help='End time of time-domain simulation', type=float)
@@ -159,7 +159,7 @@ def create_parser():
159159
prep.add_argument('--pycode-path', help='Save path for generated pycode')
160160
prep.add_argument('-m', '--models', nargs='*', help='model names to be individually prepared',
161161
)
162-
prep.add_argument('--ncpu', help='Number of parallel processes', type=int, default=os.cpu_count())
162+
prep.add_argument('--ncpu', help='Number of parallel processes', type=int, default=NCPUS_PHYSICAL)
163163
prep.add_argument('--nomp', help='Disable multiprocessing', action='store_true',)
164164
prep.add_argument('--incubate', help='Save generated pycode under the ANDES code directory to avoid codegen',
165165
action='store_true')

andes/core/block.py

+14-20
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,9 @@
1111
from collections import OrderedDict
1212
from typing import Iterable, List, Optional, Tuple, Union
1313

14-
import numpy as np
15-
1614
from andes.core.common import JacTriplet, ModelFlags, dummify
1715
from andes.core.discrete import (AntiWindup, AntiWindupRate, DeadBand,
18-
HardLimiter, LessThan, RateLimiter, Selector,)
16+
HardLimiter, LessThan, RateLimiter,)
1917
from andes.core.service import EventFlag
2018
from andes.core.var import Algeb, State
2119

@@ -1593,12 +1591,9 @@ def __init__(self, u1, u2, name=None, tex_name=None, info=None):
15931591
self.u2 = dummify(u2)
15941592
self.enforce_tex_name((u1, u2))
15951593

1596-
self.sl = Selector(self.u1, self.u2, fun=np.maximum.reduce,
1597-
info='HVGate Selector',
1598-
)
1599-
1600-
self.y = Algeb(info='HVGate output', tex_name='y', discrete=self.sl)
1601-
self.vars = {'y': self.y, 'sl': self.sl}
1594+
self.lt = LessThan(self.u1, self.u2)
1595+
self.y = Algeb(info='HVGate output', tex_name='y', discrete=self.lt)
1596+
self.vars = {'y': self.y, 'lt': self.lt}
16021597

16031598
def define(self):
16041599
"""
@@ -1619,8 +1614,8 @@ def define(self):
16191614
Not sure if this is a bug or intended.
16201615
16211616
"""
1622-
self.y.v_str = f'{self.name}_sl_s0*{self.u1.name} + {self.name}_sl_s1*{self.u2.name}'
1623-
self.y.e_str = f'{self.name}_sl_s0*{self.u1.name} + {self.name}_sl_s1*{self.u2.name} - ' \
1617+
self.y.v_str = f'{self.name}_lt_z0*{self.u1.name} + {self.name}_lt_z1*{self.u2.name}'
1618+
self.y.e_str = f'{self.name}_lt_z0*{self.u1.name} + {self.name}_lt_z1*{self.u2.name} - ' \
16241619
f'{self.name}_y'
16251620

16261621

@@ -1642,13 +1637,10 @@ def __init__(self, u1, u2, name=None, tex_name=None, info=None):
16421637
self.u2 = dummify(u2)
16431638
self.enforce_tex_name((u1, u2))
16441639

1645-
self.sl = Selector(self.u1, self.u2, fun=np.minimum.reduce,
1646-
info='LVGate Selector',
1647-
)
1640+
self.lt = LessThan(self.u1, self.u2)
1641+
self.y = Algeb(info='LVGate output', tex_name='y', discrete=self.lt)
16481642

1649-
self.y = Algeb(info='LVGate output', tex_name='y', discrete=self.sl)
1650-
1651-
self.vars = {'y': self.y, 'sl': self.sl}
1643+
self.vars = {'y': self.y, 'lt': self.lt}
16521644

16531645
def define(self):
16541646
"""
@@ -1664,8 +1656,8 @@ def define(self):
16641656
Same problem as `HVGate` as `minimum` does not sympify correctly.
16651657
16661658
"""
1667-
self.y.v_str = f'{self.name}_sl_s0*{self.u1.name} + {self.name}_sl_s1*{self.u2.name}'
1668-
self.y.e_str = f'{self.name}_sl_s0*{self.u1.name} + {self.name}_sl_s1*{self.u2.name} - ' \
1659+
self.y.v_str = f'{self.name}_lt_z1*{self.u1.name} + {self.name}_lt_z0*{self.u2.name}'
1660+
self.y.e_str = f'{self.name}_lt_z1*{self.u1.name} + {self.name}_lt_z0*{self.u2.name} - ' \
16691661
f'{self.name}_y'
16701662

16711663

@@ -1807,6 +1799,8 @@ class Piecewise(Block):
18071799
The first range (-inf, x0) applies `fun_0`, and
18081800
the last range (x_{n-1}, +inf) applies the last function `fun_n`.
18091801
1802+
The function returns zero if no condition is met.
1803+
18101804
Parameters
18111805
----------
18121806
points : list, tuple
@@ -1837,7 +1831,7 @@ def define(self):
18371831
args.append(f'({self.funs[i]}, {self.u.name} <= {self.points[i]})')
18381832
args.append(f'({self.funs[i + 1]}, {self.u.name} > {self.points[-1]})')
18391833

1840-
args_comma = ', '.join(args)
1834+
args_comma = ', '.join(args) + ', (0, True)'
18411835
pw_fun = f'Piecewise({args_comma}, evaluate=False)'
18421836

18431837
self.y.v_str = pw_fun

andes/core/discrete.py

+34-7
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ def __init__(self, name=None, tex_name=None, info=None, no_warn=False,
3838
if not hasattr(self, 'export_flags_tex'):
3939
self.export_flags_tex = []
4040

41+
self.input_list = [] # references to input variables
42+
self.param_list = [] # references to parameters
43+
4144
self.x_set = list()
4245
self.y_set = list() # NOT being used
4346

@@ -265,6 +268,9 @@ def __init__(self, u, bound, equal=False, enable=True, name=None, tex_name=None,
265268
self.export_flags = ['z0', 'z1']
266269
self.export_flags_tex = ['z_0', 'z_1']
267270

271+
self.input_list.extend([self.u])
272+
self.param_list.extend([self.bound])
273+
268274
self.has_check_var = True
269275

270276
def check_var(self, *args, **kwargs):
@@ -376,6 +382,9 @@ def __init__(self, u, lower, upper, enable=True,
376382
self.export_flags.append('zi')
377383
self.export_flags_tex.append('z_i')
378384

385+
self.input_list.extend([self.u])
386+
self.param_list.extend([self.lower, self.upper])
387+
379388
if not self.no_lower:
380389
self.export_flags.append('zl')
381390
self.export_flags_tex.append('z_l')
@@ -737,9 +746,9 @@ def check_eq(self,
737746
# logger.debug(f'AntiWindup for states {self.state.a[idx]}')
738747

739748
# Very important note:
740-
# `System.fg_to_dae` is called after `System.l_update_eq`, which calls this function.
741-
# Equation values set in `self.state.e` is collected by `System._e_to_dae`, while
742-
# variable values are collected by the separate loop in `System.fg_to_dae`.
749+
# The set equation values and variable values are collected by `System.fg_to_dae`:
750+
# - Equation values is collected by `System._e_to_dae`,
751+
# - Variable values are collected at the end of `System.fg_to_dae`.
743752
# Also, equation values are processed in `TDS` for resetting the `q`.
744753

745754

@@ -760,7 +769,7 @@ class RateLimiter(Discrete):
760769
"""
761770

762771
def __init__(self, u, lower, upper, enable=True,
763-
no_lower=False, no_upper=False, lower_cond=None, upper_cond=None,
772+
no_lower=False, no_upper=False, lower_cond=1, upper_cond=1,
764773
name=None, tex_name=None, info=None):
765774
Discrete.__init__(self, name=name, tex_name=tex_name, info=info)
766775
self.u = u
@@ -801,6 +810,9 @@ def __init__(self, u, lower, upper, enable=True,
801810
self.export_flags_tex.append('z_{ur}')
802811
self.warn_flags.append(('zur', 'upper'))
803812

813+
self.param_list.extend([self.rate_lower, self.rate_upper,
814+
self.rate_lower_cond, self.rate_upper_cond])
815+
804816
def check_eq(self, **kwargs):
805817
if not self.enable:
806818
return
@@ -864,6 +876,13 @@ class Selector(Discrete):
864876
A potential bug when more than two inputs are provided, and values in different inputs are equal.
865877
Only two inputs are allowed.
866878
879+
.. deprecated:: 1.5.9
880+
881+
Use of this class for comparison-based output is discouraged.
882+
Instead, use `LessThan` and `Limiter` to construct piesewise equations.
883+
884+
See the new implementation of ``HVGate`` and ``LVGate``.
885+
867886
Examples
868887
--------
869888
Example 1: select the largest value between `v0` and `v1` and put it into vmax.
@@ -890,9 +909,6 @@ class Selector(Discrete):
890909
--------
891910
numpy.ufunc.reduce : NumPy reduce function
892911
893-
andes.core.block.HVGate
894-
895-
andes.core.block.LVGate
896912
"""
897913

898914
def __init__(self, *args, fun, tex_name=None, info=None):
@@ -911,6 +927,8 @@ def __init__(self, *args, fun, tex_name=None, info=None):
911927
self.export_flags = [f's{i}' for i in range(len(self.input_vars))]
912928
self.export_flags_tex = [f's_{i}' for i in range(len(self.input_vars))]
913929

930+
self.input_list = args
931+
914932
self.has_check_var = True
915933

916934
def check_var(self, *args, **kwargs):
@@ -986,6 +1004,7 @@ def __init__(self, u, options: Union[list, Tuple], info: str = None,
9861004

9871005
self.export_flags = [f's{i}' for i in range(len(options))]
9881006
self.export_flags_tex = [f's_{i}' for i in range(len(options))]
1007+
self.input_list.extend([self.u])
9891008

9901009
self.has_check_var = True
9911010

@@ -1076,6 +1095,8 @@ def __init__(self, u, center, lower, upper,
10761095

10771096
self.center = dummify(center) # CURRENTLY NOT IN USE
10781097

1098+
self.param_list.extend([self.center])
1099+
10791100
def check_var(self, *args, **kwargs):
10801101
"""
10811102
Notes
@@ -1211,6 +1232,8 @@ def __init__(self, u, mode='step', delay=0,
12111232
self.delay = delay
12121233
self.export_flags = ['v']
12131234
self.export_flags_tex = ['v']
1235+
self.input_list.extend([u])
1236+
12141237
self.has_check_var = True
12151238

12161239
self.t = np.array([0])
@@ -1335,6 +1358,8 @@ def __init__(self, u, interval=1.0, offset=0.0, name=None, tex_name=None, info=N
13351358

13361359
self.export_flags = ['v']
13371360
self.export_flags_tex = ['v']
1361+
self.input_list.extend([self.u])
1362+
13381363
self.has_check_var = True
13391364

13401365
self.v = np.array([0])
@@ -1439,6 +1464,8 @@ def __init__(self, *, v, lower, upper, bsw, gsw, dt, u, enable=True,
14391464
self.err_tol = err_tol
14401465

14411466
self.has_check_var = True
1467+
self.input_list.extend([self.v])
1468+
self.param_list.extend([self.lower, self.upper, self.u])
14421469

14431470
self.t_last = None
14441471
self.t_enable = None

andes/core/model.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -244,8 +244,10 @@ def add(self, **kwargs):
244244
idx = kwargs['idx']
245245
self.uid[idx] = self.n
246246
self.n += 1
247-
if "name" in self.params and kwargs.get("name") is None:
248-
kwargs["name"] = idx
247+
if "name" in self.params:
248+
name = kwargs.get("name")
249+
if (name is None) or (not isinstance(name, str) and np.isnan(name)):
250+
kwargs["name"] = idx
249251

250252
if "idx" not in self.params:
251253
kwargs.pop("idx")

andes/core/symprocessor.py

+3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ def broadcastcond(cond):
6464
printer._print(sympy.S.NaN))
6565

6666

67+
# the line below caches Piecewise instances
68+
sympy.OldPiecewise = sympy.Piecewise
69+
6770
sympy.Piecewise = FixPiecewise
6871

6972

andes/main.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
import andes
2828
from andes.routines import routine_cli
29-
from andes.shared import Pool, Process, coloredlogs, unittest
29+
from andes.shared import Pool, Process, coloredlogs, unittest, NCPUS_PHYSICAL
3030
from andes.system import System
3131
from andes.utils.misc import elapsed, is_interactive
3232
from andes.utils.paths import get_config_path, get_log_dir, tests_root
@@ -490,7 +490,7 @@ def find_log_path(lg):
490490
return out
491491

492492

493-
def _run_multiprocess_proc(cases, ncpu=os.cpu_count(), **kwargs):
493+
def _run_multiprocess_proc(cases, ncpu=NCPUS_PHYSICAL, **kwargs):
494494
"""
495495
Run multiprocessing with `Process`.
496496
@@ -515,7 +515,7 @@ def _run_multiprocess_proc(cases, ncpu=os.cpu_count(), **kwargs):
515515
return True
516516

517517

518-
def _run_multiprocess_pool(cases, ncpu=os.cpu_count(), verbose=logging.INFO, **kwargs):
518+
def _run_multiprocess_pool(cases, ncpu=NCPUS_PHYSICAL, verbose=logging.INFO, **kwargs):
519519
"""
520520
Run multiprocessing jobs using Pool.
521521
@@ -539,7 +539,7 @@ def _run_multiprocess_pool(cases, ncpu=os.cpu_count(), verbose=logging.INFO, **k
539539
return ret
540540

541541

542-
def run(filename, input_path='', verbose=20, mp_verbose=30, ncpu=os.cpu_count(), pool=False,
542+
def run(filename, input_path='', verbose=20, mp_verbose=30, ncpu=NCPUS_PHYSICAL, pool=False,
543543
cli=False, codegen=False, shell=False, **kwargs):
544544
"""
545545
Entry point to run ANDES routines.
@@ -724,7 +724,7 @@ def prepare(quick=False, incremental=False, models=None,
724724

725725
cli = kwargs.get("cli", False)
726726
full = kwargs.get("full", False)
727-
ncpu = kwargs.get("ncpu", os.cpu_count())
727+
ncpu = kwargs.get("ncpu", NCPUS_PHYSICAL)
728728

729729
if cli is True:
730730
if not full:

andes/models/__init__.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,16 @@
3131
'ESAC1A', 'ESST1A']),
3232
('pss', ['IEEEST', 'ST2CUT']),
3333
('motor', ['Motor3', 'Motor5']),
34-
('measurement', ['BusFreq', 'BusROCOF', 'PMU']),
34+
('measurement', ['BusFreq', 'BusROCOF', 'PMU', 'PLL1']),
3535
('dc', ['Node', 'Ground', 'R', 'L', 'C', 'RCp', 'RCs', 'RLs', 'RLCs', 'RLCp']),
3636
('acdc', ['VSCShunt']),
3737
('renewable', ['REGCA1', 'REECA1', 'REECA1E', 'REECA1G',
3838
'REPCA1', 'WTDTA1', 'WTDS', 'WTARA1', 'WTPTA1', 'WTTQA1', 'WTARV1',
39-
'REGCVSG', 'REGCVSG2']),
39+
'REGCV1', 'REGCV2']),
4040
('distributed', ['PVD1', 'ESD1', 'EV1', 'EV2', 'DGPRCT1', 'DGPRCTExt']),
4141
('coi', ['COI']),
4242
# ('experimental', ['PI2', 'TestDB1', 'TestPI', 'TestLagAWFreeze', 'FixedGen']),
4343
])
44+
45+
46+
model_aliases = {"REGCVSG": "REGCV1", "REGCVSG2": "REGCV2"}

andes/models/measurement/__init__.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@
44

55
from andes.models.measurement.busfreq import BusFreq # noqa
66
from andes.models.measurement.busrocof import BusROCOF # noqa
7-
from andes.models.measurement.pmu import PMU # noqa
7+
from andes.models.measurement.pmu import PMU # noqa
8+
from andes.models.measurement.pll import PLL1 # noqa

0 commit comments

Comments
 (0)