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,71 @@
1
+ """
2
+ Distributed energy storage model.
3
+ """
4
+
5
+ from andes.core.param import NumParam
6
+
7
+ from ams.core.model import Model
8
+
9
+ from ams.models.distributed.pvd1 import PVD1Data
10
+
11
+
12
+ class ESD1Data(PVD1Data):
13
+ """
14
+ ESD1 model data.
15
+ """
16
+
17
+ def __init__(self):
18
+ PVD1Data.__init__(self)
19
+
20
+ self.SOCmin = NumParam(default=0.0, tex_name='SOC_{min}',
21
+ info='Minimum required value for SOC in limiter',
22
+ )
23
+
24
+ self.SOCmax = NumParam(default=1.0, tex_name='SOC_{max}',
25
+ info='Maximum allowed value for SOC in limiter',
26
+ )
27
+
28
+ self.SOCinit = NumParam(default=0.5, tex_name='SOC_{init}',
29
+ info='Initial state of charge',
30
+ )
31
+
32
+ self.En = NumParam(default=100.0, tex_name='E_n',
33
+ info='Rated energy capacity',
34
+ unit="MWh"
35
+ )
36
+
37
+ self.EtaC = NumParam(default=1.0, tex_name='Eta_C',
38
+ info='Efficiency during charging',
39
+ vrange=(0, 1),
40
+ )
41
+
42
+ self.EtaD = NumParam(default=1.0, tex_name='Eta_D',
43
+ info='Efficiency during discharging',
44
+ vrange=(0, 1),
45
+ )
46
+
47
+
48
+ class ESD1(ESD1Data, Model):
49
+ """
50
+ Distributed energy storage model, revised from ANDES ``ESD1`` model for
51
+ scheduling.
52
+
53
+ Following parameters are omitted from the original dynamic model:
54
+ ``fn``, ``busf``, ``xc``, ``pqflag``, ``igreg``, ``v0``, ``v1``,
55
+ ``dqdv``, ``fdbd``, ``ddn``, ``ialim``, ``vt0``, ``vt1``, ``vt2``,
56
+ ``vt3``, ``vrflag``, ``ft0``, ``ft1``, ``ft2``, ``ft3``, ``frflag``,
57
+ ``tip``, ``tiq``, ``recflag``.
58
+
59
+ Reference:
60
+
61
+ [1] ANDES Documentation, ESD1
62
+
63
+ Available:
64
+
65
+ https://docs.andes.app/en/latest/groupdoc/DG.html#esd1
66
+ """
67
+
68
+ def __init__(self, system, config):
69
+ ESD1Data.__init__(self)
70
+ Model.__init__(self, system, config)
71
+ self.group = 'DG'
@@ -0,0 +1,60 @@
1
+ """
2
+ EV model.
3
+ """
4
+
5
+ from andes.core.param import NumParam, IdxParam
6
+ from andes.core.model import ModelData
7
+
8
+ from ams.core.model import Model
9
+
10
+
11
+ class EV1(ModelData, Model):
12
+ """
13
+ Aggregated EV model for scheduling at transmission level.
14
+
15
+ For co-simulation with ADNES, it is expected to be used in
16
+ conjunction with the dynamic models `EV1` or `EV2`.
17
+
18
+ Reference:
19
+
20
+ [1] J. Wang et al., "Electric Vehicles Charging Time Constrained Deliverable Provision of Secondary
21
+ Frequency Regulation," in IEEE Transactions on Smart Grid, doi: 10.1109/TSG.2024.3356948.
22
+ """
23
+
24
+ def __init__(self, system, config):
25
+ ModelData.__init__(self)
26
+ Model.__init__(self, system, config)
27
+ self.group = 'DG'
28
+
29
+ self.bus = IdxParam(model='Bus',
30
+ info="interface bus idx",
31
+ mandatory=True,
32
+ )
33
+ self.gen = IdxParam(info="static generator index",
34
+ mandatory=True,
35
+ )
36
+ self.Sn = NumParam(default=100.0, tex_name='S_n',
37
+ info='device MVA rating',
38
+ unit='MVA',
39
+ )
40
+ self.gammap = NumParam(default=1.0,
41
+ info="P ratio of linked static gen",
42
+ tex_name=r'\gamma_P'
43
+ )
44
+ self.gammaq = NumParam(default=1.0,
45
+ info="Q ratio of linked static gen",
46
+ tex_name=r'\gamma_Q'
47
+ )
48
+ self.N = NumParam(default=10000,
49
+ info="number of related EVs",
50
+ tex_name='N'
51
+ )
52
+
53
+
54
+ class EV2(EV1):
55
+ """
56
+ EV aggregation model at transmission level, identical to :ref:`EV1`.
57
+ """
58
+
59
+ def __init__(self, system=None, config=None) -> None:
60
+ EV1.__init__(self, system, config)
@@ -0,0 +1,67 @@
1
+ """
2
+ Distributed PV models.
3
+ """
4
+
5
+ from andes.core.param import IdxParam, NumParam
6
+ from andes.core.model import ModelData
7
+
8
+ from ams.core.model import Model
9
+
10
+
11
+ class PVD1Data(ModelData):
12
+ """
13
+ PVD1 model data.
14
+ """
15
+
16
+ def __init__(self):
17
+ ModelData.__init__(self)
18
+
19
+ self.bus = IdxParam(model='Bus',
20
+ info="interface bus id (place holder)",
21
+ mandatory=True,
22
+ )
23
+
24
+ self.gen = IdxParam(info="static generator index",
25
+ mandatory=True,
26
+ )
27
+
28
+ self.Sn = NumParam(default=100.0, tex_name='S_n',
29
+ info='device MVA rating',
30
+ unit='MVA',
31
+ )
32
+
33
+ self.gammap = NumParam(default=1.0, tex_name=r'\gamma_p',
34
+ info='Ratio of ESD1.pref0 w.r.t to that of static PV',
35
+ vrange='(0, 1]',
36
+ )
37
+
38
+ self.gammaq = NumParam(default=1.0, tex_name=r'\gamma_q',
39
+ info='Ratio of ESD1.qref0 w.r.t to that of static PV',
40
+ vrange='(0, 1]',
41
+ )
42
+
43
+
44
+ class PVD1(PVD1Data, Model):
45
+ """
46
+ Distributed PV model, revised from ANDES ``PVD1`` model for
47
+ scheduling.
48
+
49
+ Following parameters are omitted from the original dynamic model:
50
+ ``fn``, ``busf``, ``xc``, ``pqflag``, ``igreg``, ``v0``, ``v1``,
51
+ ``dqdv``, ``fdbd``, ``ddn``, ``ialim``, ``vt0``, ``vt1``, ``vt2``,
52
+ ``vt3``, ``vrflag``, ``ft0``, ``ft1``, ``ft2``, ``ft3``, ``frflag``,
53
+ ``tip``, ``tiq``, ``recflag``.
54
+
55
+ Reference:
56
+
57
+ [1] ANDES Documentation, PVD1
58
+
59
+ Available:
60
+
61
+ https://docs.andes.app/en/latest/groupdoc/DG.html#pvd1
62
+ """
63
+
64
+ def __init__(self, system, config):
65
+ PVD1Data.__init__(self)
66
+ Model.__init__(self, system, config)
67
+ self.group = 'DG'
ams/models/group.py ADDED
@@ -0,0 +1,231 @@
1
+ import logging
2
+
3
+ from andes.models.group import GroupBase as adGroupBase
4
+ from andes.core.service import BackRef
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ class GroupBase(adGroupBase):
10
+ """
11
+ Base class for groups.
12
+
13
+ Add common_vars and common_params to the group class.
14
+
15
+ This class is revised from ``andes.models.group.GroupBase``.
16
+ """
17
+
18
+ def __init__(self):
19
+ super().__init__()
20
+ self.common_params.extend(('idx',))
21
+
22
+ def get_idx(self):
23
+ """
24
+ Return the value of group idx sorted in a human-readable style.
25
+
26
+ Notes
27
+ -----
28
+ This function sorts the idx values using a custom sorting key,
29
+ which handles varying length strings with letters and numbers.
30
+ """
31
+ all_idx = [mdl.idx.v for mdl in self.models.values()]
32
+ flat_list = [item for sublist in all_idx for item in sublist]
33
+ return flat_list
34
+
35
+ def _check_src(self, src: str):
36
+ """
37
+ Helper function for checking if ``src`` is a shared field.
38
+
39
+ The requirement is not strictly enforced and is only for debugging purposed.
40
+
41
+ Disable debug logging in dispath modeling.
42
+ """
43
+ if src not in self.common_vars + self.common_params:
44
+ logger.debug(f'Group <{self.class_name}> does not share property <{src}>.')
45
+
46
+ def __repr__(self):
47
+ dev_text = 'device' if self.n == 1 else 'devices'
48
+ return f'{self.class_name} ({self.n} {dev_text}) at {hex(id(self))}'
49
+
50
+ def __setattr__(self, key, value):
51
+ if hasattr(value, 'owner'):
52
+ if value.owner is None:
53
+ value.owner = self
54
+ if hasattr(value, 'name'):
55
+ if value.name is None:
56
+ value.name = key
57
+
58
+ if isinstance(value, BackRef):
59
+ self.services_ref[key] = value
60
+
61
+ super().__setattr__(key, value)
62
+
63
+ def set_backref(self, name, from_idx, to_idx):
64
+ """
65
+ Set idxes to ``BackRef``, and set them to models.
66
+ """
67
+
68
+ uid = self.idx2uid(to_idx)
69
+ self.services_ref[name].v[uid].append(from_idx)
70
+
71
+ model = self.idx2model(to_idx)
72
+ model.set_backref(name, from_idx, to_idx)
73
+
74
+
75
+ class Undefined(GroupBase):
76
+ """
77
+ The undefined group. Holds models with no ``group``.
78
+ """
79
+
80
+ def __init__(self):
81
+ super().__init__()
82
+
83
+
84
+ class ACTopology(GroupBase):
85
+ def __init__(self):
86
+ super().__init__()
87
+ self.common_vars.extend(('a', 'v'))
88
+
89
+
90
+ class RenGen(GroupBase):
91
+ """
92
+ Renewable generator (converter) group.
93
+
94
+ See ANDES Documentation SynGen here for the notes on replacing StaticGen and setting the power
95
+ ratio parameters.
96
+
97
+ Reference:
98
+
99
+ [1] ANDES Documentation, RenGen, [Online]
100
+
101
+ Available:
102
+
103
+ https://docs.andes.app/en/latest/groupdoc/RenGen.html#rengen
104
+ """
105
+
106
+ def __init__(self):
107
+ super().__init__()
108
+ self.common_params.extend(('bus', 'gen', 'Sn', 'q0'))
109
+ self.common_vars.extend(('Pe', 'Qe'))
110
+
111
+
112
+ class VSG(GroupBase):
113
+ """
114
+ Renewable generator with virtual synchronous generator (VSG) control group.
115
+
116
+ Note that this is a group separate from ``RenGen`` for VSG scheduling study.
117
+ """
118
+
119
+ def __init__(self):
120
+ super().__init__()
121
+ self.common_params.extend(('bus', 'gen', 'Sn'))
122
+ self.common_vars.extend(('Pe', 'Qe'))
123
+
124
+
125
+ class DG(GroupBase):
126
+ """
127
+ Distributed generation (small-scale).
128
+
129
+ See ANDES Documentation SynGen here for the notes on replacing StaticGen and setting the power
130
+ ratio parameters.
131
+
132
+ Reference:
133
+
134
+ [1] ANDES Documentation, SynGen, [Online]
135
+
136
+ Available:
137
+
138
+ https://docs.andes.app/en/latest/groupdoc/SynGen.html#syngen
139
+ """
140
+
141
+ def __init__(self):
142
+ super().__init__()
143
+ self.common_params.extend(('bus', 'fn'))
144
+
145
+
146
+ class Cost(GroupBase):
147
+ def __init__(self):
148
+ super().__init__()
149
+ self.common_params.extend(('gen',))
150
+
151
+
152
+ class Collection(GroupBase):
153
+ """
154
+ Collection of topology models
155
+ """
156
+
157
+ def __init__(self):
158
+ super().__init__()
159
+
160
+
161
+ class Horizon(GroupBase):
162
+ """
163
+ Time horizon group.
164
+ """
165
+
166
+ def __init__(self):
167
+ super().__init__()
168
+
169
+
170
+ class Reserve(GroupBase):
171
+ def __init__(self):
172
+ super().__init__()
173
+ self.common_params.extend(('zone',))
174
+
175
+
176
+ class StaticGen(GroupBase):
177
+ """
178
+ Generator group.
179
+
180
+ Notes
181
+ -----
182
+ For co-simulation with ANDES, check
183
+ `ANDES StaticGen <https://docs.andes.app/en/latest/groupdoc/StaticGen.html#staticgen>`_
184
+ for replacing static generators with dynamic generators.
185
+ """
186
+
187
+ def __init__(self):
188
+ super().__init__()
189
+ self.common_params.extend(('bus', 'Sn', 'Vn', 'p0', 'q0', 'ra', 'xs', 'subidx',
190
+ 'pmax', 'pmin', 'pg0', 'ctrl', 'R10', 'td1', 'td2',
191
+ 'zone'))
192
+ self.common_vars.extend(('p', 'q'))
193
+
194
+
195
+ class ACLine(GroupBase):
196
+ def __init__(self):
197
+ super().__init__()
198
+ self.common_params.extend(('bus1', 'bus2', 'r', 'x'))
199
+
200
+
201
+ class ACShort(GroupBase):
202
+ def __init__(self):
203
+ super(ACShort, self).__init__()
204
+ self.common_params.extend(('bus1', 'bus2'))
205
+
206
+
207
+ class StaticLoad(GroupBase):
208
+ """
209
+ Static load group.
210
+ """
211
+
212
+ def __init__(self):
213
+ super().__init__()
214
+ self.common_params.extend(('bus', 'p0', 'q0', 'ctrl', 'zone'))
215
+
216
+
217
+ class StaticShunt(GroupBase):
218
+ """
219
+ Static shunt compensator group.
220
+ """
221
+ pass
222
+
223
+
224
+ class Information(GroupBase):
225
+ """
226
+ Group for information container models.
227
+ """
228
+
229
+ def __init__(self):
230
+ GroupBase.__init__(self)
231
+ self.common_params = []
ams/models/info.py ADDED
@@ -0,0 +1,26 @@
1
+ """
2
+ Module for the information container models.
3
+ """
4
+ from andes.core.param import DataParam
5
+ from andes.core.model.modeldata import ModelData
6
+
7
+ from ams.core.model import Model
8
+
9
+
10
+ class Summary(ModelData, Model):
11
+ """
12
+ Class for storing system summary.
13
+ Can be used for random information or notes.
14
+ """
15
+
16
+ def __init__(self, system, config):
17
+ ModelData.__init__(self, three_params=False)
18
+
19
+ self.field = DataParam(info='field name')
20
+ self.comment = DataParam(info='information, comment, or anything')
21
+ self.comment2 = DataParam(info='comment field 2')
22
+ self.comment3 = DataParam(info='comment field 3')
23
+ self.comment4 = DataParam(info='comment field 4')
24
+
25
+ Model.__init__(self, system, config)
26
+ self.group = 'Information'
ams/models/line.py ADDED
@@ -0,0 +1,238 @@
1
+ from andes.models.line.line import LineData
2
+ from andes.models.line.jumper import JumperData
3
+ from andes.core.param import NumParam
4
+ from andes.shared import deg2rad, np, spmatrix
5
+
6
+ from ams.core.model import Model
7
+
8
+
9
+ class Line(LineData, Model):
10
+ """
11
+ AC transmission line model.
12
+
13
+ The model is also used for two-winding transformer. Transformers can set the
14
+ tap ratio in ``tap`` and/or phase shift angle ``phi``.
15
+
16
+ Note that the bus admittance matrix is built on fly and is not stored in the
17
+ object.
18
+
19
+ Notes
20
+ -----
21
+ There is a known issue that adding Algeb ``ud`` will cause Line.algebs run into
22
+ AttributeError: 'NoneType' object has no attribute 'n'. Not figured out why yet.
23
+ """
24
+
25
+ def __init__(self, system=None, config=None) -> None:
26
+ LineData.__init__(self)
27
+ Model.__init__(self, system, config)
28
+ self.group = 'ACLine'
29
+
30
+ self.amin = NumParam(default=-360 * deg2rad,
31
+ info="minimum angle difference, from bus - to bus",
32
+ unit='rad',
33
+ tex_name=r'a_{min}',
34
+ )
35
+ self.amax = NumParam(default=360 * deg2rad,
36
+ info="maximum angle difference, from bus - to bus",
37
+ unit='rad',
38
+ tex_name=r'a_{max}',
39
+ )
40
+ self.rate_a.unit = 'p.u.'
41
+ self.rate_b.unit = 'p.u.'
42
+ self.rate_c.unit = 'p.u.'
43
+ self.rate_a.default = 999.0
44
+ self.rate_b.default = 999.0
45
+ self.rate_c.default = 999.0
46
+
47
+ # NOTE: following parameters are prepared for building matrices
48
+ # they are initialized here but populated in ``System.setup()``.
49
+ self.a1a = None
50
+ self.a2a = None
51
+
52
+ # NOTE: following code are minly copied from `andes.models.line.Line`
53
+ # and they are not fully verified
54
+ # potential issues:
55
+ # `build_Bp` contains 'fdxb', which is not included in the input parameters,
56
+ # and the results are the negative of `Bbus` from `makeBdc` in PYPOWER
57
+ # `build_Bpp` ignores the line resistance for all three methods
58
+ # `build_Bdc` results are the negative of `Bbus` from `makeBdc` in PYPOWER
59
+ # `build_y` results have inignorable differences at diagonal elements with `makeYbus` in PYPOWER
60
+
61
+ def build_y(self):
62
+ """
63
+ Build bus admittance matrix. Copied from ``andes.models.line.line.Line``.
64
+
65
+ Returns
66
+ -------
67
+ Y : spmatrix
68
+ Bus admittance matrix.
69
+ """
70
+
71
+ nb = self.system.Bus.n
72
+
73
+ y1 = self.u.v * (self.g1.v + self.b1.v * 1j)
74
+ y2 = self.u.v * (self.g2.v + self.b2.v * 1j)
75
+ y12 = self.u.v / (self.r.v + self.x.v * 1j)
76
+ m = self.tap.v * np.exp(1j * self.phi.v)
77
+ m2 = self.tap.v**2
78
+ mconj = np.conj(m)
79
+
80
+ # build self and mutual admittances into Y
81
+ Y = spmatrix((y12 + y1 / m2), self.a1a, self.a1a, (nb, nb), 'z')
82
+ Y -= spmatrix(y12 / mconj, self.a1a, self.a2a, (nb, nb), 'z')
83
+ Y -= spmatrix(y12 / m, self.a2a, self.a1a, (nb, nb), 'z')
84
+ Y += spmatrix(y12 + y2, self.a2a, self.a2a, (nb, nb), 'z')
85
+
86
+ return Y
87
+
88
+ def build_Bp(self, method='fdpf'):
89
+ """
90
+ Function for building B' matrix.
91
+
92
+ Parameters
93
+ ----------
94
+ method : str
95
+ Method for building B' matrix. Choose from 'fdpf', 'fdbx', 'dcpf'.
96
+
97
+ Returns
98
+ -------
99
+ Bp : spmatrix
100
+ B' matrix.
101
+ """
102
+ nb = self.system.Bus.n
103
+
104
+ if method not in ("fdpf", "fdbx", "dcpf"):
105
+ raise ValueError(f"Invalid method {method}; choose from 'fdpf', 'fdbx', 'dcpf'")
106
+
107
+ # Build B prime matrix -- FDPF
108
+ # `y1`` neglects line charging shunt, and g1 is usually 0 in HV lines
109
+ # `y2`` neglects line charging shunt, and g2 is usually 0 in HV lines
110
+ y1 = self.u.v * self.g1.v
111
+ y2 = self.u.v * self.g2.v
112
+
113
+ # `m` neglected tap ratio
114
+ m = np.exp(self.phi.v * 1j)
115
+ mconj = np.conj(m)
116
+ m2 = np.ones(self.n)
117
+
118
+ if method in ('fdxb', 'dcpf'):
119
+ # neglect line resistance in Bp in XB method
120
+ y12 = self.u.v / (self.x.v * 1j)
121
+ else:
122
+ y12 = self.u.v / (self.r.v + self.x.v * 1j)
123
+
124
+ Bdc = spmatrix((y12 + y1) / m2, self.a1a, self.a1a, (nb, nb), 'z')
125
+ Bdc -= spmatrix(y12 / mconj, self.a1a, self.a2a, (nb, nb), 'z')
126
+ Bdc -= spmatrix(y12 / m, self.a2a, self.a1a, (nb, nb), 'z')
127
+ Bdc += spmatrix(y12 + y2, self.a2a, self.a2a, (nb, nb), 'z')
128
+ Bdc = Bdc.imag()
129
+
130
+ for item in range(nb):
131
+ if abs(Bdc[item, item]) == 0:
132
+ Bdc[item, item] = 1e-6 + 0j
133
+
134
+ return Bdc
135
+
136
+ def build_Bpp(self, method='fdpf'):
137
+ """
138
+ Function for building B'' matrix.
139
+
140
+ Parameters
141
+ ----------
142
+ method : str
143
+ Method for building B'' matrix. Choose from 'fdpf', 'fdbx', 'dcpf'.
144
+
145
+ Returns
146
+ -------
147
+ Bpp : spmatrix
148
+ B'' matrix.
149
+ """
150
+
151
+ nb = self.system.Bus.n
152
+
153
+ if method not in ("fdpf", "fdbx", "dcpf"):
154
+ raise ValueError(f"Invalid method {method}; choose from 'fdpf', 'fdbx', 'dcpf'")
155
+
156
+ # Build B double prime matrix
157
+ # y1 neglected line charging shunt, and g1 is usually 0 in HV lines
158
+ # y2 neglected line charging shunt, and g2 is usually 0 in HV lines
159
+ # m neglected phase shifter
160
+ y1 = self.u.v * (self.g1.v + self.b1.v * 1j)
161
+ y2 = self.u.v * (self.g2.v + self.b2.v * 1j)
162
+
163
+ m = self.tap.v
164
+ m2 = abs(m)**2
165
+
166
+ if method in ('fdbx', 'fdpf', 'dcpf'):
167
+ # neglect line resistance in Bpp in BX method
168
+ y12 = self.u.v / (self.x.v * 1j)
169
+ else:
170
+ y12 = self.u.v / (self.r.v + self.x.v * 1j)
171
+
172
+ Bpp = spmatrix((y12 + y1) / m2, self.a1a, self.a1a, (nb, nb), 'z')
173
+ Bpp -= spmatrix(y12 / np.conj(m), self.a1a, self.a2a, (nb, nb), 'z')
174
+ Bpp -= spmatrix(y12 / m, self.a2a, self.a1a, (nb, nb), 'z')
175
+ Bpp += spmatrix(y12 + y2, self.a2a, self.a2a, (nb, nb), 'z')
176
+ Bpp = Bpp.imag()
177
+
178
+ for item in range(nb):
179
+ if abs(Bpp[item, item]) == 0:
180
+ Bpp[item, item] = 1e-6 + 0j
181
+
182
+ return Bpp
183
+
184
+ def build_Bdc(self):
185
+ """
186
+ The MATPOWER-flavor Bdc matrix for DC power flow.
187
+
188
+ The method neglects line charging and line resistance. It retains tap ratio.
189
+
190
+ Returns
191
+ -------
192
+ Bdc : spmatrix
193
+ Bdc matrix.
194
+ """
195
+
196
+ nb = self.system.Bus.n
197
+
198
+ y12 = self.u.v / (self.x.v * 1j)
199
+ y12 = y12 / self.tap.v
200
+
201
+ Bdc = spmatrix(y12, self.a1a, self.a1a, (nb, nb), 'z')
202
+ Bdc -= spmatrix(y12, self.a1a, self.a2a, (nb, nb), 'z')
203
+ Bdc -= spmatrix(y12, self.a2a, self.a1a, (nb, nb), 'z')
204
+ Bdc += spmatrix(y12, self.a2a, self.a2a, (nb, nb), 'z')
205
+ Bdc = Bdc.imag()
206
+
207
+ for item in range(nb):
208
+ if abs(Bdc[item, item]) == 0:
209
+ Bdc[item, item] = 1e-6
210
+
211
+ return Bdc
212
+
213
+
214
+ class Jumper(JumperData, Model):
215
+ """
216
+ Jumper is a device to short two buses (merging two buses into one).
217
+
218
+ Jumper can connect two buses satisfying one of the following conditions:
219
+
220
+ - neither bus is voltage-controlled
221
+ - either bus is voltage-controlled
222
+ - both buses are voltage-controlled, and the voltages are the same.
223
+
224
+ If the buses are controlled in different voltages, power flow will
225
+ not solve (as the power flow through the jumper will be infinite).
226
+
227
+ In the solutions, the ``p`` and ``q`` are flowing out of bus1
228
+ and flowing into bus2.
229
+
230
+ Setting a Jumper's connectivity status ``u`` to zero will disconnect the two
231
+ buses. In the case of a system split, one will need to call
232
+ ``System.connectivity()`` immediately following the split to detect islands.
233
+ """
234
+
235
+ def __init__(self, system=None, config=None) -> None:
236
+ JumperData.__init__(self)
237
+ Model.__init__(self, system, config)
238
+ self.group = 'ACShort'
@@ -0,0 +1,5 @@
1
+ """
2
+ Top-level package for renewable models.
3
+ """
4
+
5
+ from ams.models.renewable.regc import REGCA1, REGCV1, REGCV2 # NOQA