ltbams 1.0.10__py3-none-any.whl → 1.0.12__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/routines/pypower.py CHANGED
@@ -131,6 +131,15 @@ class DCPF1(RoutineBase):
131
131
  unit='p.u.',
132
132
  name='vBus', tex_name=r'v_{Bus}',
133
133
  src='v', model='Bus',)
134
+ # --- load ---
135
+ self.pd = RParam(info='active demand',
136
+ name='pd', tex_name=r'p_{d}',
137
+ model='StaticLoad', src='p0',
138
+ unit='p.u.',)
139
+ self.qd = RParam(info='reactive demand',
140
+ name='qd', tex_name=r'q_{d}',
141
+ model='StaticLoad', src='q0',
142
+ unit='p.u.',)
134
143
  # --- gen ---
135
144
  self.pg = Var(info='Gen active power',
136
145
  unit='p.u.',
@@ -147,8 +156,8 @@ class DCPF1(RoutineBase):
147
156
  model='Line',)
148
157
  # --- objective ---
149
158
  self.obj = Objective(name='obj',
150
- info='total cost',
151
- e_str='0',
159
+ info='total cost, placeholder',
160
+ e_str='0', unit='$',
152
161
  sense='min',)
153
162
 
154
163
  # --- total cost ---
@@ -267,13 +276,13 @@ class DCPF1(RoutineBase):
267
276
  return False
268
277
 
269
278
  def _get_off_constrs(self):
270
- pass
279
+ logger.debug(f"{self.class_name} does not implement _get_off_constrs.")
271
280
 
272
281
  def _data_check(self, info=True, **kwargs):
273
- pass
282
+ logger.debug(f"{self.class_name} does not implement _data_check.")
274
283
 
275
284
  def update(self, params=None, build_mats=False, **kwargs):
276
- pass
285
+ logger.debug(f"{self.class_name} does not implement update.")
277
286
 
278
287
  def enable(self, name):
279
288
  raise NotImplementedError
@@ -282,7 +291,7 @@ class DCPF1(RoutineBase):
282
291
  raise NotImplementedError
283
292
 
284
293
  def _post_add_check(self):
285
- pass
294
+ logger.debug(f"{self.class_name} does not implement _post_add_check.")
286
295
 
287
296
  def addRParam(self,
288
297
  name: str,
@@ -346,16 +355,13 @@ class PFlow1(DCPF1):
346
355
  It leverages PYPOWER's internal power flow solver and maps results back to the
347
356
  AMS system.
348
357
 
349
- Known Issues
350
- ------------
351
- - Fast-Decoupled (XB version) and Fast-Decoupled (BX version) algorithms are
352
- not fully supported yet.
353
-
354
358
  Notes
355
359
  -----
356
360
  - This class does not implement the AMS-style power flow formulation.
357
361
  - For detailed mathematical formulations and algorithmic details, refer to the
358
362
  MATPOWER User's Manual, section on Power Flow.
363
+ - Fast-Decoupled (XB version) and Fast-Decoupled (BX version) algorithms are
364
+ not fully supported yet.
359
365
  """
360
366
 
361
367
  def __init__(self, system, config):
@@ -380,7 +386,7 @@ class PFlow1(DCPF1):
380
386
  pf_max_it="maximum number of iterations for Newton's method",
381
387
  pf_max_it_fd="maximum number of iterations for fast decoupled method",
382
388
  pf_max_it_gs="maximum number of iterations for Gauss-Seidel method",
383
- enforce_q_lims="enforce gen reactive power limits, at expense of |V|",
389
+ enforce_q_lims="enforce gen reactive power limits, at expense of V magnitude",
384
390
  )
385
391
  self.config.add_extra("_alt",
386
392
  pf_alg=(1, 2, 3, 4),
@@ -424,15 +430,12 @@ class DCOPF1(DCPF1):
424
430
  function) is always included in the objective, regardless of the generator's
425
431
  commitment status. See `pypower/opf_costfcn.py` for implementation details.
426
432
 
427
- Known Issues
428
- ------------
429
- - Algorithms 400, 500, 600, and 700 are not fully supported yet.
430
-
431
433
  Notes
432
434
  -----
433
435
  - This class does not implement the AMS-style DC optimal power flow formulation.
434
436
  - For detailed mathematical formulations and algorithmic details, refer to the
435
437
  MATPOWER User's Manual, section on Optimal Power Flow.
438
+ - Algorithms 400, 500, 600, and 700 are not fully supported yet.
436
439
  """
437
440
 
438
441
  def __init__(self, system, config):
@@ -522,10 +525,7 @@ class DCOPF1(DCPF1):
522
525
  scpdipm_red_it=r'o_{scpdipm\_red\_it}',
523
526
  )
