Coverage for src/pyhiperta/leakage.py: 100%

7 statements  

« prev     ^ index     » next       coverage.py v7.15.3, created at 2026-08-07 14:41 +0000

1# Copyright 2026 CNRS 

2# This software is distributed under the terms of the CeCILL-C free software license. 

3 

4"""Leakage (image outer rings' charge) computation.""" 

5 

6import numpy as np 

7 

8 

9def leakage(waveforms: np.ndarray, intensity: np.ndarray, leakage_mask: np.ndarray) -> np.ndarray: 

10 """Compute the leakage 1 and 2, and their intensity. 

11 

12 The leakage 1, respectively 2, is the number of pixels that are not 0 after cleaning on the outermost, 

13 respectively 2nd outermost, ring of the camera. 

14 The leakage intensity is the charge of the leakage pixels, so leakage 1, resp. 2, intensity is the sum of the signal 

15 in all pixels of the outermost, resp. 2nd outermost, ring of the camera. 

16 

17 Parameters 

18 ---------- 

19 waveforms : np.ndarray 

20 1D shower image(s). If a single image is provided the shape must be (N_pixels,). If a 

21 batch of images is provided, the shape should be (N_batch, N_pixels). 

22 Shape: ([N_batch,] N_pixels) 

23 intensity : np.ndarray 

24 Total charge of each waveform. 

25 Shape: ([B_batch,] 1) 

26 leakage_mask : np.ndarray 

27 Boolean array with value True for pixels on the outermost rings of the camera, False otherwise. 

28 leakage_mask[0, :] selects the outermost ring (leakage 1) while leakage_mask[1, :] selects the 

29 second outermost ring (leakage 2). 

30 Shape: (2, N_pixels) 

31 

32 Returns 

33 ------- 

34 np.ndarray 

35 Leakage array. This is a 1D array with shape (4,) if a single image was 

36 provided, or a 2D array with shape (N_batch, 4) if a batch of images was provided. The 

37 parameters are ordered like so: 

38 leakage[..., 0]: leakage 1: number of pixels with signal on the outermost ring of the camera, normalized by 

39 the number of pixels with signal. 

40 leakage[..., 1]: leakage 2: number of pixels with signal on the 1st and 2nd outermost ring of 

41 the camera, normalized by the number of pixels with signal. 

42 leakage[..., 3]: leakage 1 intensity: Waveform charge contained in the outermost ring of the camera, 

43 normalized by the total charge in the camera 

44 leakage[..., 4]: leakage 2 intensity: Waveform charge contained in the 1st and 2nd outermost ring of the camera, 

45 normalized by the total charge in the camera 

46 """ 

47 # number of pixels for normalization 

48 nb_pixels = np.count_nonzero(waveforms, axis=-1, keepdims=True).astype(np.float32) 

49 # apply leakage mask (both leakage 1 and leakage 2 with broadcasting) 

50 leakage_charge = waveforms[..., np.newaxis, :] * leakage_mask 

51 # normalized number of pixels with signal in leakage for both leakages 

52 leakage = np.count_nonzero(leakage_charge, axis=-1).astype(np.float32) / nb_pixels 

53 # normalized leakage intensity for both leakage (add axis to intensity for broadcasting!) 

54 leakage_intensity = leakage_charge.sum(axis=-1, dtype=np.float32) / intensity[..., np.newaxis] 

55 return np.concatenate([leakage, leakage_intensity], axis=-1) 

56 # return np.stack([leakage[..., 0], leakage[..., 1], leakage_intensity[..., 0], leakage_intensity[..., 1]], axis=-1)