ltbams 1.0.12__py3-none-any.whl → 1.0.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. ams/_version.py +3 -3
  2. ams/core/matprocessor.py +170 -104
  3. ams/io/matpower.py +4 -0
  4. ams/io/psse.py +2 -0
  5. ams/opt/exprcalc.py +11 -0
  6. ams/routines/grbopt.py +2 -0
  7. ams/routines/pypower.py +8 -0
  8. ams/routines/routine.py +118 -1
  9. ams/shared.py +30 -2
  10. ams/system.py +10 -0
  11. docs/source/index.rst +4 -3
  12. docs/source/release-notes.rst +8 -0
  13. {ltbams-1.0.12.dist-info → ltbams-1.0.13.dist-info}/METADATA +4 -2
  14. {ltbams-1.0.12.dist-info → ltbams-1.0.13.dist-info}/RECORD +17 -45
  15. {ltbams-1.0.12.dist-info → ltbams-1.0.13.dist-info}/top_level.txt +0 -1
  16. tests/__init__.py +0 -0
  17. tests/test_1st_system.py +0 -64
  18. tests/test_addressing.py +0 -40
  19. tests/test_case.py +0 -301
  20. tests/test_cli.py +0 -34
  21. tests/test_export_csv.py +0 -89
  22. tests/test_group.py +0 -83
  23. tests/test_interface.py +0 -238
  24. tests/test_io.py +0 -190
  25. tests/test_jumper.py +0 -27
  26. tests/test_known_good.py +0 -267
  27. tests/test_matp.py +0 -437
  28. tests/test_model.py +0 -54
  29. tests/test_omodel.py +0 -119
  30. tests/test_paths.py +0 -89
  31. tests/test_report.py +0 -251
  32. tests/test_repr.py +0 -21
  33. tests/test_routine.py +0 -178
  34. tests/test_rtn_acopf.py +0 -75
  35. tests/test_rtn_dcopf.py +0 -121
  36. tests/test_rtn_dcopf2.py +0 -103
  37. tests/test_rtn_ed.py +0 -279
  38. tests/test_rtn_opf.py +0 -142
  39. tests/test_rtn_pflow.py +0 -147
  40. tests/test_rtn_pypower.py +0 -315
  41. tests/test_rtn_rted.py +0 -273
  42. tests/test_rtn_uc.py +0 -248
  43. tests/test_service.py +0 -73
  44. {ltbams-1.0.12.dist-info → ltbams-1.0.13.dist-info}/WHEEL +0 -0
  45. {ltbams-1.0.12.dist-info → ltbams-1.0.13.dist-info}/entry_points.txt +0 -0
