@@ -215,6 +215,90 @@ def test_inferred_parameters_in_actual_measurement_1d(experiment, DAC):
215
215
assert len (df ) == num_points
216
216
217
217
218
+ def test_inferred_parameters_in_actual_measurement_2d (experiment , DAC ):
219
+ """
220
+ 2D version: both axes are DelegateParameters inferred from DAC channels.
221
+ Ensures inferred parameters on both axes are saved and exported correctly.
222
+ """
223
+ num_points_x = 5
224
+ num_points_y = 7
225
+
226
+ # Delegate parameters (axes)
227
+ del_param_1 = DelegateParameter ("del_param_1" , label = "del param 1" , source = DAC .ch1 )
228
+ del_param_2 = DelegateParameter ("del_param_2" , label = "del param 2" , source = DAC .ch2 )
229
+
230
+ # Measurement parameter depending on both delegate parameters
231
+ meas_parameter = ManualParameter ("meas_parameter" , initial_value = 0.0 )
232
+
233
+ meas = Measurement (name = "test_measurement_2d" , exp = experiment )
234
+
235
+ # Register underlying basis (standalone) parameters
236
+ meas .register_parameter (DAC .ch1 )
237
+ meas .register_parameter (DAC .ch2 )
238
+
239
+ # Register delegate parameters inferred from their respective DAC channels
240
+ meas .register_parameter (del_param_1 , basis = (DAC .ch1 ,))
241
+ meas .register_parameter (del_param_2 , basis = (DAC .ch2 ,))
242
+
243
+ # Register measurement parameter with 2D setpoints
244
+ meas .register_parameter (meas_parameter , setpoints = (del_param_1 , del_param_2 ))
245
+
246
+ with meas .run () as datasaver :
247
+ for x in np .linspace (0 , 1 , num_points_x ):
248
+ for y in np .linspace (0 , 2 , num_points_y ):
249
+ del_param_1 .set (x )
250
+ del_param_2 .set (y )
251
+ meas_parameter .set (x + y ) # simple deterministic value
252
+
253
+ datasaver .add_result (
254
+ (DAC .ch1 , DAC .ch1 ()),
255
+ (DAC .ch2 , DAC .ch2 ()),
256
+ (del_param_1 , del_param_1 ()),
257
+ (del_param_2 , del_param_2 ()),
258
+ (meas_parameter , meas_parameter ()),
259
+ )
260
+
261
+ dataset = datasaver .dataset
262
+ param_data = dataset .get_parameter_data ()
263
+
264
+ assert "meas_parameter" in param_data
265
+ assert len (param_data ) == 1 # only one top-level dependent parameter tree
266
+
267
+ tree = param_data ["meas_parameter" ]
268
+ expected_keys = {
269
+ "meas_parameter" ,
270
+ "del_param_1" ,
271
+ "del_param_2" ,
272
+ "dummy_dac_ch1" ,
273
+ "dummy_dac_ch2" ,
274
+ }
275
+ assert set (tree .keys ()) == expected_keys
276
+
277
+ total_points = num_points_x * num_points_y
278
+ for k in expected_keys :
279
+ assert len (tree [k ]) == total_points , f"{ k } should have { total_points } entries"
280
+
281
+ # xarray export
282
+ xarr = dataset .to_xarray_dataset ()
283
+ assert "meas_parameter" in xarr .data_vars
284
+ assert "del_param_1" in xarr .coords
285
+ assert "del_param_2" in xarr .coords
286
+ # inferred (basis) parameters not exported
287
+ assert "dummy_dac_ch1" not in xarr .coords
288
+ assert "dummy_dac_ch2" not in xarr .coords
289
+
290
+ assert xarr .meas_parameter .dims == ("del_param_1" , "del_param_2" )
291
+ assert xarr .meas_parameter .shape == (num_points_x , num_points_y )
292
+
293
+ # pandas export
294
+ df = dataset .to_pandas_dataframe ()
295
+ assert "meas_parameter" in df .columns
296
+ assert df .index .names == ["del_param_1" , "del_param_2" ]
297
+ assert "dummy_dac_ch1" not in df .columns
298
+ assert "dummy_dac_ch2" not in df .columns
299
+ assert len (df ) == total_points
300
+
301
+
218
302
def test_multiple_dependent_parameters_no_cross_contamination (experiment ):
219
303
"""
220
304
Test that multiple dependent parameters that depend on the same independent
0 commit comments