ltbams 1.0.11__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.
Files changed (46) hide show
  1. ams/_version.py +3 -3
  2. ams/core/matprocessor.py +183 -118
  3. ams/io/matpower.py +55 -20
  4. ams/io/psse.py +4 -0
  5. ams/opt/exprcalc.py +11 -0
  6. ams/routines/grbopt.py +2 -0
  7. ams/routines/pypower.py +21 -4
  8. ams/routines/routine.py +127 -15
  9. ams/shared.py +30 -2
  10. ams/system.py +51 -3
  11. ams/utils/paths.py +64 -0
  12. docs/source/index.rst +4 -3
  13. docs/source/release-notes.rst +25 -10
  14. {ltbams-1.0.11.dist-info → ltbams-1.0.13.dist-info}/METADATA +4 -2
  15. {ltbams-1.0.11.dist-info → ltbams-1.0.13.dist-info}/RECORD +18 -46
  16. {ltbams-1.0.11.dist-info → ltbams-1.0.13.dist-info}/WHEEL +1 -1
  17. {ltbams-1.0.11.dist-info → ltbams-1.0.13.dist-info}/top_level.txt +0 -1
  18. tests/__init__.py +0 -0
  19. tests/test_1st_system.py +0 -64
  20. tests/test_addressing.py +0 -40
  21. tests/test_case.py +0 -301
  22. tests/test_cli.py +0 -34
  23. tests/test_export_csv.py +0 -89
  24. tests/test_group.py +0 -83
  25. tests/test_interface.py +0 -238
  26. tests/test_io.py +0 -180
  27. tests/test_jumper.py +0 -27
  28. tests/test_known_good.py +0 -267
  29. tests/test_matp.py +0 -437
  30. tests/test_model.py +0 -54
  31. tests/test_omodel.py +0 -119
  32. tests/test_paths.py +0 -22
  33. tests/test_report.py +0 -251
  34. tests/test_repr.py +0 -21
  35. tests/test_routine.py +0 -178
  36. tests/test_rtn_acopf.py +0 -75
  37. tests/test_rtn_dcopf.py +0 -101
  38. tests/test_rtn_dcopf2.py +0 -103
  39. tests/test_rtn_ed.py +0 -279
  40. tests/test_rtn_opf.py +0 -142
  41. tests/test_rtn_pflow.py +0 -147
  42. tests/test_rtn_pypower.py +0 -315
  43. tests/test_rtn_rted.py +0 -273
  44. tests/test_rtn_uc.py +0 -248
  45. tests/test_service.py +0 -73
  46. {ltbams-1.0.11.dist-info → ltbams-1.0.13.dist-info}/entry_points.txt +0 -0
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):
@@ -131,6 +133,15 @@ class DCPF1(RoutineBase):
131
133
  unit='p.u.',
132
134
  name='vBus', tex_name=r'v_{Bus}',
133
135
  src='v', model='Bus',)
136
+ # --- load ---
137
+ self.pd = RParam(info='active demand',
138
+ name='pd', tex_name=r'p_{d}',
139
+ model='StaticLoad', src='p0',
140
+ unit='p.u.',)
141
+ self.qd = RParam(info='reactive demand',
142
+ name='qd', tex_name=r'q_{d}',
143
+ model='StaticLoad', src='q0',
144
+ unit='p.u.',)
134
145
  # --- gen ---