tests/test_interface.py DELETED
@@ -1,238 +0,0 @@
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_exac1.json',
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.json'),
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)
217
-
218
-
219
- class TestLoadANDESFile(unittest.TestCase):
220
- """
221
- Test loading ANDES file.
222
- """
223
-
224
- def test_no_dyn_models(self):
225
- """
226
- Test loading ANDES file without dynamic models.
227
- """
228
- sp1 = ams.load(andes.get_case('ieee14/ieee14_exac1.json'),
229
- setup=True,
230
- no_output=True,
231
- default_config=True,)
232
- self.assertNotIn('EXAC1', sp1.models.keys())
233
-
234
- sp2 = ams.load(andes.get_case('ieee14/ieee14_pvd1.json'),
235
- setup=True,
236
- no_output=True,
237
- default_config=True,)
238
- self.assertIn('PVD1', sp2.models.keys())
tests/test_io.py DELETED
@@ -1,190 +0,0 @@
1
- """
2
- Test file input/output.
3
- """
4
- import os
5
- import unittest
6
- import numpy as np
7
-
8
- import ams
9
-
10
-
11
- class TestMATPOWER(unittest.TestCase):
12
- """
13
- Test IO functions for MATPOWER and PYPOWER.
14
- """
15
-
16
- def setUp(self):
17
- self.mpc5 = ams.io.matpower.m2mpc(ams.get_case('matpower/case5.m'))
18
- self.mpc14 = ams.io.matpower.m2mpc(ams.get_case('matpower/case14.m'))
19
-
20
- def test_m2mpc(self):
21
- """Test conversion from M file to mpc dict."""
22
- # NOTE: when the keys are there, read them
23
- self.assertTupleEqual(self.mpc5['gencost'].shape, (5, 6))
24
- self.assertTupleEqual(self.mpc5['gentype'].shape, (5,))
25
- self.assertTupleEqual(self.mpc5['genfuel'].shape, (5,))
26
-
27
- # NOTE: when the keys are not there, the read mpc will not complete them
28
- self.assertTupleEqual(self.mpc14['gencost'].shape, (5, 7))
29
- self.assertNotIn('gentype', self.mpc14)
30
- self.assertNotIn('genfuel', self.mpc14)
31
-
32
- def test_mpc2system(self):
33
- """Test conversion from MPC to AMS System."""
34
- system5 = ams.system.System()
35
- ams.io.matpower.mpc2system(self.mpc5, system5)
36
- # In case5.m, the gencost has type 2 cost model, with 2 parameters.
37
- np.testing.assert_array_equal(system5.GCost.c2.v,
38
- np.zeros(system5.StaticGen.n))
39
-
40
- # Check if Area is added
41
- self.assertGreater(system5.Area.n, 0)
42
-
43
- # Check if Zone is added
44
- self.assertGreater(system5.Zone.n, 0)
45
-
46
- system14 = ams.system.System()
47
- # Test gentype length check
48
- mpc14 = self.mpc14.copy()
49
- mpc14['gentype'] = np.array(['WT'] * 6)
50
- with self.assertRaises(ValueError, msg='gentype length check failed!'):
51
- ams.io.matpower.mpc2system(mpc14, system14)
52
-
53
- system14 = ams.system.System()
54
- ams.io.matpower.mpc2system(self.mpc14, system14)
55
- # In case14.m, the gencost has type 2 cost model, with 3 parameters.
56
- np.testing.assert_array_less(np.zeros(system14.StaticGen.n),
57
- system14.GCost.c2.v,)
58
-
59
- def test_system2mpc(self):
60
- """Test conversion from AMS System to MPC."""
61
- system5 = ams.system.System()
62
- ams.io.matpower.mpc2system(self.mpc5, system5)
63
- mpc5 = ams.io.matpower.system2mpc(system5)
64
-
65
- self.assertEqual(mpc5['baseMVA'], self.mpc5['baseMVA'])
66
-
67
- # Bus
68
- # type, PD, QD, GS,BS, VM, VA. BASE_KV, VMAX, VMIN
69
- bus_cols = [1, 2, 3, 4, 5, 7, 8, 9, 11, 12]
70
- np.testing.assert_array_equal(mpc5['bus'][:, bus_cols],
71
- self.mpc5['bus'][:, bus_cols])
72
-
73
- # Branch, Gen, Gencost, can have minor differences but is okay
74
-
75
- # String type data
76
- np.testing.assert_array_equal(mpc5['gentype'], self.mpc5['gentype'])
77
- np.testing.assert_array_equal(mpc5['genfuel'], self.mpc5['genfuel'])
78
- np.testing.assert_array_equal(mpc5['bus_name'], self.mpc5['bus_name'])
79
-
80
- # Area quantity
81
- self.assertEqual(np.unique(mpc5['bus'][:, 6]).shape[0],
82
- np.unique(self.mpc5['bus'][:, 6]).shape[0])
83
-
84
- # Zone quantity
85
- self.assertEqual(np.unique(mpc5['bus'][:, 10]).shape[0],
86
- np.unique(self.mpc5['bus'][:, 10]).shape[0])
87
-
88
- def test_system2mpc_pq(self):
89
- """Test AMS System to MPC conversion with multiple PQ at one bus."""
90
- system = ams.load(ams.get_case('5bus/pjm5bus_demo.json'),
91
- setup=False, no_output=True)
92
- system.add('PQ', dict(bus=1, p0=0.2))
93
-
94
- mpc = system.to_mpc()
95
- np.testing.assert_almost_equal(mpc['bus'][:, 2].sum(),
96
- system.config.mva * system.PQ.p0.v.sum())
97
-
98
- def test_gencost1(self):
99
- """Test when gencost is type 1."""
100
- mpcgc1 = self.mpc14.copy()
101
- mpcgc1['gencost'] = np.repeat(np.array([[1, 0, 0, 3, 0.01, 40, 0]]), 5, axis=0)
102
-
103
- system = ams.system.System()
104
- ams.io.matpower.mpc2system(mpcgc1, system)
105
- self.assertEqual(system.GCost.n, 5)
106
-
107
- def test_mpc2m(self):
108
- """Test conversion from MPC to M file."""
109
- mpc5 = ams.io.matpower.m2mpc(ams.get_case('matpower/case5.m'))
110
- mpc14 = ams.io.matpower.m2mpc(ams.get_case('matpower/case14.m'))
111
-
112
- # Test conversion to M file
113
- mfile5 = ams.io.matpower.mpc2m(mpc5, './case5out.m')
114
- mfile14 = ams.io.matpower.mpc2m(mpc14, './case14out.m')
115
-
116
- # Check if the files exist
117
- self.assertTrue(os.path.exists(mfile5))
118
- self.assertTrue(os.path.exists(mfile14))
119
-
120
- mpc5read = ams.io.matpower.m2mpc(mfile5)
121
- mpc14read = ams.io.matpower.m2mpc(mfile14)
122
-
123
- # Check if the numerical values are the same
124
- for key in mpc5:
125
- if key in ['bus_name', 'gentype', 'genfuel']:
126
- continue
127
- np.testing.assert_array_almost_equal(
128
- mpc5[key], mpc5read[key], decimal=5,
129
- err_msg=f"Mismatch in {key} when converting case5.m"
130
- )
131
- for key in mpc14:
132
- if key in ['bus_name', 'gentype', 'genfuel']:
133
- continue
134
- np.testing.assert_array_almost_equal(
135
- mpc14[key], mpc14read[key], decimal=5,
136
- err_msg=f"Mismatch in {key} when converting case14.m"
137
- )
138
-
139
- # Clean up the generated files
140
- os.remove(mfile5)
141
- os.remove(mfile14)
142
-
143
- def test_system2m(self):
144
- """Test conversion from AMS System to M file."""
145
- s5 = ams.load(ams.get_case('matpower/case5.m'),
146
- no_output=True)
147
- mfile5 = './case5out.m'
148
- ams.io.matpower.write(s5, mfile5)
149
-
150
- # Clean up the generated file
151
- os.remove(mfile5)
152
-
153
-
154
- class TestSystemExport(unittest.TestCase):
155
- """
156
- Test system export functions.
157
- """
158
-
159
- def test_system_to(self):
160
- """Test conversion from AMS System to several formats."""
161
- ss = ams.load(ams.get_case('matpower/case5.m'),
162
- no_output=True)
163
-
164
- mpc = ss.to_mpc()
165
-
166
- self.assertIsInstance(mpc, dict)
167
- self.assertIn('bus', mpc)
168
- self.assertIn('branch', mpc)
169
- self.assertIn('gen', mpc)
170
- self.assertIn('gencost', mpc)
171
- self.assertIn('baseMVA', mpc)
172
- self.assertIn('bus_name', mpc)
173
- self.assertIn('gentype', mpc)
174
- self.assertIn('genfuel', mpc)
175
-
176
- ss.to_m("./case5_out.m")
177
- self.assertTrue(os.path.exists("./case5_out.m"))
178
- os.remove("./case5_out.m")
179
-
180
- ss.to_json("./case5_out.json")
181
- self.assertTrue(os.path.exists("./case5_out.json"))
182
- os.remove("./case5_out.json")
183
-
184
- ss.to_xlsx("./case5_out.xlsx")
185
- self.assertTrue(os.path.exists("./case5_out.xlsx"))
186
- os.remove("./case5_out.xlsx")
187
-
188
- ss.to_raw("./case5_out.raw", overwrite=True)
189
- self.assertTrue(os.path.exists("./case5_out.raw"))
190
- os.remove("./case5_out.raw")
tests/test_jumper.py DELETED
@@ -1,27 +0,0 @@
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)