524
527
 
525
- self.obj = Objective(name='obj',
526
- info='total cost, placeholder',
527
- e_str='sum(c2 * pg**2 + c1 * pg + c0)',
528
- sense='min',)
528
+ self.obj.e_str = 'sum(c2 * pg**2 + c1 * pg + c0)'
529
529
 
530
530
  self.pi = Var(info='Lagrange multiplier on real power mismatch',
531
531
  name='pi', unit='$/p.u.',
ams/routines/routine.py CHANGED
@@ -3,7 +3,6 @@ Module for routine data.
3
3
  """
4
4
 
5
5
  import logging
6
- import os
7
6
  from typing import Optional, Union, Type, Iterable, Dict
8
7
  from collections import OrderedDict
9
8
 
@@ -19,6 +18,8 @@ from ams.core.service import RBaseService, ValueService
19
18
  from ams.opt import OModel
20
19
  from ams.opt import Param, Var, Constraint, Objective, ExpressionCalc, Expression
21
20
 
21
+ from ams.utils.paths import get_export_path
22
+
22
23
  from ams.shared import pd
23
24
 
24
25
  logger = logging.getLogger(__name__)
@@ -441,26 +442,20 @@ class RoutineBase:
441
442
 
442
443
  Parameters
443
444
  ----------
444
- path : str
445
- path of the csv file to save
445
+ path : str, optional
446
+ Path of the csv file to export.
446
447
 
447
448
  Returns
448
449
  -------
449
- export_path
450
- The path of the exported csv file
450
+ str
451
+ The exported csv file name
451
452
  """
452
453
  if not self.converged:
453
454
  logger.warning("Routine did not converge, aborting export.")
454
455
  return None
455
456
 
456
- if not path:
457
- if self.system.files.fullname is None:
458
- logger.info("Input file name not detacted. Using `Untitled`.")
459
- file_name = f'Untitled_{self.class_name}'
460
- else:
461
- file_name = os.path.splitext(self.system.files.fullname)[0]
462
- file_name += f'_{self.class_name}'
463
- path = os.path.join(os.getcwd(), file_name + '.csv')
457
+ path, file_name = get_export_path(self.system, self.class_name,
458
+ path=path, fmt='csv')
464
459
 
465
460
  data_dict = initialize_data_dict(self)
466
461
 
@@ -473,7 +468,7 @@ class RoutineBase:
473
468
 
474
469
  pd.DataFrame(data_dict).to_csv(path, index=False)
475
470
 
476
- return file_name + '.csv'
471
+ return file_name
477
472
 
478
473
  def summary(self, **kwargs):
479
474
  """
ams/system.py CHANGED
@@ -9,7 +9,7 @@ from typing import Dict, Optional
9
9
 
10
10
  import numpy as np
11
11
 
12
- from andes.system import System as andes_System
12
+ from andes.system import System as adSystem
13
13
  from andes.system import (_config_numpy, load_config_rc)
14
14
  from andes.variables import FileMan
15
15
 
@@ -29,7 +29,7 @@ from ams.report import Report
29
29
  from ams.shared import ad_dyn_models
30
30
 
31
31
  from ams.io.matpower import system2mpc
32
- from ams.io.matpower import write as wrtite_m
32
+ from ams.io.matpower import write as write_m
33
33
  from ams.io.xlsx import write as write_xlsx
34
34
  from ams.io.json import write as write_json
35
35
  from ams.io.psse import write_raw
@@ -49,7 +49,7 @@ def disable_methods(methods):
49
49
  setattr(System, method, disable_method(getattr(System, method)))
50
50
 
51
51
 
52
- class System(andes_System):
52
+ class System(adSystem):
53
53
  """
54
54
  A subclass of ``andes.system.System``, this class encapsulates data, models,
55
55
  and routines for scheduling modeling and analysis in power systems.
@@ -602,6 +602,7 @@ class System(andes_System):
602
602
  **kwargs):
