Skip to content

Commit 0a72b0b

Browse files
authored
Use test fixtures and conftest.py (#82)
* Reconfigure tests to use fixtures, conftest.py * Remove no-longer-used floris input yaml * battery control tests use conftest fixtures * Update alliance name
1 parent b7a0c91 commit 0a72b0b

9 files changed

Lines changed: 521 additions & 590 deletions

LICENSE.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
BSD 3-Clause License
22

3-
Copyright (c) 2025 Alliance for Sustainable Energy, LLC and Colorado School of Mines.
3+
Copyright (c) 2025 Alliance for Energy Innovation, LLC and Colorado School of Mines.
44

55
Redistribution and use in source and binary forms, with or without modification, are permitted
66
provided that the following conditions are met:

tests/battery_test.py

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,8 @@
33
)
44
from hycon.interfaces import HerculesInterface
55

6-
test_hercules_dict = {
7-
"dt": 1,
8-
"time": 0,
9-
"plant": {"interconnect_limit": 10},
10-
"battery": {
11-
"size": 100.0,
12-
"energy_capacity": 400.0,
13-
"power": 100.0,
14-
"soc": 0.5,
15-
"charge_rate": 50.0 * 1e3,
16-
"discharge_rate": 100.0 * 1e3,
17-
},
18-
"external_signals": {
19-
"RT_LMP": 10.0,
20-
},
21-
}
22-
23-
24-
def test_BatteryPriceSOCController_init():
6+
7+
def test_BatteryPriceSOCController_init(test_hercules_dict):
258
test_interface = HerculesInterface(test_hercules_dict)
269

2710
# Initialize controller
@@ -34,7 +17,7 @@ def test_BatteryPriceSOCController_init():
3417
)
3518

3619

37-
def test_BatteryPriceSOCController_compute_controls():
20+
def test_BatteryPriceSOCController_compute_controls(test_hercules_dict):
3821
test_interface = HerculesInterface(test_hercules_dict)
3922

4023
# Initialize controller

