ltbams 0.9.9__py3-none-any.whl → 1.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ams/__init__.py +4 -11
- ams/_version.py +3 -3
- ams/cases/5bus/pjm5bus_demo.xlsx +0 -0
- ams/cases/5bus/pjm5bus_jumper.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced.json +1062 -0
- ams/cases/5bus/pjm5bus_uced.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced_esd1.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced_ev.xlsx +0 -0
- ams/cases/ieee123/ieee123.xlsx +0 -0
- ams/cases/ieee123/ieee123_regcv1.xlsx +0 -0
- ams/cases/ieee14/ieee14.json +1166 -0
- ams/cases/ieee14/ieee14.raw +92 -0
- ams/cases/ieee14/ieee14_conn.xlsx +0 -0
- ams/cases/ieee14/ieee14_uced.xlsx +0 -0
- ams/cases/ieee39/ieee39.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_esd1.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_pvd1.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_vis.xlsx +0 -0
- ams/cases/matpower/benchmark.json +1594 -0
- ams/cases/matpower/case118.m +787 -0
- ams/cases/matpower/case14.m +129 -0
- ams/cases/matpower/case300.m +1315 -0
- ams/cases/matpower/case39.m +205 -0
- ams/cases/matpower/case5.m +62 -0
- ams/cases/matpower/case_ACTIVSg2000.m +9460 -0
- ams/cases/npcc/npcc.m +644 -0
- ams/cases/npcc/npcc_uced.xlsx +0 -0
- ams/cases/pglib/pglib_opf_case39_epri__api.m +243 -0
- ams/cases/wecc/wecc.m +714 -0
- ams/cases/wecc/wecc_uced.xlsx +0 -0
- ams/cli.py +6 -0
- ams/core/__init__.py +2 -0
- ams/core/documenter.py +652 -0
- ams/core/matprocessor.py +782 -0
- ams/core/model.py +330 -0
- ams/core/param.py +322 -0
- ams/core/service.py +918 -0
- ams/core/symprocessor.py +224 -0
- ams/core/var.py +59 -0
- ams/extension/__init__.py +5 -0
- ams/extension/eva.py +401 -0
- ams/interface.py +1085 -0
- ams/io/__init__.py +133 -0
- ams/io/json.py +82 -0
- ams/io/matpower.py +406 -0
- ams/io/psse.py +6 -0
- ams/io/pypower.py +103 -0
- ams/io/xlsx.py +80 -0
- ams/main.py +81 -4
- ams/models/__init__.py +24 -0
- ams/models/area.py +40 -0
- ams/models/bus.py +52 -0
- ams/models/cost.py +169 -0
- ams/models/distributed/__init__.py +3 -0
- ams/models/distributed/esd1.py +71 -0
- ams/models/distributed/ev.py +60 -0
- ams/models/distributed/pvd1.py +67 -0
- ams/models/group.py +231 -0
- ams/models/info.py +26 -0
- ams/models/line.py +238 -0
- ams/models/renewable/__init__.py +5 -0
- ams/models/renewable/regc.py +119 -0
- ams/models/reserve.py +94 -0
- ams/models/shunt.py +14 -0
- ams/models/static/__init__.py +2 -0
- ams/models/static/gen.py +165 -0
- ams/models/static/pq.py +61 -0
- ams/models/timeslot.py +69 -0
- ams/models/zone.py +49 -0
- ams/opt/__init__.py +12 -0
- ams/opt/constraint.py +175 -0
- ams/opt/exprcalc.py +127 -0
- ams/opt/expression.py +188 -0
- ams/opt/objective.py +174 -0
- ams/opt/omodel.py +432 -0
- ams/opt/optzbase.py +192 -0
- ams/opt/param.py +156 -0
- ams/opt/var.py +233 -0
- ams/pypower/__init__.py +8 -0
- ams/pypower/_compat.py +9 -0
- ams/pypower/core/__init__.py +8 -0
- ams/pypower/core/pips.py +894 -0
- ams/pypower/core/ppoption.py +244 -0
- ams/pypower/core/ppver.py +18 -0
- ams/pypower/core/solver.py +2451 -0
- ams/pypower/eps.py +6 -0
- ams/pypower/idx.py +174 -0
- ams/pypower/io.py +604 -0
- ams/pypower/make/__init__.py +11 -0
- ams/pypower/make/matrices.py +665 -0
- ams/pypower/make/pdv.py +506 -0
- ams/pypower/routines/__init__.py +7 -0
- ams/pypower/routines/cpf.py +513 -0
- ams/pypower/routines/cpf_callbacks.py +114 -0
- ams/pypower/routines/opf.py +1803 -0
- ams/pypower/routines/opffcns.py +1946 -0
- ams/pypower/routines/pflow.py +852 -0
- ams/pypower/toggle.py +1098 -0
- ams/pypower/utils.py +293 -0
- ams/report.py +212 -50
- ams/routines/__init__.py +23 -0
- ams/routines/acopf.py +117 -0
- ams/routines/cpf.py +65 -0
- ams/routines/dcopf.py +241 -0
- ams/routines/dcpf.py +209 -0
- ams/routines/dcpf0.py +196 -0
- ams/routines/dopf.py +150 -0
- ams/routines/ed.py +312 -0
- ams/routines/pflow.py +255 -0
- ams/routines/pflow0.py +113 -0
- ams/routines/routine.py +1033 -0
- ams/routines/rted.py +519 -0
- ams/routines/type.py +160 -0
- ams/routines/uc.py +376 -0
- ams/shared.py +63 -9
- ams/system.py +61 -22
- ams/utils/__init__.py +3 -0
- ams/utils/misc.py +77 -0
- ams/utils/paths.py +257 -0
- docs/Makefile +21 -0
- docs/make.bat +35 -0
- docs/source/_templates/autosummary/base.rst +5 -0
- docs/source/_templates/autosummary/class.rst +35 -0
- docs/source/_templates/autosummary/module.rst +65 -0
- docs/source/_templates/autosummary/module_toctree.rst +66 -0
- docs/source/api.rst +102 -0
- docs/source/conf.py +206 -0
- docs/source/examples/index.rst +34 -0
- docs/source/genmodelref.py +61 -0
- docs/source/genroutineref.py +47 -0
- docs/source/getting_started/copyright.rst +20 -0
- docs/source/getting_started/formats/index.rst +20 -0
- docs/source/getting_started/formats/matpower.rst +183 -0
- docs/source/getting_started/formats/psse.rst +46 -0
- docs/source/getting_started/formats/pypower.rst +223 -0
- docs/source/getting_started/formats/xlsx.png +0 -0
- docs/source/getting_started/formats/xlsx.rst +23 -0
- docs/source/getting_started/index.rst +76 -0
- docs/source/getting_started/install.rst +231 -0
- docs/source/getting_started/overview.rst +26 -0
- docs/source/getting_started/testcase.rst +45 -0
- docs/source/getting_started/verification.rst +13 -0
- docs/source/images/curent.ico +0 -0
- docs/source/images/dcopf_time.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_NameOnTrans.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_Transparent.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_Transparent_Name.png +0 -0
- docs/source/images/sponsors/doe.png +0 -0
- docs/source/index.rst +108 -0
- docs/source/modeling/example.rst +159 -0
- docs/source/modeling/index.rst +17 -0
- docs/source/modeling/model.rst +210 -0
- docs/source/modeling/routine.rst +122 -0
- docs/source/modeling/system.rst +51 -0
- docs/source/release-notes.rst +398 -0
- ltbams-1.0.2.dist-info/METADATA +215 -0
- ltbams-1.0.2.dist-info/RECORD +188 -0
- {ltbams-0.9.9.dist-info → ltbams-1.0.2.dist-info}/WHEEL +1 -1
- ltbams-1.0.2.dist-info/top_level.txt +3 -0
- tests/__init__.py +0 -0
- tests/test_1st_system.py +33 -0
- tests/test_addressing.py +40 -0
- tests/test_andes_mats.py +61 -0
- tests/test_case.py +266 -0
- tests/test_cli.py +34 -0
- tests/test_export_csv.py +89 -0
- tests/test_group.py +83 -0
- tests/test_interface.py +216 -0
- tests/test_io.py +32 -0
- tests/test_jumper.py +27 -0
- tests/test_known_good.py +267 -0
- tests/test_matp.py +437 -0
- tests/test_model.py +54 -0
- tests/test_omodel.py +119 -0
- tests/test_paths.py +22 -0
- tests/test_report.py +251 -0
- tests/test_repr.py +21 -0
- tests/test_routine.py +178 -0
- tests/test_rtn_dcopf.py +101 -0
- tests/test_rtn_dcpf.py +77 -0
- tests/test_rtn_ed.py +279 -0
- tests/test_rtn_pflow.py +219 -0
- tests/test_rtn_rted.py +273 -0
- tests/test_rtn_uc.py +248 -0
- tests/test_service.py +73 -0
- ltbams-0.9.9.dist-info/LICENSE +0 -692
- ltbams-0.9.9.dist-info/METADATA +0 -859
- ltbams-0.9.9.dist-info/RECORD +0 -14
- ltbams-0.9.9.dist-info/top_level.txt +0 -1
- {ltbams-0.9.9.dist-info → ltbams-1.0.2.dist-info}/entry_points.txt +0 -0
ams/io/pypower.py
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
"""
|
2
|
+
PYPOWER reader for AMS.
|
3
|
+
"""
|
4
|
+
import logging
|
5
|
+
|
6
|
+
import numpy as np
|
7
|
+
|
8
|
+
from ams.io.matpower import mpc2system, system2mpc
|
9
|
+
|
10
|
+
logger = logging.getLogger(__name__)
|
11
|
+
|
12
|
+
|
13
|
+
def testlines(infile):
|
14
|
+
"""
|
15
|
+
Test if this file is in the PYPOWER format.
|
16
|
+
|
17
|
+
NOT YET IMPLEMENTED.
|
18
|
+
"""
|
19
|
+
return True
|
20
|
+
|
21
|
+
|
22
|
+
def read(system, file):
|
23
|
+
"""
|
24
|
+
Read a PYPOWER case file into ppc and return an AMS system by calling ``ppc2system``.
|
25
|
+
|
26
|
+
Parameters
|
27
|
+
----------
|
28
|
+
system : ams.system
|
29
|
+
Empty AMS system to load data into.
|
30
|
+
file : str
|
31
|
+
The path to the PYPOWER file.
|
32
|
+
|
33
|
+
Returns
|
34
|
+
-------
|
35
|
+
system : ams.system.System
|
36
|
+
The AMS system that loaded the data.
|
37
|
+
"""
|
38
|
+
ppc = py2ppc(file)
|
39
|
+
return ppc2system(ppc, system)
|
40
|
+
|
41
|
+
|
42
|
+
def py2ppc(infile: str) -> dict:
|
43
|
+
"""
|
44
|
+
Parse PYPOWER file and return a dictionary with the data.
|
45
|
+
|
46
|
+
Parameters
|
47
|
+
----------
|
48
|
+
infile : str
|
49
|
+
The path to the PYPOWER file.
|
50
|
+
|
51
|
+
Returns
|
52
|
+
-------
|
53
|
+
ppc : dict
|
54
|
+
The PYPOWER case dict.
|
55
|
+
"""
|
56
|
+
exec(open(f"{infile}").read())
|
57
|
+
for name, value in locals().items():
|
58
|
+
# Check if the variable name starts with "case"
|
59
|
+
if name.startswith("case"):
|
60
|
+
ppc = value()
|
61
|
+
return ppc
|
62
|
+
|
63
|
+
|
64
|
+
def ppc2system(ppc: dict, system) -> bool:
|
65
|
+
"""
|
66
|
+
Alias for ``mpc2system``. Refer to :py:mod:`ams.io.matpower.mpc2system` for more details.
|
67
|
+
|
68
|
+
Load an PYPOWER case dict into an empth AMS system.
|
69
|
+
|
70
|
+
Parameters
|
71
|
+
----------
|
72
|
+
ppc : dict
|
73
|
+
The PYPOWER case dict.
|
74
|
+
system : ams.system
|
75
|
+
Empty AMS system to load data into.
|
76
|
+
|
77
|
+
Returns
|
78
|
+
-------
|
79
|
+
bool
|
80
|
+
True if successful; False otherwise.
|
81
|
+
"""
|
82
|
+
return mpc2system(ppc, system)
|
83
|
+
|
84
|
+
|
85
|
+
def system2ppc(system) -> dict:
|
86
|
+
"""
|
87
|
+
Alias for ``system2mpc``. Refer to :py:mod:`ams.io.matpower.system2mpc` for more details.
|
88
|
+
|
89
|
+
Convert data from an AMS system to an mpc dict.
|
90
|
+
|
91
|
+
In the ``gen`` section, slack generators preceeds PV generators.
|
92
|
+
"""
|
93
|
+
mpc = system2mpc(system)
|
94
|
+
np.set_printoptions(suppress=True)
|
95
|
+
# Map the original bus indices to consecutive values
|
96
|
+
# Adjust discontinuous bus indices
|
97
|
+
BUS_I = mpc['bus'][:, 0].astype(int)
|
98
|
+
bus_map = {busi0: i for i, busi0 in enumerate(BUS_I)}
|
99
|
+
mpc['bus'][:, 0] = np.array([bus_map[busi0] for busi0 in BUS_I])
|
100
|
+
mpc['gen'][:, 0] = np.array([bus_map[busi0] for busi0 in mpc['gen'][:, 0].astype(int)])
|
101
|
+
mpc['branch'][:, 0] = np.array([bus_map[busi0] for busi0 in mpc['branch'][:, 0].astype(int)])
|
102
|
+
mpc['branch'][:, 1] = np.array([bus_map[busi0] for busi0 in mpc['branch'][:, 1].astype(int)])
|
103
|
+
return mpc
|
ams/io/xlsx.py
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
"""
|
2
|
+
Excel reader and writer for AMS.
|
3
|
+
|
4
|
+
This module leverages the existing parser and writer in andes.io.xlsx.
|
5
|
+
"""
|
6
|
+
import logging
|
7
|
+
|
8
|
+
from andes.io.xlsx import (read, testlines, confirm_overwrite, _add_book) # NOQA
|
9
|
+
|
10
|
+
from ams.shared import pd, empty_adsys, ad_models
|
11
|
+
|
12
|
+
|
13
|
+
logger = logging.getLogger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
def write(system, outfile,
|
17
|
+
skip_empty=True, overwrite=None, add_book=None,
|
18
|
+
to_andes=False,
|
19
|
+
):
|
20
|
+
"""
|
21
|
+
Write loaded AMS system data into an xlsx file
|
22
|
+
|
23
|
+
Revised function ``andes.io.xlsx.write`` to skip non-andes models.
|
24
|
+
|
25
|
+
Parameters
|
26
|
+
----------
|
27
|
+
system : System
|
28
|
+
A loaded system with parameters
|
29
|
+
outfile : str
|
30
|
+
Path to the output file
|
31
|
+
skip_empty : bool
|
32
|
+
Skip output of empty models (n = 0)
|
33
|
+
overwrite : bool, optional
|
34
|
+
None to prompt for overwrite selection; True to overwrite; False to not overwrite
|
35
|
+
add_book : str, optional
|
36
|
+
An optional model to be added to the output spreadsheet
|
37
|
+
to_andes : bool, optional
|
38
|
+
Write to an ANDES system, where non-ANDES models are skipped
|
39
|
+
|
40
|
+
Returns
|
41
|
+
-------
|
42
|
+
bool
|
43
|
+
True if file written; False otherwise
|
44
|
+
"""
|
45
|
+
if not confirm_overwrite(outfile, overwrite=overwrite):
|
46
|
+
return False
|
47
|
+
|
48
|
+
writer = pd.ExcelWriter(outfile, engine='xlsxwriter')
|
49
|
+
writer = _write_system(system, writer, skip_empty, to_andes=to_andes)
|
50
|
+
writer = _add_book(system, writer, add_book)
|
51
|
+
|
52
|
+
writer.close()
|
53
|
+
|
54
|
+
logger.info('xlsx file written to "%s"', outfile)
|
55
|
+
return True
|
56
|
+
|
57
|
+
|
58
|
+
def _write_system(system, writer, skip_empty, to_andes=False):
|
59
|
+
"""
|
60
|
+
Write the system to pandas ExcelWriter
|
61
|
+
|
62
|
+
Rewrite function ``andes.io.xlsx._write_system`` to skip non-andes sheets.
|
63
|
+
"""
|
64
|
+
for name, instance in system.models.items():
|
65
|
+
if skip_empty and instance.n == 0:
|
66
|
+
continue
|
67
|
+
instance.cache.refresh("df_in")
|
68
|
+
if to_andes:
|
69
|
+
if name not in ad_models:
|
70
|
+
continue
|
71
|
+
# NOTE: ommit parameters that are not in ANDES
|
72
|
+
skip_params = []
|
73
|
+
ams_params = list(instance.params.keys())
|
74
|
+
andes_params = list(empty_adsys.models[name].params.keys())
|
75
|
+
skip_params = list(set(ams_params) - set(andes_params))
|
76
|
+
df = instance.cache.df_in.drop(skip_params, axis=1, errors='ignore')
|
77
|
+
else:
|
78
|
+
df = instance.cache.df_in
|
79
|
+
df.to_excel(writer, sheet_name=name, freeze_panes=(1, 0))
|
80
|
+
return writer
|
ams/main.py
CHANGED
@@ -60,9 +60,16 @@ def config_logger(stream_level=logging.INFO, *,
|
|
60
60
|
`StreamHandler` verbosity level.
|
61
61
|
file_level : {10, 20, 30, 40, 50}, optional
|
62
62
|
`FileHandler` verbosity level.
|
63
|
+
|
63
64
|
Returns
|
64
65
|
-------
|
65
66
|
None
|
67
|
+
|
68
|
+
Notes
|
69
|
+
-----
|
70
|
+
Copied from the ANDES project (https://github.com/CURENT/andes).
|
71
|
+
Original author: Hantao Cui
|
72
|
+
License: GPL3
|
66
73
|
"""
|
67
74
|
lg = logging.getLogger('ams')
|
68
75
|
lg.setLevel(logging.DEBUG)
|
@@ -105,7 +112,6 @@ def config_logger(stream_level=logging.INFO, *,
|
|
105
112
|
coloredlogs.install(logger=lg, level=stream_level, fmt=sh_formatter_str)
|
106
113
|
|
107
114
|
|
108
|
-
# TODO: check ``load`` later on to see if some of them can be removed
|
109
115
|
def load(case, setup=True,
|
110
116
|
use_input_path=True,
|
111
117
|
**kwargs):
|
@@ -131,6 +137,12 @@ def load(case, setup=True,
|
|
131
137
|
If one need to add devices in addition to these from the case
|
132
138
|
file, do ``setup=False`` and call ``System.add()`` to add devices.
|
133
139
|
When done, manually invoke ``setup()`` to set up the system.
|
140
|
+
|
141
|
+
Notes
|
142
|
+
-----
|
143
|
+
Revised from the ANDES project (https://github.com/CURENT/andes).
|
144
|
+
Original author: Hantao Cui
|
145
|
+
License: GPL3
|
134
146
|
"""
|
135
147
|
if use_input_path:
|
136
148
|
input_path = kwargs.get('input_path', '')
|
@@ -180,6 +192,12 @@ def run_case(case, *, routine='pflow', profile=False,
|
|
180
192
|
add_book : str, optional
|
181
193
|
Name of the device to be added to an excel case
|
182
194
|
as a new sheet.
|
195
|
+
|
196
|
+
Notes
|
197
|
+
-----
|
198
|
+
Revised from the ANDES project (https://github.com/CURENT/andes).
|
199
|
+
Original author: Hantao Cui
|
200
|
+
License: GPL3
|
183
201
|
"""
|
184
202
|
|
185
203
|
pr = cProfile.Profile()
|
@@ -249,6 +267,12 @@ def _run_mp_proc(cases, ncpu=NCPUS_PHYSICAL, **kwargs):
|
|
249
267
|
Run multiprocessing with `Process`.
|
250
268
|
|
251
269
|
Return values from `run_case` are not preserved. Always return `True` when done.
|
270
|
+
|
271
|
+
Notes
|
272
|
+
-----
|
273
|
+
Copied from the ANDES project (https://github.com/CURENT/andes).
|
274
|
+
Original author: Hantao Cui
|
275
|
+
License: GPL3
|
252
276
|
"""
|
253
277
|
|
254
278
|
# start processes
|
@@ -283,6 +307,12 @@ def _run_mp_pool(cases, ncpu=NCPUS_PHYSICAL, verbose=logging.INFO, **kwargs):
|
|
283
307
|
Verbosity level during multiprocessing
|
284
308
|
verbose : 10, 20, 30, 40, 50
|
285
309
|
Verbosity level outside multiprocessing
|
310
|
+
|
311
|
+
Notes
|
312
|
+
-----
|
313
|
+
Copied from the ANDES project (https://github.com/CURENT/andes).
|
314
|
+
Original author: Hantao Cui
|
315
|
+
License: GPL3
|
286
316
|
"""
|
287
317
|
|
288
318
|
pool = Pool(ncpu)
|
@@ -337,6 +367,11 @@ def run(filename, input_path='', verbose=20, mp_verbose=30,
|
|
337
367
|
System or exit_code
|
338
368
|
An instance of system (if `cli == False`) or an exit code otherwise..
|
339
369
|
|
370
|
+
Notes
|
371
|
+
-----
|
372
|
+
Copied from the ANDES project (https://github.com/CURENT/andes).
|
373
|
+
Original author: Hantao Cui
|
374
|
+
License: GPL3
|
340
375
|
"""
|
341
376
|
|
342
377
|
if is_interactive() and len(logger.handlers) == 0:
|
@@ -459,6 +494,12 @@ def misc(edit_config='', save_config='', show_license=False, clean=True, recursi
|
|
459
494
|
overwrite=None, version=False, **kwargs):
|
460
495
|
"""
|
461
496
|
Miscellaneous commands.
|
497
|
+
|
498
|
+
Notes
|
499
|
+
-----
|
500
|
+
Copied from the ANDES project (https://github.com/CURENT/andes).
|
501
|
+
Original author: Hantao Cui
|
502
|
+
License: GPL3
|
462
503
|
"""
|
463
504
|
|
464
505
|
if edit_conf(edit_config):
|
@@ -487,6 +528,12 @@ def misc(edit_config='', save_config='', show_license=False, clean=True, recursi
|
|
487
528
|
def doc(attribute=None, list_supported=False, config=False, **kwargs):
|
488
529
|
"""
|
489
530
|
Quick documentation from command-line.
|
531
|
+
|
532
|
+
Notes
|
533
|
+
-----
|
534
|
+
Revised from the ANDES project (https://github.com/CURENT/andes).
|
535
|
+
Original author: Hantao Cui
|
536
|
+
License: GPL3
|
490
537
|
"""
|
491
538
|
system = System()
|
492
539
|
if attribute is not None:
|
@@ -512,19 +559,25 @@ def demo(**kwargs):
|
|
512
559
|
def versioninfo():
|
513
560
|
"""
|
514
561
|
Print version info for AMS and dependencies.
|
562
|
+
|
563
|
+
Notes
|
564
|
+
-----
|
565
|
+
Revised from the ANDES project (https://github.com/CURENT/andes).
|
566
|
+
Original author: Hantao Cui
|
567
|
+
License: GPL3
|
515
568
|
"""
|
516
569
|
|
517
570
|
import numpy as np
|
518
571
|
import cvxpy
|
519
572
|
import andes
|
520
|
-
from ams.shared import
|
573
|
+
from ams.shared import installed_solvers
|
521
574
|
|
522
575
|
versions = {'Python': platform.python_version(),
|
523
576
|
'ams': get_versions()['version'],
|
524
577
|
'andes': andes.__version__,
|
525
578
|
'numpy': np.__version__,
|
526
579
|
'cvxpy': cvxpy.__version__,
|
527
|
-
'solvers': ', '.join(
|
580
|
+
'solvers': ', '.join(installed_solvers),
|
528
581
|
}
|
529
582
|
maxwidth = max([len(k) for k in versions.keys()])
|
530
583
|
|
@@ -574,6 +627,12 @@ def edit_conf(edit_config: Optional[Union[str, bool]] = ''):
|
|
574
627
|
-------
|
575
628
|
bool
|
576
629
|
``True`` is a config file is found and an editor is opened. ``False`` if ``edit_config`` is False.
|
630
|
+
|
631
|
+
Notes
|
632
|
+
-----
|
633
|
+
Copied from the ANDES project (https://github.com/CURENT/andes).
|
634
|
+
Original author: Hantao Cui
|
635
|
+
License: GPL3
|
577
636
|
"""
|
578
637
|
ret = False
|
579
638
|
|
@@ -627,6 +686,12 @@ def save_conf(config_path=None, overwrite=None, **kwargs):
|
|
627
686
|
-------
|
628
687
|
bool
|
629
688
|
``True`` is the save action is run. ``False`` otherwise.
|
689
|
+
|
690
|
+
Notes
|
691
|
+
-----
|
692
|
+
Copied from the ANDES project (https://github.com/CURENT/andes).
|
693
|
+
Original author: Hantao Cui
|
694
|
+
License: GPL3
|
630
695
|
"""
|
631
696
|
ret = False
|
632
697
|
|
@@ -644,7 +709,7 @@ def save_conf(config_path=None, overwrite=None, **kwargs):
|
|
644
709
|
return ret
|
645
710
|
|
646
711
|
|
647
|
-
# TODO:
|
712
|
+
# TODO: change to AMS output types
|
648
713
|
def remove_output(recursive=False):
|
649
714
|
"""
|
650
715
|
Remove the outputs generated by AMS, including power flow reports
|
@@ -661,6 +726,12 @@ def remove_output(recursive=False):
|
|
661
726
|
bool
|
662
727
|
``True`` is the function body executes with success. ``False``
|
663
728
|
otherwise.
|
729
|
+
|
730
|
+
Notes
|
731
|
+
-----
|
732
|
+
Copied from the ANDES project (https://github.com/CURENT/andes).
|
733
|
+
Original author: Hantao Cui
|
734
|
+
License: GPL3
|
664
735
|
"""
|
665
736
|
found = False
|
666
737
|
cwd = os.getcwd()
|
@@ -690,6 +761,12 @@ def remove_output(recursive=False):
|
|
690
761
|
def selftest(quick=False, extra=False, **kwargs):
|
691
762
|
"""
|
692
763
|
Run unit tests.
|
764
|
+
|
765
|
+
Notes
|
766
|
+
-----
|
767
|
+
Copied from the ANDES project (https://github.com/CURENT/andes).
|
768
|
+
Original author: Hantao Cui
|
769
|
+
License: GPL3
|
693
770
|
"""
|
694
771
|
|
695
772
|
# map verbosity level from logging to unittest
|
ams/models/__init__.py
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
"""
|
2
|
+
The package for models used in scheduling modeling.
|
3
|
+
|
4
|
+
The file_classes includes the list of file classes and their corresponding classes.
|
5
|
+
"""
|
6
|
+
|
7
|
+
|
8
|
+
ams_file_classes = list([
|
9
|
+
('info', ['Summary']),
|
10
|
+
('bus', ['Bus']),
|
11
|
+
('static', ['PQ', 'Slack', 'PV']),
|
12
|
+
('shunt', ['Shunt']),
|
13
|
+
('line', ['Line', 'Jumper']),
|
14
|
+
('distributed', ['PVD1', 'ESD1', 'EV1', 'EV2']),
|
15
|
+
('renewable', ['REGCA1', 'REGCV1', 'REGCV2']),
|
16
|
+
('area', ['Area']),
|
17
|
+
('zone', ['Zone']),
|
18
|
+
('reserve', ['SFR', 'SR', 'NSR', 'VSGR']),
|
19
|
+
('cost', ['GCost', 'SFRCost', 'SRCost', 'NSRCost', 'VSGCost']),
|
20
|
+
('cost', ['DCost']),
|
21
|
+
('timeslot', ['TimeSlot', 'EDTSlot', 'UCTSlot']),
|
22
|
+
])
|
23
|
+
|
24
|
+
file_classes = ams_file_classes
|
ams/models/area.py
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from andes.models.area import AreaData
|
4
|
+
from andes.utils.tab import Tab
|
5
|
+
from andes.core.service import BackRef
|
6
|
+
|
7
|
+
from ams.core.model import Model
|
8
|
+
|
9
|
+
logger = logging.getLogger(__name__)
|
10
|
+
|
11
|
+
|
12
|
+
class Area(AreaData, Model):
|
13
|
+
"""
|
14
|
+
Area model.
|
15
|
+
"""
|
16
|
+
def __init__(self, system, config):
|
17
|
+
AreaData.__init__(self)
|
18
|
+
Model.__init__(self, system, config)
|
19
|
+
|
20
|
+
self.group = 'Collection'
|
21
|
+
|
22
|
+
self.Bus = BackRef()
|
23
|
+
self.ACTopology = BackRef()
|
24
|
+
|
25
|
+
def bus_table(self):
|
26
|
+
"""
|
27
|
+
Return a formatted table with area idx and bus idx correspondence
|
28
|
+
|
29
|
+
Returns
|
30
|
+
-------
|
31
|
+
str
|
32
|
+
Formatted table
|
33
|
+
|
34
|
+
"""
|
35
|
+
if self.n:
|
36
|
+
header = ['Area ID', 'Bus ID']
|
37
|
+
rows = [(i, j) for i, j in zip(self.idx.v, self.Bus.v)]
|
38
|
+
return Tab(header=header, data=rows).draw()
|
39
|
+
else:
|
40
|
+
return ''
|
ams/models/bus.py
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
import numpy as np
|
4
|
+
|
5
|
+
from andes.core.param import NumParam
|
6
|
+
from andes.models.bus import BusData
|
7
|
+
|
8
|
+
from ams.core.var import Algeb
|
9
|
+
from ams.core.model import Model
|
10
|
+
|
11
|
+
logger = logging.getLogger(__name__)
|
12
|
+
|
13
|
+
|
14
|
+
class Bus(BusData, Model):
|
15
|
+
"""
|
16
|
+
AC Bus model.
|
17
|
+
"""
|
18
|
+
|
19
|
+
def __init__(self, system, config):
|
20
|
+
BusData.__init__(self)
|
21
|
+
Model.__init__(self, system, config)
|
22
|
+
|
23
|
+
self.group = 'ACTopology'
|
24
|
+
# NOTE: in ANDES, self.zone is defined to trace a non-existing model "Zone"
|
25
|
+
# in AMS, model "Zone" is developed,
|
26
|
+
# so we need to change the model name of IdxParam self.zone
|
27
|
+
self.zone.model = 'Zone'
|
28
|
+
|
29
|
+
self.type = NumParam(name='type',
|
30
|
+
info='bus type, 1=PQ, 2=PV, 3=ref, 4=isolated (place holder)',
|
31
|
+
default=1,
|
32
|
+
vtype=int,
|
33
|
+
)
|
34
|
+
|
35
|
+
self.a = Algeb(name='a',
|
36
|
+
tex_name=r'\theta',
|
37
|
+
info='voltage angle',
|
38
|
+
unit='rad',
|
39
|
+
)
|
40
|
+
self.v = Algeb(name='v',
|
41
|
+
tex_name='V',
|
42
|
+
info='voltage magnitude',
|
43
|
+
unit='p.u.',
|
44
|
+
)
|
45
|
+
|
46
|
+
# island information
|
47
|
+
self.n_islanded_buses = 0
|
48
|
+
self.island_sets = list()
|
49
|
+
self.islanded_buses = list() # list of lists containing bus uid of islands
|
50
|
+
self.islands = list() # same as the above
|
51
|
+
self.islanded_a = np.array([])
|
52
|
+
self.islanded_v = np.array([])
|
ams/models/cost.py
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
"""
|
2
|
+
Cost model.
|
3
|
+
"""
|
4
|
+
|
5
|
+
from andes.core import (ModelData, IdxParam, NumParam)
|
6
|
+
|
7
|
+
from ams.core.model import Model
|
8
|
+
|
9
|
+
|
10
|
+
class GCostData(ModelData):
|
11
|
+
def __init__(self):
|
12
|
+
super().__init__()
|
13
|
+
self.gen = IdxParam(info="static generator index",
|
14
|
+
model='StaticGen',
|
15
|
+
mandatory=True,
|
16
|
+
)
|
17
|
+
self.type = NumParam(default=2,
|
18
|
+
info='Cost model type. 1 for piecewise linear, 2 for polynomial',
|
19
|
+
power=False,
|
20
|
+
tex_name=r't_{ype}',
|
21
|
+
vrange=(1, 2),
|
22
|
+
)
|
23
|
+
self.csu = NumParam(default=0,
|
24
|
+
info='startup cost in US dollars',
|
25
|
+
power=False,
|
26
|
+
tex_name=r'c_{su}',
|
27
|
+
unit='$',
|
28
|
+
)
|
29
|
+
self.csd = NumParam(default=0,
|
30
|
+
info='shutdown cost in US dollars',
|
31
|
+
power=False,
|
32
|
+
tex_name=r'c_{sd}',
|
33
|
+
unit='$',
|
34
|
+
)
|
35
|
+
self.c2 = NumParam(default=0,
|
36
|
+
info='coefficient 2',
|
37
|
+
power=False,
|
38
|
+
tex_name=r'c_{2}',
|
39
|
+
unit=r'$/(p.u.*h)^2',
|
40
|
+
)
|
41
|
+
self.c1 = NumParam(default=0,
|
42
|
+
info='coefficient 1',
|
43
|
+
power=False,
|
44
|
+
tex_name=r'c_{1}',
|
45
|
+
unit=r'$/p.u.*h',
|
46
|
+
)
|
47
|
+
self.c0 = NumParam(default=0,
|
48
|
+
info='coefficient 0',
|
49
|
+
power=False,
|
50
|
+
tex_name=r'c_{0}',
|
51
|
+
unit=r'$',
|
52
|
+
)
|
53
|
+
|
54
|
+
|
55
|
+
# TODO: double check the picewise linear part documentation
|
56
|
+
class GCost(GCostData, Model):
|
57
|
+
"""
|
58
|
+
Generator cost model, similar to MATPOWER ``gencost`` format.
|
59
|
+
|
60
|
+
``type`` is the cost model type. 1 for piecewise linear, 2 for polynomial.
|
61
|
+
|
62
|
+
In piecewise linear cost model, cost function f(p) is defined by a set of points:
|
63
|
+
(p0, c0), (p1, c1), (p2, c2), where p0 < p1 < p2.
|
64
|
+
|
65
|
+
In quadratic cost model, cost function f(p) is defined by a set of coefficients:
|
66
|
+
f(p) = c2 * p^2 + c1 * p + c0.
|
67
|
+
"""
|
68
|
+
|
69
|
+
def __init__(self, system, config):
|
70
|
+
GCostData.__init__(self)
|
71
|
+
Model.__init__(self, system, config)
|
72
|
+
self.group = 'Cost'
|
73
|
+
|
74
|
+
|
75
|
+
class SFRCost(ModelData, Model):
|
76
|
+
"""
|
77
|
+
Linear SFR cost model.
|
78
|
+
"""
|
79
|
+
|
80
|
+
def __init__(self, system, config):
|
81
|
+
ModelData.__init__(self)
|
82
|
+
Model.__init__(self, system, config)
|
83
|
+
self.group = 'Cost'
|
84
|
+
self.gen = IdxParam(info="static generator index",
|
85
|
+
model='StaticGen',
|
86
|
+
mandatory=True,)
|
87
|
+
self.cru = NumParam(default=0,
|
88
|
+
tex_name=r'c_{r}', unit=r'$/(p.u.*h)',
|
89
|
+
info='cost for RegUp reserve',)
|
90
|
+
self.crd = NumParam(default=0,
|
91
|
+
tex_name=r'c_{r}', unit=r'$/(p.u.*h)',
|
92
|
+
info='cost for RegDn reserve',)
|
93
|
+
|
94
|
+
|
95
|
+
class SRCost(ModelData, Model):
|
96
|
+
"""
|
97
|
+
Linear spinning reserve cost model.
|
98
|
+
"""
|
99
|
+
|
100
|
+
def __init__(self, system, config):
|
101
|
+
ModelData.__init__(self)
|
102
|
+
Model.__init__(self, system, config)
|
103
|
+
self.gen = IdxParam(info="static generator index",
|
104
|
+
model='StaticGen',
|
105
|
+
mandatory=True,)
|
106
|
+
self.csr = NumParam(default=0,
|
107
|
+
tex_name=r'c_{sr}', unit=r'$/(p.u.*h)',
|
108
|
+
info='cost for spinning reserve',)
|
109
|
+
|
110
|
+
|
111
|
+
class NSRCost(ModelData, Model):
|
112
|
+
"""
|
113
|
+
Linear non-spinning reserve cost model.
|
114
|
+
"""
|
115
|
+
|
116
|
+
def __init__(self, system, config):
|
117
|
+
ModelData.__init__(self)
|
118
|
+
Model.__init__(self, system, config)
|
119
|
+
self.gen = IdxParam(info="static generator index",
|
120
|
+
model='StaticGen',
|
121
|
+
mandatory=True,)
|
122
|
+
self.cnsr = NumParam(default=0,
|
123
|
+
tex_name=r'c_{nsr}', unit=r'$/(p.u.*h)',
|
124
|
+
info='cost for non-spinning reserve',)
|
125
|
+
|
126
|
+
|
127
|
+
class DCost(ModelData, Model):
|
128
|
+
"""
|
129
|
+
Linear cost model for dispatchable loads.
|
130
|
+
"""
|
131
|
+
def __init__(self, system, config):
|
132
|
+
ModelData.__init__(self)
|
133
|
+
Model.__init__(self, system, config)
|
134
|
+
self.group = 'Cost'
|
135
|
+
self.pq = IdxParam(info="static load index",
|
136
|
+
mandatory=True,)
|
137
|
+
self.cdp = NumParam(default=999,
|
138
|
+
tex_name=r'c_{d,p}', unit=r'$/(p.u.*h)',
|
139
|
+
info='cost for unserve load penalty',)
|
140
|
+
|
141
|
+
|
142
|
+
class VSGCostData(ModelData):
|
143
|
+
def __init__(self):
|
144
|
+
super().__init__()
|
145
|
+
self.reg = IdxParam(info="Renewable generator idx",
|
146
|
+
model='RenGen',
|
147
|
+
mandatory=True,
|
148
|
+
)
|
149
|
+
self.cm = NumParam(default=0,
|
150
|
+
info='cost for emulated inertia (M)',
|
151
|
+
tex_name=r'c_{r}',
|
152
|
+
unit=r'$/s',
|
153
|
+
)
|
154
|
+
self.cd = NumParam(default=0,
|
155
|
+
info='cost for emulated damping (D)',
|
156
|
+
tex_name=r'c_{r}',
|
157
|
+
unit=r'$/p.u.',
|
158
|
+
)
|
159
|
+
|
160
|
+
|
161
|
+
class VSGCost(VSGCostData, Model):
|
162
|
+
"""
|
163
|
+
Linear cost model for VSG emulated inertia (M) and damping (D).
|
164
|
+
"""
|
165
|
+
|
166
|
+
def __init__(self, system, config):
|
167
|
+
VSGCostData.__init__(self)
|
168
|
+
Model.__init__(self, system, config)
|
169
|
+
self.group = 'Cost'
|