603
603
  """
604
604
  Convert the AMS system to an ANDES system.
605
+ Wrapper method for `ams.interface.to_andes`.
605
606
 
606
607
  A preferred dynamic system file to be added has following features:
607
608
  1. The file contains both power flow and dynamic models.
@@ -718,17 +719,34 @@ class System(andes_System):
718
719
  def to_mpc(self):
719
720
  """
720
721
  Export an AMS system to a MATPOWER dict.
722
+ Wrapper method for `ams.io.matpower.system2mpc`.
721
723
 
722
724
  Returns
723
725
  -------
724
726
  dict
725
727
  A dictionary representing the MATPOWER case.
728
+
729
+ Notes
730
+ -----
731
+ - In the `gen` section, slack generators are listed before PV generators.
732
+ - For uncontrolled generators (`ctrl.v == 0`), their max and min power
733
+ limits are set to their initial power (`p0.v`) in the converted MPC.
734
+ - In the converted MPC, the indices of area (`bus[:, 6]`) and zone (`bus[:, 10]`)
735
+ may differ from the original MPC. However, the mapping relationship is preserved.
736
+ For example, if the original MPC numbers areas starting from 1, the converted
737
+ MPC may number them starting from 0.
738
+ - The coefficients `c2` and `c1` in the generator cost data are scaled by
739
+ `baseMVA`.
740
+ - Unlike the XLSX and JSON converters, this implementation uses value providers
741
+ (`v`) instead of vin. As a result, any changes made through `model.set` will be
742
+ reflected in the generated MPC.
726
743
  """
727
744
  return system2mpc(self)
728
745
 
729
746
  def to_m(self, outfile: str, overwrite: bool = None):
730
747
  """
731
748
  Export an AMS system to a MATPOWER M-file.
749
+ Wrapper method for `ams.io.matpower.write`.
732
750
 
733
751
  Parameters
734
752
  ----------
@@ -736,12 +754,28 @@ class System(andes_System):
736
754
  The output file name.
737
755
  overwrite : bool, optional
738
756
  If True, overwrite the existing file. Default is None.
739
- """
740
- return wrtite_m(self, outfile=outfile, overwrite=overwrite)
757
+
758
+ Notes
759
+ -----
760
+ - In the `gen` section, slack generators are listed before PV generators.
761
+ - For uncontrolled generators (`ctrl.v == 0`), their max and min power
762
+ limits are set to their initial power (`p0.v`) in the converted MPC.
763
+ - In the converted MPC, the indices of area (`bus[:, 6]`) and zone (`bus[:, 10]`)
764
+ may differ from the original MPC. However, the mapping relationship is preserved.
765
+ For example, if the original MPC numbers areas starting from 1, the converted
766
+ MPC may number them starting from 0.
767
+ - The coefficients `c2` and `c1` in the generator cost data are scaled by
768
+ `baseMVA`.
769
+ - Unlike the XLSX and JSON converters, this implementation uses value providers
770
+ (`v`) instead of vin. As a result, any changes made through `model.set` will be
771
+ reflected in the generated MPC.
772
+ """
773
+ return write_m(self, outfile=outfile, overwrite=overwrite)
741
774
 
