ltbams 1.0.12__tar.gz → 1.0.13__tar.gz

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 (183) hide show
  1. {ltbams-1.0.12/ltbams.egg-info → ltbams-1.0.13}/PKG-INFO +4 -2
  2. {ltbams-1.0.12 → ltbams-1.0.13}/README.md +2 -1
  3. {ltbams-1.0.12 → ltbams-1.0.13}/ams/_version.py +3 -3
  4. {ltbams-1.0.12 → ltbams-1.0.13}/ams/core/matprocessor.py +170 -104
  5. {ltbams-1.0.12 → ltbams-1.0.13}/ams/io/matpower.py +4 -0
  6. {ltbams-1.0.12 → ltbams-1.0.13}/ams/io/psse.py +2 -0
  7. {ltbams-1.0.12 → ltbams-1.0.13}/ams/opt/exprcalc.py +11 -0
  8. {ltbams-1.0.12 → ltbams-1.0.13}/ams/routines/grbopt.py +2 -0
  9. {ltbams-1.0.12 → ltbams-1.0.13}/ams/routines/pypower.py +8 -0
  10. {ltbams-1.0.12 → ltbams-1.0.13}/ams/routines/routine.py +118 -1
  11. {ltbams-1.0.12 → ltbams-1.0.13}/ams/shared.py +30 -2
  12. {ltbams-1.0.12 → ltbams-1.0.13}/ams/system.py +10 -0
  13. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/index.rst +4 -3
  14. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/release-notes.rst +8 -0
  15. {ltbams-1.0.12 → ltbams-1.0.13/ltbams.egg-info}/PKG-INFO +4 -2
  16. {ltbams-1.0.12 → ltbams-1.0.13}/ltbams.egg-info/SOURCES.txt +1 -1
  17. {ltbams-1.0.12 → ltbams-1.0.13}/ltbams.egg-info/requires.txt +1 -0
  18. {ltbams-1.0.12 → ltbams-1.0.13}/ltbams.egg-info/top_level.txt +0 -1
  19. {ltbams-1.0.12 → ltbams-1.0.13}/pyproject.toml +3 -1
  20. {ltbams-1.0.12 → ltbams-1.0.13}/requirements-dev.txt +1 -1
  21. {ltbams-1.0.12 → ltbams-1.0.13}/requirements.txt +2 -1
  22. {ltbams-1.0.12 → ltbams-1.0.13}/setup.py +1 -1
  23. ltbams-1.0.13/tests/test_export_json.py +110 -0
  24. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_known_good.py +6 -6
  25. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_matp.py +61 -34
  26. ltbams-1.0.12/tests/__init__.py +0 -0
  27. {ltbams-1.0.12 → ltbams-1.0.13}/CONTRIBUTING.rst +0 -0
  28. {ltbams-1.0.12 → ltbams-1.0.13}/LICENSE +0 -0
  29. {ltbams-1.0.12 → ltbams-1.0.13}/MANIFEST.in +0 -0
  30. {ltbams-1.0.12 → ltbams-1.0.13}/ams/__init__.py +0 -0
  31. {ltbams-1.0.12 → ltbams-1.0.13}/ams/__main__.py +0 -0
  32. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/5bus/pjm5bus_demo.json +0 -0
  33. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/5bus/pjm5bus_demo.xlsx +0 -0
  34. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/5bus/pjm5bus_ev.xlsx +0 -0
  35. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/5bus/pjm5bus_jumper.xlsx +0 -0
  36. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/hawaii40/Hawaii40.m +0 -0
  37. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/ieee123/ieee123.xlsx +0 -0
  38. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/ieee123/ieee123_regcv1.xlsx +0 -0
  39. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/ieee14/ieee14.json +0 -0
  40. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/ieee14/ieee14.raw +0 -0
  41. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/ieee14/ieee14_conn.xlsx +0 -0
  42. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/ieee14/ieee14_uced.xlsx +0 -0
  43. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/ieee39/ieee39.xlsx +0 -0
  44. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/ieee39/ieee39_uced.xlsx +0 -0
  45. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/ieee39/ieee39_uced_esd1.xlsx +0 -0
  46. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/ieee39/ieee39_uced_pvd1.xlsx +0 -0
  47. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/ieee39/ieee39_uced_vis.xlsx +0 -0
  48. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/matpower/benchmark.json +0 -0
  49. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/matpower/case118.m +0 -0
  50. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/matpower/case14.m +0 -0
  51. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/matpower/case300.m +0 -0
  52. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/matpower/case39.m +0 -0
  53. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/matpower/case5.m +0 -0
  54. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/matpower/case_ACTIVSg2000.m +0 -0
  55. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/npcc/npcc.m +0 -0
  56. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/npcc/npcc_uced.xlsx +0 -0
  57. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/pglib/pglib_opf_case39_epri__api.m +0 -0
  58. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/wecc/wecc.m +0 -0
  59. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cases/wecc/wecc_uced.xlsx +0 -0
  60. {ltbams-1.0.12 → ltbams-1.0.13}/ams/cli.py +0 -0
  61. {ltbams-1.0.12 → ltbams-1.0.13}/ams/core/__init__.py +0 -0
  62. {ltbams-1.0.12 → ltbams-1.0.13}/ams/core/common.py +0 -0
  63. {ltbams-1.0.12 → ltbams-1.0.13}/ams/core/documenter.py +0 -0
  64. {ltbams-1.0.12 → ltbams-1.0.13}/ams/core/model.py +0 -0
  65. {ltbams-1.0.12 → ltbams-1.0.13}/ams/core/param.py +0 -0
  66. {ltbams-1.0.12 → ltbams-1.0.13}/ams/core/service.py +0 -0
  67. {ltbams-1.0.12 → ltbams-1.0.13}/ams/core/symprocessor.py +0 -0
  68. {ltbams-1.0.12 → ltbams-1.0.13}/ams/core/var.py +0 -0
  69. {ltbams-1.0.12 → ltbams-1.0.13}/ams/extension/__init__.py +0 -0
  70. {ltbams-1.0.12 → ltbams-1.0.13}/ams/extension/eva.py +0 -0
  71. {ltbams-1.0.12 → ltbams-1.0.13}/ams/interface.py +0 -0
  72. {ltbams-1.0.12 → ltbams-1.0.13}/ams/io/__init__.py +0 -0
  73. {ltbams-1.0.12 → ltbams-1.0.13}/ams/io/json.py +0 -0
  74. {ltbams-1.0.12 → ltbams-1.0.13}/ams/io/pypower.py +0 -0
  75. {ltbams-1.0.12 → ltbams-1.0.13}/ams/io/xlsx.py +0 -0
  76. {ltbams-1.0.12 → ltbams-1.0.13}/ams/main.py +0 -0
  77. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/__init__.py +0 -0
  78. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/area.py +0 -0
  79. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/bus.py +0 -0
  80. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/cost.py +0 -0
  81. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/distributed/__init__.py +0 -0
  82. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/distributed/esd1.py +0 -0
  83. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/distributed/ev.py +0 -0
  84. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/distributed/pvd1.py +0 -0
  85. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/group.py +0 -0
  86. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/info.py +0 -0
  87. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/line.py +0 -0
  88. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/renewable/__init__.py +0 -0
  89. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/renewable/regc.py +0 -0
  90. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/reserve.py +0 -0
  91. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/shunt.py +0 -0
  92. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/static/__init__.py +0 -0
  93. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/static/gen.py +0 -0
  94. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/static/pq.py +0 -0
  95. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/timeslot.py +0 -0
  96. {ltbams-1.0.12 → ltbams-1.0.13}/ams/models/zone.py +0 -0
  97. {ltbams-1.0.12 → ltbams-1.0.13}/ams/opt/__init__.py +0 -0
  98. {ltbams-1.0.12 → ltbams-1.0.13}/ams/opt/constraint.py +0 -0
  99. {ltbams-1.0.12 → ltbams-1.0.13}/ams/opt/expression.py +0 -0
  100. {ltbams-1.0.12 → ltbams-1.0.13}/ams/opt/objective.py +0 -0
  101. {ltbams-1.0.12 → ltbams-1.0.13}/ams/opt/omodel.py +0 -0
  102. {ltbams-1.0.12 → ltbams-1.0.13}/ams/opt/optzbase.py +0 -0
  103. {ltbams-1.0.12 → ltbams-1.0.13}/ams/opt/param.py +0 -0
  104. {ltbams-1.0.12 → ltbams-1.0.13}/ams/opt/var.py +0 -0
  105. {ltbams-1.0.12 → ltbams-1.0.13}/ams/report.py +0 -0
  106. {ltbams-1.0.12 → ltbams-1.0.13}/ams/routines/__init__.py +0 -0
  107. {ltbams-1.0.12 → ltbams-1.0.13}/ams/routines/acopf.py +0 -0
  108. {ltbams-1.0.12 → ltbams-1.0.13}/ams/routines/dcopf.py +0 -0
  109. {ltbams-1.0.12 → ltbams-1.0.13}/ams/routines/dcopf2.py +0 -0
  110. {ltbams-1.0.12 → ltbams-1.0.13}/ams/routines/dcpf.py +0 -0
  111. {ltbams-1.0.12 → ltbams-1.0.13}/ams/routines/dopf.py +0 -0
  112. {ltbams-1.0.12 → ltbams-1.0.13}/ams/routines/ed.py +0 -0
  113. {ltbams-1.0.12 → ltbams-1.0.13}/ams/routines/pflow.py +0 -0
  114. {ltbams-1.0.12 → ltbams-1.0.13}/ams/routines/rted.py +0 -0
  115. {ltbams-1.0.12 → ltbams-1.0.13}/ams/routines/type.py +0 -0
  116. {ltbams-1.0.12 → ltbams-1.0.13}/ams/routines/uc.py +0 -0
  117. {ltbams-1.0.12 → ltbams-1.0.13}/ams/utils/__init__.py +0 -0
  118. {ltbams-1.0.12 → ltbams-1.0.13}/ams/utils/misc.py +0 -0
  119. {ltbams-1.0.12 → ltbams-1.0.13}/ams/utils/paths.py +0 -0
  120. {ltbams-1.0.12 → ltbams-1.0.13}/docs/Makefile +0 -0
  121. {ltbams-1.0.12 → ltbams-1.0.13}/docs/make.bat +0 -0
  122. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/_templates/autosummary/base.rst +0 -0
  123. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/_templates/autosummary/class.rst +0 -0
  124. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/_templates/autosummary/module.rst +0 -0
  125. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/_templates/autosummary/module_toctree.rst +0 -0
  126. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/api.rst +0 -0
  127. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/conf.py +0 -0
  128. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/examples/index.rst +0 -0
  129. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/genmodelref.py +0 -0
  130. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/genroutineref.py +0 -0
  131. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/getting_started/copyright.rst +0 -0
  132. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/getting_started/formats/index.rst +0 -0
  133. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/getting_started/formats/matpower.rst +0 -0
  134. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/getting_started/formats/psse.rst +0 -0
  135. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/getting_started/formats/pypower.rst +0 -0
  136. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/getting_started/formats/xlsx.png +0 -0
  137. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/getting_started/formats/xlsx.rst +0 -0
  138. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/getting_started/index.rst +0 -0
  139. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/getting_started/install.rst +0 -0
  140. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/getting_started/overview.rst +0 -0
  141. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/getting_started/testcase.rst +0 -0
  142. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/getting_started/verification.rst +0 -0
  143. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/images/curent.ico +0 -0
  144. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/images/dcopf_time.png +0 -0
  145. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/images/educ_pie.png +0 -0
  146. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/images/sponsors/CURENT_Logo_NameOnTrans.png +0 -0
  147. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/images/sponsors/CURENT_Logo_Transparent.png +0 -0
  148. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/images/sponsors/CURENT_Logo_Transparent_Name.png +0 -0
  149. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/images/sponsors/doe.png +0 -0
  150. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/modeling/example.rst +0 -0
  151. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/modeling/index.rst +0 -0
  152. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/modeling/model.rst +0 -0
  153. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/modeling/routine.rst +0 -0
  154. {ltbams-1.0.12 → ltbams-1.0.13}/docs/source/modeling/system.rst +0 -0
  155. {ltbams-1.0.12 → ltbams-1.0.13}/ltbams.egg-info/dependency_links.txt +0 -0
  156. {ltbams-1.0.12 → ltbams-1.0.13}/ltbams.egg-info/entry_points.txt +0 -0
  157. {ltbams-1.0.12 → ltbams-1.0.13}/setup.cfg +0 -0
  158. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_1st_system.py +0 -0
  159. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_addressing.py +0 -0
  160. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_case.py +0 -0
  161. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_cli.py +0 -0
  162. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_export_csv.py +0 -0
  163. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_group.py +0 -0
  164. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_interface.py +0 -0
  165. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_io.py +0 -0
  166. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_jumper.py +0 -0
  167. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_model.py +0 -0
  168. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_omodel.py +0 -0
  169. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_paths.py +0 -0
  170. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_report.py +0 -0
  171. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_repr.py +0 -0
  172. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_routine.py +0 -0
  173. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_rtn_acopf.py +0 -0
  174. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_rtn_dcopf.py +0 -0
  175. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_rtn_dcopf2.py +0 -0
  176. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_rtn_ed.py +0 -0
  177. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_rtn_opf.py +0 -0
  178. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_rtn_pflow.py +0 -0
  179. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_rtn_pypower.py +0 -0
  180. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_rtn_rted.py +0 -0
  181. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_rtn_uc.py +0 -0
  182. {ltbams-1.0.12 → ltbams-1.0.13}/tests/test_service.py +0 -0
  183. {ltbams-1.0.12 → ltbams-1.0.13}/versioneer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ltbams
