11"""Extract all the visible particles entering the volume."""
22
3- from typing import Dict , Any , TYPE_CHECKING , Tuple
3+ from typing import Dict , Any , TYPE_CHECKING , Tuple , Union , List
44
55from .utilities .gcd_hull import GCD_hull
66from .i3extractor import I3Extractor
@@ -45,6 +45,8 @@ def __init__(
4545 extractor_name: Name of the extractor.
4646 daughters: If True, only consider particles that are
4747 daughters of the primary.
48+ highest_energy_primary: If True, only consider particles that are
49+ daughters of the highest energy primary.
4850 """
4951 # Member variable(s)
5052 self .hull = hull
@@ -62,12 +64,6 @@ def __call__(self, frame: "icetray.I3Frame") -> Dict[str, Any]:
6264 tree_copy = deepcopy (frame [self .mctree ])
6365 if self .frame_contains_info (frame ):
6466
65- e_entrance_track , e_deposited_track = self .total_track_energy (
66- frame
67- )
68-
69- e_deposited_cascade = self .total_cascade_energy (frame )
70-
7167 primary_energy = sum (
7268 [
7369 p .energy
@@ -81,8 +77,32 @@ def __call__(self, frame: "icetray.I3Frame") -> Dict[str, Any]:
8177 )
8278 ]
8379 )
80+
81+ e_entrance_track , e_deposited_track = self .total_track_energy (
82+ frame
83+ )
84+
85+ e_deposited_cascade = self .total_cascade_energy (frame )
86+
8487 e_total = e_entrance_track + e_deposited_cascade
8588
89+ if all (
90+ (
91+ not self .daughters ,
92+ not self .highest_energy_primary ,
93+ e_total == 0 ,
94+ )
95+ ):
96+ self .warning (
97+ "No energy deposited in the hull, "
98+ "Think about increasing the padding."
99+ f"\n Current padding: { self .hull .padding } "
100+ f"\n Total energy: { e_total } "
101+ f"\n Track energy: { e_entrance_track } "
102+ f"\n Cascade energy: { e_deposited_cascade } "
103+ f"\n Event header: { frame ['I3EventHeader' ]} "
104+ )
105+
86106 if self .daughters :
87107 assert e_total <= (
88108 primary_energy * (1 + 1e-6 )
@@ -138,7 +158,9 @@ def total_track_energy(
138158 """Get the total energy of track particles on entrance."""
139159 e_entrance = 0
140160 e_deposited = 0
141- primaries = self .get_primaries (frame , self .daughters )
161+ primaries = self .get_primaries (
162+ frame , self .daughters , self .highest_energy_primary
163+ )
142164 primaries = self .check_primary_energy (frame , primaries )
143165
144166 MMCTrackList = frame [self .mmctracklist ]
@@ -151,13 +173,18 @@ def total_track_energy(
151173 ]
152174 MMCTrackList = simclasses .I3MMCTrackList (MMCTrackList )
153175
154- track_list = MuonGun .Track .harvest (frame [self .mctree ], MMCTrackList )
155- track_ids = np .array ([track .id for track in track_list ])
176+ track_list = np .array (
177+ MuonGun .Track .harvest (frame [self .mctree ], MMCTrackList )
178+ )
156179
157- total_tracks = len (track_list )
158- exclude_ids = set ()
159180 while len (track_list ) > 0 :
160181 track = track_list [0 ]
182+ track_list = track_list [1 :]
183+ try :
184+ particle = frame [self .mctree ].get_particle (track .id )
185+ except RuntimeError :
186+ # If the particle does not exist in the mctree, that means a partcle further up was processed and therefore it should not be counted
187+ continue
161188
162189 # Find distance to entrance and exit from sampling volume
163190 intersections = self .hull .surface .intersection (
@@ -167,17 +194,8 @@ def total_track_energy(
167194 # Check if the track actually enters the volume
168195 if not (
169196 np .isfinite (intersections .first )
170- and (
171- intersections .first
172- < frame [self .mctree ].get_particle (track .id ).length
173- )
197+ and (intersections .first < particle .length )
174198 ):
175- track_list = track_list [1 :]
176- track_ids = track_ids [1 :]
177- frame [self .mctree ].erase (track .id )
178- self ._logger .debug (
179- f"Remaining tracks: { len (track_list )} /{ total_tracks } "
180- )
181199 continue
182200
183201 # Get the corresponding energies
@@ -187,19 +205,10 @@ def total_track_energy(
187205 # Accumulate
188206 e_deposited += e0 - e1
189207 e_entrance += e0
190- # erase particle and children
191- exclude_ids .update (
192- [c .id for c in frame [self .mctree ].children (track .id )]
193- )
194- exclude_ids .add (track .id )
195- frame [self .mctree ].erase_children (track .id )
208+ # get descendant ids
209+ # erase particle and children from mctree
196210 frame [self .mctree ].erase (track .id )
197- track_mask = [tid not in exclude_ids for tid in track_ids ]
198- track_list = list (np .array (track_list )[track_mask ])
199- track_ids = track_ids [track_mask ]
200- self ._logger .debug (
201- f"Remaining tracks: { len (track_list )} /{ total_tracks } "
202- )
211+
203212 # Sanity check ensuring no double counting
204213 if self .daughters :
205214 assert e_entrance <= sum (
@@ -215,86 +224,50 @@ def total_cascade_energy(
215224 frame : "icetray.I3Frame" ,
216225 ) -> float :
217226 """Get the total energy of cascade particles on entrance."""
218- e_deposited = 0
219-
220- particles = self . get_primaries ( frame , self .daughters )
221-
222- particles = np . array ([ p for p in particles if ( not p . is_track )] )
227+ particles = np . array (
228+ self . get_primaries (
229+ frame , self .daughters , self . highest_energy_primary
230+ )
231+ )
223232
224233 if len (particles ) == 0 :
225- return e_deposited
226-
227- pos , direc , length = np .asarray (
228- [
229- [
230- np .array (p .pos ),
231- np .array ([p .dir .x , p .dir .y , p .dir .z ]),
232- p .length ,
233- ]
234- for p in particles
235- ],
236- dtype = object ,
237- ).T
234+ return 0.0
235+
236+ pos_list , direc_list , length_list , cascade_bool , energies = (
237+ [],
238+ [],
239+ [],
240+ [],
241+ [],
242+ )
243+ while len (particles ) > 0 :
244+ p = particles [0 ]
245+ particles = particles [1 :]
246+ p_children = dataclasses .I3MCTree .get_daughters (
247+ frame [self .mctree ], p
248+ )
249+ if len (p_children ) > 0 :
250+ particles = np .concatenate ((particles , p_children ))
251+ continue
252+ elif (
253+ p .is_track
254+ or p .shape == dataclasses .I3Particle .ParticleShape .Dark
255+ ):
256+ continue
238257
239- length = length .astype (float )
258+ pos_list .append (np .array (p .pos ))
259+ direc_list .append (np .array ([p .dir .x , p .dir .y , p .dir .z ]))
260+ length_list .append (p .length )
261+ cascade_bool .append (p .is_cascade )
262+ energies .append (p .energy )
240263
241- # replace length nan with 0
264+ length = np . array ( length_list ). astype ( float )
242265 length [np .isnan (length )] = 0
243- pos = pos + direc * length
244- pos = np .stack (pos )
245-
246- in_volume = self .hull .point_in_hull (pos )
247- particles = particles [in_volume ]
248-
249- for particle in particles :
250- if particle .is_cascade :
251- e_deposited += particle .energy
252- else :
253- daughters = dataclasses .I3MCTree .get_daughters (
254- frame [self .mctree ], particle
255- )
256-
257- pos , direc , length = np .asarray (
258- [
259- [
260- np .array (p .pos ),
261- np .array ([p .dir .x , p .dir .y , p .dir .z ]),
262- p .length ,
263- ]
264- for p in daughters
265- ],
266- dtype = object ,
267- ).T
268-
269- length = length .astype (float )
270-
271- # replace length nan with 0
272- length [np .isnan (length )] = 0
273- pos = pos + direc * length
274- pos = np .stack (pos )
275-
276- in_volume = self .hull .point_in_hull (pos )
277- daughters = np .array (daughters )[in_volume ]
278-
279- while len (daughters ) > 0 :
280- daughter = daughters [0 ]
281- daughters = daughters [1 :]
282- length = daughter .length
283- if daughter .is_track :
284- continue
285- if length == np .nan :
286- length = 0
287- if daughter .is_cascade and (
288- daughter .shape != dataclasses .I3Particle .ParticleShape .Dark
289- ):
290- e_deposited += daughter .energy
291- else :
292- daughters = np .concatenate (
293- [
294- daughters ,
295- dataclasses .I3MCTree .get_daughters (
296- frame [self .mctree ], daughter
297- ),
298- ]
299- )
300- return e_deposited
266+ pos = np .array (pos_list )
267+ direc = np .array (direc_list )
268+ cascade_bool = np .array (cascade_bool )
269+ energies = np .array (energies )
270+ pos = (pos .T + direc .T * length ).T
271+ in_hull = self .hull .point_in_hull (pos )
272+
273+ return np .sum (energies [cascade_bool & in_hull ])
0 commit comments