742
775
  def to_xlsx(self, outfile: str, overwrite: bool = None):
743
776
  """
744
777
  Export an AMS system to an Excel file.
778
+ Wrapper method for `ams.io.xlsx.write`.
745
779
 
746
780
  Parameters
747
781
  ----------
@@ -755,6 +789,7 @@ class System(andes_System):
755
789
  def to_json(self, outfile: str, overwrite: bool = None):
756
790
  """
757
791
  Export an AMS system to a JSON file.
792
+ Wrapper method for `ams.io.json.write`.
758
793
 
759
794
  Parameters
760
795
  ----------
@@ -768,6 +803,9 @@ class System(andes_System):
768
803
  def to_raw(self, outfile: str, overwrite: bool = None):
769
804
  """
770
805
  Export an AMS system to a v33 PSS/E RAW file.
806
+ Wrapper method for `ams.io.psse.write_raw`.
807
+
808
+ This method has not been fully benchmarked yet!
771
809
 
772
810
  Parameters
773
811
  ----------
ams/utils/paths.py CHANGED
@@ -184,7 +184,7 @@ def get_pycode_path(pycode_path=None, mkdir=False):
184
184
  """
185
185
 
186
186
  if pycode_path is None:
187
- pycode_path = os.path.join(get_dot_andes_path(), 'pycode')
187
+ pycode_path = os.path.join(get_dot_ams_path(), 'pycode')
188
188
 
189
189
  if mkdir is True:
190
190
  os.makedirs(pycode_path, exist_ok=True)
@@ -203,7 +203,7 @@ def get_pkl_path():
203
203
 
204
204
  """
205
205
  pkl_name = 'calls.pkl'
206
- ams_path = get_dot_andes_path()
206
+ ams_path = get_dot_ams_path()
207
207
 
208
208
  if not os.path.exists(ams_path):
209
209
  os.makedirs(ams_path)
@@ -213,7 +213,7 @@ def get_pkl_path():
213
213
  return pkl_path
214
214
 
215
215
 
216
- def get_dot_andes_path():
216
+ def get_dot_ams_path():
217
217
  """
218
218
  Return the path to ``$HOME/.ams``