tests/conftest.py

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import pytest
2+
from hycon.interfaces import HerculesADInterface, HerculesHybridADInterface, HerculesInterface
3+
from hycon.interfaces.interface_base import InterfaceBase
4+
5+
6+
@pytest.fixture
7+
def test_hercules_v1_dict():
8+
return {
9+
"dt": 1,
10+
"time": 0,
11+
"controller": {
12+
"num_turbines": 2,
13+
"initial_conditions": {"yaw": [270.0, 270.0]},
14+
"nominal_plant_power_kW": 10000,
15+
"nominal_hydrogen_rate_kgps": 0.1,
16+
"hydrogen_controller_gain": 1.0,
17+
},
18+
"hercules_comms": {
19+
"amr_wind": {
20+
"test_farm": {
21+
"turbine_wind_directions": [271.0, 272.5],
22+
"turbine_powers": [4000.0, 4001.0],
23+
"wind_speed": 10.0,
24+
}
25+
}
26+
},
27+
"py_sims": {
28+
"test_battery": {
29+
"outputs": {"power": 10.0, "soc": 0.3},
30+
"charge_rate": 20,
31+
"discharge_rate": 20,
32+
},
33+
"test_solar": {"outputs": {"power_mw": 1.0, "dni": 1000.0, "aoi": 30.0}},
34+
"test_hydrogen": {"outputs": {"H2_mfr": 0.03}},
35+
"inputs": {},
36+
},
37+
"external_signals": {
38+
"wind_power_reference": 1000.0,
39+
"plant_power_reference": 1000.0,
40+
"hydrogen_reference": 0.02,
41+
},
42+
}
43+
44+
45+
@pytest.fixture
46+
def test_hercules_dict():
47+
return {
48+
"dt": 1,
49+
"time": 0,
50+
"plant": {"interconnect_limit": None},
51+
"controller": {
52+
"test_controller_parameter": 1.0,
53+
},
54+
"wind_farm": {
55+
"n_turbines": 2,
56+
"capacity": 10000.0,
57+
"wind_direction_mean": 271.0,
58+
"turbine_powers": [4000.0, 4001.0],
59+
"wind_speed": 10.0,
60+
},
61+
"solar_farm": {
62+
"capacity": 1000.0,
63+
"power": 1000.0, # kW
64+
"dni": 1000.0,
65+
"aoi": 30.0,
66+
},
67+
"battery": {
68+
"size": 10.0e3,
69+
"energy_capacity": 40.0e3,
70+
"power": 10.0e3,
71+
"soc": 0.3,
72+
"charge_rate": 20e3,
73+
"discharge_rate": 15e3,
74+
},
75+
"electrolyzer": {
76+
"H2_mfr": 0.03,
77+
},
78+
"external_signals": {
79+
"wind_power_reference": 1000.0,
80+
"solar_power_reference": 800.0,
81+
"battery_power_reference": 0.0,
82+
"plant_power_reference": 1000.0,
83+
"forecast_ws_mean_0": 8.0,
84+
"forecast_ws_mean_1": 8.1,
85+
"ws_median_0": 8.1,
86+
"hydrogen_reference": 0.02,
87+
},
88+
}
89+
90+
91+
class StandinInterface(InterfaceBase):
92+
"""
93+
Empty class to test controllers.
94+
"""
95+
96+
def __init__(self):
97+
super().__init__()
98+
self.dt = 1.0
99+
# Set up stand-in plant parameters and controller parameters
100+
self.plant_parameters = {"n_turbines": 2}
101+
self.controller_parameters = {}
102+
103+
def get_measurements(self):
104+
pass
105+
106+
def check_controls(self):
107+
pass
108+
109+
def send_controls(self):
110+
pass
111+
112+
113+
@pytest.fixture
114+
def test_interface_standin():
115+
return StandinInterface()
116+
117+
118+
@pytest.fixture
119+
def test_interface_hercules(test_hercules_dict):
120+
"""
121+
Fixture to create a Hercules v2 dictionary for testing.
122+
"""
123+
return HerculesInterface(test_hercules_dict)
124+
125+
126+
@pytest.fixture
127+
def test_interface_hercules_ad(test_hercules_v1_dict):
128+
"""
129+
Fixture to create a HerculesADInterface for testing.
130+
"""
131+
return HerculesADInterface(test_hercules_v1_dict)
132+
133+
134+
@pytest.fixture
135+
def test_interface_hercules_hybrid_ad(test_hercules_v1_dict):
136+
"""
137+
Fixture to create a HerculesHybridADInterface for testing.
138+
"""
139+
test_hercules_v1_dict["controller"]["num_batteries"] = 1
140+
test_hercules_v1_dict["controller"]["num_solar"] = 1
141+
return HerculesHybridADInterface(test_hercules_v1_dict)
142+
143+
144+
@pytest.fixture
145+
def floris_dict():
146+
"""
147+
Fixture to create a FLORIS dictionary for testing.
148+
"""
149+
return {
150+
"name": "test_input",
151+
"description": "Two-turbine farm for testing",
152+
"floris_version": "v4",
153+
"logging": {
154+
"console": {"enable": False, "level": "WARNING"},
155+
"file": {"enable": False, "level": "WARNING"},
156+
},
157+
"solver": {"type": "turbine_grid", "turbine_grid_points": 3},
158+
"farm": {
159+
"layout_x": [0.0, 500.0],
160+
"layout_y": [0.0, 0.0],
161+
"turbine_type": ["nrel_5MW"],
162+
},
163+
"flow_field": {
164+
"air_density": 1.225,
165+
"reference_wind_height": 90.0,
166+
"turbulence_intensities": [0.06],
167+
"wind_directions": [270.0],
168+
"wind_shear": 0.12,
169+
"wind_speeds": [8.0],
170+
"wind_veer": 0.0,
171+
},
172+
"wake": {
173+
"model_strings": {
174+
"combination_model": "sosfs",
175+
"deflection_model": "gauss",
176+
"turbulence_model": "crespo_hernandez",
177+
"velocity_model": "gauss",
178+
},
179+
"enable_secondary_steering": True,
180+
"enable_yaw_added_recovery": True,
181+
"enable_active_wake_mixing": True,
182+
"enable_transverse_velocities": True,
183+
"wake_deflection_parameters": {
184+
"gauss": {
185+
"ad": 0.0,
186+
"alpha": 0.58,
187+
"bd": 0.0,
188+
"beta": 0.077,
189+
"dm": 1.0,
190+
"ka": 0.38,
191+
"kb": 0.004,
192+
},
193+
},
194+
"wake_velocity_parameters": {
195+
"gauss": {"alpha": 0.58, "beta": 0.077, "ka": 0.38, "kb": 0.004},
196+
},
197+
"wake_turbulence_parameters": {
198+
"crespo_hernandez": {
199+
"initial": 0.01,
200+
"constant": 0.9,
201+
"ai": 0.83,
202+
"downstream": -0.25,
203+
}
204+
},
205+
},
206+
}

tests/controller_base_test.py

Lines changed: 5 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,5 @@
11
import pytest
22
from hycon.controllers.controller_base import ControllerBase
3-
from hycon.interfaces.interface_base import InterfaceBase
4-
5-
6-
class StandinInterface(InterfaceBase):
7-
"""
8-
Empty class to test controllers.
9-
"""
10-
11-
def __init__(self):
12-
super().__init__()
13-
14-
def get_measurements(self):
15-
pass
16-
17-
def check_controls(self):
18-
pass
19-
20-
def send_controls(self):
21-
pass
223

234

245
class InheritanceTestClassBad(ControllerBase):
@@ -42,26 +23,22 @@ def compute_controls(self):
4223
pass
4324

4425

45-
def test_ControllerBase_methods():
26+
def test_ControllerBase_methods(test_interface_standin):
4627
"""
4728
Check that the base interface class establishes the correct methods.
4829
"""
49-
test_interface = StandinInterface()
50-
51-
controller_base = InheritanceTestClassGood(test_interface)
30+
controller_base = InheritanceTestClassGood(test_interface_standin)
5231
assert hasattr(controller_base, "_receive_measurements")
5332
assert hasattr(controller_base, "_send_controls")
5433
assert hasattr(controller_base, "step")
5534
assert hasattr(controller_base, "compute_controls")
5635

5736

58-
def test_inherited_methods():
37+
def test_inherited_methods(test_interface_standin):
5938
"""
6039
Check that a subclass of InterfaceBase inherits methods correctly.
6140
"""
62-
test_interface = StandinInterface()
63-
6441
with pytest.raises(TypeError):
65-
_ = InheritanceTestClassBad(test_interface)
42+
_ = InheritanceTestClassBad(test_interface_standin)
6643

67-
_ = InheritanceTestClassGood(test_interface)
44+
_ = InheritanceTestClassGood(test_interface_standin)

0 commit comments

Comments
 (0)