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
ams/core/service.py ADDED
@@ -0,0 +1,918 @@
1
+ """
2
+ Service.
3
+ """
4
+
5
+ import logging
6
+ from typing import Callable, Type
7
+
8
+ import numpy as np
9
+ import scipy.sparse as spr
10
+
11
+ from andes.core.service import BaseService
12
+
13
+ from ams.opt import Param
14
+
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ class RBaseService(BaseService, Param):
20
+ """
21
+ Base class for services that are used in a routine.
22
+ Revised from module `andes.core.service.BaseService`.
23
+
24
+ Parameters
25
+ ----------
26
+ name : str, optional
27
+ Instance name.
28
+ tex_name : str, optional
29
+ TeX name.
30
+ unit : str, optional
31
+ Unit.
32
+ info : str, optional
33
+ Description.
34
+ vtype : Type, optional
35
+ Variable type.
36
+ no_parse: bool, optional
37
+ True to skip parsing the service.
38
+ sparse: bool, optional
39
+ True to return output as scipy csr_matrix.
40
+ """
41
+
42
+ def __init__(self,
43
+ name: str = None,
44
+ tex_name: str = None,
45
+ unit: str = None,
46
+ info: str = None,
47
+ vtype: Type = None,
48
+ no_parse: bool = False,
49
+ sparse: bool = False,
50
+ ):
51
+ Param.__init__(self, name=name, unit=unit, info=info,
52
+ no_parse=no_parse)
53
+ BaseService.__init__(self, name=name, tex_name=tex_name, unit=unit,
54
+ info=info, vtype=vtype)
55
+ self.export = False
56
+ self.is_group = False
57
+ self.rtn = None
58
+ self.sparse = sparse
59
+
60
+ @property
61
+ def shape(self):
62
+ """
63
+ Return the shape of the service.
64
+ """
65
+ if isinstance(self.v, (np.ndarray, spr.csr_matrix)):
66
+ return self.v.shape
67
+ else:
68
+ raise TypeError(f'{self.class_name}: {self.name} is not an array.')
69
+
70
+ @property
71
+ def v(self):
72
+ """
73
+ Value of the service.
74
+ """
75
+ return None
76
+
77
+ @property
78
+ def class_name(self):
79
+ """
80
+ Return the class name
81
+ """
82
+ return self.__class__.__name__
83
+
84
+ def __repr__(self):
85
+ if self.name is None:
86
+ return f'{self.class_name}: {self.rtn.class_name}'
87
+ else:
88
+ return f'{self.class_name}: {self.rtn.class_name}.{self.name}'
89
+
90
+
91
+ class ValueService(RBaseService):
92
+ """
93
+ Service to store given numeric values.
94
+
95
+ Parameters
96
+ ----------
97
+ name : str, optional
98
+ Instance name.
99
+ tex_name : str, optional
100
+ TeX name.
101
+ unit : str, optional
102
+ Unit.
103
+ info : str, optional
104
+ Description.
105
+ vtype : Type, optional
106
+ Variable type.
107
+ no_parse: bool, optional
108
+ True to skip parsing the service.
109
+ sparse: bool, optional
110
+ True to return output as scipy csr_matrix.
111
+ """
112
+
113
+ def __init__(self,
114
+ name: str,
115
+ value: np.ndarray,
116
+ tex_name: str = None,
117
+ unit: str = None,
118
+ info: str = None,
119
+ vtype: Type = None,
120
+ no_parse: bool = False,
121
+ sparse: bool = False,
122
+ ):
123
+ super().__init__(name=name, tex_name=tex_name, unit=unit,
124
+ info=info, vtype=vtype,
125
+ no_parse=no_parse, sparse=sparse)
126
+ self._v = value
127
+
128
+ @property
129
+ def v(self):
130
+ """
131
+ Value of the service.
132
+ """
133
+ if self.sparse:
134
+ return spr.csr_matrix(self._v)
135
+ return self._v
136
+
137
+
138
+ class ROperationService(RBaseService):
139
+ """
140
+ Base calss for operational services used in routine.
141
+
142
+ Parameters
143
+ ----------
144
+ u : Callable
145
+ Input.
146
+ name : str, optional
147
+ Instance name.
148
+ tex_name : str, optional
149
+ TeX name.
150
+ unit : str, optional
151
+ Unit.
152
+ info : str, optional
153
+ Description.
154
+ vtype : Type, optional
155
+ Variable type.
156
+ no_parse: bool, optional
157
+ True to skip parsing the service.
158
+ sparse: bool, optional
159
+ True to return output as scipy csr_matrix.
160
+ """
161
+
162
+ def __init__(self,
163
+ u: Callable,
164
+ name: str = None,
165
+ tex_name: str = None,
166
+ unit: str = None,
167
+ info: str = None,
168
+ vtype: Type = None,
169
+ no_parse: bool = False,
170
+ sparse: bool = False,):
171
+ super().__init__(name=name, tex_name=tex_name, unit=unit,
172
+ info=info, vtype=vtype,
173
+ no_parse=no_parse, sparse=sparse)
174
+ self.u = u
175
+
176
+
177
+ class LoadScale(ROperationService):
178
+ """
179
+ Get zonal load by scale nodal load given the corresponding load scale factor.
180
+
181
+ Parameters
182
+ ----------
183
+ u : Callable
184
+ nodal load.
185
+ sd : Callable
186
+ zonal load factor.
187
+ name : str, optional
188
+ Instance name.
189
+ tex_name : str, optional
190
+ TeX name.
191
+ unit : str, optional
192
+ Unit.
193
+ info : str, optional
194
+ Description.
195
+ no_parse: bool, optional
196
+ True to skip parsing the service.
197
+ sparse: bool, optional
198
+ True to return output as scipy csr_matrix.
199
+ """
200
+
201
+ def __init__(self,
202
+ u: Callable,
203
+ sd: Callable,
204
+ name: str = None,
205
+ tex_name: str = None,
206
+ unit: str = None,
207
+ info: str = None,
208
+ no_parse: bool = False,
209
+ sparse: bool = False,
210
+ ):
211
+ tex_name = tex_name if tex_name is not None else u.tex_name
212
+ super().__init__(name=name, tex_name=tex_name, unit=unit,
213
+ info=info, u=u, no_parse=no_parse,
214
+ sparse=sparse)
215
+ self.sd = sd
216
+
217
+ @property
218
+ def v(self):
219
+ sys = self.rtn.system
220
+ u_idx = self.u.get_all_idxes()
221
+ ue = self.u.owner.get(src='u', attr='v', idx=u_idx)
222
+ u_bus = self.u.owner.get(src='bus', attr='v', idx=u_idx)
223
+ u_zone = sys.Bus.get(src='zone', attr='v', idx=u_bus)
224
+ u_yloc = np.array(sys.Zone.idx2uid(u_zone))
225
+ p0s = np.multiply(self.sd.v[:, u_yloc].transpose(),
226
+ (ue * self.u.v)[:, np.newaxis])
227
+ if self.sparse:
228
+ return spr.csr_matrix(p0s)
229
+ return p0s
230
+
231
+
232
+ class NumOp(ROperationService):
233
+ """
234
+ Perform an operation on a numerical array using the
235
+ function ``fun(u.v, **args)``.
236
+
237
+ Note that the scalar output is converted to a 1D array.
238
+
239
+ The `rargs` are passed to the input function.
240
+
241
+ Parameters
242
+ ----------
243
+ u : Callable
244
+ Input.
245
+ name : str, optional
246
+ Instance name.
247
+ tex_name : str, optional
248
+ TeX name.
249
+ unit : str, optional
250
+ Unit.
251
+ info : str, optional
252
+ Description.
253
+ vtype : Type, optional
254
+ Variable type.
255
+ model : str, optional
256
+ Model name.
257
+ rfun : Callable, optional
258
+ Function to apply to the output of ``fun``.
259
+ rargs : dict, optional
260
+ Keyword arguments to pass to ``rfun``.
261
+ expand_dims : int, optional
262
+ Expand the dimensions of the output array along a specified axis.
263
+ array_out : bool, optional
264
+ Whether to force the output to be an array.
265
+ sparse: bool, optional
266
+ True to return output as scipy csr_matrix.
267
+ """
268
+
269
+ def __init__(self,
270
+ u: Callable,
271
+ fun: Callable,
272
+ args: dict = None,
273
+ name: str = None,
274
+ tex_name: str = None,
275
+ unit: str = None,
276
+ info: str = None,
277
+ vtype: Type = None,
278
+ rfun: Callable = None,
279
+ rargs: dict = None,
280
+ expand_dims: int = None,
281
+ array_out=True,
282
+ no_parse: bool = False,
283
+ sparse: bool = False,):
284
+ tex_name = tex_name if tex_name is not None else u.tex_name
285
+ super().__init__(name=name, tex_name=tex_name, unit=unit,
286
+ info=info, vtype=vtype, u=u,
287
+ no_parse=no_parse, sparse=sparse)
288
+ self.fun = fun
289
+ self.args = {} if args is None else args
290
+ self.rfun = rfun
291
+ self.rargs = {} if rargs is None else rargs
292
+ self.expand_dims = expand_dims
293
+ self.array_out = array_out
294
+
295
+ @property
296
+ def v0(self):
297
+ out = self.fun(self.u.v, **self.args)
298
+ if self.array_out:
299
+ if not isinstance(out, np.ndarray):
300
+ out = np.array([out])
301
+ return out
302
+
303
+ @property
304
+ def v1(self):
305
+ if self.rfun is not None:
306
+ return self.rfun(self.v0, **self.rargs)
307
+ else:
308
+ return self.v0
309
+
310
+ @property
311
+ def v(self):
312
+ if self.expand_dims is not None:
313
+ out = np.expand_dims(self.v1, axis=int(self.expand_dims))
314
+ else:
315
+ out = self.v1
316
+ if self.sparse:
317
+ return spr.csr_matrix(out)
318
+ return out
319
+
320
+
321
+ class NumExpandDim(NumOp):
322
+ """
323
+ Expand the dimensions of the input array along a specified axis
324
+ using NumPy's ``np.expand_dims(u.v, axis=axis)``.
325
+
326
+ Parameters
327
+ ----------
328
+ u : Callable
329
+ Input.
330
+ axis : int
331
+ Axis along which to expand the dimensions (default is 0).
332
+ name : str, optional
333
+ Instance name.
334
+ tex_name : str, optional
335
+ TeX name.
336
+ unit : str, optional
337
+ Unit.
338
+ info : str, optional
339
+ Description.
340
+ vtype : Type, optional
341
+ Variable type.
342
+ array_out : bool, optional
343
+ Whether to force the output to be an array.
344
+ sparse: bool, optional
345
+ True to return output as scipy csr_matrix.
346
+ """
347
+
348
+ def __init__(self,
349
+ u: Callable,
350
+ axis: int = 0,
351
+ args: dict = None,
352
+ name: str = None,
353
+ tex_name: str = None,
354
+ unit: str = None,
355
+ info: str = None,
356
+ vtype: Type = None,
357
+ array_out: bool = True,
358
+ no_parse: bool = False,
359
+ sparse: bool = False,):
360
+ super().__init__(name=name, tex_name=tex_name, unit=unit,
361
+ info=info, vtype=vtype,
362
+ u=u, fun=np.expand_dims, args=args,
363
+ array_out=array_out,
364
+ no_parse=no_parse, sparse=sparse)
365
+ self.axis = axis
366
+
367
+ @property
368
+ def v(self):
369
+ out = self.fun(self.u.v, axis=self.axis, **self.args)
370
+ if self.sparse:
371
+ return spr.csr_matrix(out)
372
+ return out
373
+
374
+
375
+ class NumOpDual(NumOp):
376
+ """
377
+ Performan an operation on two numerical arrays using the
378
+ function ``fun(u.v, u2.v, **args)``.
379
+
380
+ Note that the scalar output is converted to a 1D array.
381
+
382
+ The optional kwargs are passed to the input function.
383
+
384
+ Parameters
385
+ ----------
386
+ u : Callable
387
+ Input.
388
+ u2 : Callable
389
+ Input2.
390
+ name : str, optional
391
+ Instance name.
392
+ tex_name : str, optional
393
+ TeX name.
394
+ unit : str, optional
395
+ Unit.
396
+ info : str, optional
397
+ Description.
398
+ vtype : Type, optional
399
+ Variable type.
400
+ rfun : Callable, optional
401
+ Function to apply to the output of ``fun``.
402
+ rargs : dict, optional
403
+ Keyword arguments to pass to ``rfun``.
404
+ expand_dims : int, optional
405
+ Expand the dimensions of the output array along a specified axis.
406
+ array_out : bool, optional
407
+ Whether to force the output to be an array.
408
+ no_parse: bool, optional
409
+ True to skip parsing the service.
410
+ sparse: bool, optional
411
+ True to return output as scipy csr_matrix.
412
+ """
413
+
414
+ def __init__(self,
415
+ u: Callable,
416
+ u2: Callable,
417
+ fun: Callable,
418
+ args: dict = None,
419
+ name: str = None,
420
+ tex_name: str = None,
421
+ unit: str = None,
422
+ info: str = None,
423
+ vtype: Type = None,
424
+ rfun: Callable = None,
425
+ rargs: dict = None,
426
+ expand_dims: int = None,
427
+ array_out=True,
428
+ no_parse: bool = False,
429
+ sparse: bool = False,):
430
+ tex_name = tex_name if tex_name is not None else u.tex_name
431
+ super().__init__(name=name, tex_name=tex_name, unit=unit,
432
+ info=info, vtype=vtype,
433
+ u=u, fun=fun, args=args,
434
+ rfun=rfun, rargs=rargs,
435
+ expand_dims=expand_dims,
436
+ array_out=array_out,
437
+ no_parse=no_parse, sparse=sparse)
438
+ self.u2 = u2
439
+
440
+ @property
441
+ def v0(self):
442
+ out = self.fun(self.u.v, self.u2.v, **self.args)
443
+ if self.array_out:
444
+ if not isinstance(out, np.ndarray):
445
+ out = np.array([out])
446
+ if self.sparse:
447
+ return spr.csr_matrix(out)
448
+ return out
449
+
450
+
451
+ class MinDur(NumOpDual):
452
+ """
453
+ Defined to form minimum on matrix for minimum online/offline
454
+ time constraints used in UC.
455
+
456
+ Parameters
457
+ ----------
458
+ u : Callable
459
+ Input, should be a ``Var`` with horizon.
460
+ u2 : Callable
461
+ Input2, should be a ``RParam``.
462
+ name : str, optional
463
+ Instance name.
464
+ tex_name : str, optional
465
+ TeX name.
466
+ unit : str, optional
467
+ Unit.
468
+ info : str, optional
469
+ Description.
470
+ vtype : Type, optional
471
+ Variable type.
472
+ no_parse: bool, optional
473
+ True to skip parsing the service.
474
+ sparse: bool, optional
475
+ True to return output as scipy csr_matrix.
476
+ """
477
+
478
+ def __init__(self,
479
+ u: Callable,
480
+ u2: Callable,
481
+ name: str = None,
482
+ tex_name: str = None,
483
+ unit: str = None,
484
+ info: str = None,
485
+ vtype: Type = None,
486
+ no_parse: bool = False,
487
+ sparse: bool = False,):
488
+ tex_name = tex_name if tex_name is not None else u.tex_name
489
+ super().__init__(name=name, tex_name=tex_name, unit=unit,
490
+ info=info, vtype=vtype,
491
+ u=u, u2=u2, fun=None, args=None,
492
+ rfun=None, rargs=None,
493
+ expand_dims=None,
494
+ no_parse=no_parse, sparse=sparse)
495
+ if self.u.horizon is None:
496
+ msg = f'{self.class_name} <{self.name}>.u: <{self.u.name}> '
497
+ msg += 'has no horizon, likely a modeling error.'
498
+ logger.error(msg)
499
+
500
+ @property
501
+ def v(self):
502
+ n_gen = self.u.n
503
+ n_ts = self.u.horizon.n
504
+ tout = np.zeros((n_gen, n_ts))
505
+ t = self.rtn.config.t # scheduling interval
506
+
507
+ # minimum online/offline duration
508
+ td = np.ceil(self.u2.v/t).astype(int)
509
+
510
+ # Create index arrays for generators and time periods
511
+ i, t = np.meshgrid(np.arange(n_gen), np.arange(n_ts), indexing='ij')
512
+ # Create a mask for valid time periods based on minimum duration
513
+ valid_mask = (t + td[i] <= n_ts)
514
+ tout[i[valid_mask], t[valid_mask]] = 1
515
+ if self.sparse:
516
+ return spr.csr_matrix(tout)
517
+ return tout
518
+
519
+
520
+ class NumHstack(NumOp):
521
+ """
522
+ Repeat an array along the second axis nc times or the length of
523
+ reference array,
524
+ using NumPy's hstack function, where nc is the column number of the
525
+ reference array,
526
+ ``np.hstack([u.v[:, np.newaxis] * ref.shape[1]], **kwargs)``.
527
+
528
+ Parameters
529
+ ----------
530
+ u : Callable
531
+ Input array.
532
+ ref : Callable
533
+ Reference array used to determine the number of repetitions.
534
+ name : str, optional
535
+ Instance name.
536
+ tex_name : str, optional
537
+ TeX name.
538
+ unit : str, optional
539
+ Unit.
540
+ info : str, optional
541
+ Description.
542
+ vtype : Type, optional
543
+ Variable type.
544
+ rfun : Callable, optional
545
+ Function to apply to the output of ``fun``.
546
+ rargs : dict, optional
547
+ Keyword arguments to pass to ``rfun``.
548
+ no_parse: bool, optional
549
+ True to skip parsing the service.
550
+ sparse: bool, optional
551
+ True to return output as scipy csr_matrix.
552
+ """
553
+
554
+ def __init__(self,
555
+ u: Callable,
556
+ ref: Callable,
557
+ args: dict = None,
558
+ name: str = None,
559
+ tex_name: str = None,
560
+ unit: str = None,
561
+ info: str = None,
562
+ vtype: Type = None,
563
+ rfun: Callable = None,
564
+ rargs: dict = None,
565
+ no_parse: bool = False,
566
+ sparse: bool = False,):
567
+ super().__init__(name=name, tex_name=tex_name, unit=unit,
568
+ info=info, vtype=vtype,
569
+ u=u, fun=np.hstack, args=args,
570
+ rfun=rfun, rargs=rargs,
571
+ no_parse=no_parse, sparse=sparse)
572
+ self.ref = ref
573
+
574
+ @property
575
+ def v0(self):
576
+ nc = 1
577
+ if isinstance(self.ref.v, (list, tuple)):
578
+ nc = len(self.ref.v)
579
+ elif hasattr(self.ref, "shape"):
580
+ nc = self.ref.shape[1]
581
+ else:
582
+ raise AttributeError(f"{self.rtn.class_name}: ref {self.ref.name} has no attribute shape nor length.")
583
+ return self.fun([self.u.v[:, np.newaxis]] * nc,
584
+ **self.args)
585
+
586
+
587
+ class ZonalSum(NumOp):
588
+ """
589
+ Build zonal sum matrix for a vector in the shape of collection model,
590
+ ``Area`` or ``Zone``.
591
+ The value array is in the shape of (nr, nc), where nr is the length of
592
+ rid instance idx, and nc is the length of the cid value.
593
+
594
+ In an IEEE-14 Bus system, we have the zonal definition by the
595
+ ``Zone`` model. Suppose in it we have two regions, "ZONE1" and
596
+ "ZONE2".
597
+
598
+ Follwing it, we have a zonal SFR requirement model ``SFR`` that
599
+ defines the zonal reserve requirements for each zone.
600
+
601
+ All 14 buses are classified to a zone by the `IdxParam` ``zone``,
602
+ and the 5 generators are connected to buses
603
+ (idx): [2, 3, 1, 6, 8], and the zone of these generators are thereby:
604
+ ['ZONE1', 'ZONE1', 'ZONE2', 'ZONE2', 'ZONE1'].
605
+
606
+ In the `RTED` model, we have the Vars ``pru`` and ``prd`` in the
607
+ shape of generators.
608
+
609
+ Then, the Zone model has idx ['ZONE1', 'ZONE2'], and the ``gsm`` value
610
+ will be [[1, 1, 0, 0, 1], [0, 0, 1, 1, 0]].
611
+
612
+ Finally, the zonal reserve requirements can be formulated as
613
+ constraints in the optimization problem: "gsm @ pru <= du" and
614
+ "gsm @ prd <= dd".
615
+
616
+ See ``gsm`` definition in :py:mod:`ams.routines.rted.RTEDModel` for
617
+ more details.
618
+
619
+ Parameters
620
+ ----------
621
+ u : Callable
622
+ Input.
623
+ zone : str
624
+ Zonal model name, e.g., "Area" or "Zone".
625
+ name : str
626
+ Instance name.
627
+ tex_name : str
628
+ TeX name.
629
+ unit : str
630
+ Unit.
631
+ info : str
632
+ Description.
633
+ vtype : Type
634
+ Variable type.
635
+ rfun : Callable, optional
636
+ Function to apply to the output of ``fun``.
637
+ rargs : dict, optional
638
+ Keyword arguments to pass to ``rfun``.
639
+ no_parse: bool, optional
640
+ True to skip parsing the service.
641
+ sparse: bool, optional
642
+ True to return output as scipy csr_matrix.
643
+ """
644
+
645
+ def __init__(self,
646
+ u: Callable,
647
+ zone: str,
648
+ name: str = None,
649
+ tex_name: str = None,
650
+ unit: str = None,
651
+ info: str = None,
652
+ vtype: Type = None,
653
+ rfun: Callable = None,
654
+ rargs: dict = None,
655
+ no_parse: bool = False,
656
+ sparse: bool = False,):
657
+ super().__init__(name=name, tex_name=tex_name, unit=unit,
658
+ info=info, vtype=vtype,
659
+ u=u, fun=None, args={},
660
+ rfun=rfun, rargs=rargs,
661
+ no_parse=no_parse, sparse=sparse)
662
+ self.zone = zone
663
+
664
+ @property
665
+ def v0(self):
666
+ try:
667
+ zone_mdl = getattr(self.rtn.system, self.zone)
668
+ except AttributeError:
669
+ raise AttributeError(f'Zonal model <{self.zone}> not found.')
670
+ ridx = None
671
+ try:
672
+ ridx = zone_mdl.idx.v
673
+ except AttributeError:
674
+ ridx = zone_mdl.get_all_idxes()
675
+
676
+ row, col = np.meshgrid(self.u.v, ridx)
677
+ # consistency check
678
+ is_subset = set(self.u.v).issubset(set(ridx))
679
+ if not is_subset:
680
+ raise ValueError(f'{self.u.model} contains undefined zone, likey a data error.')
681
+ result = (row == col).astype(int)
682
+
683
+ return result
684
+
685
+
686
+ class VarSelect(NumOp):
687
+ """
688
+ A numerical matrix to select a subset of a 2D variable,
689
+ ``u.v[:, idx]``.
690
+
691
+ For example, if need to select Energy Storage output
692
+ power from StaticGen `pg`, following definition can be used:
693
+ ```python
694
+ class RTED:
695
+ ...
696
+ self.ce = VarSelect(u=self.pg, indexer='genE')
697
+ ...
698
+ ```
699
+
700
+ Parameters
701
+ ----------
702
+ u : Callable
703
+ The input matrix variable.
704
+ indexer: str
705
+ The name of the indexer source.
706
+ gamma : str, optional
707
+ The name of the indexer gamma.
708
+ name : str, optional
709
+ The name of the instance.
710
+ tex_name : str, optional
711
+ The TeX name for the instance.
712
+ unit : str, optional
713
+ The unit of the output.
714
+ info : str, optional
715
+ A description of the operation.
716
+ vtype : Type, optional
717
+ The variable type.
718
+ rfun : Callable, optional
719
+ Function to apply to the output of ``fun``.
720
+ rargs : dict, optional
721
+ Keyword arguments to pass to ``rfun``.
722
+ array_out : bool, optional
723
+ Whether to force the output to be an array.
724
+ no_parse: bool, optional
725
+ True to skip parsing the service.
726
+ sparse: bool, optional
727
+ True to return output as scipy csr_matrix.
728
+ """
729
+
730
+ def __init__(self,
731
+ u: Callable,
732
+ indexer: str,
733
+ gamma: str = None,
734
+ name: str = None,
735
+ tex_name: str = None,
736
+ unit: str = None,
737
+ info: str = None,
738
+ vtype: Type = None,
739
+ rfun: Callable = None,
740
+ rargs: dict = None,
741
+ array_out: bool = True,
742
+ no_parse: bool = False,
743
+ sparse: bool = False,
744
+ **kwargs):
745
+ super().__init__(name=name, tex_name=tex_name, unit=unit,
746
+ info=info, vtype=vtype, u=u, fun=None,
747
+ rfun=rfun, rargs=rargs, array_out=array_out,
748
+ no_parse=no_parse, sparse=sparse,
749
+ **kwargs)
750
+ self.indexer = indexer
751
+ self.gamma = gamma
752
+
753
+ @property
754
+ def v0(self):
755
+ # FIXME: what if reference source has no idx?
756
+ # data consistency check
757
+ indexer = getattr(self.rtn, self.indexer)
758
+ err_msg = f'Indexer source {indexer.model} has no {indexer.src}.'
759
+ group = model = None
760
+ if indexer.model in self.rtn.system.groups.keys():
761
+ group = self.rtn.system.groups[indexer.model]
762
+ group_idx = group.get_all_idxes()
763
+ try:
764
+ ref = group.get(src=indexer.src, attr='v', idx=group_idx)
765
+ except AttributeError:
766
+ raise AttributeError(err_msg)
767
+ elif indexer.model in self.rtn.system.models.keys():
768
+ model = self.rtn.system.models[indexer.model]
769
+ try:
770
+ ref = model.get(src=indexer.src, attr='v', idx=model.idx.v)
771
+ except AttributeError:
772
+ raise AttributeError(err_msg)
773
+ else:
774
+ raise AttributeError(f'Indexer source model {indexer.model} has no ref.')
775
+
776
+ try:
777
+ uidx = self.u.get_all_idxes()
778
+ except AttributeError:
779
+ raise AttributeError(f'Input {self.u.name} has no idx, likey a modeling error.')
780
+
781
+ is_empty = len(ref) == 0
782
+ if is_empty:
783
+ raise ValueError(f'{indexer.model} contains no input, likey a data error.')
784
+
785
+ is_subset = set(ref).issubset(set(uidx))
786
+ if not is_subset:
787
+ raise ValueError(f'{indexer.model} contains undefined {indexer.src}, likey a data error.')
788
+
789
+ row, col = np.meshgrid(uidx, ref)
790
+ out = (row == col).astype(int)
791
+ if self.gamma:
792
+ vgamma = getattr(self.rtn, self.gamma)
793
+ out = vgamma.v[:, np.newaxis] * out
794
+ return out
795
+
796
+
797
+ class VarReduction(NumOp):
798
+ """
799
+ A numerical matrix to reduce a 2D variable to 1D,
800
+ ``np.fun(shape=(1, u.n))``.
801
+
802
+ Parameters
803
+ ----------
804
+ u : Callable
805
+ The input matrix variable.
806
+ fun : Callable
807
+ The reduction function that takes a shape argument (1D shape) as input.
808
+ name : str, optional
809
+ The name of the instance.
810
+ tex_name : str, optional
811
+ The TeX name for the instance.
812
+ unit : str, optional
813
+ The unit of the output.
814
+ info : str, optional
815
+ A description of the operation.
816
+ vtype : Type, optional
817
+ The variable type.
818
+ rfun : Callable, optional
819
+ Function to apply to the output of ``fun``.
820
+ rargs : dict, optional
821
+ Keyword arguments to pass to ``rfun``.
822
+ no_parse: bool, optional
823
+ True to skip parsing the service.
824
+ sparse: bool, optional
825
+ True to return output as scipy csr_matrix.
826
+ """
827
+
828
+ def __init__(self,
829
+ u: Callable,
830
+ fun: Callable,
831
+ name: str = None,
832
+ tex_name: str = None,
833
+ unit: str = None,
834
+ info: str = None,
835
+ vtype: Type = None,
836
+ rfun: Callable = None,
837
+ rargs: dict = None,
838
+ no_parse: bool = False,
839
+ sparse: bool = False,):
840
+ super().__init__(name=name, tex_name=tex_name, unit=unit,
841
+ info=info, vtype=vtype,
842
+ u=u, fun=None, rfun=rfun, rargs=rargs,
843
+ no_parse=no_parse, sparse=sparse,)
844
+ self.fun = fun
845
+
846
+ @property
847
+ def v0(self):
848
+ return self.fun(shape=(1, self.u.n))
849
+
850
+
851
+ class RampSub(NumOp):
852
+ """
853
+ Build a substraction matrix for a 2D variable in the shape (nr, nr-1),
854
+ where nr is the rows of the input.
855
+
856
+ This can be used for generator ramping constraints in multi-period
857
+ optimization problems.
858
+
859
+ The subtraction matrix is constructed as follows:
860
+ ``np.eye(nr, nc, k=-1) - np.eye(nr, nc, k=0)``.
861
+
862
+ Parameters
863
+ ----------
864
+ u : Callable
865
+ Input.
866
+ horizon : Callable
867
+ Horizon reference.
868
+ name : str
869
+ Instance name.
870
+ tex_name : str
871
+ TeX name.
872
+ unit : str
873
+ Unit.
874
+ info : str
875
+ Description.
876
+ vtype : Type
877
+ Variable type.
878
+ rfun : Callable, optional
879
+ Function to apply to the output of ``fun``.
880
+ rargs : dict, optional
881
+ Keyword arguments to pass to ``rfun``.
882
+ no_parse: bool, optional
883
+ True to skip parsing the service.
884
+ sparse: bool, optional
885
+ True to return output as scipy csr_matrix.
886
+ """
887
+
888
+ def __init__(self,
889
+ u: Callable,
890
+ name: str = None,
891
+ tex_name: str = None,
892
+ unit: str = None,
893
+ info: str = None,
894
+ vtype: Type = None,
895
+ rfun: Callable = None,
896
+ rargs: dict = None,
897
+ no_parse: bool = False,
898
+ sparse: bool = False,):
899
+ super().__init__(name=name, tex_name=tex_name, unit=unit,
900
+ info=info, vtype=vtype,
901
+ u=u, fun=None, rfun=rfun, rargs=rargs,
902
+ no_parse=no_parse, sparse=sparse,)
903
+
904
+ @property
905
+ def v0(self):
906
+ return self.v
907
+
908
+ @property
909
+ def v1(self):
910
+ return self.v
911
+
912
+ @property
913
+ def v(self):
914
+ nr = self.u.horizon.n
915
+ out = np.eye(nr, nr-1, k=-1) - np.eye(nr, nr-1, k=0)
916
+ if self.sparse:
917
+ return spr.csr_matrix(out)
918
+ return out