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.
- ams/__init__.py +4 -11
- ams/_version.py +3 -3
- ams/cases/5bus/pjm5bus_demo.xlsx +0 -0
- ams/cases/5bus/pjm5bus_jumper.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced.json +1062 -0
- ams/cases/5bus/pjm5bus_uced.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced_esd1.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced_ev.xlsx +0 -0
- ams/cases/ieee123/ieee123.xlsx +0 -0
- ams/cases/ieee123/ieee123_regcv1.xlsx +0 -0
- ams/cases/ieee14/ieee14.json +1166 -0
- ams/cases/ieee14/ieee14.raw +92 -0
- ams/cases/ieee14/ieee14_conn.xlsx +0 -0
- ams/cases/ieee14/ieee14_uced.xlsx +0 -0
- ams/cases/ieee39/ieee39.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_esd1.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_pvd1.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_vis.xlsx +0 -0
- ams/cases/matpower/benchmark.json +1594 -0
- ams/cases/matpower/case118.m +787 -0
- ams/cases/matpower/case14.m +129 -0
- ams/cases/matpower/case300.m +1315 -0
- ams/cases/matpower/case39.m +205 -0
- ams/cases/matpower/case5.m +62 -0
- ams/cases/matpower/case_ACTIVSg2000.m +9460 -0
- ams/cases/npcc/npcc.m +644 -0
- ams/cases/npcc/npcc_uced.xlsx +0 -0
- ams/cases/pglib/pglib_opf_case39_epri__api.m +243 -0
- ams/cases/wecc/wecc.m +714 -0
- ams/cases/wecc/wecc_uced.xlsx +0 -0
- ams/cli.py +6 -0
- ams/core/__init__.py +2 -0
- ams/core/documenter.py +652 -0
- ams/core/matprocessor.py +782 -0
- ams/core/model.py +330 -0
- ams/core/param.py +322 -0
- ams/core/service.py +918 -0
- ams/core/symprocessor.py +224 -0
- ams/core/var.py +59 -0
- ams/extension/__init__.py +5 -0
- ams/extension/eva.py +401 -0
- ams/interface.py +1085 -0
- ams/io/__init__.py +133 -0
- ams/io/json.py +82 -0
- ams/io/matpower.py +406 -0
- ams/io/psse.py +6 -0
- ams/io/pypower.py +103 -0
- ams/io/xlsx.py +80 -0
- ams/main.py +81 -4
- ams/models/__init__.py +24 -0
- ams/models/area.py +40 -0
- ams/models/bus.py +52 -0
- ams/models/cost.py +169 -0
- ams/models/distributed/__init__.py +3 -0
- ams/models/distributed/esd1.py +71 -0
- ams/models/distributed/ev.py +60 -0
- ams/models/distributed/pvd1.py +67 -0
- ams/models/group.py +231 -0
- ams/models/info.py +26 -0
- ams/models/line.py +238 -0
- ams/models/renewable/__init__.py +5 -0
- ams/models/renewable/regc.py +119 -0
- ams/models/reserve.py +94 -0
- ams/models/shunt.py +14 -0
- ams/models/static/__init__.py +2 -0
- ams/models/static/gen.py +165 -0
- ams/models/static/pq.py +61 -0
- ams/models/timeslot.py +69 -0
- ams/models/zone.py +49 -0
- ams/opt/__init__.py +12 -0
- ams/opt/constraint.py +175 -0
- ams/opt/exprcalc.py +127 -0
- ams/opt/expression.py +188 -0
- ams/opt/objective.py +174 -0
- ams/opt/omodel.py +432 -0
- ams/opt/optzbase.py +192 -0
- ams/opt/param.py +156 -0
- ams/opt/var.py +233 -0
- ams/pypower/__init__.py +8 -0
- ams/pypower/_compat.py +9 -0
- ams/pypower/core/__init__.py +8 -0
- ams/pypower/core/pips.py +894 -0
- ams/pypower/core/ppoption.py +244 -0
- ams/pypower/core/ppver.py +18 -0
- ams/pypower/core/solver.py +2451 -0
- ams/pypower/eps.py +6 -0
- ams/pypower/idx.py +174 -0
- ams/pypower/io.py +604 -0
- ams/pypower/make/__init__.py +11 -0
- ams/pypower/make/matrices.py +665 -0
- ams/pypower/make/pdv.py +506 -0
- ams/pypower/routines/__init__.py +7 -0
- ams/pypower/routines/cpf.py +513 -0
- ams/pypower/routines/cpf_callbacks.py +114 -0
- ams/pypower/routines/opf.py +1803 -0
- ams/pypower/routines/opffcns.py +1946 -0
- ams/pypower/routines/pflow.py +852 -0
- ams/pypower/toggle.py +1098 -0
- ams/pypower/utils.py +293 -0
- ams/report.py +212 -50
- ams/routines/__init__.py +23 -0
- ams/routines/acopf.py +117 -0
- ams/routines/cpf.py +65 -0
- ams/routines/dcopf.py +241 -0
- ams/routines/dcpf.py +209 -0
- ams/routines/dcpf0.py +196 -0
- ams/routines/dopf.py +150 -0
- ams/routines/ed.py +312 -0
- ams/routines/pflow.py +255 -0
- ams/routines/pflow0.py +113 -0
- ams/routines/routine.py +1033 -0
- ams/routines/rted.py +519 -0
- ams/routines/type.py +160 -0
- ams/routines/uc.py +376 -0
- ams/shared.py +63 -9
- ams/system.py +61 -22
- ams/utils/__init__.py +3 -0
- ams/utils/misc.py +77 -0
- ams/utils/paths.py +257 -0
- docs/Makefile +21 -0
- docs/make.bat +35 -0
- docs/source/_templates/autosummary/base.rst +5 -0
- docs/source/_templates/autosummary/class.rst +35 -0
- docs/source/_templates/autosummary/module.rst +65 -0
- docs/source/_templates/autosummary/module_toctree.rst +66 -0
- docs/source/api.rst +102 -0
- docs/source/conf.py +203 -0
- docs/source/examples/index.rst +34 -0
- docs/source/genmodelref.py +61 -0
- docs/source/genroutineref.py +47 -0
- docs/source/getting_started/copyright.rst +20 -0
- docs/source/getting_started/formats/index.rst +20 -0
- docs/source/getting_started/formats/matpower.rst +183 -0
- docs/source/getting_started/formats/psse.rst +46 -0
- docs/source/getting_started/formats/pypower.rst +223 -0
- docs/source/getting_started/formats/xlsx.png +0 -0
- docs/source/getting_started/formats/xlsx.rst +23 -0
- docs/source/getting_started/index.rst +76 -0
- docs/source/getting_started/install.rst +234 -0
- docs/source/getting_started/overview.rst +26 -0
- docs/source/getting_started/testcase.rst +45 -0
- docs/source/getting_started/verification.rst +13 -0
- docs/source/images/curent.ico +0 -0
- docs/source/images/dcopf_time.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_NameOnTrans.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_Transparent.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_Transparent_Name.png +0 -0
- docs/source/images/sponsors/doe.png +0 -0
- docs/source/index.rst +108 -0
- docs/source/modeling/example.rst +159 -0
- docs/source/modeling/index.rst +17 -0
- docs/source/modeling/model.rst +210 -0
- docs/source/modeling/routine.rst +122 -0
- docs/source/modeling/system.rst +51 -0
- docs/source/release-notes.rst +398 -0
- ltbams-1.0.2a1.dist-info/METADATA +210 -0
- ltbams-1.0.2a1.dist-info/RECORD +188 -0
- {ltbams-0.9.9.dist-info → ltbams-1.0.2a1.dist-info}/WHEEL +1 -1
- ltbams-1.0.2a1.dist-info/top_level.txt +3 -0
- tests/__init__.py +0 -0
- tests/test_1st_system.py +33 -0
- tests/test_addressing.py +40 -0
- tests/test_andes_mats.py +61 -0
- tests/test_case.py +266 -0
- tests/test_cli.py +34 -0
- tests/test_export_csv.py +89 -0
- tests/test_group.py +83 -0
- tests/test_interface.py +216 -0
- tests/test_io.py +32 -0
- tests/test_jumper.py +27 -0
- tests/test_known_good.py +267 -0
- tests/test_matp.py +437 -0
- tests/test_model.py +54 -0
- tests/test_omodel.py +119 -0
- tests/test_paths.py +22 -0
- tests/test_report.py +251 -0
- tests/test_repr.py +21 -0
- tests/test_routine.py +178 -0
- tests/test_rtn_dcopf.py +101 -0
- tests/test_rtn_dcpf.py +77 -0
- tests/test_rtn_ed.py +279 -0
- tests/test_rtn_pflow.py +219 -0
- tests/test_rtn_rted.py +273 -0
- tests/test_rtn_uc.py +248 -0
- tests/test_service.py +73 -0
- ltbams-0.9.9.dist-info/LICENSE +0 -692
- ltbams-0.9.9.dist-info/METADATA +0 -859
- ltbams-0.9.9.dist-info/RECORD +0 -14
- ltbams-0.9.9.dist-info/top_level.txt +0 -1
- {ltbams-0.9.9.dist-info → ltbams-1.0.2a1.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
|