ltbams 1.0.12__py3-none-any.whl → 1.0.14__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/cli.py +2 -7
- ams/core/common.py +7 -3
- ams/core/documenter.py +2 -1
- ams/core/matprocessor.py +174 -108
- ams/core/model.py +14 -6
- ams/core/param.py +5 -3
- ams/core/symprocessor.py +8 -2
- ams/core/var.py +1 -1
- ams/extension/eva.py +11 -7
- ams/interface.py +7 -7
- ams/io/json.py +20 -16
- ams/io/matpower.py +10 -6
- ams/io/psse.py +4 -1
- ams/io/xlsx.py +21 -16
- ams/main.py +53 -45
- ams/models/distributed/esd1.py +4 -7
- ams/models/distributed/ev.py +10 -6
- ams/models/distributed/pvd1.py +4 -7
- ams/models/group.py +17 -18
- ams/models/renewable/regc.py +14 -22
- ams/models/timeslot.py +30 -0
- ams/models/zone.py +2 -4
- ams/opt/exprcalc.py +11 -0
- ams/opt/optzbase.py +4 -3
- ams/report.py +2 -7
- ams/routines/dcopf.py +7 -4
- ams/routines/dcopf2.py +14 -4
- ams/routines/dopf.py +2 -2
- ams/routines/ed.py +5 -5
- ams/routines/grbopt.py +2 -0
- ams/routines/pflow.py +1 -1
- ams/routines/pypower.py +8 -0
- ams/routines/routine.py +125 -1
- ams/routines/rted.py +5 -5
- ams/routines/uc.py +2 -2
- ams/shared.py +99 -2
- ams/system.py +103 -18
- ams/utils/paths.py +6 -10
- docs/source/genroutineref.py +12 -0
- docs/source/index.rst +4 -3
- docs/source/release-notes.rst +14 -0
- {ltbams-1.0.12.dist-info → ltbams-1.0.14.dist-info}/METADATA +21 -23
- {ltbams-1.0.12.dist-info → ltbams-1.0.14.dist-info}/RECORD +47 -75
- {ltbams-1.0.12.dist-info → ltbams-1.0.14.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.14.dist-info}/WHEEL +0 -0
- {ltbams-1.0.12.dist-info → ltbams-1.0.14.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-31T23:37:40-0700",
|
12
12
|
"dirty": false,
|
13
13
|
"error": null,
|
14
|
-
"full-revisionid": "
|
15
|
-
"version": "1.0.
|
14
|
+
"full-revisionid": "595ed3a3be7f707c7c4aa79ac73bffa58919dc81",
|
15
|
+
"version": "1.0.14"
|
16
16
|
}
|
17
17
|
''' # END VERSION_JSON
|
18
18
|
|
ams/cli.py
CHANGED
@@ -23,18 +23,13 @@ command_aliases = {
|
|
23
23
|
|
24
24
|
def create_parser():
|
25
25
|
"""
|
26
|
-
Create a parser for the command-line interface
|
26
|
+
Create a parser for the command-line interface,
|
27
|
+
revised from `andes.cli.create_parser`.
|
27
28
|
|
28
29
|
Returns
|
29
30
|
-------
|
30
31
|
argparse.ArgumentParser
|
31
32
|
Parser with all AMS options
|
32
|
-
|
33
|
-
Notes
|
34
|
-
-----
|
35
|
-
Revised from the ANDES project (https://github.com/CURENT/andes).
|
36
|
-
Original author: Hantao Cui
|
37
|
-
License: GPL3
|
38
33
|
"""
|
39
34
|
|
40
35
|
parser = argparse.ArgumentParser()
|
ams/core/common.py
CHANGED
@@ -1,16 +1,20 @@
|
|
1
1
|
import logging
|
2
2
|
|
3
|
-
from andes.core.common import Config as
|
3
|
+
from andes.core.common import Config as adConfig
|
4
4
|
|
5
5
|
logger = logging.getLogger(__name__)
|
6
6
|
|
7
7
|
|
8
|
-
class Config(
|
8
|
+
class Config(adConfig):
|
9
9
|
"""
|
10
10
|
A class for storing configuration, can be used in system,
|
11
11
|
model, routine, and other modules.
|
12
12
|
|
13
|
-
Revised from `andes.core.common.Config
|
13
|
+
Revised from `andes.core.common.Config`, where update method
|
14
|
+
is modified to ensure values in the dictionary are set
|
15
|
+
in the configuration object.
|
16
|
+
|
17
|
+
.. versionadded:: 1.0.11
|
14
18
|
"""
|
15
19
|
|
16
20
|
def __init__(self, name, dct=None, **kwargs):
|
ams/core/documenter.py
CHANGED
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.
|
@@ -143,13 +244,12 @@ class MatProcessor:
|
|
143
244
|
The connectivity matrices `Cft`, `Cg`, `Cl`, and `Csh` ***have taken*** the
|
144
245
|
devices connectivity into account.
|
145
246
|
|
146
|
-
The MParams
|
247
|
+
The MParams row names and col names are assigned in `System.setup()`.
|
147
248
|
"""
|
148
249
|
|
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
|
-
raise ValueError(f"Line {line} not found.")
|
630
|
+
raise ValueError(f"Line {line} not found.") from None
|
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
|
@@ -623,7 +725,7 @@ class MatProcessor:
|
|
623
725
|
try:
|
624
726
|
luid = [system.Line.idx2uid(line)]
|
625
727
|
except ValueError:
|
626
|
-
raise ValueError(f"Line {line} not found.")
|
728
|
+
raise ValueError(f"Line {line} not found.") from None
|
627
729
|
elif isinstance(line, list):
|
628
730
|
luid = system.Line.idx2uid(line)
|
629
731
|
|
@@ -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
|
@@ -745,9 +811,9 @@ class MatProcessor:
|
|
745
811
|
try:
|
746
812
|
luid = [self.system.Line.idx2uid(line)]
|
747
813
|
except ValueError:
|
748
|
-
raise ValueError(f"Line {line} not found.")
|
814
|
+
raise ValueError(f"Line {line} not found.") from None
|
749
815
|
elif isinstance(line, list):
|
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/core/model.py
CHANGED
@@ -235,9 +235,11 @@ class Model:
|
|
235
235
|
|
236
236
|
Notes
|
237
237
|
-----
|
238
|
-
|
239
|
-
|
240
|
-
|
238
|
+
.. versionchanged:: 0.9.14
|
239
|
+
|
240
|
+
The ``attr`` argument was added to allow altering specific attributes.
|
241
|
+
This is useful when manipulating parameter values in the system base
|
242
|
+
and ensuring that changes are reflected in the dumped case file.
|
241
243
|
"""
|
242
244
|
|
243
245
|
instance = self.__dict__[src]
|
@@ -307,6 +309,11 @@ class Model:
|
|
307
309
|
Return the index of the model instance.
|
308
310
|
Equivalent to ``self.idx.v``, develoepd for consistency with group method
|
309
311
|
``get_idx``.
|
312
|
+
|
313
|
+
Notes
|
314
|
+
-----
|
315
|
+
.. deprecated:: 1.0.0
|
316
|
+
Use ``get_all_idxes`` instead.
|
310
317
|
"""
|
311
318
|
return self.idx.v
|
312
319
|
|
@@ -314,13 +321,14 @@ class Model:
|
|
314
321
|
"""
|
315
322
|
Return all the indexes of this model.
|
316
323
|
|
317
|
-
.. note::
|
318
|
-
New in version 1.0.0. Add to follow the group method ``get_all_idxes``.
|
319
|
-
|
320
324
|
Returns
|
321
325
|
-------
|
322
326
|
list
|
323
327
|
A list of indexes.
|
328
|
+
|
329
|
+
Notes
|
330
|
+
-----
|
331
|
+
.. versionadded:: 1.0.0
|
324
332
|
"""
|
325
333
|
return self.idx.v
|
326
334
|
|
ams/core/param.py
CHANGED
@@ -252,6 +252,9 @@ class RParam(Param):
|
|
252
252
|
Notes
|
253
253
|
-----
|
254
254
|
- The value will sort by the indexer if indexed.
|
255
|
+
|
256
|
+
.. deprecated:: 1.0.0
|
257
|
+
Use ``get_all_idxes`` instead.
|
255
258
|
"""
|
256
259
|
if self.indexer is None:
|
257
260
|
if self.is_group:
|
@@ -283,9 +286,6 @@ class RParam(Param):
|
|
283
286
|
"""
|
284
287
|
Get all the indexes of the parameter.
|
285
288
|
|
286
|
-
.. note::
|
287
|
-
New in version 1.0.0.
|
288
|
-
|
289
289
|
Returns
|
290
290
|
-------
|
291
291
|
idx : list
|
@@ -294,6 +294,8 @@ class RParam(Param):
|
|
294
294
|
Notes
|
295
295
|
-----
|
296
296
|
- The value will sort by the indexer if indexed.
|
297
|
+
|
298
|
+
.. versionadded:: 1.0.0
|
297
299
|
"""
|
298
300
|
if self.indexer is None:
|
299
301
|
if self.is_group:
|
ams/core/symprocessor.py
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
"""
|
2
|
-
Symbolic processor class for AMS routines
|
2
|
+
Symbolic processor class for AMS routines,
|
3
|
+
revised from `andes.core.symprocessor`.
|
3
4
|
|
4
|
-
|
5
|
+
Revised from the ANDES project:
|
6
|
+
https://github.com/CURENT/andes
|
7
|
+
|
8
|
+
Original author: Hantao Cui
|
9
|
+
|
10
|
+
License: GNU General Public License v3.0 (GPL-3.0)
|
5
11
|
"""
|
6
12
|
|
7
13
|
import logging
|
ams/core/var.py
CHANGED
ams/extension/eva.py
CHANGED
@@ -2,13 +2,6 @@
|
|
2
2
|
EV Aggregator module.
|
3
3
|
|
4
4
|
EVD is the generated datasets, and EVA is the aggregator model.
|
5
|
-
|
6
|
-
Reference:
|
7
|
-
[1] J. Wang et al., "Electric Vehicles Charging Time Constrained Deliverable Provision of Secondary
|
8
|
-
Frequency Regulation," in IEEE Transactions on Smart Grid, doi: 10.1109/TSG.2024.3356948.
|
9
|
-
[2] M. Wang, Y. Mu, Q. Shi, H. Jia and F. Li, "Electric Vehicle Aggregator Modeling and Control for
|
10
|
-
Frequency Regulation Considering Progressive State Recovery," in IEEE Transactions on Smart Grid,
|
11
|
-
vol. 11, no. 5, pp. 4176-4189, Sept. 2020, doi: 10.1109/TSG.2020.2981843.
|
12
5
|
"""
|
13
6
|
|
14
7
|
import logging
|
@@ -50,6 +43,17 @@ class EVD(ModelData, Model):
|
|
50
43
|
"""
|
51
44
|
In the EVD, each single EV is recorded as a device with its own parameters.
|
52
45
|
The parameters are generated from given statistical distributions.
|
46
|
+
|
47
|
+
References
|
48
|
+
----------
|
49
|
+
1. J. Wang et al., "Electric Vehicles Charging Time Constrained
|
50
|
+
Deliverable Provision of Secondary Frequency Regulation," in IEEE
|
51
|
+
Transactions on Smart Grid, vol. 15, no. 4, pp. 3892-3903, July
|
52
|
+
2024, doi: 10.1109/TSG.2024.3356948.
|
53
|
+
2. M. Wang, Y. Mu, Q. Shi, H. Jia and F. Li, "Electric Vehicle Aggregator
|
54
|
+
Modeling and Control for Frequency Regulation Considering Progressive
|
55
|
+
State Recovery," in IEEE Transactions on Smart Grid, vol. 11, no. 5,
|
56
|
+
pp. 4176-4189, Sept. 2020, doi: 10.1109/TSG.2020.2981843.
|
53
57
|
"""
|
54
58
|
|
55
59
|
def __init__(self, N=10000, Ns=20, Tagc=4, SOCf=0.2, r=0.5,
|