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.
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 +206 -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 +231 -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.2.dist-info/METADATA +215 -0
  158. ltbams-1.0.2.dist-info/RECORD +188 -0
  159. {ltbams-0.9.9.dist-info → ltbams-1.0.2.dist-info}/WHEEL +1 -1
  160. ltbams-1.0.2.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.2.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,119 @@
1
+ """
2
+ RenGen scheduling model.
3
+ """
4
+
5
+ from andes.core.param import NumParam, IdxParam, ExtParam
6
+ from andes.core.model import ModelData
7
+ from ams.core.model import Model
8
+
9
+
10
+ class REGCData(ModelData):
11
+ """
12
+ Data container for RenGen scheduling model.
13
+ """
14
+
15
+ def __init__(self):
16
+ ModelData.__init__(self)
17
+ self.bus = IdxParam(model='Bus',
18
+ info="interface bus idx",
19
+ mandatory=True,
20
+ )
21
+ self.gen = IdxParam(info="static generator index",
22
+ mandatory=True,
23
+ )
24
+ self.Sn = NumParam(default=100.0, tex_name='S_n',
25
+ info='device MVA rating',
26
+ unit='MVA',
27
+ )
28
+ self.gammap = NumParam(default=1.0,
29
+ info="P ratio of linked static gen",
30
+ tex_name=r'\gamma_P'
31
+ )
32
+ self.gammaq = NumParam(default=1.0,
33
+ info="Q ratio of linked static gen",
34
+ tex_name=r'\gamma_Q'
35
+ )
36
+
37
+
38
+ class REGCA1(REGCData, Model):
39
+ """
40
+ Renewable generator scheduling model.
41
+
42
+ Reference:
43
+
44
+ [1] ANDES Documentation, REGCA1
45
+
46
+ Available:
47
+ https://docs.andes.app/en/latest/groupdoc/RenGen.html#regca1
48
+ """
49
+
50
+ def __init__(self, system=None, config=None) -> None:
51
+ REGCData.__init__(self)
52
+ Model.__init__(self, system, config)
53
+ self.group = 'RenGen'
54
+
55
+
56
+ class REGCV1(REGCData, Model):
57
+ """
58
+ Voltage-controlled converter model (virtual synchronous generator) with
59
+ inertia emulation.
60
+
61
+ Here Mmax and Dmax are assumed to be constant, but they might subject to
62
+ the operating condition of the converter.
63
+
64
+ Notes
65
+ -----
66
+ - The generation is defined by group :ref:`StaticGen`
67
+ - Generation cost is defined by model :ref:`GCost`
68
+ - Inertia emulation cost is defined by model :ref:`VSGCost`
69
+
70
+ Reference:
71
+
72
+ [1] ANDES Documentation, REGCV1
73
+
74
+ Available:
75
+
76
+ https://docs.andes.app/en/latest/groupdoc/RenGen.html#regcv1
77
+ """
78
+
79
+ def __init__(self, system=None, config=None) -> None:
80
+ REGCData.__init__(self)
81
+ Model.__init__(self, system, config)
82
+ self.group = 'VSG'
83
+ self.zone = ExtParam(model='Bus', src='zone',
84
+ indexer=self.bus, export=False,
85
+ info='Retrieved zone idx',
86
+ vtype=str, default=None)
87
+ self.M = NumParam(default=10, tex_name='M',
88
+ info='Inertia emulation',
89
+ unit='s',
90
+ power=True,)
91
+ self.D = NumParam(default=0, tex_name='D',
92
+ info='Damping emulation',
93
+ unit='p.u.',
94
+ power=True,)
95
+ self.Mmax = NumParam(default=99, tex_name='M_{max}',
96
+ info='Maximum inertia emulation',
97
+ unit='s',
98
+ power=True,)
99
+ self.Dmax = NumParam(default=99, tex_name='D_{max}',
100
+ info='Maximum damping emulation',
101
+ unit='p.u.',
102
+ power=True,)
103
+
104
+
105
+ class REGCV2(REGCV1):
106
+ """
107
+ Voltage-controlled VSC, identical to :ref:`REGCV1`.
108
+
109
+ Reference:
110
+
111
+ [1] ANDES Documentation, REGCV2
112
+
113
+ Available:
114
+
115
+ https://docs.andes.app/en/latest/groupdoc/RenGen.html#regcv2
116
+ """
117
+
118
+ def __init__(self, system=None, config=None) -> None:
119
+ REGCV1.__init__(self, system, config)
ams/models/reserve.py ADDED
@@ -0,0 +1,94 @@
1
+ """
2
+ Reserve model.
3
+ """
4
+
5
+ from andes.core import (ModelData, IdxParam, NumParam)
6
+
7
+ from ams.core.model import Model
8
+
9
+
10
+ class ReserveData(ModelData):
11
+ def __init__(self):
12
+ super().__init__()
13
+ self.zone = IdxParam(model='Zone',
14
+ default=None, info="Zone code",)
15
+
16
+
17
+ class SFR(ReserveData, Model):
18
+ """
19
+ Zonal secondary frequency reserve (SFR) model.
20
+
21
+ Notes
22
+ -----
23
+ - ``Zone`` model is required for this model, and zone is defined by Param ``Bus.zone``.
24
+ """
25
+
26
+ def __init__(self, system, config):
27
+ ReserveData.__init__(self)
28
+ Model.__init__(self, system, config)
29
+ self.group = 'Reserve'
30
+ self.du = NumParam(default=0,
31
+ info='Zonal RegUp reserve demand',
32
+ tex_name=r'd_{u}',)
33
+ self.dd = NumParam(default=0,
34
+ info='Zonal RegDown reserve demand',
35
+ tex_name=r'd_{d}',)
36
+
37
+
38
+ class SR(ReserveData, Model):
39
+ """
40
+ Zonal spinning reserve (SR) model.
41
+
42
+ Notes
43
+ -----
44
+ - ``Zone`` model is required for this model, and zone is defined by Param ``Bus.zone``.
45
+ - ``demand`` is multiplied to online unused generation capacity.
46
+ """
47
+
48
+ def __init__(self, system, config):
49
+ ReserveData.__init__(self)
50
+ Model.__init__(self, system, config)
51
+ self.group = 'Reserve'
52
+ self.demand = NumParam(default=0.1,
53
+ info='Zonal spinning reserve demand',
54
+ tex_name=r'd_{SR}')
55
+
56
+
57
+ class NSR(ReserveData, Model):
58
+ """
59
+ Zonal non-spinning reserve (NSR) model.
60
+
61
+ Notes
62
+ -----
63
+ - ``Zone`` model is required for this model, and zone is defined by Param ``Bus.zone``.
64
+ - ``demand`` is multiplied to offline generation capacity.
65
+ """
66
+
67
+ def __init__(self, system, config):
68
+ ReserveData.__init__(self)
69
+ Model.__init__(self, system, config)
70
+ self.group = 'Reserve'
71
+ self.demand = NumParam(default=0.1,
72
+ info='Zonal non-spinning reserve demand',
73
+ tex_name=r'd_{NSR}')
74
+
75
+
76
+ class VSGR(ReserveData, Model):
77
+ """
78
+ Zonal VSG provided reserve model.
79
+
80
+ Notes
81
+ -----
82
+ - ``Zone`` model is required for this model, and zone is defined by Param ``Bus.zone``.
83
+ """
84
+
85
+ def __init__(self, system, config):
86
+ ReserveData.__init__(self)
87
+ Model.__init__(self, system, config)
88
+ self.group = 'Reserve'
89
+ self.dvm = NumParam(default=0, unit='s',
90
+ info='Zonal virtual inertia demand',
91
+ tex_name=r'd_{VM}')
92
+ self.dvd = NumParam(default=0, unit='p.u.',
93
+ info='Zonal virtual damping demand',
94
+ tex_name=r'd_{VD}')
ams/models/shunt.py ADDED
@@ -0,0 +1,14 @@
1
+ from andes.models.shunt.shunt import ShuntData
2
+
3
+ from ams.core.model import Model
4
+
5
+
6
+ class Shunt(ShuntData, Model):
7
+ """
8
+ Phasor-domain shunt compensator Model.
9
+ """
10
+
11
+ def __init__(self, system=None, config=None):
12
+ ShuntData.__init__(self)
13
+ Model.__init__(self, system, config)
14
+ self.group = 'StaticShunt'
@@ -0,0 +1,2 @@
1
+ from ams.models.static.pq import PQ # NOQA
2
+ from ams.models.static.gen import PV, Slack # NOQA
@@ -0,0 +1,165 @@
1
+ from collections import OrderedDict
2
+
3
+ from andes.core.param import NumParam, ExtParam
4
+ from andes.models.static.pv import PVData
5
+ from andes.models.static.slack import SlackData
6
+
7
+ from ams.core.model import Model
8
+ from ams.core.var import Algeb
9
+
10
+
11
+ class GenParam:
12
+ """
13
+ Additional parameters for static generators.
14
+ """
15
+
16
+ def __init__(self) -> None:
17
+ self.ctrl = NumParam(default=1,
18
+ info="generator controllability",
19
+ tex_name=r'c_{trl}',
20
+ unit='bool',)
21
+ self.uf = NumParam(default=0,
22
+ info="enforced on/off status; 0 unenforced, 1 enforced on, -1 enforced off",
23
+ tex_name=r'u_{f}',
24
+ unit='int',
25
+ vrange=(-1, 1))
26
+ self.Pc1 = NumParam(default=0.0,
27
+ info="lower real power output of PQ capability curve",
28
+ tex_name=r'P_{c1}',
29
+ unit='p.u.')
30
+ self.Pc2 = NumParam(default=0.0,
31
+ info="upper real power output of PQ capability curve",
32
+ tex_name=r'P_{c2}',
33
+ unit='p.u.')
34
+ self.Qc1min = NumParam(default=0.0,
35
+ info="minimum reactive power output at Pc1",
36
+ tex_name=r'Q_{c1min}',
37
+ unit='p.u.')
38
+ self.Qc1max = NumParam(default=0.0,
39
+ info="maximum reactive power output at Pc1",
40
+ tex_name=r'Q_{c1max}',
41
+ unit='p.u.')
42
+ self.Qc2min = NumParam(default=0.0,
43
+ info="minimum reactive power output at Pc2",
44
+ tex_name=r'Q_{c2min}',
45
+ unit='p.u.')
46
+ self.Qc2max = NumParam(default=0.0,
47
+ info="maximum reactive power output at Pc2",
48
+ tex_name=r'Q_{c2max}',
49
+ unit='p.u.')
50
+ self.Ragc = NumParam(default=999.0,
51
+ info="ramp rate for load following/AGC",
52
+ tex_name=r'R_{agc}',
53
+ unit='p.u./h')
54
+ self.R10 = NumParam(default=999.0,
55
+ info="ramp rate for 10 minute reserves",
56
+ tex_name=r'R_{10}',
57
+ unit='p.u./h')
58
+ self.R30 = NumParam(default=999.0,
59
+ info="30 minute ramp rate",
60
+ tex_name=r'R_{30}',
61
+ unit='p.u./h')
62
+ self.Rq = NumParam(default=999.0,
63
+ info="ramp rate for reactive power (2 sec timescale)",
64
+ tex_name=r'R_{q}',
65
+ unit='p.u./h')
66
+ self.apf = NumParam(default=0.0,
67
+ info="area participation factor",
68
+ tex_name=r'a_{pf}')
69
+ self.pg0 = NumParam(default=0.0,
70
+ info='active power start point (system base)',
71
+ tex_name=r'p_{g0}',
72
+ unit='p.u.',
73
+ )
74
+ self.td1 = NumParam(default=1,
75
+ info='minimum ON duration',
76
+ tex_name=r't_{d1}',
77
+ unit='h',
78
+ )
79
+ self.td2 = NumParam(default=0.5,
80
+ info='minimum OFF duration',
81
+ tex_name=r't_{d2}',
82
+ unit='h',
83
+ )
84
+
85
+
86
+ class PVModel(Model):
87
+ """
88
+ PV generator model (power flow) with q limit and PV-PQ conversion.
89
+ """
90
+
91
+ def __init__(self, system=None, config=None):
92
+ super().__init__(system, config)
93
+ self.group = 'StaticGen'
94
+
95
+ self.config.add(OrderedDict((('pv2pq', 0),
96
+ ('npv2pq', 0),
97
+ ('min_iter', 2),
98
+ ('err_tol', 0.01),
99
+ ('abs_violation', 1),
100
+ )))
101
+ self.config.add_extra("_help",
102
+ pv2pq="convert PV to PQ in PFlow at Q limits",
103
+ npv2pq="max. # of conversion each iteration, 0 - auto",
104
+ min_iter="iteration number starting from which to enable switching",
105
+ err_tol="iteration error below which to enable switching",
106
+ abs_violation='use absolute (1) or relative (0) limit violation',
107
+ )
108
+
109
+ self.config.add_extra("_alt",
110
+ pv2pq=(0, 1),
111
+ npv2pq=">=0",
112
+ min_iter='int',
113
+ err_tol='float',
114
+ abs_violation=(0, 1),
115
+ )
116
+ self.config.add_extra("_tex",
117
+ pv2pq="z_{pv2pq}",
118
+ npv2pq="n_{pv2pq}",
119
+ min_iter="sw_{iter}",
120
+ err_tol=r"\epsilon_{tol}"
121
+ )
122
+
123
+ self.zone = ExtParam(model='Bus', src='zone', indexer=self.bus, export=False,
124
+ info='Retrieved zone idx', vtype=str, default=None,
125
+ )
126
+
127
+ self.ud = Algeb(info='commitment decision',
128
+ unit='bool',
129
+ tex_name=r'u_d',
130
+ name='ud',
131
+ )
132
+ self.p = Algeb(info='active power generation',
133
+ unit='p.u.',
134
+ tex_name='p',
135
+ name='p',
136
+ )
137
+ self.q = Algeb(info='reactive power generation',
138
+ unit='p.u.',
139
+ tex_name='q',
140
+ name='q',
141
+ )
142
+
143
+
144
+ class PV(PVData, GenParam, PVModel):
145
+ """
146
+ PV generator model.
147
+
148
+ TODO: implement type conversion in config
149
+ """
150
+
151
+ def __init__(self, system, config):
152
+ PVData.__init__(self)
153
+ GenParam.__init__(self)
154
+ PVModel.__init__(self, system, config)
155
+
156
+
157
+ class Slack(SlackData, GenParam, PVModel):
158
+ """
159
+ Slack generator model.
160
+ """
161
+
162
+ def __init__(self, system=None, config=None):
163
+ SlackData.__init__(self)
164
+ GenParam.__init__(self)
165
+ PVModel.__init__(self, system, config)
@@ -0,0 +1,61 @@
1
+ from collections import OrderedDict
2
+
3
+ from andes.core.param import NumParam, ExtParam
4
+ from andes.models.static.pq import PQData
5
+
6
+ from ams.core.model import Model
7
+
8
+
9
+ class PQ(PQData, Model):
10
+ """
11
+ PQ load model.
12
+
13
+ TODO: implement type conversion in config
14
+ """
15
+
16
+ def __init__(self, system, config):
17
+ PQData.__init__(self)
18
+ Model.__init__(self, system, config)
19
+ self.group = 'StaticLoad'
20
+ self.config.add(OrderedDict((('pq2z', 1),
21
+ ('p2p', 0.0),
22
+ ('p2i', 0.0),
23
+ ('p2z', 1.0),
24
+ ('q2q', 0.0),
25
+ ('q2i', 0.0),
26
+ ('q2z', 1.0),
27
+ )))
28
+ self.config.add_extra("_help",
29
+ pq2z="pq2z conversion if out of voltage limits",
30
+ p2p="P constant power percentage for TDS. Must have (p2p+p2i+p2z)=1",
31
+ p2i="P constant current percentage",
32
+ p2z="P constant impedance percentage",
33
+ q2q="Q constant power percentage for TDS. Must have (q2q+q2i+q2z)=1",
34
+ q2i="Q constant current percentage",
35
+ q2z="Q constant impedance percentage",
36
+ )
37
+ self.config.add_extra("_alt",
38
+ pq2z="(0, 1)",
39
+ p2p="float",
40
+ p2i="float",
41
+ p2z="float",
42
+ q2q="float",
43
+ q2i="float",
44
+ q2z="float",
45
+ )
46
+ self.config.add_extra("_tex",
47
+ pq2z="z_{pq2z}",
48
+ p2p=r"\gamma_{p2p}",
49
+ p2i=r"\gamma_{p2i}",
50
+ p2z=r"\gamma_{p2z}",
51
+ q2q=r"\gamma_{q2q}",
52
+ q2i=r"\gamma_{q2i}",
53
+ q2z=r"\gamma_{q2z}",
54
+ )
55
+
56
+ self.zone = ExtParam(model='Bus', src='zone', indexer=self.bus, export=False,
57
+ info='Retrieved zone idx', vtype=str, default=None,
58
+ )
59
+ self.ctrl = NumParam(default=1,
60
+ info="load controllability",
61
+ tex_name=r'c_{trl}',)
ams/models/timeslot.py ADDED
@@ -0,0 +1,69 @@
1
+ """
2
+ Model for rolling horizon used in scheduling.
3
+ """
4
+
5
+ from andes.core import ModelData, NumParam
6
+ from andes.models.timeseries import str_list_iconv
7
+ from ams.core.model import Model
8
+
9
+
10
+ def str_list_oconv(x):
11
+ """
12
+ Convert list into a list literal.
13
+ """
14
+ # NOTE: convert elements to string from number first, then join them
15
+ str_x = [str(i) for i in x]
16
+ return ','.join(str_x)
17
+
18
+
19
+ class TimeSlot(ModelData, Model):
20
+ """
21
+ Time slot data for rolling horizon.
22
+ """
23
+
24
+ def __init__(self, system=None, config=None):
25
+ ModelData.__init__(self)
26
+ Model.__init__(self, system, config)
27
+ self.group = 'Horizon'
28
+
29
+ self.sd = NumParam(info='zonal load scaling factor',
30
+ tex_name=r's_{d}',
31
+ iconvert=str_list_iconv,
32
+ oconvert=str_list_oconv,
33
+ vtype=float)
34
+
35
+
36
+ class EDTSlot(TimeSlot):
37
+ """
38
+ Time slot model for ED.
39
+
40
+ `sd` is the zonal load scaling factor.
41
+ Cells in `sd` should have `nz` values seperated by comma,
42
+ where `nz` is the number of `Zone` in the system.
43
+
44
+ `ug` is the unit commitment decisions.
45
+ Cells in `ug` should have `ng` values seperated by comma,
46
+ where `ng` is the number of `StaticGen` in the system.
47
+ """
48
+
49
+ def __init__(self, system=None, config=None):
50
+ TimeSlot.__init__(self, system, config)
51
+
52
+ self.ug = NumParam(info='unit commitment decisions',
53
+ tex_name=r'u_{g}',
54
+ iconvert=str_list_iconv,
55
+ oconvert=str_list_oconv,
56
+ vtype=int)
57
+
58
+
59
+ class UCTSlot(TimeSlot):
60
+ """
61
+ Time slot model for UC.
62
+
63
+ `sd` is the zonal load scaling factor.
64
+ Cells in `sd` should have `nz` values seperated by comma,
65
+ where `nz` is the number of `Zone` in the system.
66
+ """
67
+
68
+ def __init__(self, system=None, config=None):
69
+ TimeSlot.__init__(self, system, config)
ams/models/zone.py ADDED
@@ -0,0 +1,49 @@
1
+ import logging
2
+
3
+ from andes.core.model import ModelData
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 Zone(ModelData, Model):
13
+ """
14
+ Zone model for zonal items.
15
+
16
+ An ``area`` can have multiple zones.
17
+
18
+ Notes
19
+ -----
20
+ 1. Zone is a collection of buses.
21
+ 2. Model ``Zone`` is not actually defined in ANDES.
22
+
23
+ """
24
+
25
+ def __init__(self, system, config):
26
+ ModelData.__init__(self)
27
+ Model.__init__(self, system, config)
28
+
29
+ self.group = 'Collection'
30
+
31
+ self.Bus = BackRef()
32
+ self.ACTopology = BackRef()
33
+
34
+ def bus_table(self):
35
+ """
36
+ Return a formatted table with area idx and bus idx correspondence
37
+
38
+ Returns
39
+ -------
40
+ str
41
+ Formatted table
42
+
43
+ """
44
+ if self.n:
45
+ header = ['Zone ID', 'Bus ID']
46
+ rows = [(i, j) for i, j in zip(self.idx.v, self.Bus.v)]
47
+ return Tab(header=header, data=rows).draw()
48
+ else:
49
+ return ''
ams/opt/__init__.py ADDED
@@ -0,0 +1,12 @@
1
+ """
2
+ Module for optimization modeling.
3
+ """
4
+
5
+ from ams.opt.optzbase import OptzBase, ensure_symbols, ensure_mats_and_parsed # NOQA
6
+ from ams.opt.var import Var # NOQA
7
+ from ams.opt.exprcalc import ExpressionCalc # NOQA
8
+ from ams.opt.param import Param # NOQA
9
+ from ams.opt.constraint import Constraint # NOQA
10
+ from ams.opt.objective import Objective # NOQA
11
+ from ams.opt.expression import Expression # NOQA
12
+ from ams.opt.omodel import OModelBase, OModel # NOQA