135
146
  self.pg = Var(info='Gen active power',
136
147
  unit='p.u.',
@@ -267,13 +278,13 @@ class DCPF1(RoutineBase):
267
278
  return False
268
279
 
269
280
  def _get_off_constrs(self):
270
- pass
281
+ logger.debug(f"{self.class_name} does not implement _get_off_constrs.")
271
282
 
272
283
  def _data_check(self, info=True, **kwargs):
273
- pass
284
+ logger.debug(f"{self.class_name} does not implement _data_check.")
274
285
 
275
286
  def update(self, params=None, build_mats=False, **kwargs):
276
- pass
287
+ logger.debug(f"{self.class_name} does not implement update.")
277
288
 
278
289
  def enable(self, name):
279
290
  raise NotImplementedError
@@ -282,7 +293,7 @@ class DCPF1(RoutineBase):
282
293
  raise NotImplementedError
283
294
 
284
295
  def _post_add_check(self):
285
- pass
296
+ logger.debug(f"{self.class_name} does not implement _post_add_check.")
286
297
 
287
298
  def addRParam(self,
288
299
  name: str,
@@ -353,6 +364,8 @@ class PFlow1(DCPF1):
353
364
  MATPOWER User's Manual, section on Power Flow.
354
365
  - Fast-Decoupled (XB version) and Fast-Decoupled (BX version) algorithms are
355
366
  not fully supported yet.
367
+
368
+ .. versionadded:: 1.0.10
356
369
  """
357
370
 
358
371
  def __init__(self, system, config):
@@ -427,6 +440,8 @@ class DCOPF1(DCPF1):
427
440
  - For detailed mathematical formulations and algorithmic details, refer to the
428
441
  MATPOWER User's Manual, section on Optimal Power Flow.
429
442
  - Algorithms 400, 500, 600, and 700 are not fully supported yet.
443
+
444
+ .. versionadded:: 1.0.10
430
445
  """
431
446
 
432
447
  def __init__(self, system, config):
@@ -577,6 +592,8 @@ class ACOPF1(DCOPF1):
577
592
  - This class does not implement the AMS-style AC optimal power flow formulation.
578
593
  - For detailed mathematical formulations and algorithmic details, refer to the
579
594
  MATPOWER User's Manual, section on Optimal Power Flow.
595
+
596
+ .. versionadded:: 1.0.10
580
597
  """
581
598
 
582
599
  def __init__(self, system, config):
ams/routines/routine.py CHANGED
@@ -3,9 +3,9 @@ Module for routine data.
3
3
  """
4
4
 
5
5
  import logging
6
- import os
7
- from typing import Optional, Union, Type, Iterable, Dict
6
+ import json
8
7
  from collections import OrderedDict
8
+ from typing import Optional, Union, Type, Iterable, Dict
9
9
 
10
10
  import numpy as np
11
11
 
@@ -19,6 +19,8 @@ from ams.core.service import RBaseService, ValueService
19
19
  from ams.opt import OModel
20
20
  from ams.opt import Param, Var, Constraint, Objective, ExpressionCalc, Expression
21
21
 
22
+ from ams.utils.paths import get_export_path
23
+
22
24
  from ams.shared import pd
23
25
 
24
26
  logger = logging.getLogger(__name__)
@@ -430,6 +432,90 @@ class RoutineBase:
430
432
  logger.warning(msg)
431
433
  return False
432
434
 
435
+ def load_json(self, path):
436
+ """
437
+ Load scheduling results from a json file.
438
+
439
+ Parameters
440
+ ----------
441
+ path : str
442
+ Path of the json file to load.
443
+
444
+ Returns
445
+ -------
446
+ bool
447
+ True if the loading is successful, False otherwise.
448
+ """
449
+ try:
450
+ with open(path, 'r') as f:
451
+ data = json.load(f)
452
+ except Exception as e:
453
+ logger.error(f"Failed to load JSON file: {e}")
454
+ return False
455
+
456
+ if not self.initialized:
457
+ self.init()
458
+
459
+ # Unpack variables and expressions from JSON
460
+ for group, group_data in data.items():
461
+ if not isinstance(group_data, dict):
462
+ continue
463
+ for key, values in group_data.items():
464
+ if key == 'idx':
465
+ continue
466
+ # Find the corresponding variable or expression
467
+ if key in self.vars:
468
+ var = self.vars[key]
469
+ # Assign values to the variable
470
+ try:
471
+ var.v = np.array(values)
472
+ except Exception as e:
473
+ logger.warning(f"Failed to assign values to var '{key}': {e}")
474
+ elif key in self.exprs:
475
+ continue
476
+ elif key in self.exprcs:
477
+ exprc = self.exprcs[key]
478
+ # Assign values to the expression calculation
479
+ try:
480
+ exprc.v = np.array(values)
481
+ except Exception as e:
482
+ logger.warning(f"Failed to assign values to exprc '{key}': {e}")
483
+ logger.info(f"Loaded results from {path}")
484
+ return True
485
+
486
+ def export_json(self, path=None):
487
+ """
488
+ Export scheduling results to a json file.
489
+
490
+ Parameters
491
+ ----------
492
+ path : str, optional
493
+ Path of the json file to export.
494
+
495
+ Returns
496
+ -------
497
+ str
498
+ The exported json file name
499
+ """
500
+ if not self.converged:
501
+ logger.warning("Routine did not converge, aborting export.")
502
+ return None
503
+
504
+ path, file_name = get_export_path(self.system, self.class_name,
505
+ path=path, fmt='json')
506
+
507
+ data_dict = dict()
508
+
509
+ group_data(self, data_dict, self.vars, 'v')
510
+ group_data(self, data_dict, self.exprs, 'v')
511
+ group_data(self, data_dict, self.exprcs, 'v')
512
+
513
+ with open(path, 'w') as f:
514
+ json.dump(data_dict, f, indent=4,
515
+ default=lambda x: x.tolist() if isinstance(x, np.ndarray) else x)
516
+
517
+ return file_name
518
+
433
519
  def export_csv(self, path=None):
434
520
  """
435
521
  Export scheduling results to a csv file.
@@ -441,26 +527,20 @@ class RoutineBase:
441
527
 
442
528
  Parameters
443
529
  ----------
444
- path : str
445
- path of the csv file to save
530
+ path : str, optional
531
+ Path of the csv file to export.
446
532
 
447
533
  Returns
448
534
  -------
449
- export_path
450
- The path of the exported csv file
535
+ str
536
+ The exported csv file name
451
537
  """
452
538
  if not self.converged:
453
539
  logger.warning("Routine did not converge, aborting export.")
454
540
  return None
455
541
 
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')
542
+ path, file_name = get_export_path(self.system, self.class_name,
543
+ path=path, fmt='csv')
464
544
 
465
545
  data_dict = initialize_data_dict(self)
466
546
 
@@ -473,7 +553,7 @@ class RoutineBase:
473
553
 
474
554
  pd.DataFrame(data_dict).to_csv(path, index=False)
475
555
 
476
- return file_name + '.csv'
556
+ return file_name
477
557
 
478
558
  def summary(self, **kwargs):
479
559
  """
@@ -1027,3 +1107,35 @@ def collect_data(rtn: RoutineBase, data_dict: Dict, items: Dict, attr: str):
1027
1107
  logger.debug(f"Error with collecting data for '{key}': {e}")
1028
1108
  data_v = [np.nan] * len(idx_v)
1029
1109
  data_dict.update(OrderedDict(zip([f'{key} {dev}' for dev in idx_v], data_v)))
1110
+
1111
+
1112
+ def group_data(rtn: RoutineBase, data_dict: Dict, items: Dict, attr: str):
1113
+ """
1114
+ Collect data for export from grouped items.
1115
+
1116
+ Parameters
1117
+ ----------
1118
+ rtn : ams.routines.routine.RoutineBase
1119
+ The routine to collect data from.
1120
+ data_dict : Dict
1121
+ The data dictionary to populate.
1122
+ items : dict
1123
+ Dictionary of items to collect data from.
1124
+ attr : str
1125
+ Attribute to collect data for.
1126
+ """
1127
+ for key, item in items.items():
1128
+ if item.owner is None:
1129
+ continue
1130
+ if item.owner.class_name not in data_dict.keys():
1131
+ idx_v = item.get_all_idxes()
1132
+ data_dict[item.owner.class_name] = dict(idx=idx_v)
1133
+ else:
1134
+ idx_v = data_dict[item.owner.class_name]['idx']
1135
+ try:
1136
+ data_v = rtn.get(src=key, attr=attr, idx=idx_v,
1137
+ horizon=rtn.timeslot.v if hasattr(rtn, 'timeslot') else None)
1138
+ except Exception as e:
1139
+ logger.warning(f"Error with collecting data for '{key}': {e}")
1140
+ data_v = [np.nan] * item.owner.n
1141
+ data_dict[item.owner.class_name][key] = data_v
ams/shared.py CHANGED
@@ -4,6 +4,7 @@ Shared constants and delayed imports.
4
4
  This module is supplementary to the ``andes.shared`` module.
5
5
  """
6
6
  import logging
7
+ import sys
7
8
  import unittest
8
9
  from functools import wraps
9
10
  from time import strftime
@@ -24,6 +25,7 @@ ppoption = LazyImport('from pypower.ppoption import ppoption')
24
25
  runpf = LazyImport('from pypower.runpf import runpf')
25
26
  runopf = LazyImport('from pypower.runopf import runopf')
26
27
  opf = LazyImport('from gurobi_optimods import opf')
28
+ tqdm = LazyImport('from tqdm.auto import tqdm')
27
29
 
28
30
  # --- an empty ANDES system ---
29
31
  empty_adsys = adSystem(autogen_stale=False)
@@ -118,7 +120,7 @@ def skip_unittest_without_PYPOWER(f):
118
120
  try:
119
121
  import pypower # NOQA
120
122
  except ImportError:
121
- raise unittest.SkipTest("PYPOWER is not installed.")
123
+ raise unittest.SkipTest("PYPOWER is not available.")
122
124
  return f(*args, **kwargs)
123
125
  return wrapper
124
126
 
@@ -131,6 +133,32 @@ def skip_unittest_without_gurobi_optimods(f):
131
133
  try:
132
134
  import gurobi_optimods # NOQA
133
135
  except ImportError:
134
- raise unittest.SkipTest("Gurobi is not installed.")
136
+ raise unittest.SkipTest("gurobi_optimods is not available.")
135
137
  return f(*args, **kwargs)
136
138
  return wrapper
139
+
140
+
141
+ def _init_pbar(total, unit, no_tqdm):
142
+ """Initializes and returns a tqdm progress bar."""
143
+ pbar = tqdm(total=total, unit=unit, ncols=80, ascii=True,
144
+ file=sys.stdout, disable=no_tqdm)
145
+ pbar.update(0)
146
+ return pbar
147
+
148
+
149
+ def _update_pbar(pbar, current, total):
150
+ """Updates and closes the progress bar."""
151
+ perc = np.round(min((current / total) * 100, 100), 2)
152
+ if pbar.total is not None: # Check if pbar is still valid
153
+ last_pc = pbar.n / pbar.total * 100 # Get current percentage based on updated value
154
+ else:
155
+ last_pc = 0
156
+
157
+ perc_diff = perc - last_pc
158
+ if perc_diff >= 1:
159
+ pbar.update(perc_diff)
160
+
161
+ # Ensure pbar finishes at 100% and closes
162
+ if pbar.n < pbar.total: # Check if it's not already at total
163
+ pbar.update(pbar.total - pbar.n) # Update remaining
164
+ pbar.close()
ams/system.py CHANGED
@@ -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
@@ -602,6 +602,7 @@ class System(adSystem):
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,36 @@ class System(adSystem):
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.
743
+
744
+ .. versionadded:: 1.0.10
726
745
  """
727
746
  return system2mpc(self)
728
747
 
729
748
  def to_m(self, outfile: str, overwrite: bool = None):
730
749
  """
731
750
  Export an AMS system to a MATPOWER M-file.
751
+ Wrapper method for `ams.io.matpower.write`.
732
752
 
733
753
  Parameters
734
754
  ----------
@@ -736,12 +756,30 @@ class System(adSystem):
736
756
  The output file name.
737
757
  overwrite : bool, optional
738
758
  If True, overwrite the existing file. Default is None.
739
- """
740
- return wrtite_m(self, outfile=outfile, overwrite=overwrite)
759
+
760
+ Notes
761
+ -----
762
+ - In the `gen` section, slack generators are listed before PV generators.
763
+ - For uncontrolled generators (`ctrl.v == 0`), their max and min power
764
+ limits are set to their initial power (`p0.v`) in the converted MPC.
765
+ - In the converted MPC, the indices of area (`bus[:, 6]`) and zone (`bus[:, 10]`)
766
+ may differ from the original MPC. However, the mapping relationship is preserved.
767
+ For example, if the original MPC numbers areas starting from 1, the converted
768
+ MPC may number them starting from 0.
769
+ - The coefficients `c2` and `c1` in the generator cost data are scaled by
770
+ `baseMVA`.
771
+ - Unlike the XLSX and JSON converters, this implementation uses value providers
772
+ (`v`) instead of vin. As a result, any changes made through `model.set` will be
773
+ reflected in the generated MPC.
774
+
775
+ .. versionadded:: 1.0.10
776
+ """
777
+ return write_m(self, outfile=outfile, overwrite=overwrite)
741
778
 
742
779
  def to_xlsx(self, outfile: str, overwrite: bool = None):
743
780
  """
744
781
  Export an AMS system to an Excel file.
782
+ Wrapper method for `ams.io.xlsx.write`.
745
783
 
746
784
  Parameters
747
785
  ----------
@@ -749,12 +787,15 @@ class System(adSystem):
749
787
  The output file name.
750
788
  overwrite : bool, optional
751
789
  If True, overwrite the existing file. Default is None.
790
+
791
+ .. versionadded:: 1.0.10
752
792
  """
753
793
  return write_xlsx(self, outfile=outfile, overwrite=overwrite)
754
794
 
755
795
  def to_json(self, outfile: str, overwrite: bool = None):
756
796
  """
757
797
  Export an AMS system to a JSON file.
798
+ Wrapper method for `ams.io.json.write`.
758
799
 
759
800
  Parameters
760
801
  ----------
@@ -762,12 +803,17 @@ class System(adSystem):
762
803
  The output file name.
763
804
  overwrite : bool, optional
764
805
  If True, overwrite the existing file. Default is None.
806
+
807
+ .. versionadded:: 1.0.10
765
808
  """
766
809
  return write_json(self, outfile=outfile, overwrite=overwrite)
767
810
 
768
811
  def to_raw(self, outfile: str, overwrite: bool = None):
769
812
  """
770
813
  Export an AMS system to a v33 PSS/E RAW file.
814
+ Wrapper method for `ams.io.psse.write_raw`.
815
+
816
+ This method has not been fully benchmarked yet!
771
817
 
772
818
  Parameters
773
819
  ----------
@@ -775,6 +821,8 @@ class System(adSystem):
775
821
  The output file name.
776
822
  overwrite : bool, optional
777
823
  If True, overwrite the existing file. Default is None.
824
+
825
+ .. versionadded:: 1.0.10
778
826
  """
779
827
  return write_raw(self, outfile=outfile, overwrite=overwrite)
780
828
 
ams/utils/paths.py CHANGED
@@ -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
docs/source/index.rst CHANGED
@@ -89,9 +89,10 @@ dynamic simulator ANDES.
89
89
  publication.
90
90
 
91
91
 
92
- .. [Wang2025] J. Wang et al., "Dynamics-incorporated Modeling Framework for Stability
93
- Constrained Scheduling Under High-penetration of Renewable Energy," in IEEE
94
- Transactions on Sustainable Energy, doi: 10.1109/TSTE.2025.3528027.
92
+ .. [Wang2025] J. Wang et al., "Dynamics-Incorporated Modeling Framework for Stability
93
+ Constrained Scheduling Under High-Penetration of Renewable Energy," in IEEE
94
+ Transactions on Sustainable Energy, vol. 16, no. 3, pp. 1673-1685, July 2025,
95
+ doi: 10.1109/TSTE.2025.3528027.
95
96
 
96
97
 
97
98
  .. toctree::
@@ -9,6 +9,21 @@ 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.13 (2025-08-18)
13
+ ----------------------
14
+
15
+ - Add methods ``export_npz``, ``load_npz``, ``load_csv`` in ``MatProcessor``
16
+ to save and load matrices from files
17
+ - Add methods ``export_json``, ``load_json`` in ``RoutineBase``
18
+ to save and load routine results from JSON files
19
+
20
+ v1.0.12 (2025-05-29)
21
+ ----------------------
22
+
23
+ - Add RParam pd and qd in ``DCPF1`` for easy access to load
24
+ - Bug fix in ``RoutineBase.export_csv`` when path is specified
25
+ - Fix bug in ``io.matpower.system2mpc`` with multiple PQ at one bus
26
+
12
27
  v1.0.11 (2025-05-23)
13
28
  ----------------------
14
29
 
@@ -18,8 +33,8 @@ v1.0.11 (2025-05-23)
18
33
  v1.0.10 (2025-05-23)
19
34
  ----------------------
20
35
 
21
- - Add bus type correction in ``system.System.setup()``
22
- - Revise ``ams.io.psse.read`` to complete model Zone when necessary
36
+ - Add bus type correction in ``system.System.setup``
37
+ - Revise ``io.psse.read`` to complete model Zone when necessary
23
38
  - Use numerical Area and Zone idx in MATPOWER and PSSE RAW file conversion
24
39
  - Support JSON format addfile when converting to ANDES case
25
40
  - Add PSS/E v33 RAW file writer
@@ -34,7 +49,7 @@ v1.0.10 (2025-05-23)
34
49
  - Revise ``andes.common.config.Config.update`` to ensure configuration parameters
35
50
  are consistently updated in both the object and its internal ``_dict``
36
51
  - Remove legacy revised PYPOWER module
37
- - Remove function ``ams.shared.ppc2df``
52
+ - Remove function ``shared.ppc2df``
38
53
 
39
54
  v1.0.9 (2025-04-23)
40
55
  --------------------
@@ -62,7 +77,7 @@ v1.0.7 (2025-04-14)
62
77
  - Extend common parameters in groups ``StaticGen`` and ``StaticLoad`` with ``area``
63
78
  - Set case ``pjm5bus_demo.xlsx`` as a all-inclusive case
64
79
  - Include module ``MatProcessor`` in the API documentation
65
- - Improve Line parameters correction in ``System.setup()``
80
+ - Improve Line parameters correction in ``System.setup``
66
81
  - Make func ``interface._to_andes_pflow`` public
67
82
  - Discard ``sync_adsys`` step in func ``to_andes_pflow`` to fix mistake in
68
83
  parameters conversion
@@ -91,7 +106,7 @@ v1.0.4 (2025-04-05)
91
106
  v1.0.3 (2025-03-17)
92
107
  --------------------
93
108
 
94
- - Bug fix in function ``ams.interface.parse_addfile``, released in v1.0.3a1
109
+ - Bug fix in function ``interface.parse_addfile``, released in v1.0.3a1
95
110
 
96
111
  v1.0.2 (2025-02-01)
97
112
  --------------------
@@ -136,10 +151,10 @@ v0.9.12 (2024-11-23)
136
151
 
137
152
  - Refactor ``OModel.initialized`` as a property method
138
153
  - Add a demo to show using ``Constraint.e`` for debugging
139
- - Fix ``ams.opt.omodel.Param.evaluate`` when its value is a number
140
- - Improve ``ams.opt.omodel.ExpressionCalc`` for better performance
141
- - Refactor module ``ams.opt``
142
- - Add class ``ams.opt.Expression``
154
+ - Fix ``opt.omodel.Param.evaluate`` when its value is a number
155
+ - Improve ``opt.omodel.ExpressionCalc`` for better performance
156
+ - Refactor module ``opt``
157
+ - Add class ``opt.Expression``
143
158
  - Switch from PYPOWER to ANDES in routine ``PFlow``
144
159
  - Switch from PYPOWER to regular formulation in routine ``DCPF``
145
160
  - Refactor routines ``DCPF`` and ``DCOPF``
@@ -382,7 +397,7 @@ v0.6.5 (2023-06-27)
382
397
  -------------------
383
398
 
384
399
  - Update documentation with auto-generated model and routine reference
385
- - Add interface with ANDES ``ams.interop.andes``
400
+ - Add interface with ANDES ``interop.andes``
386
401
  - Add routine RTED and example of RTED-TDS co-simulation
387
402
  - Draft development documentation
388
403
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ltbams
3
- Version: 1.0.11
3
+ Version: 1.0.13
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
@@ -23,6 +23,7 @@ Requires-Dist: openpyxl
23
23
  Requires-Dist: andes>=1.9.3
24
24
  Requires-Dist: pybind11
25
25
  Requires-Dist: cvxpy
26
+ Requires-Dist: cffi
26
27
  Provides-Extra: dev
27
28
  Requires-Dist: pytest; extra == "dev"
28
29
  Requires-Dist: pytest-cov; extra == "dev"
@@ -60,6 +61,7 @@ Python Software for Power System Scheduling Modeling and Co-Simulation with Dyna
60
61
  | Badges | | |
61
62
  |---|---|---|
62
63
  | Repo | ![Project Status: Active](https://www.repostatus.org/badges/latest/active.svg) | ![Repo Size](https://img.shields.io/github/repo-size/CURENT/ams) |
64
+ | Python | [![Python Versions](https://img.shields.io/badge/Python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12-blue)](https://www.python.org/) |
63
65
  | 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) |
64
66
  | 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) |
65
67
  | 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) |
@@ -217,7 +219,7 @@ sa
217
219
  # Citing AMS
218
220
  If you use AMS for research or consulting, please cite the following paper in your publication that uses AMS:
219
221
 
220
- > J. Wang et al., "Dynamics-incorporated Modeling Framework for Stability Constrained Scheduling Under High-penetration of Renewable Energy," in IEEE Transactions on Sustainable Energy, doi: 10.1109/TSTE.2025.3528027.
222
+ > J. Wang et al., "Dynamics-Incorporated Modeling Framework for Stability Constrained Scheduling Under High-Penetration of Renewable Energy," in IEEE Transactions on Sustainable Energy, vol. 16, no. 3, pp. 1673-1685, July 2025, doi: 10.1109/TSTE.2025.3528027.
221
223
 
222
224
  # Sponsors and Contributors
223
225
  AMS is the scheduling simulation engine for the CURENT Largescale Testbed (LTB).