STAR
Cal STAR — firmware, DAQ server, and engine design
Loading...
Searching...
No Matches
test_robust_ddp_identify.py
Go to the documentation of this file.
1"""Unit tests for parameter identification module."""
2
3import unittest
4import numpy as np
5
6from engine.control.robust_ddp.identify import ParameterIdentifier, update_params
7from engine.control.robust_ddp.data_models import ControllerConfig, ControllerState, Measurement
9 IDX_P_COPV,
10 IDX_P_REG,
11 IDX_P_U_F,
12 IDX_P_U_O,
13 IDX_P_D_F,
14 IDX_P_D_O,
15 IDX_V_U_F,
16 IDX_V_U_O,
17 step,
18 DynamicsParams,
19)
20
21
22class TestParameterIdentification(unittest.TestCase):
23 """Test parameter identification."""
24
25 def setUp(self):
26 """Set up test fixtures."""
28 N=10,
29 dt=0.01,
30 alpha_F=10.0,
31 alpha_O=10.0,
32 tau_line_F=0.01,
33 tau_line_O=0.01,
34 copv_cF=1e5,
35 copv_cO=1e5,
36 copv_loss=1e3,
37 rho_F=800.0,
38 rho_O=1140.0,
39 )
40
42 """Test identification of alpha_F from synthetic data."""
43 # True parameter
44 alpha_F_true = 15.0
45
46 # Create identifier with wrong initial value
47 identifier = ParameterIdentifier(self.cfg, forgetting_factor=0.95)
48 identifier.alpha_F = 5.0 # Wrong initial value
49
50 # Generate synthetic data
51 x = np.array([30e6, 24e6, 3e6, 3.5e6, 2.5e6, 3e6, 0.01, 0.01])
52 u = np.array([0.8, 0.5]) # Fuel valve on
53 dt = 0.01
54
55 # Create dynamics params with true alpha_F
56 params_true = DynamicsParams.from_config(self.cfg)
57 params_true.alpha_F = alpha_F_true
58
59 # Simulate and identify
60 alpha_initial = identifier.alpha_F
61 for _ in range(100):
62 # Step dynamics with true parameter
63 x_next = step(x, u, dt, params_true, 0.5, 1.0)
64
65 # Create measurement
66 meas = Measurement(
67 P_copv=x[IDX_P_COPV],
68 P_reg=x[IDX_P_REG],
69 P_u_fuel=x[IDX_P_U_F],
70 P_u_ox=x[IDX_P_U_O],
71 P_d_fuel=x[IDX_P_D_F],
72 P_d_ox=x[IDX_P_D_O],
73 )
74
75 # Update identifier
76 identifier.update_params(
77 ControllerState(), meas, self.cfg, x, u, dt, mdot_F=0.5, mdot_O=1.0
78 )
79
80 x = x_next
81
82 alpha_final = identifier.alpha_F
83
84 # Should move toward true value
85 error_initial = abs(alpha_initial - alpha_F_true)
86 error_final = abs(alpha_final - alpha_F_true)
87
88 self.assertLess(error_final, error_initial, "Parameter should move toward true value")
89 self.assertLess(error_final, error_initial * 0.5, "Should converge significantly")
90
92 """Test identification of alpha_O from synthetic data."""
93 # True parameter
94 alpha_O_true = 20.0
95
96 # Create identifier
97 identifier = ParameterIdentifier(self.cfg, forgetting_factor=0.95)
98 identifier.alpha_O = 5.0 # Wrong initial value
99
100 # Generate synthetic data
101 x = np.array([30e6, 24e6, 3e6, 3.5e6, 2.5e6, 3e6, 0.01, 0.01])
102 u = np.array([0.3, 0.9]) # Oxidizer valve on
103 dt = 0.01
104
105 # Create dynamics params with true alpha_O
106 params_true = DynamicsParams.from_config(self.cfg)
107 params_true.alpha_O = alpha_O_true
108
109 # Simulate and identify
110 alpha_initial = identifier.alpha_O
111 for _ in range(100):
112 x_next = step(x, u, dt, params_true, 0.5, 1.0)
113
114 meas = Measurement(
115 P_copv=x[IDX_P_COPV],
116 P_reg=x[IDX_P_REG],
117 P_u_fuel=x[IDX_P_U_F],
118 P_u_ox=x[IDX_P_U_O],
119 P_d_fuel=x[IDX_P_D_F],
120 P_d_ox=x[IDX_P_D_O],
121 )
122
123 identifier.update_params(
124 ControllerState(), meas, self.cfg, x, u, dt, mdot_F=0.5, mdot_O=1.0
125 )
126
127 x = x_next
128
129 alpha_final = identifier.alpha_O
130
131 # Should move toward true value
132 error_initial = abs(alpha_initial - alpha_O_true)
133 error_final = abs(alpha_final - alpha_O_true)
134
135 self.assertLess(error_final, error_initial)
136
138 """Test identification of tau_line_F from synthetic data."""
139 # True parameter
140 tau_line_F_true = 0.05
141
142 # Create identifier
143 identifier = ParameterIdentifier(self.cfg, forgetting_factor=0.95)
144 identifier.tau_line_F = 0.01 # Wrong initial value
145
146 # Generate synthetic data
147 x = np.array([30e6, 24e6, 5e6, 3.5e6, 2.5e6, 3e6, 0.01, 0.01])
148 u = np.array([0.5, 0.5])
149 dt = 0.01
150
151 # Create dynamics params with true tau
152 params_true = DynamicsParams.from_config(self.cfg)
153 params_true.tau_line_F = tau_line_F_true
154
155 # Simulate and identify
156 tau_initial = identifier.tau_line_F
157 for _ in range(200): # More steps for tau (slower dynamics)
158 x_next = step(x, u, dt, params_true, 0.5, 1.0)
159
160 meas = Measurement(
161 P_copv=x[IDX_P_COPV],
162 P_reg=x[IDX_P_REG],
163 P_u_fuel=x[IDX_P_U_F],
164 P_u_ox=x[IDX_P_U_O],
165 P_d_fuel=x[IDX_P_D_F],
166 P_d_ox=x[IDX_P_D_O],
167 )
168
169 identifier.update_params(
170 ControllerState(), meas, self.cfg, x, u, dt, mdot_F=0.5, mdot_O=1.0
171 )
172
173 x = x_next
174
175 tau_final = identifier.tau_line_F
176
177 # Should move toward true value
178 error_initial = abs(tau_initial - tau_line_F_true)
179 error_final = abs(tau_final - tau_line_F_true)
180
181 self.assertLess(error_final, error_initial)
182
184 """Test identification of tau_line_O from synthetic data."""
185 # True parameter
186 tau_line_O_true = 0.08
187
188 # Create identifier
189 identifier = ParameterIdentifier(self.cfg, forgetting_factor=0.95)
190 identifier.tau_line_O = 0.01 # Wrong initial value
191
192 # Generate synthetic data
193 x = np.array([30e6, 24e6, 3e6, 5.5e6, 2.5e6, 3e6, 0.01, 0.01])
194 u = np.array([0.5, 0.5])
195 dt = 0.01
196
197 # Create dynamics params with true tau
198 params_true = DynamicsParams.from_config(self.cfg)
199 params_true.tau_line_O = tau_line_O_true
200
201 # Simulate and identify
202 tau_initial = identifier.tau_line_O
203 for _ in range(200):
204 x_next = step(x, u, dt, params_true, 0.5, 1.0)
205
206 meas = Measurement(
207 P_copv=x[IDX_P_COPV],
208 P_reg=x[IDX_P_REG],
209 P_u_fuel=x[IDX_P_U_F],
210 P_u_ox=x[IDX_P_U_O],
211 P_d_fuel=x[IDX_P_D_F],
212 P_d_ox=x[IDX_P_D_O],
213 )
214
215 identifier.update_params(
216 ControllerState(), meas, self.cfg, x, u, dt, mdot_F=0.5, mdot_O=1.0
217 )
218
219 x = x_next
220
221 tau_final = identifier.tau_line_O
222
223 # Should move toward true value
224 error_initial = abs(tau_initial - tau_line_O_true)
225 error_final = abs(tau_final - tau_line_O_true)
226
227 self.assertLess(error_final, error_initial)
228
230 """Test identification of COPV consumption coefficients."""
231 # True parameters
232 copv_cF_true = 1.5e5
233 copv_cO_true = 1.2e5
234
235 # Create identifier
236 identifier = ParameterIdentifier(self.cfg, forgetting_factor=0.95)
237 identifier.copv_cF = 5e4 # Wrong initial value
238 identifier.copv_cO = 5e4
239
240 # Generate synthetic data
241 x = np.array([30e6, 24e6, 3e6, 3.5e6, 2.5e6, 3e6, 0.01, 0.01])
242 u = np.array([0.7, 0.6]) # Both valves on
243 dt = 0.01
244
245 # Create dynamics params with true coefficients
246 params_true = DynamicsParams.from_config(self.cfg)
247 params_true.copv_cF = copv_cF_true
248 params_true.copv_cO = copv_cO_true
249
250 # Simulate and identify
251 cF_initial = identifier.copv_cF
252 cO_initial = identifier.copv_cO
253
254 for _ in range(200):
255 x_next = step(x, u, dt, params_true, 0.5, 1.0)
256
257 meas = Measurement(
258 P_copv=x[IDX_P_COPV],
259 P_reg=x[IDX_P_REG],
260 P_u_fuel=x[IDX_P_U_F],
261 P_u_ox=x[IDX_P_U_O],
262 P_d_fuel=x[IDX_P_D_F],
263 P_d_ox=x[IDX_P_D_O],
264 )
265
266 identifier.update_params(
267 ControllerState(), meas, self.cfg, x, u, dt, mdot_F=0.5, mdot_O=1.0
268 )
269
270 x = x_next
271
272 cF_final = identifier.copv_cF
273 cO_final = identifier.copv_cO
274
275 # Should move toward true values
276 error_F_initial = abs(cF_initial - copv_cF_true)
277 error_F_final = abs(cF_final - copv_cF_true)
278 error_O_initial = abs(cO_initial - copv_cO_true)
279 error_O_final = abs(cO_final - copv_cO_true)
280
281 self.assertLess(error_F_final, error_F_initial)
282 self.assertLess(error_O_final, error_O_initial)
283
285 """Test that parameters stay within bounds."""
286 identifier = ParameterIdentifier(self.cfg)
287
288 # Try to push parameters out of bounds
289 identifier.alpha_F = 200.0 # Above max
290 identifier.alpha_O = 0.01 # Below min
291 identifier.tau_line_F = 0.0001 # Below min
292 identifier.tau_line_O = 2.0 # Above max
293 identifier.copv_cF = 1e8 # Above max
294 identifier.copv_cO = 1e2 # Below min
295
296 # Update should bound them
297 x = np.array([30e6, 24e6, 3e6, 3.5e6, 2.5e6, 3e6, 0.01, 0.01])
298 u = np.array([0.5, 0.5])
299 meas = Measurement(
300 P_copv=x[IDX_P_COPV],
301 P_reg=x[IDX_P_REG],
302 P_u_fuel=x[IDX_P_U_F],
303 P_u_ox=x[IDX_P_U_O],
304 P_d_fuel=x[IDX_P_D_F],
305 P_d_ox=x[IDX_P_D_O],
306 )
307
308 identifier.update_params(
309 ControllerState(), meas, self.cfg, x, u, 0.01, mdot_F=0.5, mdot_O=1.0
310 )
311
312 # Check bounds
313 self.assertGreaterEqual(identifier.alpha_F, identifier.alpha_min)
314 self.assertLessEqual(identifier.alpha_F, identifier.alpha_max)
315 self.assertGreaterEqual(identifier.alpha_O, identifier.alpha_min)
316 self.assertLessEqual(identifier.alpha_O, identifier.alpha_max)
317 self.assertGreaterEqual(identifier.tau_line_F, identifier.tau_min)
318 self.assertLessEqual(identifier.tau_line_F, identifier.tau_max)
319 self.assertGreaterEqual(identifier.tau_line_O, identifier.tau_min)
320 self.assertLessEqual(identifier.tau_line_O, identifier.tau_max)
321 self.assertGreaterEqual(identifier.copv_cF, identifier.copv_c_min)
322 self.assertLessEqual(identifier.copv_cF, identifier.copv_c_max)
323 self.assertGreaterEqual(identifier.copv_cO, identifier.copv_c_min)
324 self.assertLessEqual(identifier.copv_cO, identifier.copv_c_max)
325
327 """Test get_params method."""
328 identifier = ParameterIdentifier(self.cfg)
329
330 params = identifier.get_params()
331
332 self.assertIn("alpha_F", params)
333 self.assertIn("alpha_O", params)
334 self.assertIn("tau_line_F", params)
335 self.assertIn("tau_line_O", params)
336 self.assertIn("copv_cF", params)
337 self.assertIn("copv_cO", params)
338
339 self.assertEqual(params["alpha_F"], identifier.alpha_F)
340 self.assertEqual(params["alpha_O"], identifier.alpha_O)
341
343 """Test slow update of config parameters."""
344 identifier = ParameterIdentifier(self.cfg)
345
346 # Set identifier to different values
347 identifier.alpha_F = 20.0
348 identifier.alpha_O = 25.0
349
350 # Initial config values
351 alpha_F_initial = self.cfg.alpha_F
352 alpha_O_initial = self.cfg.alpha_O
353
354 # Update config (should move slowly)
355 identifier.update_config(self.cfg)
356
357 # Should move toward identifier values but not all the way
358 self.assertGreater(self.cfg.alpha_F, alpha_F_initial)
359 self.assertLess(self.cfg.alpha_F, identifier.alpha_F)
360 self.assertGreater(self.cfg.alpha_O, alpha_O_initial)
361 self.assertLess(self.cfg.alpha_O, identifier.alpha_O)
362
363
364if __name__ == "__main__":
365 unittest.main()
366
void setUp(void)
Definition test_main.cpp:17