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.
Files changed (75) hide show
  1. ams/_version.py +3 -3
  2. ams/cli.py +2 -7
  3. ams/core/common.py +7 -3
  4. ams/core/documenter.py +2 -1
  5. ams/core/matprocessor.py +174 -108
  6. ams/core/model.py +14 -6
  7. ams/core/param.py +5 -3
  8. ams/core/symprocessor.py +8 -2
  9. ams/core/var.py +1 -1
  10. ams/extension/eva.py +11 -7
  11. ams/interface.py +7 -7
  12. ams/io/json.py +20 -16
  13. ams/io/matpower.py +10 -6
  14. ams/io/psse.py +4 -1
  15. ams/io/xlsx.py +21 -16
  16. ams/main.py +53 -45
  17. ams/models/distributed/esd1.py +4 -7
  18. ams/models/distributed/ev.py +10 -6
  19. ams/models/distributed/pvd1.py +4 -7
  20. ams/models/group.py +17 -18
  21. ams/models/renewable/regc.py +14 -22
  22. ams/models/timeslot.py +30 -0
  23. ams/models/zone.py +2 -4
  24. ams/opt/exprcalc.py +11 -0
  25. ams/opt/optzbase.py +4 -3
  26. ams/report.py +2 -7
  27. ams/routines/dcopf.py +7 -4
  28. ams/routines/dcopf2.py +14 -4
  29. ams/routines/dopf.py +2 -2
  30. ams/routines/ed.py +5 -5
  31. ams/routines/grbopt.py +2 -0
  32. ams/routines/pflow.py +1 -1
  33. ams/routines/pypower.py +8 -0
  34. ams/routines/routine.py +125 -1
  35. ams/routines/rted.py +5 -5
  36. ams/routines/uc.py +2 -2
  37. ams/shared.py +99 -2
  38. ams/system.py +103 -18
  39. ams/utils/paths.py +6 -10
  40. docs/source/genroutineref.py +12 -0
  41. docs/source/index.rst +4 -3
  42. docs/source/release-notes.rst +14 -0
  43. {ltbams-1.0.12.dist-info → ltbams-1.0.14.dist-info}/METADATA +21 -23
  44. {ltbams-1.0.12.dist-info → ltbams-1.0.14.dist-info}/RECORD +47 -75
  45. {ltbams-1.0.12.dist-info → ltbams-1.0.14.dist-info}/top_level.txt +0 -1
  46. tests/__init__.py +0 -0
  47. tests/test_1st_system.py +0 -64
  48. tests/test_addressing.py +0 -40
  49. tests/test_case.py +0 -301
  50. tests/test_cli.py +0 -34
  51. tests/test_export_csv.py +0 -89
  52. tests/test_group.py +0 -83
  53. tests/test_interface.py +0 -238
  54. tests/test_io.py +0 -190
  55. tests/test_jumper.py +0 -27
  56. tests/test_known_good.py +0 -267
  57. tests/test_matp.py +0 -437
  58. tests/test_model.py +0 -54
  59. tests/test_omodel.py +0 -119
  60. tests/test_paths.py +0 -89
  61. tests/test_report.py +0 -251
  62. tests/test_repr.py +0 -21
  63. tests/test_routine.py +0 -178
  64. tests/test_rtn_acopf.py +0 -75
  65. tests/test_rtn_dcopf.py +0 -121
  66. tests/test_rtn_dcopf2.py +0 -103
  67. tests/test_rtn_ed.py +0 -279
  68. tests/test_rtn_opf.py +0 -142
  69. tests/test_rtn_pflow.py +0 -147
  70. tests/test_rtn_pypower.py +0 -315
  71. tests/test_rtn_rted.py +0 -273
  72. tests/test_rtn_uc.py +0 -248
  73. tests/test_service.py +0 -73
  74. {ltbams-1.0.12.dist-info → ltbams-1.0.14.dist-info}/WHEEL +0 -0
  75. {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-05-29T20:16:06-0400",
11
+ "date": "2025-08-31T23:37:40-0700",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "2166f4368af3d382720af38408153a70502eb9f5",
15
- "version": "1.0.12"
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 AndesConfig
3
+ from andes.core.common import Config as adConfig
4
4
 
5
5
  logger = logging.getLogger(__name__)
6
6
 
7
7
 
8
- class Config(AndesConfig):
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
@@ -13,7 +13,8 @@ logger = logging.getLogger(__name__)
13
13
 
14
14
  class Documenter:
15
15
  """
16
- Helper class for documenting models.
16
+ Helper class for documenting models, inspired by
17
+ `andes.core.documenter.Documenter`.
17
18
 
18
19
  Parameters
19
20
  ----------
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.shared import tqdm, tqdm_nb
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' row names and col names are assigned in `System.setup()`.
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=False, owner=self)
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=False, owner=self)
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=False,
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
- Note: There may be minor discrepancies between PTDF-based line flow and DCOPF-calculated line flow.
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 : np.ndarray or scipy.sparse.lil_matrix
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
- if is_notebook():
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
- # show progress in percentage
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 = np.zeros((nline, nbus))
566
- H[:, noslack] = np.linalg.solve(Bbus.todense()[np.ix_(noslack, noref)].T,
567
- Bf.todense()[np.ix_(luid, noref)].T).T
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=False):
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 : np.ndarray, scipy.sparse.lil_matrix
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
- if incremental | (isinstance(ptdf, sps.spmatrix)):
642
- # initialize progress bar
643
- if is_notebook():
644
- self.pbar = tqdm_nb(total=100, unit='%', file=sys.stdout,
645
- disable=no_tqdm)
646
- else:
647
- self.pbar = tqdm(total=100, unit='%', ncols=80, ascii=True,
648
- file=sys.stdout, disable=no_tqdm)
649
-
650
- self.pbar.update(0)
651
- last_pc = 0
652
-
653
- LODF = sps.lil_matrix((nbranch, nline))
654
-
655
- # NOTE: for LODF, we are doing it columns by columns
656
- # reshape luid to list of list by step
657
- luidp = [luid[i:i + step] for i in range(0, len(luid), step)]
658
- for luidi in luidp:
659
- H_chunk = ptdf @ self.Cft._v[:, luidi]
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
- LODF = LODF - Rsid
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) & (line is None):
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 line
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 : np.ndarray, scipy.sparse.csr_matrix
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
- New in version 0.9.14: Added the signature `attr` to alter specific attributes.
239
- This feature is useful when you need to manipulate parameter values in the system
240
- base and ensure that these changes are reflected in the dumped case file.
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
- This module is revised from ``andes.core.symprocessor``.
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
@@ -14,7 +14,7 @@ class Algeb:
14
14
  """
15
15
  Algebraic variable class.
16
16
 
17
- This class is simplified from ``andes.core.var.Algeb``.
17
+ Simplified from `andes.core.var.Algeb`.
18
18
  """
19
19
 
20
20
  def __init__(self,
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,