@@ -269,92 +269,53 @@ def test_psi_uncertainty():
269269 pix_area = np .full (9 , 1.0 ) * u .cm ** 2 ,
270270 )
271271
272- # symmetric image should have large uncertainty
272+ image = np .zeros ((3 , 3 ))
273+ image [0 , 0 ] = 2
274+ image [0 , 1 ] = 0
275+ image [0 , 2 ] = 0
276+ image [1 , 0 ] = 0
277+ image [1 , 1 ] = 5
278+ image [1 , 2 ] = 0
279+ image [2 , 0 ] = 0
280+ image [2 , 1 ] = 0
281+ image [2 , 2 ] = 2
282+ image = image .ravel ()
283+ hillas = hillas_parameters (geom , image )
284+ assert np .isclose (
285+ hillas .psi_uncertainty .to_value (u .deg ), 20.25711711353489 , rtol = 1e-05
286+ )
287+
273288 image = np .zeros ((3 , 3 ))
274289 image [0 , 0 ] = 5
275- image [0 , 1 ] = 5
276- image [0 , 2 ] = 5
277- image [1 , 0 ] = 5
290+ image [0 , 1 ] = 0
291+ image [0 , 2 ] = 0
292+ image [1 , 0 ] = 0
278293 image [1 , 1 ] = 10
279- image [1 , 2 ] = 5
280- image [2 , 0 ] = 5
281- image [2 , 1 ] = 5
294+ image [1 , 2 ] = 0
295+ image [2 , 0 ] = 0
296+ image [2 , 1 ] = 0
282297 image [2 , 2 ] = 5
283298 image = image .ravel ()
284299 hillas = hillas_parameters (geom , image )
285- assert hillas .psi_uncertainty .value > 1e10
286-
287- # check two images with identical morphology but different intensities
288- image1 = np .zeros ((3 , 3 ))
289- image1 [0 , 0 ] = 5
290- image1 [1 , 1 ] = 10
291- image1 [2 , 2 ] = 5
292- image2 = np .zeros ((3 , 3 ))
293- intensity_scaling = 2.0
294- image2 += image1 * intensity_scaling
295- image1 = image1 .ravel ()
296- image2 = image2 .ravel ()
297- hillas1 = hillas_parameters (geom , image1 )
298- hillas2 = hillas_parameters (geom , image2 )
299- assert (
300- abs (
301- hillas1 .psi_uncertainty .value / hillas2 .psi_uncertainty .value
302- - np .sqrt (intensity_scaling )
303- )
304- < 1e-4
300+ assert np .isclose (
301+ hillas .psi_uncertainty .to_value (u .deg ), 12.811725781509189 , rtol = 1e-05
305302 )
306303
307- # check psi uncertainty for a random image with the manual calculation
308- image = np .random .random ((3 , 3 )) * 100.0
304+ image = np .zeros ((3 , 3 ))
305+ image [0 , 0 ] = 5
306+ image [0 , 1 ] = 0
307+ image [0 , 2 ] = 2
308+ image [1 , 0 ] = 0
309+ image [1 , 1 ] = 10
310+ image [1 , 2 ] = 0
311+ image [2 , 0 ] = 0
312+ image [2 , 1 ] = 2
313+ image [2 , 2 ] = 5
309314 image = image .ravel ()
310315 hillas = hillas_parameters (geom , image )
311- # Now calculate the uncertainty manually
312- unit = geom .pix_x .unit
313- pix_x = geom .pix_x .to_value (unit )
314- pix_y = geom .pix_y .to_value (unit )
315- pix_area = geom .pix_area .to_value (unit ** 2 )
316- image = np .asanyarray (image , dtype = np .float64 )
317- # calculate the cog as the mean of the coordinates weighted with the image
318- cog_x = np .average (pix_x , weights = image )
319- cog_y = np .average (pix_y , weights = image )
320- delta_x = pix_x - cog_x
321- delta_y = pix_y - cog_y
322- cov = np .cov (delta_x , delta_y , aweights = image , ddof = 0 )
323- eig_vals , eig_vecs = np .linalg .eigh (cov )
324- # round eig_vals to get rid of nans when eig val is something like -8.47032947e-22
325- HILLAS_ATOL = np .finfo (np .float64 ).eps
326- near_zero = np .isclose (eig_vals , 0 , atol = HILLAS_ATOL )
327- eig_vals [near_zero ] = 0
328- # width and length are eigen values of the PCA
329- width , length = np .sqrt (eig_vals )
330- # psi is the angle of the eigenvector to length to the x-axis
331- vx , vy = eig_vecs [0 , 1 ], eig_vecs [1 , 1 ]
332- # psi will be consistently defined in the range (-pi/2, pi/2)
333- if length == 0 :
334- psi = psi_uncert = np .nan
335- else :
336- if vx != 0 :
337- psi = np .arctan (vy / vx )
338- else :
339- psi = np .pi / 2
340- # calculate higher order moments along shower axes
341- cos_psi = np .cos (psi )
342- sin_psi = np .sin (psi )
343- longi = delta_x * cos_psi + delta_y * sin_psi
344- trans = delta_x * - sin_psi + delta_y * cos_psi
345- # lsq solution to determine uncertainty on phi
346- W = np .diag (image / pix_area )
347- X = np .column_stack ([longi , np .ones_like (longi )])
348- lsq_cov = np .linalg .inv (X .T @ W @ X )
349- p = lsq_cov @ X .T @ W @ trans
350- # p[0] is the psi angle in the rotated frame, which should be zero.
351- # Now we add the non-zero residual psi angle in the rotated frame to psi uncertainty
352- # We also add additional uncertainty to account for elongation of the image (i.e. width / length)
353- psi_uncert = np .sqrt (lsq_cov [0 , 0 ] + p [0 ] * p [0 ]) * (
354- 1.0 + pow (np .tan (width / length * np .pi / 2.0 ), 2 )
355- )
356-
357- assert abs (hillas .psi_uncertainty .value - psi_uncert ) / psi_uncert < 0.01
316+ assert np .isclose (
317+ hillas .psi_uncertainty .to_value (u .deg ), 23.560635267712282 , rtol = 1e-05
318+ )
358319
359320
360321def test_reconstruction_in_telescope_frame (prod5_lst ):
0 commit comments