ltbams 0.9.12__tar.gz → 0.9.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 (202) hide show
  1. {ltbams-0.9.12/ltbams.egg-info → ltbams-0.9.13}/PKG-INFO +21 -7
  2. {ltbams-0.9.12 → ltbams-0.9.13}/README.md +20 -6
  3. {ltbams-0.9.12 → ltbams-0.9.13}/ams/_version.py +3 -3
  4. ltbams-0.9.13/ams/cases/5bus/pjm5bus_demo.xlsx +0 -0
  5. {ltbams-0.9.12 → ltbams-0.9.13}/ams/interface.py +46 -2
  6. ltbams-0.9.13/ams/report.py +356 -0
  7. {ltbams-0.9.12 → ltbams-0.9.13}/ams/routines/acopf.py +5 -8
  8. {ltbams-0.9.12 → ltbams-0.9.13}/ams/routines/dcopf.py +47 -0
  9. {ltbams-0.9.12 → ltbams-0.9.13}/ams/routines/dcpf.py +4 -0
  10. {ltbams-0.9.12 → ltbams-0.9.13}/ams/routines/dcpf0.py +6 -0
  11. {ltbams-0.9.12 → ltbams-0.9.13}/ams/routines/ed.py +2 -0
  12. {ltbams-0.9.12 → ltbams-0.9.13}/ams/routines/routine.py +59 -16
  13. {ltbams-0.9.12 → ltbams-0.9.13}/ams/routines/rted.py +18 -15
  14. {ltbams-0.9.12 → ltbams-0.9.13}/ams/routines/uc.py +3 -0
  15. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/release-notes.rst +27 -9
  16. {ltbams-0.9.12 → ltbams-0.9.13/ltbams.egg-info}/PKG-INFO +21 -7
  17. {ltbams-0.9.12 → ltbams-0.9.13}/ltbams.egg-info/SOURCES.txt +2 -1
  18. {ltbams-0.9.12 → ltbams-0.9.13}/ltbams.egg-info/requires.txt +0 -1
  19. {ltbams-0.9.12 → ltbams-0.9.13}/requirements.txt +0 -1
  20. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_export_csv.py +8 -4
  21. ltbams-0.9.12/tests/test_interop.py → ltbams-0.9.13/tests/test_interface.py +39 -0
  22. ltbams-0.9.13/tests/test_report.py +245 -0
  23. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_rtn_dcopf.py +24 -0
  24. ltbams-0.9.13/tests/test_rtn_dcpf.py +77 -0
  25. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_rtn_ed.py +99 -8
  26. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_rtn_rted.py +111 -14
  27. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_rtn_uc.py +86 -3
  28. ltbams-0.9.12/ams/cases/5bus/pjm5bus_demo.xlsx +0 -0
  29. ltbams-0.9.12/ams/report.py +0 -211
  30. ltbams-0.9.12/tests/test_report.py +0 -76
  31. {ltbams-0.9.12 → ltbams-0.9.13}/CONTRIBUTING.rst +0 -0
  32. {ltbams-0.9.12 → ltbams-0.9.13}/LICENSE +0 -0
  33. {ltbams-0.9.12 → ltbams-0.9.13}/MANIFEST.in +0 -0
  34. {ltbams-0.9.12 → ltbams-0.9.13}/ams/__init__.py +0 -0
  35. {ltbams-0.9.12 → ltbams-0.9.13}/ams/__main__.py +0 -0
  36. {ltbams-0.9.12 → ltbams-0.9.13}/ams/benchmarks.py +0 -0
  37. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/5bus/pjm5bus_jumper.xlsx +0 -0
  38. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/5bus/pjm5bus_uced.json +0 -0
  39. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/5bus/pjm5bus_uced.xlsx +0 -0
  40. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/5bus/pjm5bus_uced_esd1.xlsx +0 -0
  41. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/5bus/pjm5bus_uced_ev.xlsx +0 -0
  42. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/ieee123/ieee123.xlsx +0 -0
  43. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/ieee123/ieee123_regcv1.xlsx +0 -0
  44. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/ieee14/ieee14.json +0 -0
  45. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/ieee14/ieee14.raw +0 -0
  46. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/ieee14/ieee14_uced.xlsx +0 -0
  47. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/ieee39/ieee39.xlsx +0 -0
  48. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/ieee39/ieee39_uced.xlsx +0 -0
  49. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/ieee39/ieee39_uced_esd1.xlsx +0 -0
  50. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/ieee39/ieee39_uced_pvd1.xlsx +0 -0
  51. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/ieee39/ieee39_uced_vis.xlsx +0 -0
  52. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/matpower/benchmark.json +0 -0
  53. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/matpower/case118.m +0 -0
  54. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/matpower/case14.m +0 -0
  55. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/matpower/case300.m +0 -0
  56. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/matpower/case39.m +0 -0
  57. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/matpower/case5.m +0 -0
  58. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/matpower/case_ACTIVSg2000.m +0 -0
  59. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/npcc/npcc.m +0 -0
  60. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/npcc/npcc_uced.xlsx +0 -0
  61. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/pglib/pglib_opf_case39_epri__api.m +0 -0
  62. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/wecc/wecc.m +0 -0
  63. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cases/wecc/wecc_uced.xlsx +0 -0
  64. {ltbams-0.9.12 → ltbams-0.9.13}/ams/cli.py +0 -0
  65. {ltbams-0.9.12 → ltbams-0.9.13}/ams/core/__init__.py +0 -0
  66. {ltbams-0.9.12 → ltbams-0.9.13}/ams/core/documenter.py +0 -0
  67. {ltbams-0.9.12 → ltbams-0.9.13}/ams/core/matprocessor.py +0 -0
  68. {ltbams-0.9.12 → ltbams-0.9.13}/ams/core/model.py +0 -0
  69. {ltbams-0.9.12 → ltbams-0.9.13}/ams/core/param.py +0 -0
  70. {ltbams-0.9.12 → ltbams-0.9.13}/ams/core/service.py +0 -0
  71. {ltbams-0.9.12 → ltbams-0.9.13}/ams/core/symprocessor.py +0 -0
  72. {ltbams-0.9.12 → ltbams-0.9.13}/ams/core/var.py +0 -0
  73. {ltbams-0.9.12 → ltbams-0.9.13}/ams/extension/__init__.py +0 -0
  74. {ltbams-0.9.12 → ltbams-0.9.13}/ams/extension/eva.py +0 -0
  75. {ltbams-0.9.12 → ltbams-0.9.13}/ams/io/__init__.py +0 -0
  76. {ltbams-0.9.12 → ltbams-0.9.13}/ams/io/json.py +0 -0
  77. {ltbams-0.9.12 → ltbams-0.9.13}/ams/io/matpower.py +0 -0
  78. {ltbams-0.9.12 → ltbams-0.9.13}/ams/io/psse.py +0 -0
  79. {ltbams-0.9.12 → ltbams-0.9.13}/ams/io/pypower.py +0 -0
  80. {ltbams-0.9.12 → ltbams-0.9.13}/ams/io/xlsx.py +0 -0
  81. {ltbams-0.9.12 → ltbams-0.9.13}/ams/main.py +0 -0
  82. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/__init__.py +0 -0
  83. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/area.py +0 -0
  84. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/bus.py +0 -0
  85. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/cost.py +0 -0
  86. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/distributed/__init__.py +0 -0
  87. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/distributed/esd1.py +0 -0
  88. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/distributed/ev.py +0 -0
  89. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/distributed/pvd1.py +0 -0
  90. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/group.py +0 -0
  91. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/info.py +0 -0
  92. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/line.py +0 -0
  93. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/region.py +0 -0
  94. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/renewable/__init__.py +0 -0
  95. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/renewable/regc.py +0 -0
  96. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/reserve.py +0 -0
  97. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/shunt.py +0 -0
  98. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/static/__init__.py +0 -0
  99. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/static/gen.py +0 -0
  100. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/static/pq.py +0 -0
  101. {ltbams-0.9.12 → ltbams-0.9.13}/ams/models/timeslot.py +0 -0
  102. {ltbams-0.9.12 → ltbams-0.9.13}/ams/opt/__init__.py +0 -0
  103. {ltbams-0.9.12 → ltbams-0.9.13}/ams/opt/constraint.py +0 -0
  104. {ltbams-0.9.12 → ltbams-0.9.13}/ams/opt/exprcalc.py +0 -0
  105. {ltbams-0.9.12 → ltbams-0.9.13}/ams/opt/expression.py +0 -0
  106. {ltbams-0.9.12 → ltbams-0.9.13}/ams/opt/objective.py +0 -0
  107. {ltbams-0.9.12 → ltbams-0.9.13}/ams/opt/omodel.py +0 -0
  108. {ltbams-0.9.12 → ltbams-0.9.13}/ams/opt/optbase.py +0 -0
  109. {ltbams-0.9.12 → ltbams-0.9.13}/ams/opt/param.py +0 -0
  110. {ltbams-0.9.12 → ltbams-0.9.13}/ams/opt/var.py +0 -0
  111. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/__init__.py +0 -0
  112. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/_compat.py +0 -0
  113. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/core/__init__.py +0 -0
  114. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/core/pips.py +0 -0
  115. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/core/ppoption.py +0 -0
  116. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/core/ppver.py +0 -0
  117. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/core/solver.py +0 -0
  118. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/eps.py +0 -0
  119. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/idx.py +0 -0
  120. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/io.py +0 -0
  121. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/make/__init__.py +0 -0
  122. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/make/matrices.py +0 -0
  123. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/make/pdv.py +0 -0
  124. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/routines/__init__.py +0 -0
  125. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/routines/cpf.py +0 -0
  126. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/routines/cpf_callbacks.py +0 -0
  127. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/routines/opf.py +0 -0
  128. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/routines/opffcns.py +0 -0
  129. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/routines/pflow.py +0 -0
  130. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/toggle.py +0 -0
  131. {ltbams-0.9.12 → ltbams-0.9.13}/ams/pypower/utils.py +0 -0
  132. {ltbams-0.9.12 → ltbams-0.9.13}/ams/routines/__init__.py +0 -0
  133. {ltbams-0.9.12 → ltbams-0.9.13}/ams/routines/cpf.py +0 -0
  134. {ltbams-0.9.12 → ltbams-0.9.13}/ams/routines/dopf.py +0 -0
  135. {ltbams-0.9.12 → ltbams-0.9.13}/ams/routines/pflow.py +0 -0
  136. {ltbams-0.9.12 → ltbams-0.9.13}/ams/routines/pflow0.py +0 -0
  137. {ltbams-0.9.12 → ltbams-0.9.13}/ams/routines/type.py +0 -0
  138. {ltbams-0.9.12 → ltbams-0.9.13}/ams/shared.py +0 -0
  139. {ltbams-0.9.12 → ltbams-0.9.13}/ams/system.py +0 -0
  140. {ltbams-0.9.12 → ltbams-0.9.13}/ams/utils/__init__.py +0 -0
  141. {ltbams-0.9.12 → ltbams-0.9.13}/ams/utils/paths.py +0 -0
  142. {ltbams-0.9.12 → ltbams-0.9.13}/docs/Makefile +0 -0
  143. {ltbams-0.9.12 → ltbams-0.9.13}/docs/make.bat +0 -0
  144. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/_templates/autosummary/base.rst +0 -0
  145. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/_templates/autosummary/class.rst +0 -0
  146. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/_templates/autosummary/module.rst +0 -0
  147. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/_templates/autosummary/module_toctree.rst +0 -0
  148. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/api.rst +0 -0
  149. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/conf.py +0 -0
  150. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/examples/index.rst +0 -0
  151. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/genmodelref.py +0 -0
  152. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/genroutineref.py +0 -0
  153. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/getting_started/copyright.rst +0 -0
  154. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/getting_started/formats/index.rst +0 -0
  155. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/getting_started/formats/matpower.rst +0 -0
  156. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/getting_started/formats/psse.rst +0 -0
  157. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/getting_started/formats/pypower.rst +0 -0
  158. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/getting_started/formats/xlsx.png +0 -0
  159. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/getting_started/formats/xlsx.rst +0 -0
  160. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/getting_started/index.rst +0 -0
  161. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/getting_started/install.rst +0 -0
  162. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/getting_started/overview.rst +0 -0
  163. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/getting_started/testcase.rst +0 -0
  164. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/getting_started/verification.rst +0 -0
  165. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/images/dcopf_time.png +0 -0
  166. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/images/sponsors/CURENT_Logo_NameOnTrans.png +0 -0
  167. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/images/sponsors/CURENT_Logo_Transparent.png +0 -0
  168. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/images/sponsors/CURENT_Logo_Transparent_Name.png +0 -0
  169. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/images/sponsors/doe.png +0 -0
  170. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/index.rst +0 -0
  171. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/modeling/example.rst +0 -0
  172. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/modeling/index.rst +0 -0
  173. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/modeling/model.rst +0 -0
  174. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/modeling/routine.rst +0 -0
  175. {ltbams-0.9.12 → ltbams-0.9.13}/docs/source/modeling/system.rst +0 -0
  176. {ltbams-0.9.12 → ltbams-0.9.13}/ltbams.egg-info/dependency_links.txt +0 -0
  177. {ltbams-0.9.12 → ltbams-0.9.13}/ltbams.egg-info/entry_points.txt +0 -0
  178. {ltbams-0.9.12 → ltbams-0.9.13}/ltbams.egg-info/top_level.txt +0 -0
  179. {ltbams-0.9.12 → ltbams-0.9.13}/pyproject.toml +0 -0
  180. {ltbams-0.9.12 → ltbams-0.9.13}/requirements-extra.txt +0 -0
  181. {ltbams-0.9.12 → ltbams-0.9.13}/setup.cfg +0 -0
  182. {ltbams-0.9.12 → ltbams-0.9.13}/setup.py +0 -0
  183. {ltbams-0.9.12 → ltbams-0.9.13}/tests/__init__.py +0 -0
  184. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_1st_system.py +0 -0
  185. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_addressing.py +0 -0
  186. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_andes_mats.py +0 -0
  187. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_benchmarks.py +0 -0
  188. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_case.py +0 -0
  189. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_cli.py +0 -0
  190. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_group.py +0 -0
  191. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_io.py +0 -0
  192. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_jumper.py +0 -0
  193. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_known_good.py +0 -0
  194. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_matp.py +0 -0
  195. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_model.py +0 -0
  196. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_omodel.py +0 -0
  197. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_paths.py +0 -0
  198. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_repr.py +0 -0
  199. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_routine.py +0 -0
  200. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_rtn_pflow.py +0 -0
  201. {ltbams-0.9.12 → ltbams-0.9.13}/tests/test_service.py +0 -0
  202. {ltbams-0.9.12 → ltbams-0.9.13}/versioneer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ltbams
