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.
- ams/__init__.py +4 -11
- ams/_version.py +3 -3
- ams/cases/5bus/pjm5bus_demo.xlsx +0 -0
- ams/cases/5bus/pjm5bus_jumper.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced.json +1062 -0
- ams/cases/5bus/pjm5bus_uced.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced_esd1.xlsx +0 -0
- ams/cases/5bus/pjm5bus_uced_ev.xlsx +0 -0
- ams/cases/ieee123/ieee123.xlsx +0 -0
- ams/cases/ieee123/ieee123_regcv1.xlsx +0 -0
- ams/cases/ieee14/ieee14.json +1166 -0
- ams/cases/ieee14/ieee14.raw +92 -0
- ams/cases/ieee14/ieee14_conn.xlsx +0 -0
- ams/cases/ieee14/ieee14_uced.xlsx +0 -0
- ams/cases/ieee39/ieee39.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_esd1.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_pvd1.xlsx +0 -0
- ams/cases/ieee39/ieee39_uced_vis.xlsx +0 -0
- ams/cases/matpower/benchmark.json +1594 -0
- ams/cases/matpower/case118.m +787 -0
- ams/cases/matpower/case14.m +129 -0
- ams/cases/matpower/case300.m +1315 -0
- ams/cases/matpower/case39.m +205 -0
- ams/cases/matpower/case5.m +62 -0
- ams/cases/matpower/case_ACTIVSg2000.m +9460 -0
- ams/cases/npcc/npcc.m +644 -0
- ams/cases/npcc/npcc_uced.xlsx +0 -0
- ams/cases/pglib/pglib_opf_case39_epri__api.m +243 -0
- ams/cases/wecc/wecc.m +714 -0
- ams/cases/wecc/wecc_uced.xlsx +0 -0
- ams/cli.py +6 -0
- ams/core/__init__.py +2 -0
- ams/core/documenter.py +652 -0
- ams/core/matprocessor.py +782 -0
- ams/core/model.py +330 -0
- ams/core/param.py +322 -0
- ams/core/service.py +918 -0
- ams/core/symprocessor.py +224 -0
- ams/core/var.py +59 -0
- ams/extension/__init__.py +5 -0
- ams/extension/eva.py +401 -0
- ams/interface.py +1085 -0
- ams/io/__init__.py +133 -0
- ams/io/json.py +82 -0
- ams/io/matpower.py +406 -0
- ams/io/psse.py +6 -0
- ams/io/pypower.py +103 -0
- ams/io/xlsx.py +80 -0
- ams/main.py +81 -4
- ams/models/__init__.py +24 -0
- ams/models/area.py +40 -0
- ams/models/bus.py +52 -0
- ams/models/cost.py +169 -0
- ams/models/distributed/__init__.py +3 -0
- ams/models/distributed/esd1.py +71 -0
- ams/models/distributed/ev.py +60 -0
- ams/models/distributed/pvd1.py +67 -0
- ams/models/group.py +231 -0
- ams/models/info.py +26 -0
- ams/models/line.py +238 -0
- ams/models/renewable/__init__.py +5 -0
- ams/models/renewable/regc.py +119 -0
- ams/models/reserve.py +94 -0
- ams/models/shunt.py +14 -0
- ams/models/static/__init__.py +2 -0
- ams/models/static/gen.py +165 -0
- ams/models/static/pq.py +61 -0
- ams/models/timeslot.py +69 -0
- ams/models/zone.py +49 -0
- ams/opt/__init__.py +12 -0
- ams/opt/constraint.py +175 -0
- ams/opt/exprcalc.py +127 -0
- ams/opt/expression.py +188 -0
- ams/opt/objective.py +174 -0
- ams/opt/omodel.py +432 -0
- ams/opt/optzbase.py +192 -0
- ams/opt/param.py +156 -0
- ams/opt/var.py +233 -0
- ams/pypower/__init__.py +8 -0
- ams/pypower/_compat.py +9 -0
- ams/pypower/core/__init__.py +8 -0
- ams/pypower/core/pips.py +894 -0
- ams/pypower/core/ppoption.py +244 -0
- ams/pypower/core/ppver.py +18 -0
- ams/pypower/core/solver.py +2451 -0
- ams/pypower/eps.py +6 -0
- ams/pypower/idx.py +174 -0
- ams/pypower/io.py +604 -0
- ams/pypower/make/__init__.py +11 -0
- ams/pypower/make/matrices.py +665 -0
- ams/pypower/make/pdv.py +506 -0
- ams/pypower/routines/__init__.py +7 -0
- ams/pypower/routines/cpf.py +513 -0
- ams/pypower/routines/cpf_callbacks.py +114 -0
- ams/pypower/routines/opf.py +1803 -0
- ams/pypower/routines/opffcns.py +1946 -0
- ams/pypower/routines/pflow.py +852 -0
- ams/pypower/toggle.py +1098 -0
- ams/pypower/utils.py +293 -0
- ams/report.py +212 -50
- ams/routines/__init__.py +23 -0
- ams/routines/acopf.py +117 -0
- ams/routines/cpf.py +65 -0
- ams/routines/dcopf.py +241 -0
- ams/routines/dcpf.py +209 -0
- ams/routines/dcpf0.py +196 -0
- ams/routines/dopf.py +150 -0
- ams/routines/ed.py +312 -0
- ams/routines/pflow.py +255 -0
- ams/routines/pflow0.py +113 -0
- ams/routines/routine.py +1033 -0
- ams/routines/rted.py +519 -0
- ams/routines/type.py +160 -0
- ams/routines/uc.py +376 -0
- ams/shared.py +63 -9
- ams/system.py +61 -22
- ams/utils/__init__.py +3 -0
- ams/utils/misc.py +77 -0
- ams/utils/paths.py +257 -0
- docs/Makefile +21 -0
- docs/make.bat +35 -0
- docs/source/_templates/autosummary/base.rst +5 -0
- docs/source/_templates/autosummary/class.rst +35 -0
- docs/source/_templates/autosummary/module.rst +65 -0
- docs/source/_templates/autosummary/module_toctree.rst +66 -0
- docs/source/api.rst +102 -0
- docs/source/conf.py +206 -0
- docs/source/examples/index.rst +34 -0
- docs/source/genmodelref.py +61 -0
- docs/source/genroutineref.py +47 -0
- docs/source/getting_started/copyright.rst +20 -0
- docs/source/getting_started/formats/index.rst +20 -0
- docs/source/getting_started/formats/matpower.rst +183 -0
- docs/source/getting_started/formats/psse.rst +46 -0
- docs/source/getting_started/formats/pypower.rst +223 -0
- docs/source/getting_started/formats/xlsx.png +0 -0
- docs/source/getting_started/formats/xlsx.rst +23 -0
- docs/source/getting_started/index.rst +76 -0
- docs/source/getting_started/install.rst +231 -0
- docs/source/getting_started/overview.rst +26 -0
- docs/source/getting_started/testcase.rst +45 -0
- docs/source/getting_started/verification.rst +13 -0
- docs/source/images/curent.ico +0 -0
- docs/source/images/dcopf_time.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_NameOnTrans.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_Transparent.png +0 -0
- docs/source/images/sponsors/CURENT_Logo_Transparent_Name.png +0 -0
- docs/source/images/sponsors/doe.png +0 -0
- docs/source/index.rst +108 -0
- docs/source/modeling/example.rst +159 -0
- docs/source/modeling/index.rst +17 -0
- docs/source/modeling/model.rst +210 -0
- docs/source/modeling/routine.rst +122 -0
- docs/source/modeling/system.rst +51 -0
- docs/source/release-notes.rst +398 -0
- ltbams-1.0.2.dist-info/METADATA +215 -0
- ltbams-1.0.2.dist-info/RECORD +188 -0
- {ltbams-0.9.9.dist-info → ltbams-1.0.2.dist-info}/WHEEL +1 -1
- ltbams-1.0.2.dist-info/top_level.txt +3 -0
- tests/__init__.py +0 -0
- tests/test_1st_system.py +33 -0
- tests/test_addressing.py +40 -0
- tests/test_andes_mats.py +61 -0
- tests/test_case.py +266 -0
- tests/test_cli.py +34 -0
- tests/test_export_csv.py +89 -0
- tests/test_group.py +83 -0
- tests/test_interface.py +216 -0
- tests/test_io.py +32 -0
- tests/test_jumper.py +27 -0
- tests/test_known_good.py +267 -0
- tests/test_matp.py +437 -0
- tests/test_model.py +54 -0
- tests/test_omodel.py +119 -0
- tests/test_paths.py +22 -0
- tests/test_report.py +251 -0
- tests/test_repr.py +21 -0
- tests/test_routine.py +178 -0
- tests/test_rtn_dcopf.py +101 -0
- tests/test_rtn_dcpf.py +77 -0
- tests/test_rtn_ed.py +279 -0
- tests/test_rtn_pflow.py +219 -0
- tests/test_rtn_rted.py +273 -0
- tests/test_rtn_uc.py +248 -0
- tests/test_service.py +73 -0
- ltbams-0.9.9.dist-info/LICENSE +0 -692
- ltbams-0.9.9.dist-info/METADATA +0 -859
- ltbams-0.9.9.dist-info/RECORD +0 -14
- ltbams-0.9.9.dist-info/top_level.txt +0 -1
- {ltbams-0.9.9.dist-info → ltbams-1.0.2.dist-info}/entry_points.txt +0 -0
tests/test_export_csv.py
ADDED
@@ -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__())
|
tests/test_interface.py
ADDED
@@ -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)
|