ltbams 1.0.12__py3-none-any.whl → 1.0.13__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/_version.py +3 -3
- ams/core/matprocessor.py +170 -104
- ams/io/matpower.py +4 -0
- ams/io/psse.py +2 -0
- ams/opt/exprcalc.py +11 -0
- ams/routines/grbopt.py +2 -0
- ams/routines/pypower.py +8 -0
- ams/routines/routine.py +118 -1
- ams/shared.py +30 -2
- ams/system.py +10 -0
- docs/source/index.rst +4 -3
- docs/source/release-notes.rst +8 -0
- {ltbams-1.0.12.dist-info → ltbams-1.0.13.dist-info}/METADATA +4 -2
- {ltbams-1.0.12.dist-info → ltbams-1.0.13.dist-info}/RECORD +17 -45
- {ltbams-1.0.12.dist-info → ltbams-1.0.13.dist-info}/top_level.txt +0 -1
- tests/__init__.py +0 -0
- tests/test_1st_system.py +0 -64
- tests/test_addressing.py +0 -40
- tests/test_case.py +0 -301
- tests/test_cli.py +0 -34
- tests/test_export_csv.py +0 -89
- tests/test_group.py +0 -83
- tests/test_interface.py +0 -238
- tests/test_io.py +0 -190
- tests/test_jumper.py +0 -27
- tests/test_known_good.py +0 -267
- tests/test_matp.py +0 -437
- tests/test_model.py +0 -54
- tests/test_omodel.py +0 -119
- tests/test_paths.py +0 -89
- tests/test_report.py +0 -251
- tests/test_repr.py +0 -21
- tests/test_routine.py +0 -178
- tests/test_rtn_acopf.py +0 -75
- tests/test_rtn_dcopf.py +0 -121
- tests/test_rtn_dcopf2.py +0 -103
- tests/test_rtn_ed.py +0 -279
- tests/test_rtn_opf.py +0 -142
- tests/test_rtn_pflow.py +0 -147
- tests/test_rtn_pypower.py +0 -315
- tests/test_rtn_rted.py +0 -273
- tests/test_rtn_uc.py +0 -248
- tests/test_service.py +0 -73
- {ltbams-1.0.12.dist-info → ltbams-1.0.13.dist-info}/WHEEL +0 -0
- {ltbams-1.0.12.dist-info → ltbams-1.0.13.dist-info}/entry_points.txt +0 -0
ams/_version.py
CHANGED
@@ -8,11 +8,11 @@ import json
|
|
8
8
|
|
9
9
|
version_json = '''
|
10
10
|
{
|
11
|
-
"date": "2025-
|
11
|
+
"date": "2025-08-18T15:13:43-0700",
|
12
12
|
"dirty": false,
|
13
13
|
"error": null,
|
14
|
-
"full-revisionid": "
|
15
|
-
"version": "1.0.
|
14
|
+
"full-revisionid": "1aab83cb951d517c906fe117499d482da8b6e66b",
|
15
|
+
"version": "1.0.13"
|
16
16
|
}
|
17
17
|
''' # END VERSION_JSON
|
18
18
|
|
ams/core/matprocessor.py
CHANGED
@@ -3,20 +3,18 @@ Module for system matrix make.
|
|
3
3
|
"""
|
4
4
|
|
5
5
|
import logging
|
6
|
-
import sys
|
7
6
|
from typing import Optional
|
8
7
|
|
9
8
|
import numpy as np
|
10
9
|
|
11
10
|
from andes.thirdparty.npfunc import safe_div
|
12
|
-
from andes.
|
13
|
-
from andes.utils.misc import elapsed, is_notebook
|
11
|
+
from andes.utils.misc import elapsed
|
14
12
|
|
15
13
|
from ams.opt import Param
|
16
14
|
|
17
15
|
from ams.utils.paths import get_export_path
|
18
16
|
|
19
|
-
from ams.shared import pd, sps
|
17
|
+
from ams.shared import pd, sps, _init_pbar, _update_pbar
|
20
18
|
|
21
19
|
logger = logging.getLogger(__name__)
|
22
20
|
|
@@ -73,6 +71,109 @@ class MParam(Param):
|
|
73
71
|
self.col_names = col_names
|
74
72
|
self.row_names = row_names
|
75
73
|
|
74
|
+
def load_npz(self, path=None):
|
75
|
+
"""
|
76
|
+
Load the FULL matrix from a npz file.
|
77
|
+
|
78
|
+
Parameters
|
79
|
+
----------
|
80
|
+
path : str, optional
|
81
|
+
Path of the npz file to load.
|
82
|
+
|
83
|
+
Returns
|
84
|
+
-------
|
85
|
+
MParam
|
86
|
+
The loaded MParam instance.
|
87
|
+
|
88
|
+
.. versionadded:: 1.0.13
|
89
|
+
"""
|
90
|
+
|
91
|
+
if path is None:
|
92
|
+
raise ValueError("Path to the npz file is required.")
|
93
|
+
|
94
|
+
data = sps.load_npz(path) if self.sparse else np.load(path)
|
95
|
+
|
96
|
+
if self.sparse:
|
97
|
+
self._v = data.tocsr()
|
98
|
+
logging.debug(f"Loading sparse matrix {self.name} from npz format.")
|
99
|
+
else:
|
100
|
+
self._v = data['v']
|
101
|
+
logging.warning(f"Loading dense matrix {self.name} from npz format.")
|
102
|
+
|
103
|
+
return self
|
104
|
+
|
105
|
+
def load_csv(self, path=None, chunksize=None, dtype=float):
|
106
|
+
"""
|
107
|
+
Load the matrix from an EXPORTED CSV file.
|
108
|
+
|
109
|
+
Parameters
|
110
|
+
----------
|
111
|
+
path : str, optional
|
112
|
+
Path of the csv file to load.
|
113
|
+
chunksize : int, optional
|
114
|
+
If specified, read the csv file in chunks of this size.
|
115
|
+
|
116
|
+
Returns
|
117
|
+
-------
|
118
|
+
MParam
|
119
|
+
The loaded MParam instance.
|
120
|
+
|
121
|
+
.. versionadded:: 1.0.13
|
122
|
+
"""
|
123
|
+
|
124
|
+
if path is None:
|
125
|
+
raise ValueError("Path to the csv file is required.")
|
126
|
+
|
127
|
+
if chunksize:
|
128
|
+
chunks = pd.read_csv(path, index_col=0, chunksize=chunksize, dtype=dtype)
|
129
|
+
df = pd.concat(chunks)
|
130
|
+
else:
|
131
|
+
df = pd.read_csv(path, index_col=0, dtype=dtype)
|
132
|
+
|
133
|
+
if self.sparse:
|
134
|
+
self._v = sps.csr_matrix(df.values)
|
135
|
+
logging.debug(f"Loading sparse matrix {self.name} from csv format.")
|
136
|
+
else:
|
137
|
+
self._v = df.values
|
138
|
+
self.col_names = df.columns.tolist()
|
139
|
+
self.row_names = df.index.tolist()
|
140
|
+
|
141
|
+
logging.debug(f"Loading matrix {self.name} from csv format.")
|
142
|
+
return self
|
143
|
+
|
144
|
+
def export_npz(self, path=None):
|
145
|
+
"""
|
146
|
+
Export the matrix to a npz file.
|
147
|
+
|
148
|
+
Parameters
|
149
|
+
----------
|
150
|
+
path : str, optional
|
151
|
+
Path of the npz file to export.
|
152
|
+
|
153
|
+
Returns
|
154
|
+
-------
|
155
|
+
str
|
156
|
+
The exported npz file name
|
157
|
+
|
158
|
+
.. versionadded:: 1.0.13
|
159
|
+
"""
|
160
|
+
|
161
|
+
path, file_name = get_export_path(self.owner.system,
|
162
|
+
self.name,
|
163
|
+
path=path,
|
164
|
+
fmt='npz')
|
165
|
+
|
166
|
+
if sps.issparse(self._v):
|
167
|
+
sps.save_npz(path, self._v.tocsr())
|
168
|
+
logging.debug(f"Saving sparse matrix {self.name} to npz format.")
|
169
|
+
elif isinstance(self._v, np.ndarray):
|
170
|
+
np.savez(path, v=self._v) # Save with a key 'v' inside the NPZ archive
|
171
|
+
logging.warning(f"Saving dense matrix {self.name} to npz format.")
|
172
|
+
else:
|
173
|
+
raise TypeError(f"Unsupported matrix type: {type(self._v)}")
|
174
|
+
|
175
|
+
return file_name
|
176
|
+
|
76
177
|
def export_csv(self, path=None):
|
77
178
|
"""
|
78
179
|
Export the matrix to a CSV file.
|
@@ -149,7 +250,6 @@ class MatProcessor:
|
|
149
250
|
def __init__(self, system):
|
150
251
|
self.system = system
|
151
252
|
self.initialized = False
|
152
|
-
self.pbar = None
|
153
253
|
|
154
254
|
self.Cft = MParam(name='Cft', tex_name=r'C_{ft}',
|
155
255
|
info='Line connectivity matrix',
|
@@ -182,10 +282,10 @@ class MatProcessor:
|
|
182
282
|
|
183
283
|
self.PTDF = MParam(name='PTDF', tex_name=r'P_{TDF}',
|
184
284
|
info='Power transfer distribution factor',
|
185
|
-
v=None, sparse=
|
285
|
+
v=None, sparse=True, owner=self)
|
186
286
|
self.LODF = MParam(name='LODF', tex_name=r'O_{TDF}',
|
187
287
|
info='Line outage distribution factor',
|
188
|
-
v=None, sparse=
|
288
|
+
v=None, sparse=True, owner=self)
|
189
289
|
|
190
290
|
def build(self, force=False):
|
191
291
|
"""
|
@@ -270,6 +370,8 @@ class MatProcessor:
|
|
270
370
|
row = np.array([system.Bus.idx2uid(x) for x in on_gen_bus])
|
271
371
|
col = np.array([idx_gen.index(x) for x in on_gen_idx])
|
272
372
|
self.Cg._v = sps.csr_matrix((np.ones(len(on_gen_idx)), (row, col)), (nb, ng))
|
373
|
+
self.Cg.col_names = idx_gen
|
374
|
+
self.Cg.row_names = system.Bus.idx.v
|
273
375
|
return self.Cg._v
|
274
376
|
|
275
377
|
def build_cl(self):
|
@@ -297,6 +399,8 @@ class MatProcessor:
|
|
297
399
|
row = np.array([system.Bus.idx2uid(x) for x in on_load_bus])
|
298
400
|
col = np.array([system.PQ.idx2uid(x) for x in on_load_idx])
|
299
401
|
self.Cl._v = sps.csr_matrix((np.ones(len(on_load_idx)), (row, col)), (nb, npq))
|
402
|
+
self.Cl.col_names = idx_load
|
403
|
+
self.Cl.row_names = system.Bus.idx.v
|
300
404
|
return self.Cl._v
|
301
405
|
|
302
406
|
def build_csh(self):
|
@@ -324,6 +428,8 @@ class MatProcessor:
|
|
324
428
|
row = np.array([system.Bus.idx2uid(x) for x in on_shunt_bus])
|
325
429
|
col = np.array([system.Shunt.idx2uid(x) for x in on_shunt_idx])
|
326
430
|
self.Csh._v = sps.csr_matrix((np.ones(len(on_shunt_idx)), (row, col)), (nb, nsh))
|
431
|
+
self.Csh.col_names = idx_shunt
|
432
|
+
self.Csh.row_names = system.Bus.idx.v
|
327
433
|
return self.Csh._v
|
328
434
|
|
329
435
|
def build_cft(self):
|
@@ -356,6 +462,10 @@ class MatProcessor:
|
|
356
462
|
col_line = np.array([system.Line.idx2uid(x) for x in on_line_idx + on_line_idx])
|
357
463
|
self.Cft._v = sps.csr_matrix((data_line, (row_line, col_line)), (nb, nl))
|
358
464
|
self.CftT._v = self.Cft._v.T
|
465
|
+
self.Cft.col_names = idx_line
|
466
|
+
self.Cft.row_names = system.Bus.idx.v
|
467
|
+
self.CftT.col_names = system.Bus.idx.v
|
468
|
+
self.CftT.row_names = idx_line
|
359
469
|
return self.Cft._v
|
360
470
|
|
361
471
|
def build_bf(self):
|
@@ -383,6 +493,8 @@ class MatProcessor:
|
|
383
493
|
t = system.Bus.idx2uid(system.Line.get(src='bus2', attr='v', idx=idx_line))
|
384
494
|
ir = np.r_[range(nl), range(nl)] # double set of row indices
|
385
495
|
self.Bf._v = sps.csr_matrix((np.r_[b, -b], (ir, np.r_[f, t])), (nl, nb))
|
496
|
+
self.Bf.col_names = system.Bus.idx.v
|
497
|
+
self.Bf.row_names = system.Line.idx.v
|
386
498
|
return self.Bf._v
|
387
499
|
|
388
500
|
def build_bbus(self):
|
@@ -395,6 +507,8 @@ class MatProcessor:
|
|
395
507
|
DC bus admittance matrix.
|
396
508
|
"""
|
397
509
|
self.Bbus._v = self.Cft._v * self.Bf._v
|
510
|
+
self.Bbus.col_names = self.system.Bus.idx.v
|
511
|
+
self.Bbus.row_names = self.system.Bus.idx.v
|
398
512
|
return self.Bbus._v
|
399
513
|
|
400
514
|
def build_pfinj(self):
|
@@ -410,6 +524,8 @@ class MatProcessor:
|
|
410
524
|
b = self._calc_b()
|
411
525
|
phi = self.system.Line.get(src='phi', attr='v', idx=idx_line)
|
412
526
|
self.Pfinj._v = b * (-phi)
|
527
|
+
# NOTE: leave the row_names empty for the vector
|
528
|
+
self.Pfinj.col_names = self.system.Line.idx.v
|
413
529
|
return self.Pfinj._v
|
414
530
|
|
415
531
|
def build_pbusinj(self):
|
@@ -422,6 +538,8 @@ class MatProcessor:
|
|
422
538
|
Bus power injection vector.
|
423
539
|
"""
|
424
540
|
self.Pbusinj._v = self.Cft._v * self.Pfinj._v
|
541
|
+
# NOTE: leave the row_names empty for the vector
|
542
|
+
self.Pbusinj.col_names = self.system.Bus.idx.v
|
425
543
|
return self.Pbusinj._v
|
426
544
|
|
427
545
|
def _calc_b(self):
|
@@ -452,7 +570,7 @@ class MatProcessor:
|
|
452
570
|
return b
|
453
571
|
|
454
572
|
def build_ptdf(self, line=None, no_store=False,
|
455
|
-
incremental=False, step=1000, no_tqdm=
|
573
|
+
incremental=False, step=1000, no_tqdm=True,
|
456
574
|
permc_spec=None, use_umfpack=True):
|
457
575
|
"""
|
458
576
|
Build the Power Transfer Distribution Factor (PTDF) matrix and optionally store it in `MParam.PTDF`.
|
@@ -460,11 +578,8 @@ class MatProcessor:
|
|
460
578
|
PTDF[m, n] represents the increased line flow on line `m` for a 1 p.u. power injection at bus `n`.
|
461
579
|
It is similar to the Generation Shift Factor (GSF).
|
462
580
|
|
463
|
-
|
464
|
-
|
465
|
-
For large cases, use `incremental=True` to calculate the sparse PTDF in chunks, which will be stored
|
466
|
-
as a `scipy.sparse.lil_matrix`. In this mode, the PTDF is calculated in chunks, and a progress bar
|
467
|
-
will be shown unless `no_tqdm=True`.
|
581
|
+
For large cases, use `incremental=True` to calculate the sparse PTDF in chunks. In this mode, the
|
582
|
+
PTDF is calculated in chunks, and thus more memory friendly.
|
468
583
|
|
469
584
|
Parameters
|
470
585
|
----------
|
@@ -486,7 +601,7 @@ class MatProcessor:
|
|
486
601
|
|
487
602
|
Returns
|
488
603
|
-------
|
489
|
-
PTDF :
|
604
|
+
PTDF : scipy.sparse.lil_matrix
|
490
605
|
Power transfer distribution factor.
|
491
606
|
|
492
607
|
References
|
@@ -506,13 +621,18 @@ class MatProcessor:
|
|
506
621
|
|
507
622
|
if line is None:
|
508
623
|
luid = system.Line.idx2uid(system.Line.idx.v)
|
624
|
+
self.PTDF.row_names = system.Line.idx.v
|
509
625
|
elif isinstance(line, (int, str)):
|
510
626
|
try:
|
511
627
|
luid = [system.Line.idx2uid(line)]
|
628
|
+
self.PTDF.row_names = [line]
|
512
629
|
except ValueError:
|
513
630
|
raise ValueError(f"Line {line} not found.")
|
514
631
|
elif isinstance(line, list):
|
515
632
|
luid = system.Line.idx2uid(line)
|
633
|
+
self.PTDF.row_names = line
|
634
|
+
|
635
|
+
self.PTDF.col_names = system.Bus.idx.v
|
516
636
|
|
517
637
|
# build other matrices if not built
|
518
638
|
if not self.initialized:
|
@@ -527,15 +647,7 @@ class MatProcessor:
|
|
527
647
|
|
528
648
|
if incremental:
|
529
649
|
# initialize progress bar
|
530
|
-
|
531
|
-
self.pbar = tqdm_nb(total=100, unit='%', file=sys.stdout,
|
532
|
-
disable=no_tqdm)
|
533
|
-
else:
|
534
|
-
self.pbar = tqdm(total=100, unit='%', ncols=80, ascii=True,
|
535
|
-
file=sys.stdout, disable=no_tqdm)
|
536
|
-
|
537
|
-
self.pbar.update(0)
|
538
|
-
last_pc = 0
|
650
|
+
pbar = _init_pbar(total=100, unit='%', no_tqdm=no_tqdm)
|
539
651
|
|
540
652
|
H = sps.lil_matrix((nline, system.Bus.n))
|
541
653
|
|
@@ -548,23 +660,13 @@ class MatProcessor:
|
|
548
660
|
use_umfpack=use_umfpack).T
|
549
661
|
H[start:end, noslack] = sol
|
550
662
|
|
551
|
-
|
552
|
-
perc = np.round(min((end / nline) * 100, 100), 2)
|
663
|
+
_update_pbar(pbar, end, nline)
|
553
664
|
|
554
|
-
perc_diff = perc - last_pc
|
555
|
-
if perc_diff >= 1:
|
556
|
-
self.pbar.update(perc_diff)
|
557
|
-
last_pc = perc
|
558
|
-
|
559
|
-
# finish progress bar
|
560
|
-
self.pbar.update(100 - last_pc)
|
561
|
-
# removed `pbar` so that System object can be serialized
|
562
|
-
self.pbar.close()
|
563
|
-
self.pbar = None
|
564
665
|
else:
|
565
|
-
H =
|
566
|
-
|
567
|
-
|
666
|
+
H = sps.lil_matrix((nline, nbus))
|
667
|
+
sol = np.linalg.solve(Bbus.todense()[np.ix_(noslack, noref)].T,
|
668
|
+
Bf.todense()[np.ix_(luid, noref)].T).T
|
669
|
+
H[:, noslack] = sol
|
568
670
|
|
569
671
|
# reshape results into 1D array if only one line
|
570
672
|
if isinstance(line, (int, str)):
|
@@ -576,7 +678,7 @@ class MatProcessor:
|
|
576
678
|
return H
|
577
679
|
|
578
680
|
def build_lodf(self, line=None, no_store=False,
|
579
|
-
incremental=False, step=1000, no_tqdm=
|
681
|
+
incremental=False, step=1000, no_tqdm=True):
|
580
682
|
"""
|
581
683
|
Build the Line Outage Distribution Factor matrix and store it in the
|
582
684
|
MParam `LODF`.
|
@@ -607,7 +709,7 @@ class MatProcessor:
|
|
607
709
|
|
608
710
|
Returns
|
609
711
|
-------
|
610
|
-
LODF :
|
712
|
+
LODF : scipy.sparse.lil_matrix
|
611
713
|
Line outage distribution factor.
|
612
714
|
|
613
715
|
References
|
@@ -635,81 +737,45 @@ class MatProcessor:
|
|
635
737
|
# build PTDF if not built
|
636
738
|
if self.PTDF._v is None:
|
637
739
|
ptdf = self.build_ptdf(no_store=True, incremental=incremental, step=step)
|
638
|
-
if incremental and isinstance(self.PTDF._v, np.ndarray):
|
639
|
-
ptdf = sps.lil_matrix(self.PTDF._v)
|
640
740
|
|
641
|
-
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
self.
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
# NOTE:
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
h_chunk = H_chunk.diagonal(-luidi[0])
|
661
|
-
rden = safe_div(np.ones(H_chunk.shape),
|
662
|
-
np.tile(np.ones_like(h_chunk) - h_chunk, (nbranch, 1)))
|
663
|
-
H_chunk = H_chunk.multiply(rden).tolil()
|
664
|
-
# NOTE: use lil_matrix to set diagonal values as -1
|
665
|
-
rsid = sps.diags(H_chunk.diagonal(-luidi[0])) + sps.eye(H_chunk.shape[1])
|
666
|
-
if H_chunk.shape[0] > rsid.shape[0]:
|
667
|
-
Rsid = sps.lil_matrix(H_chunk.shape)
|
668
|
-
Rsid[luidi, :] = rsid
|
669
|
-
else:
|
670
|
-
Rsid = rsid
|
671
|
-
H_chunk = H_chunk - Rsid
|
672
|
-
LODF[:, [luid.index(i) for i in luidi]] = H_chunk
|
673
|
-
|
674
|
-
# show progress in percentage
|
675
|
-
perc = np.round(min((luid.index(luidi[-1]) / nline) * 100, 100), 2)
|
676
|
-
|
677
|
-
perc_diff = perc - last_pc
|
678
|
-
if perc_diff >= 1:
|
679
|
-
self.pbar.update(perc_diff)
|
680
|
-
last_pc = perc
|
681
|
-
|
682
|
-
# finish progress bar
|
683
|
-
self.pbar.update(100 - last_pc)
|
684
|
-
# removed `pbar` so that System object can be serialized
|
685
|
-
self.pbar.close()
|
686
|
-
self.pbar = None
|
687
|
-
else:
|
688
|
-
H = ptdf @ self.Cft._v[:, luid]
|
689
|
-
h = np.diag(H, -luid[0])
|
690
|
-
LODF = safe_div(H,
|
691
|
-
np.tile(np.ones_like(h) - h, (nbranch, 1)))
|
692
|
-
# # NOTE: reset the diagonal elements to -1
|
693
|
-
rsid = np.diag(np.diag(LODF, -luid[0])) + np.eye(nline, nline)
|
694
|
-
if LODF.shape[0] > rsid.shape[0]:
|
695
|
-
Rsid = np.zeros_like(LODF)
|
696
|
-
Rsid[luid, :] = rsid
|
741
|
+
# initialize progress bar
|
742
|
+
pbar = _init_pbar(total=100, unit='%', no_tqdm=no_tqdm)
|
743
|
+
|
744
|
+
LODF = sps.lil_matrix((nbranch, nline))
|
745
|
+
|
746
|
+
# NOTE: for LODF, we are doing it columns by columns
|
747
|
+
# reshape luid to list of list by step
|
748
|
+
luidp = [luid[i:i + step] for i in range(0, len(luid), step)]
|
749
|
+
for luidi in luidp:
|
750
|
+
H_chunk = ptdf @ self.Cft._v[:, luidi]
|
751
|
+
h_chunk = H_chunk.diagonal(-luidi[0])
|
752
|
+
rden = safe_div(np.ones(H_chunk.shape),
|
753
|
+
np.tile(np.ones_like(h_chunk) - h_chunk, (nbranch, 1)))
|
754
|
+
H_chunk = H_chunk.multiply(rden).tolil()
|
755
|
+
# NOTE: use lil_matrix to set diagonal values as -1
|
756
|
+
rsid = sps.diags(H_chunk.diagonal(-luidi[0])) + sps.eye(H_chunk.shape[1])
|
757
|
+
if H_chunk.shape[0] > rsid.shape[0]:
|
758
|
+
Rsid = sps.lil_matrix(H_chunk.shape)
|
759
|
+
Rsid[luidi, :] = rsid
|
697
760
|
else:
|
698
761
|
Rsid = rsid
|
699
|
-
|
762
|
+
H_chunk = H_chunk - Rsid
|
763
|
+
LODF[:, [luid.index(i) for i in luidi]] = H_chunk
|
764
|
+
|
765
|
+
_update_pbar(pbar, luid.index(luidi[-1]), nline)
|
700
766
|
|
701
767
|
# reshape results into 1D array if only one line
|
702
768
|
if isinstance(line, (int, str)):
|
703
769
|
LODF = LODF[:, 0]
|
704
770
|
|
705
|
-
if (not no_store)
|
771
|
+
if (not no_store) and (line is None):
|
706
772
|
self.LODF._v = LODF
|
707
773
|
return LODF
|
708
774
|
|
709
775
|
def build_otdf(self, line=None):
|
710
776
|
"""
|
711
|
-
Build the Outrage Transfer Distribution Factor (OTDF) matrix for
|
712
|
-
k outage: $OTDF_k = PTDF + LODF[:, k] @ PTDF[k, ]$.
|
777
|
+
Build the Outrage Transfer Distribution Factor (OTDF) matrix for
|
778
|
+
**line k** outage: $OTDF_k = PTDF + LODF[:, k] @ PTDF[k, ]$.
|
713
779
|
|
714
780
|
OTDF_k[m, n] means the increased line flow on line `m` when there is
|
715
781
|
1 p.u. power injection at bus `n` when line `k` is outage.
|
@@ -725,7 +791,7 @@ class MatProcessor:
|
|
725
791
|
|
726
792
|
Returns
|
727
793
|
-------
|
728
|
-
OTDF :
|
794
|
+
OTDF : scipy.sparse.csr_matrix
|
729
795
|
Line outage distribution factor.
|
730
796
|
|
731
797
|
References
|
@@ -750,4 +816,4 @@ class MatProcessor:
|
|
750
816
|
luid = self.system.Line.idx2uid(line)
|
751
817
|
|
752
818
|
otdf = ptdf + lodf[:, luid] @ ptdf[luid, :]
|
753
|
-
return otdf
|
819
|
+
return otdf.tocsr()
|
ams/io/matpower.py
CHANGED
@@ -646,6 +646,8 @@ def mpc2m(mpc: dict, outfile: str) -> str:
|
|
646
646
|
MATPOWER mpc dictionary.
|
647
647
|
outfile : str
|
648
648
|
Path to the output M-file.
|
649
|
+
|
650
|
+
.. versionadded:: 1.0.10
|
649
651
|
"""
|
650
652
|
with open(outfile, 'w') as f:
|
651
653
|
# Add version info
|
@@ -758,6 +760,8 @@ def write(system, outfile: str, overwrite: bool = None) -> bool:
|
|
758
760
|
- Unlike the XLSX and JSON converters, this implementation uses value providers
|
759
761
|
(`v`) instead of vin. As a result, any changes made through `model.set` will be
|
760
762
|
reflected in the generated MPC.
|
763
|
+
|
764
|
+
.. versionadded:: 1.0.10
|
761
765
|
"""
|
762
766
|
if not confirm_overwrite(outfile, overwrite=overwrite):
|
763
767
|
return False
|
ams/io/psse.py
CHANGED
@@ -56,6 +56,8 @@ def write_raw(system, outfile: str, overwrite: bool = None):
|
|
56
56
|
The output file path.
|
57
57
|
overwrite : bool, optional
|
58
58
|
If True, overwrite the file if it exists. If False, do not overwrite.
|
59
|
+
|
60
|
+
.. versionadded:: 1.0.10
|
59
61
|
"""
|
60
62
|
if not confirm_overwrite(outfile, overwrite=overwrite):
|
61
63
|
return False
|
ams/opt/exprcalc.py
CHANGED
@@ -102,6 +102,17 @@ class ExpressionCalc(OptzBase):
|
|
102
102
|
else:
|
103
103
|
return self.optz.value
|
104
104
|
|
105
|
+
@v.setter
|
106
|
+
def v(self, value):
|
107
|
+
"""
|
108
|
+
Set the ExpressionCalc value.
|
109
|
+
"""
|
110
|
+
if self.optz is None:
|
111
|
+
raise ValueError("ExpressionCalc is not evaluated yet.")
|
112
|
+
if not isinstance(value, (int, float, np.ndarray)):
|
113
|
+
raise TypeError(f"Value must be a number or numpy array, got {type(value)}.")
|
114
|
+
self.optz.value = value
|
115
|
+
|
105
116
|
@property
|
106
117
|
def e(self):
|
107
118
|
"""
|
ams/routines/grbopt.py
CHANGED
ams/routines/pypower.py
CHANGED
@@ -32,6 +32,8 @@ class DCPF1(RoutineBase):
|
|
32
32
|
- This class does not implement the AMS-style DC power flow formulation.
|
33
33
|
- For detailed mathematical formulations and algorithmic details, refer to the
|
34
34
|
MATPOWER User's Manual, section on Power Flow.
|
35
|
+
|
36
|
+
.. versionadded:: 1.0.10
|
35
37
|
"""
|
36
38
|
|
37
39
|
def __init__(self, system, config):
|
@@ -362,6 +364,8 @@ class PFlow1(DCPF1):
|
|
362
364
|
MATPOWER User's Manual, section on Power Flow.
|
363
365
|
- Fast-Decoupled (XB version) and Fast-Decoupled (BX version) algorithms are
|
364
366
|
not fully supported yet.
|
367
|
+
|
368
|
+
.. versionadded:: 1.0.10
|
365
369
|
"""
|
366
370
|
|
367
371
|
def __init__(self, system, config):
|
@@ -436,6 +440,8 @@ class DCOPF1(DCPF1):
|
|
436
440
|
- For detailed mathematical formulations and algorithmic details, refer to the
|
437
441
|
MATPOWER User's Manual, section on Optimal Power Flow.
|
438
442
|
- Algorithms 400, 500, 600, and 700 are not fully supported yet.
|
443
|
+
|
444
|
+
.. versionadded:: 1.0.10
|
439
445
|
"""
|
440
446
|
|
441
447
|
def __init__(self, system, config):
|
@@ -586,6 +592,8 @@ class ACOPF1(DCOPF1):
|
|
586
592
|
- This class does not implement the AMS-style AC optimal power flow formulation.
|
587
593
|
- For detailed mathematical formulations and algorithmic details, refer to the
|
588
594
|
MATPOWER User's Manual, section on Optimal Power Flow.
|
595
|
+
|
596
|
+
.. versionadded:: 1.0.10
|
589
597
|
"""
|
590
598
|
|
591
599
|
def __init__(self, system, config):
|