219
219
  """
@@ -255,3 +255,67 @@ def confirm_overwrite(outfile, overwrite=None):
255
255
  pass
256
256
 
257
257
  return True
258
+
259
+
260
+ def get_export_path(system, fname, path=None, fmt='csv'):
261
+ """
262
+ Get the absolute export path and the derived file name for
263
+ an AMS system export.
264
+
265
+ This function is not intended to be used directly by users.
266
+
267
+ Parameters
268
+ ----------
269
+ system : ams.system.System
270
+ The AMS system to export. (Mocked in example)
271
+ fname : str
272
+ The descriptive file name, e.g., 'PTDF', or 'DCOPF'.
273
+ path : str, optional
274
+ The desired output path.
275
+ - If it's a directory, the file name will be generated.
276
+ - If it's a full file path (with extension), the filename part will be used
277
+ as the base, and the `fmt` argument will determine the final extension.
278
+ - If None, the current working directory will be used, and the filename will
279
+ be generated.
280
+ fmt : str, optional
281
+ The file format to export, e.g., 'csv', 'json'. Default is 'csv'.
282
+
283
+ Returns
284
+ -------
285
+ tuple
286
+ (str, str): A tuple containing:
287
+ - The **absolute export path** (e.g., '/home/user/project/data_Routine.csv').
288
+ - The **export file name** (e.g., 'data_Routine.csv'), including the format extension.
289
+ """
290
+ # Determine the base name from system.files.fullname or default to "Untitled"
291
+ if system.files.fullname is None:
292
+ logger.info("Input file name not detected. Using `Untitled`.")
293
+ base_name_prefix = f'Untitled_{fname}'
294
+ else:
295
+ base_name_prefix = os.path.splitext(os.path.basename(system.files.fullname))[0]
296
+ base_name_prefix += f'_{fname}'
297
+
298
+ target_extension = fmt.lower() # Ensure consistent extension casing
299
+
300
+ if path:
301
+ abs_path = os.path.abspath(path) # Resolve to absolute path early
302
+
303
+ # Check if the provided path is likely intended as a directory
304
+ if not os.path.splitext(abs_path)[1]: # No extension implies it's a directory
305
+ dir_path = abs_path
306
+ final_file_name = f"{base_name_prefix}.{target_extension}"
307
+ full_export_path = os.path.join(dir_path, final_file_name)
308
+ else:
309
+ # Path includes a filename. Use its directory, and its base name.
310
+ dir_path = os.path.dirname(abs_path)
311
+ # Use the provided filename's base, but enforce the target_extension
312
+ provided_base_filename = os.path.splitext(os.path.basename(abs_path))[0]
313
+ final_file_name = f"{provided_base_filename}.{target_extension}"
314
+ full_export_path = os.path.join(dir_path, final_file_name)
315
+ else:
316
+ # No path provided, use current working directory
317
+ dir_path = os.getcwd()
318
+ final_file_name = f"{base_name_prefix}.{target_extension}"
319
+ full_export_path = os.path.join(dir_path, final_file_name)
320
+
321
+ return full_export_path, final_file_name
@@ -28,4 +28,5 @@ folder of the repository
28
28
  ../_examples/demo/demo_ESD1.ipynb
29
29
  ../_examples/demo/demo_AGC.ipynb
30
30
  ../_examples/demo/demo_debug.ipynb
31
- ../_examples/demo/demo_mat.ipynb
31
+ ../_examples/demo/demo_mat.ipynb
32
+ ../_examples/demo/demo_wrapper_routines.ipynb
@@ -7,7 +7,7 @@ import ams
7
7
 
8
8
  if not (os.path.isfile('routineref.rst') and os.path.isfile('configref.rst')):
9
9
 
10
- ss = ams.load(ams.get_case('ieee14/ieee14_uced.xlsx'))
10
+ ss = ams.load(ams.get_case('5bus/pjm5bus_demo.json'))
11
11
 
12
12
  # write the top-level index file
13
13
 
@@ -9,11 +9,24 @@ The APIs before v3.0.0 are in beta and may change without prior notice.
9
9
  v1.0
10
10
  ==========
11
11
 
12
- v1.0.10 (2024-xx-xx)
13
- --------------------
12
+ v1.0.12 (2025-05-29)
13
+ ----------------------
14
+
15
+ - Add RParam pd and qd in ``DCPF1`` for easy access to load
16
+ - Bug fix in ``RoutineBase.export_csv`` when path is specified
17
+ - Fix bug in ``io.matpower.system2mpc`` with multiple PQ at one bus
18
+
19
+ v1.0.11 (2025-05-23)
20
+ ----------------------
21
+
22
+ - Refactor Documenter
23
+ - Fix bug in documentation building
24
+
25
+ v1.0.10 (2025-05-23)
26
+ ----------------------
14
27
 
15
- - Add bus type correction in ``system.System.setup()``
16
- - Revise ``ams.io.psse.read`` to complete model Zone when necessary
28
+ - Add bus type correction in ``system.System.setup``
29
+ - Revise ``io.psse.read`` to complete model Zone when necessary
17
30
  - Use numerical Area and Zone idx in MATPOWER and PSSE RAW file conversion
18
31
  - Support JSON format addfile when converting to ANDES case
19
32
  - Add PSS/E v33 RAW file writer
@@ -28,9 +41,9 @@ v1.0.10 (2024-xx-xx)
28
41
  - Revise ``andes.common.config.Config.update`` to ensure configuration parameters
29
42
  are consistently updated in both the object and its internal ``_dict``
30
43
  - Remove legacy revised PYPOWER module
31
- - Remove function ``ams.shared.ppc2df``
44
+ - Remove function ``shared.ppc2df``
32
45
 
33
- v1.0.9 (2024-04-23)
46
+ v1.0.9 (2025-04-23)
34
47
  --------------------
35
48
 
36
49
  Improve MATPOWER file converter:
@@ -38,7 +51,7 @@ Improve MATPOWER file converter:
38
51
  - Add a M file case writer
39
52
  - Include Area and Zone in both MATPOWER read and write
40
53
 
41
- v1.0.8 (2024-04-20)
54
+ v1.0.8 (2025-04-20)
42
55
  --------------------
43
56
 
44
57
  - Run workflow "Publish" only on push tag event
@@ -48,7 +61,7 @@ v1.0.8 (2024-04-20)
48
61
  - Include ``gentype`` and ``genfuel`` when parsing MATPOWER cases
49
62
  - Fix logging level in ``ACOPF.run``
50
63
 
51
- v1.0.7 (2024-04-14)
64
+ v1.0.7 (2025-04-14)
52
65
  --------------------
53
66
 
54
67
  - Address several wording issues in the documentation
@@ -56,50 +69,50 @@ v1.0.7 (2024-04-14)
56
69
  - Extend common parameters in groups ``StaticGen`` and ``StaticLoad`` with ``area``
57
70
  - Set case ``pjm5bus_demo.xlsx`` as a all-inclusive case
58
71
  - Include module ``MatProcessor`` in the API documentation
59
- - Improve Line parameters correction in ``System.setup()``
72
+ - Improve Line parameters correction in ``System.setup``
60
73
  - Make func ``interface._to_andes_pflow`` public
61
74
  - Discard ``sync_adsys`` step in func ``to_andes_pflow`` to fix mistake in
62
75
  parameters conversion
63
76
  - Update case files
64
77
 
65
- v1.0.6 (2024-04-10)
78
+ v1.0.6 (2025-04-10)
66
79
  --------------------
67
80
 
68
81
  - Enhance handling of Type 1 gencost: Automatically fallback to Type 2 gencost
69
82
  - Add parameter correction for zero line angle difference
70
83
 
71
- v1.0.5 (2024-04-09)
84
+ v1.0.5 (2025-04-09)
72
85
  --------------------
73
86
 
74
87
  - Include sensitivity matrices calculation demo in documentation
75
88
  - Add ``DCOPF2``, a PTDF-based DCOPF routine
76
89
  - Fix bug when update routine parameters before it is initialized
77
90
 
78
- v1.0.4 (2024-04-05)
91
+ v1.0.4 (2025-04-05)
79
92
  --------------------
80
93
 
81
94
  - Fix format in release notes
82
95
  - Add badges of GitHub relesase and commits in README
83
96
  - Add a demo to show sensitivity matrices calculation
84
97
 
85
- v1.0.3 (2024-03-17)
98
+ v1.0.3 (2025-03-17)
86
99
  --------------------
87
100
 
88
- - Bug fix in function ``ams.interface.parse_addfile``, released in v1.0.3a1
101
+ - Bug fix in function ``interface.parse_addfile``, released in v1.0.3a1
89
102
 
90
- v1.0.2 (2024-02-01)
103
+ v1.0.2 (2025-02-01)
91
104
  --------------------
92
105
 
93
106
  - Enhance the GitHub Actions workflow file
94
107
  - Deprecate andes logger configuration in ``config_logger``
95
108
  - Deprecate solver specification in ``demo_ESD1``
96
109
 
97
- v1.0.1 (2024-01-26)
110
+ v1.0.1 (2025-01-26)
98
111
  --------------------
99
112
 
100
113
  Hotfix: removed dependencies on `SCIP` and `pyscipopt` to resolve installation issues
101
114
 
102
- v1.0.0 (2024-01-24)
115
+ v1.0.0 (2025-01-24)
103
116
  --------------------
104
117
 
105
118
  - **Breaking Change**: rename model ``Region`` to ``Zone`` for clarity. Prior case
@@ -130,10 +143,10 @@ v0.9.12 (2024-11-23)
130
143
 
131
144
  - Refactor ``OModel.initialized`` as a property method
132
145
  - Add a demo to show using ``Constraint.e`` for debugging
133
- - Fix ``ams.opt.omodel.Param.evaluate`` when its value is a number
134
- - Improve ``ams.opt.omodel.ExpressionCalc`` for better performance
135
- - Refactor module ``ams.opt``
136
- - Add class ``ams.opt.Expression``
146
+ - Fix ``opt.omodel.Param.evaluate`` when its value is a number
147
+ - Improve ``opt.omodel.ExpressionCalc`` for better performance
148
+ - Refactor module ``opt``
149
+ - Add class ``opt.Expression``
137
150
  - Switch from PYPOWER to ANDES in routine ``PFlow``
138
151
  - Switch from PYPOWER to regular formulation in routine ``DCPF``
139
152
  - Refactor routines ``DCPF`` and ``DCOPF``
@@ -376,7 +389,7 @@ v0.6.5 (2023-06-27)
376
389
  -------------------
377
390
 
378
391
  - Update documentation with auto-generated model and routine reference
379
- - Add interface with ANDES ``ams.interop.andes``
392
+ - Add interface with ANDES ``interop.andes``
380
393
  - Add routine RTED and example of RTED-TDS co-simulation
381
394
  - Draft development documentation
382
395
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ltbams
3
- Version: 1.0.10
3
+ Version: 1.0.12
4
4
  Summary: Python software for scheduling modeling and co-simulation with dynamics.
5
5
  Home-page: https://github.com/CURENT/ams
6
6
  Author: Jinning Wang
@@ -57,21 +57,18 @@ Python Software for Power System Scheduling Modeling and Co-Simulation with Dyna
57
57
 
58
58
  <img src="docs/source/images/sponsors/CURENT_Logo_NameOnTrans.png" alt="CURENT ERC Logo" width="300" height="auto">
59
59
 
60
- | | Stable | Develop |
61
- |---|---|---|
62
- | Documentation | [![Documentation Status](https://readthedocs.org/projects/ams/badge/?version=stable)](https://ams.readthedocs.io/en/stable/?badge=stable) | [![Develop Documentation](https://readthedocs.org/projects/ams/badge/?version=develop)](https://ams.readthedocs.io/en/develop/?badge=develop) |
63
-
64
60
  | Badges | | |
65
61
  |---|---|---|
66
62
  | Repo | ![Project Status: Active](https://www.repostatus.org/badges/latest/active.svg) | ![Repo Size](https://img.shields.io/github/repo-size/CURENT/ams) |
67
- | Version | ![PyPI Version](https://img.shields.io/pypi/v/ltbams.svg) | ![Conda Version](https://anaconda.org/conda-forge/ltbams/badges/version.svg) |
63
+ | Version | [![PyPI Version](https://img.shields.io/pypi/v/ltbams.svg)](https://pypi.org/project/ltbams/) | [![Anaconda-Server Badge](https://anaconda.org/conda-forge/ltbams/badges/version.svg)](https://anaconda.org/conda-forge/ltbams) |
68
64
  | Tag | [![GitHub Tag](https://img.shields.io/github/v/tag/CURENT/ams)](https://github.com/CURENT/ams/tags) | ![GitHub commits since latest release (branch)](https://img.shields.io/github/commits-since/curent/ams/latest/develop) |
69
- | Download | ![PyPI - Downloads](https://img.shields.io/pypi/dm/ltbams) | ![Anaconda-Server Badge](https://anaconda.org/conda-forge/ltbams/badges/downloads.svg) |
65
+ | Documentation | [![Read the Docs](https://img.shields.io/readthedocs/ams?label=stable)](https://ltb.readthedocs.io/projects/ams/en/stable/?badge=stable) | [![Read the Docs](https://img.shields.io/readthedocs/ams?label=develop)](https://ltb.readthedocs.io/projects/ams/en/develop/?badge=develop) |
66
+ | Download | [![PyPI - Downloads](https://img.shields.io/pypi/dm/ltbams)](https://pypi.org/project/ltbams/) | [![Anaconda-Server Badge](https://anaconda.org/conda-forge/ltbams/badges/downloads.svg)](https://anaconda.org/conda-forge/ltbams) |
70
67
  | Code Quality | ![Codacy Badge](https://app.codacy.com/project/badge/Grade/69456da1b8634f2f984bd769e35f0050) | ![Codacy Badge](https://app.codacy.com/project/badge/Coverage/69456da1b8634f2f984bd769e35f0050) |
71
68
  | Code Cov | ![codecov](https://codecov.io/gh/CURENT/ams/graph/badge.svg?token=RZI5GLLBQH) | |
72
69
  | Last Commit | ![GitHub last commit (master)](https://img.shields.io/github/last-commit/CURENT/ams/master?label=last%20commit%20to%20master) | ![GitHub last commit (develop)](https://img.shields.io/github/last-commit/CURENT/ams/develop?label=last%20commit%20to%20develop) |
73
- | CI | ![Compatibility Tests](https://github.com/CURENT/ams/actions/workflows/compatibility.yml/badge.svg) | ![Azure Pipline](https://dev.azure.com/curentltb/ams/_apis/build/status%2FCURENT.ams?branchName=master) |
74
- | CD | ![Publish to TestPyPI and PyPI](https://github.com/CURENT/ams/actions/workflows/publish-pypi.yml/badge.svg?branch=master) | |
70
+ | CI | [![Compatibility](https://github.com/CURENT/ams/actions/workflows/compatibility.yml/badge.svg)](https://github.com/CURENT/ams/actions/workflows/compatibility.yml) | [![Build Status](https://dev.azure.com/curentltb/ams/_apis/build/status%2FCURENT.ams?branchName=scuc)](https://dev.azure.com/curentltb/ams/_build/latest?definitionId=2&branchName=scuc) |
71
+ | CD | [![Publish](https://github.com/CURENT/ams/actions/workflows/publish-pypi.yml/badge.svg)](https://github.com/CURENT/ams/actions/workflows/publish-pypi.yml) | |
75
72
  | Structure | [![Structure](https://img.shields.io/badge/code_base-visualize-blue)](https://mango-dune-07a8b7110.1.azurestaticapps.net/?repo=CURENT%2Fams) | [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/CURENT/ams) |
76
73
  | Dependency | [![libraries](https://img.shields.io/librariesio/release/pypi/ltbams)](https://libraries.io/pypi/ltbams) | |
77
74
  | Try on Binder | [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/curent/ams/master) | |
@@ -237,7 +234,7 @@ See [GitHub contributors][GitHub contributors] for the contributor list.
237
234
  AMS is licensed under the [GPL v3 License](./LICENSE).
238
235
 
239
236
  # Related Projects
240
- - [Popular Open Source Libraries for Power System Analysis](https://github.com/jinningwang/best-of-ps)
237
+ - [Popular Open Source Libraries for Power System Analysis](https://github.com/ps-wiki/best-of-ps)
241
238
  - [G-PST Tools Portal](https://g-pst.github.io/tools/): An open tools portal with a classification approach
242
239
  - [Open Source Software (OSS) for Electricity Market Research, Teaching, and Training](https://www2.econ.iastate.edu/tesfatsi/ElectricOSS.htm)
243
240