ltbams 0.9.9__py3-none-any.whl → 1.0.2a1__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.
Files changed (191) hide show
  1. ams/__init__.py +4 -11
  2. ams/_version.py +3 -3
  3. ams/cases/5bus/pjm5bus_demo.xlsx +0 -0
  4. ams/cases/5bus/pjm5bus_jumper.xlsx +0 -0
  5. ams/cases/5bus/pjm5bus_uced.json +1062 -0
  6. ams/cases/5bus/pjm5bus_uced.xlsx +0 -0
  7. ams/cases/5bus/pjm5bus_uced_esd1.xlsx +0 -0
  8. ams/cases/5bus/pjm5bus_uced_ev.xlsx +0 -0
  9. ams/cases/ieee123/ieee123.xlsx +0 -0
  10. ams/cases/ieee123/ieee123_regcv1.xlsx +0 -0
  11. ams/cases/ieee14/ieee14.json +1166 -0
  12. ams/cases/ieee14/ieee14.raw +92 -0
  13. ams/cases/ieee14/ieee14_conn.xlsx +0 -0
  14. ams/cases/ieee14/ieee14_uced.xlsx +0 -0
  15. ams/cases/ieee39/ieee39.xlsx +0 -0
  16. ams/cases/ieee39/ieee39_uced.xlsx +0 -0
  17. ams/cases/ieee39/ieee39_uced_esd1.xlsx +0 -0
  18. ams/cases/ieee39/ieee39_uced_pvd1.xlsx +0 -0
  19. ams/cases/ieee39/ieee39_uced_vis.xlsx +0 -0
  20. ams/cases/matpower/benchmark.json +1594 -0
  21. ams/cases/matpower/case118.m +787 -0
  22. ams/cases/matpower/case14.m +129 -0
  23. ams/cases/matpower/case300.m +1315 -0
  24. ams/cases/matpower/case39.m +205 -0
  25. ams/cases/matpower/case5.m +62 -0
  26. ams/cases/matpower/case_ACTIVSg2000.m +9460 -0
  27. ams/cases/npcc/npcc.m +644 -0
  28. ams/cases/npcc/npcc_uced.xlsx +0 -0
  29. ams/cases/pglib/pglib_opf_case39_epri__api.m +243 -0
  30. ams/cases/wecc/wecc.m +714 -0
  31. ams/cases/wecc/wecc_uced.xlsx +0 -0
  32. ams/cli.py +6 -0
  33. ams/core/__init__.py +2 -0
  34. ams/core/documenter.py +652 -0
  35. ams/core/matprocessor.py +782 -0
  36. ams/core/model.py +330 -0
  37. ams/core/param.py +322 -0
  38. ams/core/service.py +918 -0
  39. ams/core/symprocessor.py +224 -0
  40. ams/core/var.py +59 -0
  41. ams/extension/__init__.py +5 -0
  42. ams/extension/eva.py +401 -0
  43. ams/interface.py +1085 -0
  44. ams/io/__init__.py +133 -0
  45. ams/io/json.py +82 -0
  46. ams/io/matpower.py +406 -0
  47. ams/io/psse.py +6 -0
  48. ams/io/pypower.py +103 -0
  49. ams/io/xlsx.py +80 -0
  50. ams/main.py +81 -4
  51. ams/models/__init__.py +24 -0
  52. ams/models/area.py +40 -0
  53. ams/models/bus.py +52 -0
  54. ams/models/cost.py +169 -0
  55. ams/models/distributed/__init__.py +3 -0
  56. ams/models/distributed/esd1.py +71 -0
  57. ams/models/distributed/ev.py +60 -0
  58. ams/models/distributed/pvd1.py +67 -0
  59. ams/models/group.py +231 -0
  60. ams/models/info.py +26 -0
  61. ams/models/line.py +238 -0
  62. ams/models/renewable/__init__.py +5 -0
  63. ams/models/renewable/regc.py +119 -0
  64. ams/models/reserve.py +94 -0
  65. ams/models/shunt.py +14 -0
  66. ams/models/static/__init__.py +2 -0
  67. ams/models/static/gen.py +165 -0
  68. ams/models/static/pq.py +61 -0
  69. ams/models/timeslot.py +69 -0
  70. ams/models/zone.py +49 -0
  71. ams/opt/__init__.py +12 -0
  72. ams/opt/constraint.py +175 -0
  73. ams/opt/exprcalc.py +127 -0
  74. ams/opt/expression.py +188 -0
  75. ams/opt/objective.py +174 -0
  76. ams/opt/omodel.py +432 -0
  77. ams/opt/optzbase.py +192 -0
  78. ams/opt/param.py +156 -0
  79. ams/opt/var.py +233 -0
  80. ams/pypower/__init__.py +8 -0
  81. ams/pypower/_compat.py +9 -0
  82. ams/pypower/core/__init__.py +8 -0
  83. ams/pypower/core/pips.py +894 -0
  84. ams/pypower/core/ppoption.py +244 -0
  85. ams/pypower/core/ppver.py +18 -0
  86. ams/pypower/core/solver.py +2451 -0
  87. ams/pypower/eps.py +6 -0
  88. ams/pypower/idx.py +174 -0
  89. ams/pypower/io.py +604 -0
  90. ams/pypower/make/__init__.py +11 -0
  91. ams/pypower/make/matrices.py +665 -0
  92. ams/pypower/make/pdv.py +506 -0
  93. ams/pypower/routines/__init__.py +7 -0
  94. ams/pypower/routines/cpf.py +513 -0
  95. ams/pypower/routines/cpf_callbacks.py +114 -0
  96. ams/pypower/routines/opf.py +1803 -0
  97. ams/pypower/routines/opffcns.py +1946 -0
  98. ams/pypower/routines/pflow.py +852 -0
  99. ams/pypower/toggle.py +1098 -0
  100. ams/pypower/utils.py +293 -0
  101. ams/report.py +212 -50
  102. ams/routines/__init__.py +23 -0
  103. ams/routines/acopf.py +117 -0
  104. ams/routines/cpf.py +65 -0
  105. ams/routines/dcopf.py +241 -0
  106. ams/routines/dcpf.py +209 -0
  107. ams/routines/dcpf0.py +196 -0
  108. ams/routines/dopf.py +150 -0
  109. ams/routines/ed.py +312 -0
  110. ams/routines/pflow.py +255 -0
  111. ams/routines/pflow0.py +113 -0
  112. ams/routines/routine.py +1033 -0
  113. ams/routines/rted.py +519 -0
  114. ams/routines/type.py +160 -0
  115. ams/routines/uc.py +376 -0
  116. ams/shared.py +63 -9
  117. ams/system.py +61 -22
  118. ams/utils/__init__.py +3 -0
  119. ams/utils/misc.py +77 -0
  120. ams/utils/paths.py +257 -0
  121. docs/Makefile +21 -0
  122. docs/make.bat +35 -0
  123. docs/source/_templates/autosummary/base.rst +5 -0
  124. docs/source/_templates/autosummary/class.rst +35 -0
  125. docs/source/_templates/autosummary/module.rst +65 -0
  126. docs/source/_templates/autosummary/module_toctree.rst +66 -0
  127. docs/source/api.rst +102 -0
  128. docs/source/conf.py +203 -0
  129. docs/source/examples/index.rst +34 -0
  130. docs/source/genmodelref.py +61 -0
  131. docs/source/genroutineref.py +47 -0
  132. docs/source/getting_started/copyright.rst +20 -0
  133. docs/source/getting_started/formats/index.rst +20 -0
  134. docs/source/getting_started/formats/matpower.rst +183 -0
  135. docs/source/getting_started/formats/psse.rst +46 -0
  136. docs/source/getting_started/formats/pypower.rst +223 -0
  137. docs/source/getting_started/formats/xlsx.png +0 -0
  138. docs/source/getting_started/formats/xlsx.rst +23 -0
  139. docs/source/getting_started/index.rst +76 -0
  140. docs/source/getting_started/install.rst +234 -0
  141. docs/source/getting_started/overview.rst +26 -0
  142. docs/source/getting_started/testcase.rst +45 -0
  143. docs/source/getting_started/verification.rst +13 -0
  144. docs/source/images/curent.ico +0 -0
  145. docs/source/images/dcopf_time.png +0 -0
  146. docs/source/images/sponsors/CURENT_Logo_NameOnTrans.png +0 -0
  147. docs/source/images/sponsors/CURENT_Logo_Transparent.png +0 -0
  148. docs/source/images/sponsors/CURENT_Logo_Transparent_Name.png +0 -0
  149. docs/source/images/sponsors/doe.png +0 -0
  150. docs/source/index.rst +108 -0
  151. docs/source/modeling/example.rst +159 -0
  152. docs/source/modeling/index.rst +17 -0
  153. docs/source/modeling/model.rst +210 -0
  154. docs/source/modeling/routine.rst +122 -0
  155. docs/source/modeling/system.rst +51 -0
  156. docs/source/release-notes.rst +398 -0
  157. ltbams-1.0.2a1.dist-info/METADATA +210 -0
  158. ltbams-1.0.2a1.dist-info/RECORD +188 -0
  159. {ltbams-0.9.9.dist-info → ltbams-1.0.2a1.dist-info}/WHEEL +1 -1
  160. ltbams-1.0.2a1.dist-info/top_level.txt +3 -0
  161. tests/__init__.py +0 -0
  162. tests/test_1st_system.py +33 -0
  163. tests/test_addressing.py +40 -0
  164. tests/test_andes_mats.py +61 -0
  165. tests/test_case.py +266 -0
  166. tests/test_cli.py +34 -0
  167. tests/test_export_csv.py +89 -0
  168. tests/test_group.py +83 -0
  169. tests/test_interface.py +216 -0
  170. tests/test_io.py +32 -0
  171. tests/test_jumper.py +27 -0
  172. tests/test_known_good.py +267 -0
  173. tests/test_matp.py +437 -0
  174. tests/test_model.py +54 -0
  175. tests/test_omodel.py +119 -0
  176. tests/test_paths.py +22 -0
  177. tests/test_report.py +251 -0
  178. tests/test_repr.py +21 -0
  179. tests/test_routine.py +178 -0
  180. tests/test_rtn_dcopf.py +101 -0
  181. tests/test_rtn_dcpf.py +77 -0
  182. tests/test_rtn_ed.py +279 -0
  183. tests/test_rtn_pflow.py +219 -0
  184. tests/test_rtn_rted.py +273 -0
  185. tests/test_rtn_uc.py +248 -0
  186. tests/test_service.py +73 -0
  187. ltbams-0.9.9.dist-info/LICENSE +0 -692
  188. ltbams-0.9.9.dist-info/METADATA +0 -859
  189. ltbams-0.9.9.dist-info/RECORD +0 -14
  190. ltbams-0.9.9.dist-info/top_level.txt +0 -1
  191. {ltbams-0.9.9.dist-info → ltbams-1.0.2a1.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 INSTALLED_SOLVERS
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(INSTALLED_SOLVERS),
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: list AMS output files here
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'
@@ -0,0 +1,3 @@
1
+ from ams.models.distributed.pvd1 import PVD1 # NOQA
2
+ from ams.models.distributed.esd1 import ESD1 # NOQA
3
+ from ams.models.distributed.ev import EV1, EV2 # NOQA