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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. ams/_version.py +3 -3
  2. ams/core/matprocessor.py +183 -118
  3. ams/io/matpower.py +55 -20
  4. ams/io/psse.py +4 -0
  5. ams/opt/exprcalc.py +11 -0
  6. ams/routines/grbopt.py +2 -0
  7. ams/routines/pypower.py +21 -4
  8. ams/routines/routine.py +127 -15
  9. ams/shared.py +30 -2
  10. ams/system.py +51 -3
  11. ams/utils/paths.py +64 -0
  12. docs/source/index.rst +4 -3
  13. docs/source/release-notes.rst +25 -10
  14. {ltbams-1.0.11.dist-info → ltbams-1.0.13.dist-info}/METADATA +4 -2
  15. {ltbams-1.0.11.dist-info → ltbams-1.0.13.dist-info}/RECORD +18 -46
  16. {ltbams-1.0.11.dist-info → ltbams-1.0.13.dist-info}/WHEEL +1 -1
  17. {ltbams-1.0.11.dist-info → ltbams-1.0.13.dist-info}/top_level.txt +0 -1
  18. tests/__init__.py +0 -0
  19. tests/test_1st_system.py +0 -64
  20. tests/test_addressing.py +0 -40
  21. tests/test_case.py +0 -301
  22. tests/test_cli.py +0 -34
  23. tests/test_export_csv.py +0 -89
  24. tests/test_group.py +0 -83
  25. tests/test_interface.py +0 -238
  26. tests/test_io.py +0 -180
  27. tests/test_jumper.py +0 -27
  28. tests/test_known_good.py +0 -267
  29. tests/test_matp.py +0 -437
  30. tests/test_model.py +0 -54
  31. tests/test_omodel.py +0 -119
  32. tests/test_paths.py +0 -22
  33. tests/test_report.py +0 -251
  34. tests/test_repr.py +0 -21
  35. tests/test_routine.py +0 -178
  36. tests/test_rtn_acopf.py +0 -75
  37. tests/test_rtn_dcopf.py +0 -101
  38. tests/test_rtn_dcopf2.py +0 -103
  39. tests/test_rtn_ed.py +0 -279
  40. tests/test_rtn_opf.py +0 -142
  41. tests/test_rtn_pflow.py +0 -147
  42. tests/test_rtn_pypower.py +0 -315
  43. tests/test_rtn_rted.py +0 -273
  44. tests/test_rtn_uc.py +0 -248
  45. tests/test_service.py +0 -73
  46. {ltbams-1.0.11.dist-info → ltbams-1.0.13.dist-info}/entry_points.txt +0 -0
tests/test_group.py DELETED
@@ -1,83 +0,0 @@
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__())
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,180 +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_gencost1(self):
89
- """Test when gencost is type 1."""
90
- mpcgc1 = self.mpc14.copy()
91
- mpcgc1['gencost'] = np.repeat(np.array([[1, 0, 0, 3, 0.01, 40, 0]]), 5, axis=0)
92
-
93
- system = ams.system.System()
94
- ams.io.matpower.mpc2system(mpcgc1, system)
95
- self.assertEqual(system.GCost.n, 5)
96
-
97
- def test_mpc2m(self):
98
- """Test conversion from MPC to M file."""
99
- mpc5 = ams.io.matpower.m2mpc(ams.get_case('matpower/case5.m'))
100
- mpc14 = ams.io.matpower.m2mpc(ams.get_case('matpower/case14.m'))
101
-
102
- # Test conversion to M file
103
- mfile5 = ams.io.matpower.mpc2m(mpc5, './case5out.m')
104
- mfile14 = ams.io.matpower.mpc2m(mpc14, './case14out.m')
105
-
106
- # Check if the files exist
107
- self.assertTrue(os.path.exists(mfile5))
108
- self.assertTrue(os.path.exists(mfile14))
109
-
110
- mpc5read = ams.io.matpower.m2mpc(mfile5)
111
- mpc14read = ams.io.matpower.m2mpc(mfile14)
112
-
113
- # Check if the numerical values are the same
114
- for key in mpc5:
115
- if key in ['bus_name', 'gentype', 'genfuel']:
116
- continue
117
- np.testing.assert_array_almost_equal(
118
- mpc5[key], mpc5read[key], decimal=5,
119
- err_msg=f"Mismatch in {key} when converting case5.m"
120
- )
121
- for key in mpc14:
122
- if key in ['bus_name', 'gentype', 'genfuel']:
123
- continue
124
- np.testing.assert_array_almost_equal(
125
- mpc14[key], mpc14read[key], decimal=5,
126
- err_msg=f"Mismatch in {key} when converting case14.m"
127
- )
128
-
129
- # Clean up the generated files
130
- os.remove(mfile5)
131
- os.remove(mfile14)
132
-
133
- def test_system2m(self):
134
- """Test conversion from AMS System to M file."""
135
- s5 = ams.load(ams.get_case('matpower/case5.m'),
136
- no_output=True)
137
- mfile5 = './case5out.m'
138
- ams.io.matpower.write(s5, mfile5)
139
-
140
- # Clean up the generated file
141
- os.remove(mfile5)
142
-
143
-
144
- class TestSystemExport(unittest.TestCase):
145
- """
146
- Test system export functions.
147
- """
148
-
149
- def test_system_to(self):
150
- """Test conversion from AMS System to several formats."""
151
- ss = ams.load(ams.get_case('matpower/case5.m'),
152
- no_output=True)
153
-
154
- mpc = ss.to_mpc()
155
-
156
- self.assertIsInstance(mpc, dict)
157
- self.assertIn('bus', mpc)
158
- self.assertIn('branch', mpc)
159
- self.assertIn('gen', mpc)
160
- self.assertIn('gencost', mpc)
161
- self.assertIn('baseMVA', mpc)
162
- self.assertIn('bus_name', mpc)
163
- self.assertIn('gentype', mpc)
164
- self.assertIn('genfuel', mpc)
165
-
166
- ss.to_m("./case5_out.m")
167
- self.assertTrue(os.path.exists("./case5_out.m"))
168
- os.remove("./case5_out.m")
169
-
170
- ss.to_json("./case5_out.json")
171
- self.assertTrue(os.path.exists("./case5_out.json"))
172
- os.remove("./case5_out.json")
173
-
174
- ss.to_xlsx("./case5_out.xlsx")
175
- self.assertTrue(os.path.exists("./case5_out.xlsx"))
176
- os.remove("./case5_out.xlsx")
177
-
178
- ss.to_raw("./case5_out.raw", overwrite=True)
179
- self.assertTrue(os.path.exists("./case5_out.raw"))
180
- 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)