ltbams 0.9.9__py3-none-any.whl → 1.0.2__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 (191) hide show
  1. ams/__init__.py +4 -11
  2. ams/_version.py +3 -3
  3. ams/cases/5bus/pjm5bus_demo.xlsx +0 -0
  4. ams/cases/5bus/pjm5bus_jumper.xlsx +0 -0
  5. ams/cases/5bus/pjm5bus_uced.json +1062 -0
  6. ams/cases/5bus/pjm5bus_uced.xlsx +0 -0
  7. ams/cases/5bus/pjm5bus_uced_esd1.xlsx +0 -0
  8. ams/cases/5bus/pjm5bus_uced_ev.xlsx +0 -0
  9. ams/cases/ieee123/ieee123.xlsx +0 -0
  10. ams/cases/ieee123/ieee123_regcv1.xlsx +0 -0
  11. ams/cases/ieee14/ieee14.json +1166 -0
  12. ams/cases/ieee14/ieee14.raw +92 -0
  13. ams/cases/ieee14/ieee14_conn.xlsx +0 -0
  14. ams/cases/ieee14/ieee14_uced.xlsx +0 -0
  15. ams/cases/ieee39/ieee39.xlsx +0 -0
  16. ams/cases/ieee39/ieee39_uced.xlsx +0 -0
  17. ams/cases/ieee39/ieee39_uced_esd1.xlsx +0 -0
  18. ams/cases/ieee39/ieee39_uced_pvd1.xlsx +0 -0
  19. ams/cases/ieee39/ieee39_uced_vis.xlsx +0 -0
  20. ams/cases/matpower/benchmark.json +1594 -0
  21. ams/cases/matpower/case118.m +787 -0
  22. ams/cases/matpower/case14.m +129 -0
  23. ams/cases/matpower/case300.m +1315 -0
  24. ams/cases/matpower/case39.m +205 -0
  25. ams/cases/matpower/case5.m +62 -0
  26. ams/cases/matpower/case_ACTIVSg2000.m +9460 -0
  27. ams/cases/npcc/npcc.m +644 -0
  28. ams/cases/npcc/npcc_uced.xlsx +0 -0
  29. ams/cases/pglib/pglib_opf_case39_epri__api.m +243 -0
  30. ams/cases/wecc/wecc.m +714 -0
  31. ams/cases/wecc/wecc_uced.xlsx +0 -0
  32. ams/cli.py +6 -0
  33. ams/core/__init__.py +2 -0
  34. ams/core/documenter.py +652 -0
  35. ams/core/matprocessor.py +782 -0
  36. ams/core/model.py +330 -0
  37. ams/core/param.py +322 -0
  38. ams/core/service.py +918 -0
  39. ams/core/symprocessor.py +224 -0
  40. ams/core/var.py +59 -0
  41. ams/extension/__init__.py +5 -0
  42. ams/extension/eva.py +401 -0
  43. ams/interface.py +1085 -0
  44. ams/io/__init__.py +133 -0
  45. ams/io/json.py +82 -0
  46. ams/io/matpower.py +406 -0
  47. ams/io/psse.py +6 -0
  48. ams/io/pypower.py +103 -0
  49. ams/io/xlsx.py +80 -0
  50. ams/main.py +81 -4
  51. ams/models/__init__.py +24 -0
  52. ams/models/area.py +40 -0
  53. ams/models/bus.py +52 -0
  54. ams/models/cost.py +169 -0
  55. ams/models/distributed/__init__.py +3 -0
  56. ams/models/distributed/esd1.py +71 -0
  57. ams/models/distributed/ev.py +60 -0
  58. ams/models/distributed/pvd1.py +67 -0
  59. ams/models/group.py +231 -0
  60. ams/models/info.py +26 -0
  61. ams/models/line.py +238 -0
  62. ams/models/renewable/__init__.py +5 -0
  63. ams/models/renewable/regc.py +119 -0
  64. ams/models/reserve.py +94 -0
  65. ams/models/shunt.py +14 -0
  66. ams/models/static/__init__.py +2 -0
  67. ams/models/static/gen.py +165 -0
  68. ams/models/static/pq.py +61 -0
  69. ams/models/timeslot.py +69 -0
  70. ams/models/zone.py +49 -0
  71. ams/opt/__init__.py +12 -0
  72. ams/opt/constraint.py +175 -0
  73. ams/opt/exprcalc.py +127 -0
  74. ams/opt/expression.py +188 -0
  75. ams/opt/objective.py +174 -0
  76. ams/opt/omodel.py +432 -0
  77. ams/opt/optzbase.py +192 -0
  78. ams/opt/param.py +156 -0
  79. ams/opt/var.py +233 -0
  80. ams/pypower/__init__.py +8 -0
  81. ams/pypower/_compat.py +9 -0
  82. ams/pypower/core/__init__.py +8 -0
  83. ams/pypower/core/pips.py +894 -0
  84. ams/pypower/core/ppoption.py +244 -0
  85. ams/pypower/core/ppver.py +18 -0
  86. ams/pypower/core/solver.py +2451 -0
  87. ams/pypower/eps.py +6 -0
  88. ams/pypower/idx.py +174 -0
  89. ams/pypower/io.py +604 -0
  90. ams/pypower/make/__init__.py +11 -0
  91. ams/pypower/make/matrices.py +665 -0
  92. ams/pypower/make/pdv.py +506 -0
  93. ams/pypower/routines/__init__.py +7 -0
  94. ams/pypower/routines/cpf.py +513 -0
  95. ams/pypower/routines/cpf_callbacks.py +114 -0
  96. ams/pypower/routines/opf.py +1803 -0
  97. ams/pypower/routines/opffcns.py +1946 -0
  98. ams/pypower/routines/pflow.py +852 -0
  99. ams/pypower/toggle.py +1098 -0
  100. ams/pypower/utils.py +293 -0
  101. ams/report.py +212 -50
  102. ams/routines/__init__.py +23 -0
  103. ams/routines/acopf.py +117 -0
  104. ams/routines/cpf.py +65 -0
  105. ams/routines/dcopf.py +241 -0
  106. ams/routines/dcpf.py +209 -0
  107. ams/routines/dcpf0.py +196 -0
  108. ams/routines/dopf.py +150 -0
  109. ams/routines/ed.py +312 -0
  110. ams/routines/pflow.py +255 -0
  111. ams/routines/pflow0.py +113 -0
  112. ams/routines/routine.py +1033 -0
  113. ams/routines/rted.py +519 -0
  114. ams/routines/type.py +160 -0
  115. ams/routines/uc.py +376 -0
  116. ams/shared.py +63 -9
  117. ams/system.py +61 -22
  118. ams/utils/__init__.py +3 -0
  119. ams/utils/misc.py +77 -0
  120. ams/utils/paths.py +257 -0
  121. docs/Makefile +21 -0
  122. docs/make.bat +35 -0
  123. docs/source/_templates/autosummary/base.rst +5 -0
  124. docs/source/_templates/autosummary/class.rst +35 -0
  125. docs/source/_templates/autosummary/module.rst +65 -0
  126. docs/source/_templates/autosummary/module_toctree.rst +66 -0
  127. docs/source/api.rst +102 -0
  128. docs/source/conf.py +206 -0
  129. docs/source/examples/index.rst +34 -0
  130. docs/source/genmodelref.py +61 -0
  131. docs/source/genroutineref.py +47 -0
  132. docs/source/getting_started/copyright.rst +20 -0
  133. docs/source/getting_started/formats/index.rst +20 -0
  134. docs/source/getting_started/formats/matpower.rst +183 -0
  135. docs/source/getting_started/formats/psse.rst +46 -0
  136. docs/source/getting_started/formats/pypower.rst +223 -0
  137. docs/source/getting_started/formats/xlsx.png +0 -0
  138. docs/source/getting_started/formats/xlsx.rst +23 -0
  139. docs/source/getting_started/index.rst +76 -0
  140. docs/source/getting_started/install.rst +231 -0
  141. docs/source/getting_started/overview.rst +26 -0
  142. docs/source/getting_started/testcase.rst +45 -0
  143. docs/source/getting_started/verification.rst +13 -0
  144. docs/source/images/curent.ico +0 -0
  145. docs/source/images/dcopf_time.png +0 -0
  146. docs/source/images/sponsors/CURENT_Logo_NameOnTrans.png +0 -0
  147. docs/source/images/sponsors/CURENT_Logo_Transparent.png +0 -0
  148. docs/source/images/sponsors/CURENT_Logo_Transparent_Name.png +0 -0
  149. docs/source/images/sponsors/doe.png +0 -0
  150. docs/source/index.rst +108 -0
  151. docs/source/modeling/example.rst +159 -0
  152. docs/source/modeling/index.rst +17 -0
  153. docs/source/modeling/model.rst +210 -0
  154. docs/source/modeling/routine.rst +122 -0
  155. docs/source/modeling/system.rst +51 -0
  156. docs/source/release-notes.rst +398 -0
  157. ltbams-1.0.2.dist-info/METADATA +215 -0
  158. ltbams-1.0.2.dist-info/RECORD +188 -0
  159. {ltbams-0.9.9.dist-info → ltbams-1.0.2.dist-info}/WHEEL +1 -1
  160. ltbams-1.0.2.dist-info/top_level.txt +3 -0
  161. tests/__init__.py +0 -0
  162. tests/test_1st_system.py +33 -0
  163. tests/test_addressing.py +40 -0
  164. tests/test_andes_mats.py +61 -0
  165. tests/test_case.py +266 -0
  166. tests/test_cli.py +34 -0
  167. tests/test_export_csv.py +89 -0
  168. tests/test_group.py +83 -0
  169. tests/test_interface.py +216 -0
  170. tests/test_io.py +32 -0
  171. tests/test_jumper.py +27 -0
  172. tests/test_known_good.py +267 -0
  173. tests/test_matp.py +437 -0
  174. tests/test_model.py +54 -0
  175. tests/test_omodel.py +119 -0
  176. tests/test_paths.py +22 -0
  177. tests/test_report.py +251 -0
  178. tests/test_repr.py +21 -0
  179. tests/test_routine.py +178 -0
  180. tests/test_rtn_dcopf.py +101 -0
  181. tests/test_rtn_dcpf.py +77 -0
  182. tests/test_rtn_ed.py +279 -0
  183. tests/test_rtn_pflow.py +219 -0
  184. tests/test_rtn_rted.py +273 -0
  185. tests/test_rtn_uc.py +248 -0
  186. tests/test_service.py +73 -0
  187. ltbams-0.9.9.dist-info/LICENSE +0 -692
  188. ltbams-0.9.9.dist-info/METADATA +0 -859
  189. ltbams-0.9.9.dist-info/RECORD +0 -14
  190. ltbams-0.9.9.dist-info/top_level.txt +0 -1
  191. {ltbams-0.9.9.dist-info → ltbams-1.0.2.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,89 @@
1
+ """
2
+ Test routine export to CSV.
3
+ """
4
+ import unittest
5
+ import os
6
+ import csv
7
+
8
+ import numpy as np
9
+
10
+ import ams
11
+
12
+
13
+ class TestExportCSV(unittest.TestCase):
14
+ """
15
+ Tests for Routine export to CSV.
16
+ """
17
+
18
+ def setUp(self) -> None:
19
+ self.ss = ams.main.load(
20
+ ams.get_case("5bus/pjm5bus_demo.xlsx"),
21
+ default_config=True,
22
+ no_output=True,
23
+ )
24
+ self.expected_csv_DCOPF = 'pjm5bus_demo_DCOPF.csv'
25
+ self.expected_csv_ED = 'pjm5bus_demo_ED.csv'
26
+
27
+ def test_no_export(self):
28
+ """
29
+ Test no export when routine is not converged.
30
+ """
31
+ self.assertIsNone(self.ss.DCOPF.export_csv())
32
+
33
+ def test_export_DCOPF(self):
34
+ """
35
+ Test export DCOPF to CSV.
36
+ """
37
+ self.ss.DCOPF.run(solver='CLARABEL')
38
+ self.ss.DCOPF.export_csv()
39
+ self.assertTrue(os.path.exists(self.expected_csv_DCOPF))
40
+
41
+ n_rows = 0
42
+ n_cols = 0
43
+ with open(self.expected_csv_DCOPF, 'r') as csv_file:
44
+ csv_reader = csv.reader(csv_file)
45
+ for row in csv_reader:
46
+ n_rows += 1
47
+ # Check if this row has more columns than the previous rows
48
+ if n_cols == 0 or len(row) > n_cols:
49
+ n_cols = len(row)
50
+
51
+ n_cols_expected = np.sum([v.owner.n for v in self.ss.DCOPF.vars.values()])
52
+ n_cols_expected += np.sum([v.owner.n for v in self.ss.DCOPF.exprs.values()])
53
+ n_cols_expected += np.sum([v.owner.n for v in self.ss.DCOPF.exprcs.values()])
54
+ # cols number plus one for the index column
55
+ self.assertEqual(n_cols, n_cols_expected + 1)
56
+ # header row plus data row
57
+ n_rows_expected = 2
58
+ self.assertEqual(n_rows, n_rows_expected)
59
+
60
+ os.remove(self.expected_csv_DCOPF)
61
+
62
+ def test_export_ED(self):
63
+ """
64
+ Test export ED to CSV.
65
+ """
66
+ self.ss.ED.run(solver='CLARABEL')
67
+ self.ss.ED.export_csv()
68
+ self.assertTrue(os.path.exists(self.expected_csv_ED))
69
+
70
+ n_rows = 0
71
+ n_cols = 0
72
+ with open(self.expected_csv_ED, 'r') as csv_file:
73
+ csv_reader = csv.reader(csv_file)
74
+ for row in csv_reader:
75
+ n_rows += 1
76
+ # Check if this row has more columns than the previous rows
77
+ if n_cols == 0 or len(row) > n_cols:
78
+ n_cols = len(row)
79
+
80
+ n_cols_expected = np.sum([v.owner.n for v in self.ss.ED.vars.values()])
81
+ n_cols_expected += np.sum([v.owner.n for v in self.ss.ED.exprs.values()])
82
+ n_cols_expected += np.sum([v.owner.n for v in self.ss.ED.exprcs.values()])
83
+ # cols number plus one for the index column
84
+ self.assertEqual(n_cols, n_cols_expected + 1)
85
+ # header row plus data row
86
+ n_rows_expected = len(self.ss.ED.timeslot.v) + 1
87
+ self.assertTrue(n_rows, n_rows_expected)
88
+
89
+ os.remove(self.expected_csv_ED)
tests/test_group.py ADDED
@@ -0,0 +1,83 @@
1
+ """
2
+ Tests for group functions.
3
+ """
4
+ import unittest
5
+
6
+ import numpy as np
7
+
8
+ import ams
9
+
10
+
11
+ class TestGroup(unittest.TestCase):
12
+ """
13
+ Test the group class functions.
14
+ """
15
+
16
+ def setUp(self):
17
+ self.ss = ams.run(ams.get_case("ieee39/ieee39_uced_esd1.xlsx"),
18
+ default_config=True,
19
+ no_output=True,
20
+ )
21
+
22
+ def test_group_access(self):
23
+ """
24
+ Test methods such as `idx2model`
25
+ """
26
+ ss = self.ss
27
+
28
+ # --- idx2uid ---
29
+ self.assertIsNone(ss.DG.idx2uid(None))
30
+ self.assertListEqual(ss.DG.idx2uid([None]), [None])
31
+
32
+ # --- idx2model ---
33
+ # what works
34
+ self.assertIs(ss.DG.idx2model('ESD1_1'), ss.ESD1)
35
+ self.assertListEqual(ss.DG.idx2model(['ESD1_1']), [ss.ESD1])
36
+
37
+ # what does not work
38
+ self.assertRaises(KeyError, ss.DG.idx2model, idx='1')
39
+ self.assertRaises(KeyError, ss.DG.idx2model, idx=88)
40
+ self.assertRaises(KeyError, ss.DG.idx2model, idx=[1, 88])
41
+
42
+ # --- get ---
43
+ self.assertRaises(KeyError, ss.DG.get, 'EtaC', 999)
44
+
45
+ np.testing.assert_equal(ss.DG.get('EtaC', 'ESD1_1',), 1.0)
46
+
47
+ np.testing.assert_equal(ss.DG.get('EtaC', ['ESD1_1'], allow_none=True,),
48
+ [1.0])
49
+ np.testing.assert_equal(ss.DG.get('EtaC', ['ESD1_1', None],
50
+ allow_none=True, default=0.95),
51
+ [1.0, 0.95])
52
+
53
+ # --- set ---
54
+ ss.DG.set('EtaC', 'ESD1_1', 'v', 0.95)
55
+ np.testing.assert_equal(ss.DG.get('EtaC', 'ESD1_1',), 0.95)
56
+
57
+ ss.DG.set('EtaC', ['ESD1_1'], 'v', 0.97)
58
+ np.testing.assert_equal(ss.DG.get('EtaC', ['ESD1_1'],), [0.97])
59
+
60
+ ss.DG.set('EtaC', ['ESD1_1'], 'v', [0.99])
61
+ np.testing.assert_equal(ss.DG.get('EtaC', ['ESD1_1'],), [0.99])
62
+
63
+ # --- find_idx ---
64
+ self.assertListEqual(ss.DG.find_idx('name', ['ESD1_1']),
65
+ ss.ESD1.find_idx('name', ['ESD1_1']),
66
+ )
67
+
68
+ self.assertListEqual(ss.DG.find_idx(['name', 'Sn'],
69
+ [('ESD1_1',),
70
+ (100.0,)]),
71
+ ss.ESD1.find_idx(['name', 'Sn'],
72
+ [('ESD1_1',),
73
+ (100.0,)]),)
74
+
75
+ # --- get group idx ---
76
+ self.assertListEqual(ss.DG.get_all_idxes(), ss.ESD1.idx.v)
77
+
78
+ def test_group_repr(self):
79
+ """
80
+ Test `Group.__repr__()` method.
81
+ """
82
+ for grp in self.ss.groups.items():
83
+ print(grp.__repr__())
@@ -0,0 +1,216 @@
1
+ """
2
+ Test interface.
3
+ """
4
+
5
+ import unittest
6
+ import numpy as np
7
+
8
+ import andes
9
+ import ams
10
+
11
+ from ams.interface import (build_group_table, make_link_table, to_andes,
12
+ parse_addfile, verify_pf)
13
+
14
+
15
+ class TestAndesConversion(unittest.TestCase):
16
+ """
17
+ Tests conversion from AMS to ANDES.
18
+ """
19
+ ad_cases = [
20
+ 'ieee14/ieee14_full.xlsx',
21
+ 'ieee39/ieee39_full.xlsx',
22
+ 'npcc/npcc.xlsx',
23
+ ]
24
+ am_cases = [
25
+ 'ieee14/ieee14.json',
26
+ 'ieee39/ieee39.xlsx',
27
+ 'npcc/npcc_uced.xlsx',
28
+ ]
29
+
30
+ def setUp(self) -> None:
31
+ """
32
+ Test setup.
33
+ """
34
+
35
+ def test_basic_functions(self):
36
+ """
37
+ Test basic functions defined in ANDES Interop.
38
+ """
39
+ for ad_case in self.ad_cases:
40
+ sa = andes.load(andes.get_case(ad_case),
41
+ setup=True, no_output=True, default_config=True,)
42
+ # --- test build_group_table ---
43
+ ssa_stg = build_group_table(adsys=sa,
44
+ grp_name='StaticGen',
45
+ param_name=['u', 'name', 'idx', 'bus'],
46
+ mdl_name=[])
47
+ self.assertEqual(ssa_stg.shape,
48
+ (sa.StaticGen.n, 4))
49
+
50
+ ssa_gov = build_group_table(adsys=sa, grp_name='TurbineGov',
51
+ param_name=['idx', 'syn'],
52
+ mdl_name=[])
53
+ self.assertEqual(ssa_gov.shape,
54
+ (sa.TurbineGov.n, 2))
55
+ # --- test make_link_table ---
56
+ link_table = make_link_table(adsys=sa)
57
+ stg_idx = [str(i) for i in sa.PV.idx.v + sa.Slack.idx.v]
58
+ self.assertSetEqual(set(stg_idx),
59
+ set(link_table['stg_idx'].values))
60
+ bus_idx = [str(i) for i in sa.PV.bus.v + sa.Slack.bus.v]
61
+ self.assertSetEqual(set(bus_idx),
62
+ set(link_table['bus_idx'].values))
63
+
64
+ def test_convert(self):
65
+ """
66
+ Test conversion from AMS case to ANDES case.
67
+ """
68
+ for ad_case, am_case in zip(self.ad_cases, self.am_cases):
69
+ sp = ams.load(ams.get_case(am_case),
70
+ setup=True, no_output=True, default_config=True,)
71
+ # before addfile
72
+ sa = to_andes(sp, setup=False)
73
+ self.assertEqual(set(sp.PV.idx.v), set(sa.PV.idx.v))
74
+ self.assertEqual(set(sp.Bus.idx.v), set(sa.Bus.idx.v))
75
+ self.assertEqual(set(sp.Line.idx.v), set(sa.Line.idx.v))
76
+ self.assertEqual(np.sum(sp.PQ.p0.v), np.sum(sa.PQ.p0.v))
77
+
78
+ # after addfile
79
+ sa = parse_addfile(adsys=sa, amsys=sp,
80
+ addfile=andes.get_case(ad_case))
81
+ sa.setup()
82
+ set1 = set(sa.GENROU.gen.v)
83
+ set2 = set(sp.StaticGen.get_all_idxes())
84
+ # set2 includes set1, ensure GENROU.gen are all in StaticGen.idx
85
+ self.assertEqual(set1, set1 & set2)
86
+
87
+ # ensure PFlow models consistency
88
+ pflow_mdls = list(sa.PFlow.models.keys())
89
+ for mdl in pflow_mdls:
90
+ self.assertTrue(sp.models[mdl].as_df().equals(sa.PFlow.models[mdl].as_df()))
91
+
92
+ def test_convert_after_update(self):
93
+ """
94
+ Test conversion from AMS case to ANDES case after updating parameters.
95
+ """
96
+ for am_case in self.am_cases:
97
+ sp = ams.load(ams.get_case(am_case),
98
+ setup=True, no_output=True, default_config=True,)
99
+ # record initial values
100
+ pq_idx = sp.PQ.idx.v
101
+ p0 = sp.PQ.p0.v.copy()
102
+ sa = to_andes(sp, setup=False, no_output=True, default_config=True)
103
+ # before update
104
+ np.testing.assert_array_equal(sp.PQ.p0.v, sa.PQ.p0.v)
105
+ # after update
106
+ sp.PQ.alter(src='p0', idx=pq_idx, value=0.9*p0)
107
+ sa = to_andes(sp, setup=False, no_output=True, default_config=True)
108
+ np.testing.assert_array_equal(sp.PQ.p0.v, sa.PQ.p0.v)
109
+
110
+ def test_extra_dyn(self):
111
+ """
112
+ Test conversion when extra dynamic models exist.
113
+ """
114
+ sp = ams.load(ams.get_case('ieee14/ieee14_uced.xlsx'),
115
+ setup=True, no_output=True, default_config=True,)
116
+ sa = to_andes(sp, addfile=andes.get_case('ieee14/ieee14_full.xlsx'),
117
+ setup=True, no_output=True, default_config=True,
118
+ verify=False, tol=1e-3)
119
+ self.assertGreaterEqual(sa.PVD1.n, 0)
120
+
121
+ def test_verify_pf(self):
122
+ """
123
+ Test verification of power flow results.
124
+ """
125
+ sp = ams.load(ams.get_case('matpower/case300.m'),
126
+ setup=True, no_output=True, default_config=True,)
127
+ sa = to_andes(sp,
128
+ setup=True, no_output=True, default_config=True,
129
+ verify=False, tol=1e-3)
130
+ self.assertTrue(verify_pf(amsys=sp, adsys=sa, tol=1e-7))
131
+
132
+ def test_to_andes_toggle(self):
133
+ """
134
+ Test conversion when there is Toggle in ANDES case.
135
+ """
136
+ sp = ams.load(ams.get_case('5bus/pjm5bus_demo.xlsx'),
137
+ setup=True,
138
+ no_output=True,
139
+ default_config=True)
140
+ sa = sp.to_andes(setup=True,
141
+ addfile=andes.get_case('5bus/pjm5bus.xlsx'),
142
+ verify=False)
143
+ self.assertGreater(sa.Toggle.n, 0)
144
+
145
+ def test_to_andes_alter(self):
146
+ """
147
+ Test conversion when there is Alter in ANDES case.
148
+ """
149
+ sp = ams.load(ams.get_case('ieee14/ieee14_uced.xlsx'),
150
+ setup=True,
151
+ no_output=True,
152
+ default_config=True)
153
+ sa = sp.to_andes(setup=True,
154
+ addfile=andes.get_case('ieee14/ieee14_alter.xlsx'),
155
+ verify=False)
156
+ self.assertGreater(sa.Alter.n, 0)
157
+
158
+ def test_to_andes_fault(self):
159
+ """
160
+ Test conversion when there is Toggle in ANDES case.
161
+ """
162
+ sp = ams.load(ams.get_case('ieee14/ieee14_uced.xlsx'),
163
+ setup=True,
164
+ no_output=True,
165
+ default_config=True)
166
+ sa = sp.to_andes(setup=True,
167
+ addfile=andes.get_case('ieee14/ieee14_fault.xlsx'),
168
+ verify=False)
169
+ self.assertGreater(sa.Fault.n, 0)
170
+
171
+
172
+ class TestDataExchange(unittest.TestCase):
173
+ """
174
+ Tests for data exchange between AMS and ANDES.
175
+ """
176
+
177
+ def setUp(self) -> None:
178
+ """
179
+ Test setup. This is executed before each test case.
180
+ """
181
+ self.sp = ams.load(ams.get_case('ieee14/ieee14_uced.xlsx'),
182
+ setup=True,
183
+ no_output=True,
184
+ default_config=True,)
185
+ self.sp.RTED.run(solver='CLARABEL')
186
+ self.sp.RTED.dc2ac()
187
+ self.stg_idx = self.sp.RTED.pg.get_all_idxes()
188
+
189
+ def test_data_exchange(self):
190
+ """
191
+ Test data exchange between AMS and ANDES.
192
+ """
193
+ sa = to_andes(self.sp, setup=True,
194
+ addfile=andes.get_case('ieee14/ieee14_full.xlsx'),
195
+ no_output=True,
196
+ default_config=True,)
197
+ # alleviate limiter
198
+ sa.TGOV1.set(src='VMAX', idx=sa.TGOV1.idx.v, attr='v', value=100*np.ones(sa.TGOV1.n))
199
+ sa.TGOV1.set(src='VMIN', idx=sa.TGOV1.idx.v, attr='v', value=np.zeros(sa.TGOV1.n))
200
+
201
+ # --- test before PFlow ---
202
+ self.sp.dyn.send(adsys=sa, routine='RTED')
203
+ p0 = sa.StaticGen.get(src='p0', attr='v', idx=self.stg_idx)
204
+ pg = self.sp.RTED.get(src='pg', attr='v', idx=self.stg_idx)
205
+ np.testing.assert_array_equal(p0, pg)
206
+
207
+ # --- test after TDS ---
208
+ self.assertFalse(self.sp.dyn.is_tds)
209
+ sa.PFlow.run()
210
+ sa.TDS.init()
211
+ self.assertTrue(self.sp.dyn.is_tds)
212
+
213
+ sa.TDS.config.tf = 1
214
+ sa.TDS.run()
215
+ self.sp.dyn.send(adsys=sa, routine='RTED')
216
+ self.sp.dyn.receive(adsys=sa, routine='RTED', no_update=False)
tests/test_io.py ADDED
@@ -0,0 +1,32 @@
1
+ """
2
+ Test file input/output.
3
+ """
4
+
5
+ import unittest
6
+ import numpy as np
7
+
8
+ import ams
9
+
10
+
11
+ class TestMATPOWER(unittest.TestCase):
12
+
13
+ def setUp(self):
14
+ self.mpc5 = ams.io.matpower.m2mpc(ams.get_case('matpower/case5.m'))
15
+ self.mpc14 = ams.io.matpower.m2mpc(ams.get_case('matpower/case14.m'))
16
+
17
+ def test_m2mpc(self):
18
+ self.assertTupleEqual(self.mpc5['gencost'].shape, (5, 6))
19
+ self.assertTupleEqual(self.mpc14['gencost'].shape, (5, 7))
20
+
21
+ def test_mpc2system(self):
22
+ system5 = ams.system.System()
23
+ ams.io.matpower.mpc2system(self.mpc5, system5)
24
+ # In case5.m, the gencost has type 2 cost model, with 2 parameters.
25
+ np.testing.assert_array_equal(system5.GCost.c2.v,
26
+ np.zeros(system5.StaticGen.n))
27
+
28
+ system14 = ams.system.System()
29
+ ams.io.matpower.mpc2system(self.mpc14, system14)
30
+ # In case14.m, the gencost has type 2 cost model, with 3 parameters.
31
+ np.testing.assert_array_less(np.zeros(system14.StaticGen.n),
32
+ system14.GCost.c2.v,)
tests/test_jumper.py ADDED
@@ -0,0 +1,27 @@
1
+ """
2
+ Test `Jumper` class.
3
+ """
4
+
5
+ import unittest
6
+
7
+ import ams
8
+
9
+
10
+ class TessJumper(unittest.TestCase):
11
+ """
12
+ Test `Jumper` class.
13
+ """
14
+
15
+ def setUp(self) -> None:
16
+ """
17
+ Test setup.
18
+ """
19
+ self.sp = ams.load(ams.get_case("5bus/pjm5bus_jumper.xlsx"),
20
+ setup=True, no_output=True, default_config=True)
21
+
22
+ def test_to_andes(self):
23
+ """
24
+ Test `to_andes` method when `Jumper` exists.
25
+ """
26
+ sa = self.sp.to_andes()
27
+ self.assertEqual(sa.Jumper.n, 1)