Skip to content

Commit f85e940

Browse files
committed
Add test for unit commitment
Signed-off-by: Davey Elder <iandavidelder@gmail.com>
1 parent e59cdf0 commit f85e940

4 files changed

Lines changed: 190 additions & 0 deletions

File tree

tests/conftest.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ def refresh_databases() -> None:
103103
('materials.sql', 'materials.sqlite'),
104104
('simple_linked_tech.sql', 'simple_linked_tech.sqlite'),
105105
('storageville.sql', 'storageville.sqlite'),
106+
('test_week.sql', 'test_week.sqlite'),
106107
(None, 'utopia_stochastic.sqlite'),
107108
]
108109

@@ -221,6 +222,11 @@ def system_test_run(
221222
if myopic_overrides is not None:
222223
config.myopic_inputs = {**(config.myopic_inputs or {}), **myopic_overrides}
223224

225+
# Allow parametrized tests to override myopic settings per case.
226+
time_sequencing_override = request.param.get('time_sequencing')
227+
if time_sequencing_override is not None:
228+
config.time_sequencing = time_sequencing_override
229+
224230
sequencer = TemoaSequencer(config=config)
225231

226232
sequencer.start()

tests/test_full_runs.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ class MyopicStressCase(TypedDict):
4949
myopic: MyopicSettings
5050

5151

52+
class TimeSequenceCase(TypedDict):
53+
name: str
54+
filename: str
55+
time_sequencing: str
56+
57+
5258
myopic_stress_tests: list[MyopicStressCase] = [
5359
{
5460
'name': (
@@ -68,6 +74,16 @@ class MyopicStressCase(TypedDict):
6874
]
6975

7076

77+
time_sequence_tests: list[TimeSequenceCase] = [
78+
{
79+
'name': (f'test_week | {time_sequencing}'),
80+
'filename': 'config_test_week.toml',
81+
'time_sequencing': time_sequencing,
82+
}
83+
for time_sequencing in ['consecutive_days', 'representative_periods', 'seasonal_timeslices']
84+
]
85+
86+
7187
@pytest.mark.parametrize(
7288
'system_test_run',
7389
argvalues=legacy_config_files,
@@ -210,6 +226,33 @@ def test_myopic_stress_tests(
210226
)
211227

212228

229+
@pytest.mark.parametrize(
230+
'system_test_run',
231+
argvalues=time_sequence_tests,
232+
indirect=True,
233+
ids=[d['name'] for d in time_sequence_tests],
234+
)
235+
def test_time_sequence_tests(
236+
system_test_run: tuple[str, SolverResults | None, TemoaModel | None, TemoaSequencer],
237+
) -> None:
238+
_, _, _, sequencer = system_test_run
239+
import contextlib
240+
241+
objectives = {
242+
'consecutive_days': 1445.875735,
243+
'representative_periods': 1467.912766,
244+
'seasonal_timeslices': 1467.912766,
245+
}
246+
247+
with contextlib.closing(sqlite3.connect(sequencer.config.output_database)) as con:
248+
cur = con.cursor()
249+
res = cur.execute('SELECT SUM(total_system_cost) FROM main.output_objective').fetchone()
250+
obj = res[0]
251+
assert obj == pytest.approx(objectives[sequencer.config.time_sequencing], abs=0.00001), (
252+
'objective function value did not match expected for time sequencing test'
253+
)
254+
255+
213256
@pytest.mark.parametrize(
214257
'system_test_run',
215258
argvalues=stochastic_files,
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
scenario = "test_week"
2+
scenario_mode = "perfect_foresight"
3+
extensions= ["unit_commitment"]
4+
input_database = "tests/testing_outputs/test_week.sqlite"
5+
output_database = "tests/testing_outputs/test_week.sqlite"
6+
neos = false
7+
solver_name = "appsi_highs"
8+
save_excel = false
9+
save_duals = false
10+
save_lp_file = false
11+
days_per_period = 7
12+
reserve_margin = "static"
13+
14+
[MGA]
15+
slack = 0.1
16+
iterations = 4
17+
weight = "integer"
18+
19+
[myopic]
20+
view_depth = 1
21+
step_size = 1
22+
evolving = false
23+
evolution_script = ''

tests/testing_data/test_week.sql

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
REPLACE INTO metadata VALUES('DB_MAJOR',4,'DB major version number');
2+
REPLACE INTO metadata VALUES('DB_MINOR',0,'DB minor version number');
3+
REPLACE INTO metadata_real VALUES('global_discount_rate',0.05000000000000000277,'Discount Rate for future costs');
4+
REPLACE INTO metadata_real VALUES('default_loan_rate',0.05000000000000000277,'Default Loan Rate if not specified in LoanRate table');
5+
REPLACE INTO sector_label VALUES('energy',NULL);
6+
REPLACE INTO capacity_factor_tech VALUES('region','d01','h01','uc_cheap',0.0,NULL);
7+
REPLACE INTO capacity_factor_tech VALUES('region','d02','h12','uc_cheap',0.0,NULL);
8+
REPLACE INTO capacity_factor_tech VALUES('region','d03','h20','uc_cheap',0.0,NULL);
9+
REPLACE INTO capacity_factor_tech VALUES('region','d04','h24','uc_cheap',0.0,NULL);
10+
REPLACE INTO capacity_factor_tech VALUES('region','d05','h01','uc_cheap',0.0,NULL);
11+
REPLACE INTO capacity_factor_tech VALUES('region','d06','h22','uc_cheap',0.0,NULL);
12+
REPLACE INTO capacity_factor_tech VALUES('region','d05','h16','uc_expensive',0.0,NULL);
13+
REPLACE INTO capacity_factor_tech VALUES('region','d01','h16','uc_expensive',0.0,NULL);
14+
REPLACE INTO capacity_to_activity VALUES('region','uc_cheap',168.0,NULL,NULL);
15+
REPLACE INTO capacity_to_activity VALUES('region','uc_expensive',168.0,NULL,NULL);
16+
REPLACE INTO commodity VALUES('source','s',NULL,NULL);
17+
REPLACE INTO commodity VALUES('demand','d',NULL,NULL);
18+
REPLACE INTO commodity VALUES('co2','e',NULL,NULL);
19+
REPLACE INTO commodity VALUES('dummy','p',NULL,NULL);
20+
REPLACE INTO commodity_type VALUES('s','source commodity');
21+
REPLACE INTO commodity_type VALUES('a','annual commodity');
22+
REPLACE INTO commodity_type VALUES('p','physical commodity');
23+
REPLACE INTO commodity_type VALUES('d','demand commodity');
24+
REPLACE INTO commodity_type VALUES('e','emissions commodity');
25+
REPLACE INTO commodity_type VALUES('w','waste commodity');
26+
REPLACE INTO commodity_type VALUES('wa','waste annual commodity');
27+
REPLACE INTO commodity_type VALUES('wp','waste physical commodity');
28+
REPLACE INTO cost_emission VALUES('region',0,'co2',1.0,'$/kt',NULL);
29+
REPLACE INTO cost_fixed VALUES('region',0,'uc_cheap',0,8.0,'$/MWy',NULL);
30+
REPLACE INTO cost_fixed VALUES('region',0,'uc_expensive',0,10.0,'$/MWy',NULL);
31+
REPLACE INTO cost_invest VALUES('region','uc_cheap',0,8.0,'$/MW',NULL);
32+
REPLACE INTO cost_invest VALUES('region','uc_expensive',0,10.0,'$/MW',NULL);
33+
REPLACE INTO cost_invest VALUES('region','daily_storage',0,5.0,'$/MW',NULL);
34+
REPLACE INTO cost_invest VALUES('region','seasonal_storage',0,5.0,'$/MW',NULL);
35+
REPLACE INTO cost_variable VALUES('region',0,'uc_cheap',0,8.0,'$/MWh',NULL);
36+
REPLACE INTO cost_variable VALUES('region',0,'uc_expensive',0,10.0,'$/MWh',NULL);
37+
REPLACE INTO demand VALUES('region',0,'demand',168.0,'MWh',NULL);
38+
REPLACE INTO efficiency VALUES('region','source','uc_cheap',0,'dummy',1.0,'MW/MW',NULL);
39+
REPLACE INTO efficiency VALUES('region','source','uc_expensive',0,'dummy',1.0,'MW/MW',NULL);
40+
REPLACE INTO efficiency VALUES('region','dummy','demand_sink',0,'demand',1.0,'MW/MW',NULL);
41+
REPLACE INTO efficiency VALUES('region','dummy','daily_storage',0,'dummy',1.0,'MW/MW',NULL);
42+
REPLACE INTO efficiency VALUES('region','dummy','seasonal_storage',0,'dummy',1.0,'MW/MW',NULL);
43+
REPLACE INTO lifetime_tech VALUES('region','uc_cheap',1.0,NULL,NULL);
44+
REPLACE INTO lifetime_tech VALUES('region','uc_expensive',1.0,NULL,NULL);
45+
REPLACE INTO operator VALUES('e','equal to');
46+
REPLACE INTO operator VALUES('le','less than or equal to');
47+
REPLACE INTO operator VALUES('ge','greater than or equal to');
48+
REPLACE INTO limit_activity VALUES('region',0,'uc_expensive','ge',10.0,NULL,NULL);
49+
REPLACE INTO limit_activity VALUES('region',0,'uc_cheap','ge',10.0,NULL,NULL);
50+
REPLACE INTO limit_activity VALUES('region',0,'daily_storage','ge',10.0,NULL,NULL);
51+
REPLACE INTO limit_activity VALUES('region',0,'seasonal_storage','ge',10.0,NULL,NULL);
52+
REPLACE INTO limit_capacity VALUES('region',0,'uc_cheap','le',1.0,NULL,NULL);
53+
REPLACE INTO limit_capacity VALUES('region',0,'uc_expensive','le',2.0,NULL,NULL);
54+
REPLACE INTO ramp_down_hourly VALUES('region','uc_expensive',0.5,NULL);
55+
REPLACE INTO ramp_down_hourly VALUES('region','uc_cheap',0.5,NULL);
56+
REPLACE INTO ramp_up_hourly VALUES('region','uc_expensive',0.5,NULL);
57+
REPLACE INTO ramp_up_hourly VALUES('region','uc_cheap',0.5,NULL);
58+
REPLACE INTO ramp_up_hourly VALUES('region','seasonal_storage',0.5,NULL);
59+
REPLACE INTO ramp_up_hourly VALUES('region','daily_storage',0.5,NULL);
60+
REPLACE INTO region VALUES('region',NULL);REPLACE INTO storage_duration VALUES('region','daily_storage',1.0,NULL);
61+
REPLACE INTO storage_duration VALUES('region','seasonal_storage',1.0,NULL);
62+
REPLACE INTO technology_type VALUES('p','production technology');
63+
REPLACE INTO technology_type VALUES('pb','baseload production technology');
64+
REPLACE INTO technology_type VALUES('ps','storage production technology');
65+
REPLACE INTO time_of_day VALUES(1,'h01',1.0,NULL);
66+
REPLACE INTO time_of_day VALUES(2,'h02',1.0,NULL);
67+
REPLACE INTO time_of_day VALUES(3,'h03',1.0,NULL);
68+
REPLACE INTO time_of_day VALUES(4,'h04',1.0,NULL);
69+
REPLACE INTO time_of_day VALUES(5,'h05',1.0,NULL);
70+
REPLACE INTO time_of_day VALUES(6,'h06',1.0,NULL);
71+
REPLACE INTO time_of_day VALUES(7,'h07',1.0,NULL);
72+
REPLACE INTO time_of_day VALUES(8,'h08',1.0,NULL);
73+
REPLACE INTO time_of_day VALUES(9,'h09',1.0,NULL);
74+
REPLACE INTO time_of_day VALUES(10,'h10',1.0,NULL);
75+
REPLACE INTO time_of_day VALUES(11,'h11',1.0,NULL);
76+
REPLACE INTO time_of_day VALUES(12,'h12',1.0,NULL);
77+
REPLACE INTO time_of_day VALUES(13,'h13',1.0,NULL);
78+
REPLACE INTO time_of_day VALUES(14,'h14',1.0,NULL);
79+
REPLACE INTO time_of_day VALUES(15,'h15',1.0,NULL);
80+
REPLACE INTO time_of_day VALUES(16,'h16',1.0,NULL);
81+
REPLACE INTO time_of_day VALUES(17,'h17',1.0,NULL);
82+
REPLACE INTO time_of_day VALUES(18,'h18',1.0,NULL);
83+
REPLACE INTO time_of_day VALUES(19,'h19',1.0,NULL);
84+
REPLACE INTO time_of_day VALUES(20,'h20',1.0,NULL);
85+
REPLACE INTO time_of_day VALUES(21,'h21',1.0,NULL);
86+
REPLACE INTO time_of_day VALUES(22,'h22',1.0,NULL);
87+
REPLACE INTO time_of_day VALUES(23,'h23',1.0,NULL);
88+
REPLACE INTO time_of_day VALUES(24,'h24',1.0,NULL);
89+
REPLACE INTO time_period VALUES(0,0,'f');
90+
REPLACE INTO time_period VALUES(1,1,'f');
91+
REPLACE INTO time_period_type VALUES('e','existing vintages');
92+
REPLACE INTO time_period_type VALUES('f','future');REPLACE INTO technology VALUES('uc_cheap','p','energy',NULL,NULL,0,0,0,0,0,0,0,0,NULL);
93+
REPLACE INTO technology VALUES('uc_expensive','p','energy',NULL,NULL,0,0,0,0,0,0,0,0,NULL);
94+
REPLACE INTO technology VALUES('demand_sink','p','energy',NULL,NULL,1,0,0,0,0,0,0,0,NULL);
95+
REPLACE INTO technology VALUES('daily_storage','ps','energy',NULL,NULL,0,0,0,0,0,0,0,0,NULL);
96+
REPLACE INTO technology VALUES('seasonal_storage','ps','energy',NULL,NULL,0,0,0,0,0,0,0,1,NULL);
97+
REPLACE INTO time_season VALUES(1,'d01',0.1428571428571428493,'1/7');
98+
REPLACE INTO time_season VALUES(2,'d02',0.1428571428571428493,'1/7');
99+
REPLACE INTO time_season VALUES(3,'d03',0.1428571428571428493,'1/7');
100+
REPLACE INTO time_season VALUES(4,'d04',0.1428571428571428493,'1/7');
101+
REPLACE INTO time_season VALUES(5,'d05',0.1428571428571428493,'1/7');
102+
REPLACE INTO time_season VALUES(6,'d06',0.1428571428571428493,'1/7');
103+
REPLACE INTO time_season VALUES(7,'d07',0.1428571428571428493,'1/7');
104+
REPLACE INTO time_season_sequential VALUES(1,'d01','d03',0.1428571428571428493,'1/7');
105+
REPLACE INTO time_season_sequential VALUES(2,'d02','d05',0.1428571428571428493,'1/7');
106+
REPLACE INTO time_season_sequential VALUES(3,'d03','d01',0.1428571428571428493,'1/7');
107+
REPLACE INTO time_season_sequential VALUES(4,'d04','d07',0.1428571428571428493,'1/7');
108+
REPLACE INTO time_season_sequential VALUES(5,'d05','d04',0.1428571428571428493,'1/7');
109+
REPLACE INTO time_season_sequential VALUES(6,'d06','d06',0.1428571428571428493,'1/7');
110+
REPLACE INTO time_season_sequential VALUES(7,'d07','d02',0.1428571428571428493,'1/7');
111+
REPLACE INTO unit_commitment VALUES('region','uc_cheap',0.2000000000000000111,0.5,1.0,3,5,0,NULL,NULL);
112+
REPLACE INTO unit_commitment VALUES('region','uc_expensive',0.2000000000000000111,0.5,1.0,2,1,1,NULL,NULL);
113+
REPLACE INTO unit_commitment_startup_cost VALUES('region','uc_expensive',5.0,NULL,NULL);
114+
REPLACE INTO unit_commitment_startup_cost VALUES('region','uc_cheap',4.0,NULL,NULL);
115+
REPLACE INTO unit_commitment_startup_emissions VALUES('region','co2','uc_expensive',5.0,NULL,NULL);
116+
REPLACE INTO unit_commitment_startup_emissions VALUES('region','co2','uc_cheap',4.0,NULL,NULL);
117+
REPLACE INTO unit_commitment_startup_input VALUES('region','source','uc_expensive',5.0,NULL,NULL);
118+
REPLACE INTO unit_commitment_startup_input VALUES('region','source','uc_cheap',4.0,NULL,NULL);

0 commit comments

Comments
 (0)