3
- Version: 1.0.12
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).
@@ -10,6 +10,7 @@ Python Software for Power System Scheduling Modeling and Co-Simulation with Dyna
10
10
  | Badges | | |
11
11
  |---|---|---|
12
12
  | Repo | ![Project Status: Active](https://www.repostatus.org/badges/latest/active.svg) | ![Repo Size](https://img.shields.io/github/repo-size/CURENT/ams) |
13
+ | 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/) |
13
14
  | 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) |
14
15
  | 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) |
15
16
  | 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) |
@@ -167,7 +168,7 @@ sa
167
168
  # Citing AMS
168
169
  If you use AMS for research or consulting, please cite the following paper in your publication that uses AMS:
169
170
 
170
- > 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.
171
+ > 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.
171
172
 
172
173
  # Sponsors and Contributors
173
174
  AMS is the scheduling simulation engine for the CURENT Largescale Testbed (LTB).
@@ -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-18T15:13:43-0700",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "2166f4368af3d382720af38408153a70502eb9f5",
15
- "version": "1.0.12"
14
+ "full-revisionid": "1aab83cb951d517c906fe117499d482da8b6e66b",
15
+ "version": "1.0.13"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -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.
@@ -149,7 +250,6 @@ class MatProcessor:
149
250
  def __init__(self, system):
