11# pyright: reportMissingParameterType=false
2- from unittest .mock import AsyncMock , patch
2+ import re
3+ from unittest .mock import AsyncMock , MagicMock , patch
34
45import lmfit
56import numpy as np
1011from ibex_bluesky_core .devices .muon import (
1112 MuonAsymmetryReducer ,
1213 damped_oscillator ,
13- damped_oscillator_multiple ,
14+ double_damped_oscillator ,
1415)
1516from ibex_bluesky_core .devices .simpledae import MEventsWaiter , PeriodPerPointController , SimpleDae
1617
2324damped_oscillator_params .add ("phi_0" , 0.0 )
2425damped_oscillator_params .add ("lambda_0" , 0.001 )
2526
26- multi_damped_oscillator_model = lmfit .Model (damped_oscillator_multiple )
27-
28- multi_damped_oscillator_params = lmfit .Parameters ()
29- multi_damped_oscillator_params .add ("B" , 0.0 )
30- multi_damped_oscillator_params .add ("A_0" , 0.1 , min = 0 )
31- multi_damped_oscillator_params .add ("omega_0" , 0.1 , min = 0 )
32- multi_damped_oscillator_params .add ("phi_0" , 0.0 )
33- multi_damped_oscillator_params .add ("lambda_0" , 0.001 )
34- multi_damped_oscillator_params .add ("A_1" , 0.1 , min = 0 )
35- multi_damped_oscillator_params .add ("omega_1" , 0.1 , min = 0 )
36- multi_damped_oscillator_params .add ("phi_1" , 0.0 )
37- multi_damped_oscillator_params .add ("lambda_1" , 0.001 )
38-
3927
4028@pytest .fixture
4129def asymmetry_reducer ():
@@ -61,18 +49,7 @@ def rebinning_asymmetry_reducer():
6149
6250
6351@pytest .fixture
64- def multi_damped_oscillator_asymmetry_reducer ():
65- return MuonAsymmetryReducer (
66- forward_detectors = np .array ([1 ]),
67- backward_detectors = np .array ([2 ]),
68- prefix = "UNITTEST:" ,
69- model = multi_damped_oscillator_model ,
70- fit_parameters = multi_damped_oscillator_params ,
71- )
72-
73-
74- @pytest .fixture
75- async def simpledae_single_oscillator (rebinning_asymmetry_reducer ):
52+ async def simpledae (rebinning_asymmetry_reducer ):
7653 dae = SimpleDae (
7754 prefix = "UNITTEST:" ,
7855 reducer = rebinning_asymmetry_reducer ,
@@ -83,63 +60,50 @@ async def simpledae_single_oscillator(rebinning_asymmetry_reducer):
8360 return dae
8461
8562
86- @pytest .fixture
87- async def simpledae_multi_oscillator (multi_damped_oscillator_asymmetry_reducer ):
88- dae = SimpleDae (
89- prefix = "UNITTEST:" ,
90- reducer = multi_damped_oscillator_asymmetry_reducer ,
91- waiter = MEventsWaiter (5000 ),
92- controller = PeriodPerPointController (save_run = False ),
63+ def test_damped_oscillator ():
64+ np .testing .assert_allclose (
65+ damped_oscillator (
66+ t = np .array ([0 , 1 , 2 , 3 , 4 , 5 ], dtype = np .float64 ),
67+ B = 1 ,
68+ A_0 = 0.0 ,
69+ phi_0 = 0.0 ,
70+ omega_0 = 0.0 ,
71+ lambda_0 = 0.0 ,
72+ ),
73+ np .array ([1 , 1 , 1 , 1 , 1 , 1 ], dtype = np .float64 ),
9374 )
94- await dae .connect (mock = True )
95- return dae
9675
9776
98- def test_asymmetry_reducer_readable_signals_single_oscillator (
99- simpledae_single_oscillator , rebinning_asymmetry_reducer
100- ):
101- assert set (
102- rebinning_asymmetry_reducer .additional_readable_signals (simpledae_single_oscillator )
103- ) == {
104- simpledae_single_oscillator .reducer .B ,
105- simpledae_single_oscillator .reducer .B_err ,
106- simpledae_single_oscillator .reducer .A_0 ,
107- simpledae_single_oscillator .reducer .A_0_err ,
108- simpledae_single_oscillator .reducer .omega_0 ,
109- simpledae_single_oscillator .reducer .omega_0_err ,
110- simpledae_single_oscillator .reducer .phi_0 ,
111- simpledae_single_oscillator .reducer .phi_0_err ,
112- simpledae_single_oscillator .reducer .lambda_0 ,
113- simpledae_single_oscillator .reducer .lambda_0_err ,
114- }
77+ def test_double_damped_oscillator ():
78+ np .testing .assert_allclose (
79+ double_damped_oscillator (
80+ t = np .array ([0 , 1 , 2 , 3 , 4 , 5 ], dtype = np .float64 ),
81+ B = 1 ,
82+ A_0 = 0.0 ,
83+ phi_0 = 0.0 ,
84+ omega_0 = 0.0 ,
85+ lambda_0 = 0.0 ,
86+ A_1 = 0.0 ,
87+ phi_1 = 0.0 ,
88+ omega_1 = 0.0 ,
89+ lambda_1 = 0.0 ,
90+ ),
91+ np .array ([1 , 1 , 1 , 1 , 1 , 1 ], dtype = np .float64 ),
92+ )
11593
11694
117- def test_asymmetry_reducer_readable_signals_multiple_oscillator (
118- simpledae_multi_oscillator , multi_damped_oscillator_asymmetry_reducer
119- ):
120- assert set (
121- multi_damped_oscillator_asymmetry_reducer .additional_readable_signals (
122- simpledae_multi_oscillator
123- )
124- ) == {
125- simpledae_multi_oscillator .reducer .B ,
126- simpledae_multi_oscillator .reducer .B_err ,
127- simpledae_multi_oscillator .reducer .A_0 ,
128- simpledae_multi_oscillator .reducer .A_0_err ,
129- simpledae_multi_oscillator .reducer .omega_0 ,
130- simpledae_multi_oscillator .reducer .omega_0_err ,
131- simpledae_multi_oscillator .reducer .phi_0 ,
132- simpledae_multi_oscillator .reducer .phi_0_err ,
133- simpledae_multi_oscillator .reducer .lambda_0 ,
134- simpledae_multi_oscillator .reducer .lambda_0_err ,
135- simpledae_multi_oscillator .reducer .A_1 ,
136- simpledae_multi_oscillator .reducer .A_1_err ,
137- simpledae_multi_oscillator .reducer .omega_1 ,
138- simpledae_multi_oscillator .reducer .omega_1_err ,
139- simpledae_multi_oscillator .reducer .phi_1 ,
140- simpledae_multi_oscillator .reducer .phi_1_err ,
141- simpledae_multi_oscillator .reducer .lambda_1 ,
142- simpledae_multi_oscillator .reducer .lambda_1_err ,
95+ def test_asymmetry_reducer_readable_signals (simpledae , rebinning_asymmetry_reducer ):
96+ assert set (rebinning_asymmetry_reducer .additional_readable_signals (simpledae )) == {
97+ simpledae .reducer .B ,
98+ simpledae .reducer .B_err ,
99+ simpledae .reducer .A_0 ,
100+ simpledae .reducer .A_0_err ,
101+ simpledae .reducer .omega_0 ,
102+ simpledae .reducer .omega_0_err ,
103+ simpledae .reducer .phi_0 ,
104+ simpledae .reducer .phi_0_err ,
105+ simpledae .reducer .lambda_0 ,
106+ simpledae .reducer .lambda_0_err ,
143107 }
144108
145109
@@ -193,11 +157,9 @@ def test_rebin_and_sum_with_no_rebinning(asymmetry_reducer):
193157 scipp .testing .assert_allclose (result .coords ["tof" ], time )
194158
195159
196- async def test_asymmetry_reducer (simpledae_single_oscillator ):
197- simpledae_single_oscillator .trigger_and_get_specdata = AsyncMock (return_value = None )
198- simpledae_single_oscillator .reducer ._first_det .read_spectrum_dataarray = AsyncMock (
199- return_value = None
200- )
160+ async def test_asymmetry_reducer (simpledae ):
161+ simpledae .trigger_and_get_specdata = AsyncMock (return_value = None )
162+ simpledae .reducer ._first_det .read_spectrum_dataarray = AsyncMock (return_value = None )
201163
202164 with patch (
203165 "ibex_bluesky_core.devices.muon.MuonAsymmetryReducer._calculate_asymmetry" ,
@@ -215,10 +177,10 @@ async def test_asymmetry_reducer(simpledae_single_oscillator):
215177 },
216178 )
217179
218- await simpledae_single_oscillator .reducer .reduce_data (simpledae_single_oscillator )
180+ await simpledae .reducer .reduce_data (simpledae )
219181
220- assert await simpledae_single_oscillator .reducer .B .get_value () == pytest .approx (0.0 , abs = 1e-8 )
221- assert await simpledae_single_oscillator .reducer .A_0 .get_value () == pytest .approx (0.0 , abs = 1e-8 )
182+ assert await simpledae .reducer .B .get_value () == pytest .approx (0.0 , abs = 1e-8 )
183+ assert await simpledae .reducer .A_0 .get_value () == pytest .approx (0.0 , abs = 1e-8 )
222184
223185
224186async def test_asymmetry_reducer_real_data ():
@@ -281,3 +243,54 @@ async def test_asymmetry_reducer_real_data():
281243 assert await dae .reducer .omega_0 .get_value () == pytest .approx (omega_0 , abs = 1e-3 )
282244 assert await dae .reducer .phi_0 .get_value () == pytest .approx (phi_0 , abs = 1e-3 )
283245 assert await dae .reducer .lambda_0 .get_value () == pytest .approx (lambda_0 , abs = 1e-3 )
246+
247+
248+ def test_missing_parameters ():
249+ with pytest .raises (ValueError , match = r"Missing parameters: .*" ):
250+ MuonAsymmetryReducer (
251+ forward_detectors = np .array ([1 ]),
252+ backward_detectors = np .array ([2 ]),
253+ time_bin_edges = sc .linspace ("tof" , 0 , 5 , num = 20 , unit = sc .units .ns , dtype = "float64" ),
254+ prefix = "UNITTEST:" ,
255+ model = damped_oscillator_model ,
256+ fit_parameters = lmfit .Parameters (),
257+ )
258+
259+
260+ def test_calculate_asymmetry (asymmetry_reducer ):
261+ with (
262+ patch .object (asymmetry_reducer , "_rebin_and_sum" ) as rebin_sum_mock ,
263+ patch ("ibex_bluesky_core.devices.muon.calculate_polarisation" ) as calculate_asymmetry_mock ,
264+ ):
265+ fake_da = MagicMock (spec = sc .DataArray )
266+ rebin_sum_mock .return_value = sc .scalar (value = 5.0 , variance = 10.0 )
267+ asymmetry_reducer ._calculate_asymmetry (
268+ np .array ([[0 ], [0 ], [0 ]]), first_spec_dataarray = fake_da
269+ )
270+
271+ calculate_asymmetry_mock .assert_called_with (
272+ sc .scalar (value = 5.0 , variance = 10.5 ),
273+ sc .scalar (value = 5.0 , variance = 10.5 ),
274+ asymmetry_reducer ._alpha ,
275+ )
276+
277+
278+ async def test_if_fit_fails_then_get_error (asymmetry_reducer , simpledae ):
279+ await asymmetry_reducer .connect (mock = True )
280+
281+ with (
282+ patch .object (asymmetry_reducer , "_calculate_asymmetry" ),
283+ patch .object (asymmetry_reducer , "_fit_data" ) as fit_data_mock ,
284+ patch .object (simpledae , "trigger_and_get_specdata" ),
285+ patch .object (asymmetry_reducer ._first_det , "read_spectrum_dataarray" ),
286+ ):
287+ fit_data_mock .return_value = None
288+
289+ with pytest .raises (
290+ ValueError ,
291+ match = re .escape (
292+ "MuonAsymmetryReducer failed to fit asymmetry model to muon data.\n "
293+ "Check beamline setup."
294+ ),
295+ ):
296+ await asymmetry_reducer .reduce_data (simpledae )
0 commit comments