3
- Version: 0.9.12
3
+ Version: 0.9.13
4
4
  Summary: Python software for scheduling modeling and co-simulation with dynanics.
5
5
  Home-page: https://github.com/CURENT/ams
6
6
  Author: Jinning Wang
@@ -35,11 +35,12 @@ License-File: LICENSE
35
35
 
36
36
  # LTB AMS
37
37
 
38
- Python Software for Power System Scheduling Modeling and Co-Simulation with Dynanic, serving as the market simulator for the [CURENT Largescale Testbed][LTB Repository].
38
+ Python Software for Power System Scheduling Modeling and Co-Simulation with Dynamics, serving as the market simulator for the [CURENT Largescale Testbed][LTB Repository].
39
39
 
40
40
  [![License: GPL-3.0](https://img.shields.io/badge/License-GPL--3.0-blue.svg)](https://github.com/CURENT/ams/blob/master/LICENSE)
41
41
  ![platforms](https://anaconda.org/conda-forge/ltbams/badges/platforms.svg)
42
42
  [![Python Versions](https://img.shields.io/badge/Python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](https://www.python.org/)
43
+ [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)
43
44
  ![Repo Size](https://img.shields.io/github/repo-size/CURENT/ams)
44
45
  [![GitHub last commit (master)](https://img.shields.io/github/last-commit/CURENT/ams/master?label=last%20commit%20to%20master)](https://github.com/CURENT/ams/commits/master/)
45
46
  [![GitHub last commit (develop)](https://img.shields.io/github/last-commit/CURENT/ams/develop?label=last%20commit%20to%20develop)](https://github.com/CURENT/ams/commits/develop/)
@@ -66,6 +67,10 @@ Python Software for Power System Scheduling Modeling and Co-Simulation with Dyna
66
67
  With the built-in interface with ANDES, AMS enables **Dynamics Incorporated**
67
68
  **Stability-Constrained Scheduling**.
68
69
 
70
+ This package can be helpful for power system engineers, researchers, and students
71
+ who need to conduct scheduling studies and transient stability studies at given
72
+ operating points.
73
+
69
74
  AMS is a **Modeling Framework** that provides a descriptive way to formulate
70
75
  scheduling problems. The optimization problems are then handled by **CVXPY**
71
76
  and solved with third-party solvers.
@@ -102,6 +107,7 @@ Use the following resources to get involved.
102
107
  - Version **0.9.9** has known issues and has been yanked from PyPI
103
108
  - `kvxopt` is recommended to install via `conda` as sometimes ``pip`` struggles to set the correct path for compiled libraries
104
109
  - `cvxpy` versions **below 1.5** are incompatible with `numpy` versions **2.0 and above**
110
+ - If solver `SCIP` run into import error, try to reinstall its Python interface by running `pip install pyscipopt --no-binary scip --force`
105
111
 
106
112
  AMS is released as ``ltbams`` on PyPI and conda-forge.
107
113
  Install from PyPI using pip:
@@ -124,15 +130,23 @@ pip install git+https://github.com/CURENT/ams.git
124
130
 
125
131
  # Example Usage
126
132
 
127
- Using AMS to run a Real-Time Economic Dispatch (RTED) simulation:
128
-
129
133
  ```python
130
134
  import ams
135
+ import andes
136
+
137
+ ss = ams.load(ams.get_case('ieee14/ieee14_uced.xlsx'))
138
+
139
+ # solve RTED
140
+ ss.RTED.run(solver='CLARABEL')
131
141
 
132
- ss = ams.load(ams.get_case('ieee14_uced.xlsx'))
133
- ss.RTED.run()
142
+ ss.RTED.pg.v
143
+ >>> array([1.8743862, 0.3226138, 0.01 , 0.02 , 0.01 ])
134
144
 
135
- print(ss.RTED.pg.v)
145
+ # convert to ANDES case
146
+ sa = ss.to_andes(addfile=andes.get_case('ieee14/ieee14_full.xlsx'),
147
+ setup=True, verify=False)
148
+ sa
149
+ >>> <andes.system.System at 0x14bd98190>
136
150
  ```
137
151
 
138
152
  # Sponsors and Contributors
@@ -1,10 +1,11 @@
1
1
  # LTB AMS
2
2
 
3
- Python Software for Power System Scheduling Modeling and Co-Simulation with Dynanic, serving as the market simulator for the [CURENT Largescale Testbed][LTB Repository].
3
+ Python Software for Power System Scheduling Modeling and Co-Simulation with Dynamics, serving as the market simulator for the [CURENT Largescale Testbed][LTB Repository].
4
4
 
5
5
  [![License: GPL-3.0](https://img.shields.io/badge/License-GPL--3.0-blue.svg)](https://github.com/CURENT/ams/blob/master/LICENSE)
6
6
  ![platforms](https://anaconda.org/conda-forge/ltbams/badges/platforms.svg)
7
7
  [![Python Versions](https://img.shields.io/badge/Python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](https://www.python.org/)
8
+ [![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)
8
9
  ![Repo Size](https://img.shields.io/github/repo-size/CURENT/ams)
9
10
  [![GitHub last commit (master)](https://img.shields.io/github/last-commit/CURENT/ams/master?label=last%20commit%20to%20master)](https://github.com/CURENT/ams/commits/master/)
10
11
  [![GitHub last commit (develop)](https://img.shields.io/github/last-commit/CURENT/ams/develop?label=last%20commit%20to%20develop)](https://github.com/CURENT/ams/commits/develop/)
@@ -31,6 +32,10 @@ Python Software for Power System Scheduling Modeling and Co-Simulation with Dyna
31
32
  With the built-in interface with ANDES, AMS enables **Dynamics Incorporated**
32
33
  **Stability-Constrained Scheduling**.
33
34
 
35
+ This package can be helpful for power system engineers, researchers, and students
36
+ who need to conduct scheduling studies and transient stability studies at given
37
+ operating points.
38
+
34
39
  AMS is a **Modeling Framework** that provides a descriptive way to formulate
35
40
  scheduling problems. The optimization problems are then handled by **CVXPY**
36
41
  and solved with third-party solvers.
@@ -67,6 +72,7 @@ Use the following resources to get involved.
67
72
  - Version **0.9.9** has known issues and has been yanked from PyPI
68
73
  - `kvxopt` is recommended to install via `conda` as sometimes ``pip`` struggles to set the correct path for compiled libraries
69
74
  - `cvxpy` versions **below 1.5** are incompatible with `numpy` versions **2.0 and above**
75
+ - If solver `SCIP` run into import error, try to reinstall its Python interface by running `pip install pyscipopt --no-binary scip --force`
70
76
 
71
77
  AMS is released as ``ltbams`` on PyPI and conda-forge.
72
78
  Install from PyPI using pip:
@@ -89,15 +95,23 @@ pip install git+https://github.com/CURENT/ams.git
89
95
 
90
96
  # Example Usage
91
97
 
92
- Using AMS to run a Real-Time Economic Dispatch (RTED) simulation:
93
-
94
98
  ```python
95
99
  import ams
100
+ import andes
101
+
102
+ ss = ams.load(ams.get_case('ieee14/ieee14_uced.xlsx'))
103
+
104
+ # solve RTED
105
+ ss.RTED.run(solver='CLARABEL')
96
106
 
97
- ss = ams.load(ams.get_case('ieee14_uced.xlsx'))
98
- ss.RTED.run()
107
+ ss.RTED.pg.v
108
+ >>> array([1.8743862, 0.3226138, 0.01 , 0.02 , 0.01 ])
99
109
 
100
- print(ss.RTED.pg.v)
110
+ # convert to ANDES case
111
+ sa = ss.to_andes(addfile=andes.get_case('ieee14/ieee14_full.xlsx'),
112
+ setup=True, verify=False)
113
+ sa
114
+ >>> <andes.system.System at 0x14bd98190>
101
115
  ```
102
116
 
103
117
  # Sponsors and Contributors
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-11-23T17:09:40-0500",
11
+ "date": "2024-12-05T16:49:45-0500",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "377b36f113e1e88f8c2d21d3c6ab8ff3728cffb0",
15
- "version": "0.9.12"
14
+ "full-revisionid": "db9279e8765429827062032b803c756025c018b2",
15
+ "version": "0.9.13"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -78,7 +78,6 @@ def sync_adsys(amsys, adsys):
78
78
  ad_mdl.set(src=param, attr='v', idx=idx,
79
79
  value=am_mdl.get(src=param, attr='v', idx=idx))
80
80
  except Exception:
81
- logger.debug(f"Skip updating {mname}.{param}")
82
81
  continue
83
82
  return True
84
83
 
@@ -333,6 +332,27 @@ def parse_addfile(adsys, amsys, addfile):
333
332
  df[idxn] = df[idxn].replace(idx_map[mdl_guess])
334
333
  logger.debug(f'Adjust {idxp.class_name} <{name}.{idxp.name}>')
335
334
 
335
+ # NOTE: Group TimedEvent needs special treatment
336
+ # adjust Toggle and Fault models
337
+ toggle_df = df_models.get('Toggle') or df_models.get('Toggler')
338
+ if toggle_df is not None:
339
+ toggle_df['dev'] = toggle_df.apply(replace_dev, axis=1,
340
+ mdl='model', dev='dev',
341
+ idx_map=idx_map)
342
+
343
+ alter_df = df_models.get('Alter')
344
+ if alter_df is not None:
345
+ alter_df['dev'] = alter_df.apply(replace_dev, axis=1,
346
+ mdl='model', dev='dev',
347
+ idx_map=idx_map)
348
+
349
+ # adjust Fault model
350
+ fault_df = df_models.get('Fault')
351
+ if fault_df is not None:
352
+ fault_df['bus'] = fault_df.apply(replace_dev, axis=1,
353
+ mdl='bus', dev='bus',
354
+ idx_map=idx_map)
355
+
336
356
  # add dynamic models
337
357
  for name, df in df_models.items():
338
358
  # drop rows that all nan
@@ -966,7 +986,7 @@ def make_link_table(adsys):
966
986
  right=ssa_rg[['stg_idx', 'rg_idx']])
967
987
 
968
988
  # NOTE: use this instead of fillna to avoid type conversion
969
- idxc = ['stg_idx', 'syg_idx', 'dg_idx', 'rg_idx']
989
+ idxc = ['syg_idx', 'dg_idx', 'rg_idx']
970
990
  ssa_key0[idxc] = ssa_key0[idxc].astype('str').replace({'nan': ''}).astype('bool')
971
991
 
972
992
  dyr = ssa_key0['syg_idx'] + ssa_key0['dg_idx'] + ssa_key0['rg_idx']
@@ -1037,3 +1057,27 @@ def verify_pf(amsys, adsys, tol=1e-3):
1037
1057
  logger.warning(msg)
1038
1058
  logger.warning(diff_msg)
1039
1059
  return check
1060
+
1061
+
1062
+ def replace_dev(row, mdl, dev, idx_map):
1063
+ """
1064
+ Replace the device idx in the row based on the idx_map.
1065
+
1066
+ Parameters
1067
+ ----------
1068
+ row : pd.Series
1069
+ The row of the DataFrame.
1070
+ mdl : str
1071
+ The column name for the Model.
1072
+ dev : str
1073
+ The column name for the Device idx.
1074
+ idx_map : dict
1075
+ The index map for replacement.
1076
+
1077
+ Returns
1078
+ -------
1079
+ str
1080
+ The new device idx.
1081
+ """
1082
+ old_idx = row[dev]
1083
+ return idx_map.get(row[mdl], {}).get(old_idx, old_idx)
@@ -0,0 +1,356 @@
1
+ """
2
+ Module for report generation.
3
+ """
4
+ import logging
5
+ from collections import OrderedDict
6
+ from time import strftime
7
+ from typing import List, Dict, Optional
8
+
9
+ from andes.io.txt import dump_data
10
+ from andes.shared import np
11
+ from andes.utils.misc import elapsed
12
+
13
+ from ams import __version__ as version
14
+ from ams.shared import copyright_msg
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ DECIMALS = 6
20
+
21
+
22
+ def report_info(system) -> list:
23
+ info = list()
24
+ info.append('AMS' + ' ' + version + '\n')
25
+ info.append(f'{copyright_msg}\n\n')
26
+ info.append('AMS comes with ABSOLUTELY NO WARRANTY\n')
27
+ info.append('Case file: ' + str(system.files.case) + '\n')
28
+ info.append('Report time: ' + strftime("%m/%d/%Y %I:%M:%S %p") + '\n\n')
29
+ return info
30
+
31
+
32
+ class Report:
33
+ """
34
+ Report class to store routine analysis reports.
35
+
36
+ Notes
37
+ -----
38
+ Revised from the ANDES project (https://github.com/CURENT/andes).
39
+ Original author: Hantao Cui
40
+ License: GPL3
41
+ """
42
+
43
+ def __init__(self, system):
44
+ self.system = system
45
+ self.basic = OrderedDict()
46
+
47
+ @property
48
+ def info(self):
49
+ return report_info(self.system)
50
+
51
+ def update(self):
52
+ """
53
+ Update values based on the requested content
54
+ """
55
+ system = self.system
56
+ self.basic.update({
57
+ 'Buses': system.Bus.n,
58
+ 'Generators': system.PV.n + system.Slack.n,
59
+ 'Loads': system.PQ.n,
60
+ 'Shunts': system.Shunt.n,
61
+ 'Lines': system.Line.n,
62
+ 'Transformers': np.count_nonzero(system.Line.trans.v == 1),
63
+ 'Areas': system.Area.n,
64
+ 'Regions': system.Region.n,
65
+ })
66
+
67
+ def collect(self, rtn, horizon=None):
68
+ """
69
+ Collect report data.
70
+
71
+ Parameters
72
+ ----------
73
+ rtn : Routine
74
+ Routine object to collect data from.
75
+ horizon : str, optional
76
+ Timeslot to collect data from. Only single timeslot is supported.
77
+ """
78
+ text = list()
79
+ header = list()
80
+ row_name = list()
81
+ data = list()
82
+
83
+ if not rtn.converged:
84
+ return text, header, row_name, data
85
+
86
+ owners = collect_owners(rtn)
87
+ owners = collect_vars(owners, rtn, horizon, DECIMALS)
88
+ owners = collect_exprs(owners, rtn, horizon, DECIMALS)
89
+ owners = collect_exprcs(owners, rtn, horizon, DECIMALS)
90
+
91
+ dump_collected_data(owners, text, header, row_name, data)
92
+
93
+ return text, header, row_name, data
94
+
95
+ def write(self):
96
+ """
97
+ Write report to file.
98
+ """
99
+ system = self.system
100
+ if system.files.no_output is True:
101
+ return
102
+
103
+ text = list()
104
+ header = list()
105
+ row_name = list()
106
+ data = list()
107
+ self.update()
108
+
109
+ t, _ = elapsed()
110
+
111
+ # --- system info section ---
112
+ text.append(self.info)
113
+ header.append(None)
114
+ row_name.append(None)
115
+ data.append(None)
116
+
117
+ # --- system summary section ---
118
+ text.append(['='*10 + ' System Statistics ' + '='*10 + '\n'])
119
+ header.append(None)
120
+ row_name.append(self.basic.keys())
121
+ data.append(list(self.basic.values()))
122
+
123
+ # --- rountine data section ---
124
+ rtns_to_collect = [rtn for rtn in system.routines.values() if rtn.converged]
125
+ for rtn in rtns_to_collect:
126
+ # --- routine summary ---
127
+ text.append(['='*30 + f' {rtn.class_name} ' + '='*30])
128
+ header.append(None)
129
+ row_name.append(None)
130
+ data.append(None)
131
+ if hasattr(rtn, 'timeslot'):
132
+ for slot in rtn.timeslot.v:
133
+ # --- timeslot summary ---
134
+ text.append(['-'*28 + f' {slot} ' + '-'*28])
135
+ header.append(None)
136
+ row_name.append(None)
137
+ data.append(None)
138
+ text_sum, header_sum, row_name_sum, data_sum = self.collect(rtn, horizon=[slot])
139
+ # --- timeslot data ---
140
+ text.extend(text_sum)
141
+ header.extend(header_sum)
142
+ row_name.extend(row_name_sum)
143
+ data.extend(data_sum)
144
+ else:
145
+ # single-period
146
+ text_sum, header_sum, row_name_sum, data_sum = self.collect(rtn)
147
+ # --- routine extended ---
148
+ text.append([''])
149
+ row_name.append(
150
+ ['Generation', 'Load'])
151
+
152
+ if hasattr(rtn, 'pd'):
153
+ pd = rtn.pd.v.sum().round(DECIMALS)
154
+ else:
155
+ pd = rtn.system.PQ.p0.v.sum().round(DECIMALS)
156
+ if hasattr(rtn, 'qd'):
157
+ qd = rtn.qd.v.sum().round(DECIMALS)
158
+ else:
159
+ qd = rtn.system.PQ.q0.v.sum().round(DECIMALS)
160
+
161
+ if not hasattr(rtn, 'qg'):
162
+ header.append(['P (p.u.)'])
163
+ Pcol = [rtn.pg.v.sum().round(DECIMALS), pd]
164
+ data.append([Pcol])
165
+ else:
166
+ header.append(['P (p.u.)', 'Q (p.u.)'])
167
+ Pcol = [rtn.pg.v.sum().round(DECIMALS), pd]
168
+ Qcol = [rtn.qg.v.sum().round(DECIMALS), qd]
169
+ data.append([Pcol, Qcol])
170
+
171
+ # --- routine data ---
172
+ text.extend(text_sum)
173
+ header.extend(header_sum)
174
+ row_name.extend(row_name_sum)
175
+ data.extend(data_sum)
176
+ dump_data(text, header, row_name, data, system.files.txt)
177
+
178
+ _, s = elapsed(t)
179
+ logger.info(f'Report saved to "{system.files.txt}" in {s}.')
180
+
181
+
182
+ def dump_collected_data(owners: dict, text: List, header: List, row_name: List, data: List) -> None:
183
+ """
184
+ Dump collected data into the provided lists.
185
+
186
+ Parameters
187
+ ----------
188
+ owners : dict
189
+ Dictionary of owners.
190
+ text : list
191
+ List to append text data to.
192
+ header : list
193
+ List to append header data to.
194
+ row_name : list
195
+ List to append row names to.
196
+ data : list
197
+ List to append data to.
198
+ """
199
+ for key, val in owners.items():
200
+ text.append([f'{key} DATA:\n'])
201
+ row_name.append(val['idx'])
202
+ header.append(val['header'])
203
+ data.append(val['data'])
204
+
205
+
206
+ def collect_exprcs(owners: Dict, rtn, horizon: Optional[str], decimals: int) -> Dict:
207
+ """
208
+ Collect expression calculations and populate the data dictionary.
209
+
210
+ Parameters
211
+ ----------
212
+ owners : dict
213
+ Dictionary of owners.
214
+ rtn : Routine
215
+ Routine object to collect data from.
216
+ horizon : str, optional
217
+ Timeslot to collect data from. Only single timeslot is supported.
218
+ decimals : int
219
+ Number of decimal places to round the data.
220
+
221
+ Returns
222
+ -------
223
+ dict
224
+ Updated dictionary of owners with collected ExpressionCalc data.
225
+ """
226
+ for key, exprc in rtn.exprcs.items():
227
+ if exprc.owner is None:
228
+ continue
229
+ owner_name = exprc.owner.class_name
230
+ idx_v = owners[owner_name]['idx']
231
+ header_v = key if exprc.unit is None else f'{key} ({exprc.unit})'
232
+ try:
233
+ data_v = rtn.get(src=key, attr='v', idx=idx_v, horizon=horizon).round(decimals)
234
+ except Exception:
235
+ data_v = [np.nan] * len(idx_v)
236
+ owners[owner_name]['header'].append(header_v)
237
+ owners[owner_name]['data'].append(data_v)
238
+
239
+ return owners
240
+
241
+
242
+ def collect_exprs(owners: Dict, rtn, horizon: Optional[str], decimals: int) -> Dict:
243
+ """
244
+ Collect expressions and populate the data dictionary.
245
+
246
+ Parameters
247
+ ----------
248
+ owners : dict
249
+ Dictionary of owners.
250
+ rtn : Routine
251
+ Routine object to collect data from.
252
+ horizon : str, optional
253
+ Timeslot to collect data from. Only single timeslot is supported.
254
+ decimals : int
255
+ Number of decimal places to round the data.
256
+
257
+ Returns
258
+ -------
259
+ dict
260
+ Updated dictionary of owners with collected Expression data.
261
+ """
262
+ for key, expr in rtn.exprs.items():
263
+ if expr.owner is None:
264
+ continue
265
+ owner_name = expr.owner.class_name
266
+ idx_v = owners[owner_name]['idx']
267
+ header_v = key if expr.unit is None else f'{key} ({expr.unit})'
268
+ try:
269
+ data_v = rtn.get(src=key, attr='v', idx=idx_v, horizon=horizon).round(decimals)
270
+ except Exception:
271
+ data_v = [np.nan] * len(idx_v)
272
+ owners[owner_name]['header'].append(header_v)
273
+ owners[owner_name]['data'].append(data_v)
274
+
275
+ return owners
276
+
277
+
278
+ def collect_vars(owners: Dict, rtn, horizon: Optional[str], decimals: int) -> Dict:
279
+ """
280
+ Collect variables and populate the data dictionary.
281
+
282
+ Parameters
283
+ ----------
284
+ owners : dict
285
+ Dictionary of owners.
286
+ rtn : Routine
287
+ Routine object to collect data from.
288
+ horizon : str, optional
289
+ Timeslot to collect data from. Only single timeslot is supported.
290
+ decimals : int
291
+ Number of decimal places to round the data.
292
+
293
+ Returns
294
+ -------
295
+ dict
296
+ Updated dictionary of owners with collected Var data.
297
+ """
298
+
299
+ for key, var in rtn.vars.items():
300
+ if var.owner is None:
301
+ continue
302
+ owner_name = var.owner.class_name
303
+ idx_v = owners[owner_name]['idx']
304
+ header_v = key if var.unit is None else f'{key} ({var.unit})'
305
+ try:
306
+ data_v = rtn.get(src=key, attr='v', idx=idx_v, horizon=horizon).round(decimals)
307
+ except Exception:
308
+ data_v = [np.nan] * len(idx_v)
309
+ owners[owner_name]['header'].append(header_v)
310
+ owners[owner_name]['data'].append(data_v)
311
+
312
+ return owners
313
+
314
+
315
+ def collect_owners(rtn):
316
+ """
317
+ Initialize an owners dictionary for data collection.
318
+
319
+ Returns
320
+ -------
321
+ dict
322
+ A dictionary of initialized owners.
323
+ """
324
+ # initialize data section by model
325
+ owners_all = ['Bus', 'Line', 'StaticGen',
326
+ 'PV', 'Slack', 'RenGen',
327
+ 'DG', 'ESD1', 'PVD1',
328
+ 'StaticLoad']
329
+
330
+ # Filter owners that exist in the system
331
+ owners_e = list({
332
+ var.owner.class_name for var in rtn.vars.values() if var.owner is not None
333
+ }.union(
334
+ expr.owner.class_name for expr in rtn.exprs.values() if expr.owner is not None
335
+ ).union(
336
+ exprc.owner.class_name for exprc in rtn.exprcs.values() if exprc.owner is not None
337
+ ))
338
+
339
+ # Use a dictionary comprehension to create vars_by_owner
340
+ owners = {
341
+ name: {'idx': [],
342
+ 'name': [],
343
+ 'header': [],
344
+ 'data': [], }
345
+ for name in owners_all if name in owners_e and getattr(rtn.system, name).n > 0
346
+ }
347
+
348
+ for key, val in owners.items():
349
+ owner = getattr(rtn.system, key)
350
+ idx_v = owner.get_idx()
351
+ val['idx'] = idx_v
352
+ val['name'] = owner.get(src='name', attr='v', idx=idx_v)
353
+ val['header'].append('Name')
354
+ val['data'].append(val['name'])
355
+
356
+ return owners
@@ -34,14 +34,11 @@ class ACOPF(DCPF0):
34
34
  self.type = 'ACED'
35
35
 
36
36
  self.map1 = OrderedDict() # ACOPF does not receive
37
- self.map2 = OrderedDict([
38
- ('Bus', {
39
- 'vBus': 'v0',
40
- }),
41
- ('StaticGen', {
42
- 'pg': 'p0',
43
- }),
44
- ])
37
+ self.map2.update({
38
+ 'vBus': ('Bus', 'v0'),
39
+ 'ug': ('StaticGen', 'u'),
40
+ 'pg': ('StaticGen', 'p0'),
41
+ })
45
42
 
46
43
  # --- params ---
47
44
  self.c2 = RParam(info='Gen cost coefficient 2',
@@ -146,6 +146,53 @@ class DCOPF(DCPFBase):
146
146
  info='total cost', unit='$',
147
147
  sense='min', e_str=obj,)
148
148
 
149
+ def dc2ac(self, kloss=1.0, **kwargs):
150
+ """
151
+ Convert the RTED results with ACOPF.
152
+
153
+ Parameters
154
+ ----------
155
+ kloss : float, optional
156
+ The loss factor for the conversion. Defaults to 1.2.
157
+ """
158
+ exec_time = self.exec_time
159
+ if self.exec_time == 0 or self.exit_code != 0:
160
+ logger.warning(f'{self.class_name} is not executed successfully, quit conversion.')
161
+ return False
162
+
163
+ # --- ACOPF ---
164
+ # scale up load
165
+ pq_idx = self.system.StaticLoad.get_idx()
166
+ pd0 = self.system.StaticLoad.get(src='p0', attr='v', idx=pq_idx).copy()
167
+ qd0 = self.system.StaticLoad.get(src='q0', attr='v', idx=pq_idx).copy()
168
+ self.system.StaticLoad.set(src='p0', idx=pq_idx, attr='v', value=pd0 * kloss)
169
+ self.system.StaticLoad.set(src='q0', idx=pq_idx, attr='v', value=qd0 * kloss)
170
+ # run ACOPF
171
+ ACOPF = self.system.ACOPF
172
+ ACOPF.run()
173
+ # scale load back
174
+ self.system.StaticLoad.set(src='p0', idx=pq_idx, attr='v', value=pd0)
175
+ self.system.StaticLoad.set(src='q0', idx=pq_idx, attr='v', value=qd0)
176
+ if not ACOPF.exit_code == 0:
177
+ logger.warning('<ACOPF> did not converge, conversion failed.')
178
+ # NOTE: mock results to fit interface with ANDES
179
+ self.vBus = ACOPF.vBus
180
+ self.vBus.optz.value = np.ones(self.system.Bus.n)
181
+ self.aBus.optz.value = np.zeros(self.system.Bus.n)
182
+ return False
183
+ self.pg.optz.value = ACOPF.pg.v
184
+
185
+ # NOTE: mock results to fit interface with ANDES
186
+ self.vBus.optz.value = ACOPF.vBus.v
187
+ self.aBus.optz.value = ACOPF.aBus.v
188
+ self.exec_time = exec_time
189
+
190
+ # --- set status ---
191
+ self.system.recent = self
192
+ self.converted = True
193
+ logger.warning(f'<{self.class_name}> converted to AC.')
194
+ return True
195
+
149
196
  def run(self, **kwargs):
150
197
  """
151
198
  Run the routine.
@@ -88,6 +88,10 @@ class DCPFBase(RoutineBase):
88
88
  v0=self.pg0)
89
89
 
90
90
  # --- bus ---
91
+ self.vBus = Var(info='Bus voltage magnitude, placeholder',
92
+ unit='p.u.',
93
+ name='vBus', tex_name=r'v_{Bus}',
94
+ src='v', model='Bus',)
91
95
  self.aBus = Var(info='Bus voltage angle',
92
96
  unit='rad',
93
97
  name='aBus', tex_name=r'\theta_{bus}',
@@ -35,6 +35,11 @@ class DCPF0(RoutineBase):
35
35
  self.info = 'DC Power Flow'
36
36
  self.type = 'PF'
37
37
 
38
+ self.ug = RParam(info='Gen connection status',
39
+ name='ug', tex_name=r'u_{g}',
40
+ model='StaticGen', src='u',
41
+ no_parse=True)
42
+
38
43
  # --- routine data ---
39
44
  self.x = RParam(info="line reactance",
40
45
  name='x', tex_name='x',
@@ -169,6 +174,7 @@ class DCPF0(RoutineBase):
169
174
  except Exception as e:
170
175
  logger.error(f"Failed to unpack results from {self.class_name}.\n{e}")
171
176
  return False
177
+ self.system.report()
172
178
  return True
173
179
  else:
174
180
  msg = f"{self.class_name} failed in "