150
251
  self.system = system
151
252
  self.initialized = False
152
- self.pbar = None
153
253
 
154
254
  self.Cft = MParam(name='Cft', tex_name=r'C_{ft}',
155
255
  info='Line connectivity matrix',
@@ -182,10 +282,10 @@ class MatProcessor:
182
282
 
183
283
  self.PTDF = MParam(name='PTDF', tex_name=r'P_{TDF}',
184
284
  info='Power transfer distribution factor',
185
- v=None, sparse=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
630
  raise ValueError(f"Line {line} not found.")
514
631
  elif isinstance(line, list):
515
632
  luid = system.Line.idx2uid(line)
633
+ self.PTDF.row_names = line
634
+
635
+ self.PTDF.col_names = system.Bus.idx.v
516
636
 
517
637
  # build other matrices if not built
518
638
  if not self.initialized:
@@ -527,15 +647,7 @@ class MatProcessor:
527
647
 
528
648
  if incremental:
529
649
  # initialize progress bar
530
- 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
@@ -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
@@ -750,4 +816,4 @@ class MatProcessor:
750
816
  luid = self.system.Line.idx2uid(line)
751
817
 
752
818
  otdf = ptdf + lodf[:, luid] @ ptdf[luid, :]
753
- return otdf
819
+ return otdf.tocsr()
@@ -646,6 +646,8 @@ def mpc2m(mpc: dict, outfile: str) -> str:
646
646
  MATPOWER mpc dictionary.
647
647
  outfile : str
648
648
  Path to the output M-file.
649
+
650
+ .. versionadded:: 1.0.10
649
651
  """
650
652
  with open(outfile, 'w') as f:
651
653
  # Add version info
@@ -758,6 +760,8 @@ def write(system, outfile: str, overwrite: bool = None) -> bool:
758
760
  - Unlike the XLSX and JSON converters, this implementation uses value providers
759
761
  (`v`) instead of vin. As a result, any changes made through `model.set` will be
760
762
  reflected in the generated MPC.
763
+
764
+ .. versionadded:: 1.0.10
761
765
  """
762
766
  if not confirm_overwrite(outfile, overwrite=overwrite):
763
767
  return False
@@ -56,6 +56,8 @@ def write_raw(system, outfile: str, overwrite: bool = None):
56
56
  The output file path.
57
57
  overwrite : bool, optional
58
58
  If True, overwrite the file if it exists. If False, do not overwrite.
59
+
60
+ .. versionadded:: 1.0.10
59
61
  """
60
62
  if not confirm_overwrite(outfile, overwrite=overwrite):
61
63
  return False
@@ -102,6 +102,17 @@ class ExpressionCalc(OptzBase):
102
102
  else:
103
103
  return self.optz.value
104
104
 
105
+ @v.setter
106
+ def v(self, value):
107
+ """
108
+ Set the ExpressionCalc value.
109
+ """
110
+ if self.optz is None:
111
+ raise ValueError("ExpressionCalc is not evaluated yet.")
112
+ if not isinstance(value, (int, float, np.ndarray)):
113
+ raise TypeError(f"Value must be a number or numpy array, got {type(value)}.")
114
+ self.optz.value = value
115
+
105
116
  @property
106
117
  def e(self):
107
118
  """
@@ -34,6 +34,8 @@ class OPF(DCPF1):
34
34
  Refer to the gurobi-optimods documentation for further details:
35
35
 
36
36
  https://gurobi-optimods.readthedocs.io/en/stable/mods/opf/opf.html
37
+
38
+ .. versionadded:: 1.0.10
37
39
  """
38
40
 
39
41
  def __init__(self, system, config):
@@ -32,6 +32,8 @@ class DCPF1(RoutineBase):
32
32
  - This class does not implement the AMS-style DC power flow formulation.
33
33
  - For detailed mathematical formulations and algorithmic details, refer to the
34
34
  MATPOWER User's Manual, section on Power Flow.
35
+
36
+ .. versionadded:: 1.0.10
35
37
  """
36
38
 
37
39
  def __init__(self, system, config):
@@ -362,6 +364,8 @@ class PFlow1(DCPF1):
362
364
  MATPOWER User's Manual, section on Power Flow.
363
365
  - Fast-Decoupled (XB version) and Fast-Decoupled (BX version) algorithms are
364
366
  not fully supported yet.
367
+
368
+ .. versionadded:: 1.0.10
365
369
  """
366
370
 
367
371
  def __init__(self, system, config):
@@ -436,6 +440,8 @@ class DCOPF1(DCPF1):
436
440
  - For detailed mathematical formulations and algorithmic details, refer to the
437
441
  MATPOWER User's Manual, section on Optimal Power Flow.
438
442
  - Algorithms 400, 500, 600, and 700 are not fully supported yet.
443
+
444
+ .. versionadded:: 1.0.10
439
445
  """
440
446
 
441
447
  def __init__(self, system, config):
@@ -586,6 +592,8 @@ class ACOPF1(DCOPF1):
586
592
  - This class does not implement the AMS-style AC optimal power flow formulation.
587
593
  - For detailed mathematical formulations and algorithmic details, refer to the
588
594
  MATPOWER User's Manual, section on Optimal Power Flow.
595
+
596
+ .. versionadded:: 1.0.10
589
597
  """
590
598
 
591
599
  def __init__(self, system, config):