Skip to content

Commit c701098

Browse files
Merge pull request #9 from dkoh0207/develop
Added new cut varaibles and heuristics for nue selection.
2 parents 83653fc + b44c150 commit c701098

File tree

12 files changed

+1198
-166
lines changed

12 files changed

+1198
-166
lines changed

spine/data/out/interaction.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,14 @@ def from_particles(cls, particles):
279279
@inherit_docstring(RecoBase, InteractionBase)
280280
class RecoInteraction(InteractionBase, RecoBase):
281281
"""Reconstructed interaction information."""
282+
283+
leading_shower_vertex_distance: float = -1.
284+
leading_shower_axial_spread: float = None
285+
leading_shower_directional_spread: float = None
286+
leading_shower_start_dedx: float = None
287+
leading_shower_trunk_straightness: float = None
288+
leading_shower_adjacent_bragg_pearsonr: float = None
289+
leading_shower_split_angle: float = None
282290

283291
# Attributes that must never be stored to file
284292
_skip_attrs = (
@@ -307,7 +315,145 @@ def __str__(self):
307315
Basic information about the interaction properties
308316
"""
309317
return 'Reco' + super().__str__()
318+
319+
320+
@property
321+
def leading_shower(self):
322+
"""Leading shower of this interaction
310323
324+
Returns
325+
-------
326+
RecoParticle
327+
Primary shower with the highest reco kinetic energy.
328+
"""
329+
showers = [part for part in self.primary_particles if part.shape == 0]
330+
if len(showers) == 0:
331+
return None
332+
return max(showers, key=lambda x: x.ke)
333+
334+
@leading_shower.setter
335+
def leading_shower(self, leading_shower):
336+
pass
337+
338+
@property
339+
def leading_shower_start_dedx(self):
340+
"""Leading shower dE/dx
341+
342+
Returns
343+
-------
344+
float
345+
dE/dx of the leading shower
346+
"""
347+
leading_shower = self.leading_shower
348+
if leading_shower is None:
349+
return -1.
350+
return leading_shower.start_dedx
351+
352+
@leading_shower_start_dedx.setter
353+
def leading_shower_start_dedx(self, leading_shower_start_dedx):
354+
pass
355+
356+
@property
357+
def leading_shower_directional_spread(self):
358+
"""Distance-weighted mean cosine distance from mean direction
359+
of the leading shower.
360+
361+
See ShowerSpreadProcessor under post/reco/shower.py for more details.
362+
363+
Returns
364+
-------
365+
float
366+
Measure of shower directional spread
367+
"""
368+
leading_shower = self.leading_shower
369+
if leading_shower is None:
370+
return -1.
371+
return leading_shower.directional_spread
372+
373+
@leading_shower_directional_spread.setter
374+
def leading_shower_directional_spread(self, leading_shower_directional_spread):
375+
pass
376+
377+
@property
378+
def leading_shower_axial_spread(self):
379+
"""Pearson R correlation coefficient between the distance from
380+
shower start to a shower point (x) and the perpendicular distance
381+
from the shower point to the shower axis.
382+
383+
Returns
384+
-------
385+
coeff
386+
Measure of shower axial spread
387+
"""
388+
leading_shower = self.leading_shower
389+
if leading_shower is None:
390+
return -np.inf
391+
return leading_shower.axial_spread
392+
393+
@leading_shower_axial_spread.setter
394+
def leading_shower_axial_spread(self, leading_shower_axial_spread):
395+
pass
396+
397+
@property
398+
def leading_shower_trunk_straightness(self):
399+
"""Measure of how straight the trunk of the leading shower is.
400+
401+
Returns
402+
-------
403+
float
404+
Measure of shower trunk straightness (between 0 and 1)
405+
"""
406+
leading_shower = self.leading_shower
407+
if leading_shower is None:
408+
return -1.
409+
return leading_shower.trunk_straightness
410+
411+
@leading_shower_trunk_straightness.setter
412+
def leading_shower_trunk_straightness(self, leading_shower_trunk_straightness):
413+
pass
414+
415+
@property
416+
def leading_shower_adjacent_bragg_pearsonr(self):
417+
"""Pearson R correlation coefficient between:
418+
x: distance from shower start to a point on a track adjacent to the
419+
shower.
420+
y: the local dedx value at that point.
421+
422+
If this value is strongly negative (close to -1), it suggests that
423+
the shower is likely to be adjacent to a bragg peak.
424+
425+
Returns
426+
-------
427+
float
428+
Measure of how likely the leading shower is adjacent
429+
to a bragg peak (between -1 and 1)
430+
"""
431+
leading_shower = self.leading_shower
432+
if leading_shower is None:
433+
return -np.inf
434+
return leading_shower.adjacent_bragg_pearsonr
435+
436+
@leading_shower_adjacent_bragg_pearsonr.setter
437+
def leading_shower_adjacent_bragg_pearsonr(self, leading_shower_adjacent_bragg_pearsonr):
438+
pass
439+
440+
@property
441+
def leading_shower_split_angle(self):
442+
"""Angle between the two branches of the leading shower.
443+
444+
Returns
445+
-------
446+
float
447+
Angle between the two branches of the leading shower
448+
"""
449+
leading_shower = self.leading_shower
450+
if leading_shower is None:
451+
return -1.
452+
return leading_shower.split_angle
453+
454+
@leading_shower_split_angle.setter
455+
def leading_shower_split_angle(self, leading_shower_split_angle):
456+
pass
311457

312458
@dataclass(eq=False)
313459
@inherit_docstring(TruthBase, InteractionBase)

spine/data/out/particle.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,13 +218,30 @@ class RecoParticle(ParticleBase, RecoBase):
218218
shower_split_angle: float
219219
Estimate of the opening angle of the shower. If particle is not a
220220
shower, then this is set to -1. (units of degrees)
221+
shower_spread: float
222+
Estimate of the spread of the shower (roughly mean cosine
223+
from mean direction)
224+
axial_pearsonr: float
225+
Pearson correlation coefficient of the axial profile of the shower.
226+
trunk_validity: float
227+
Explained variance ratio of the trunk of the particle.
228+
shower_dedx: float
229+
dE/dx of the shower in MeV/cm, using direction reconstruction.
230+
adjacent_bragg: float
231+
A number that represents whether the shower has a adjacent track
232+
with a Bragg peak.
221233
"""
222234
pid_scores: np.ndarray = None
223235
primary_scores: np.ndarray = None
224236
ppn_ids: np.ndarray = None
225237
ppn_points: np.ndarray = None
226238
vertex_distance: float = -1.
227-
shower_split_angle: float = -1.
239+
split_angle: float = -1.
240+
directional_spread: float = -1.
241+
axial_spread: float = -np.inf
242+
trunk_straightness: float = -1.
243+
start_dedx: float = -1.
244+
adjacent_bragg_pearsonr: float = -np.inf
228245

229246
# Fixed-length attributes
230247
_fixed_length_attrs = (

spine/driver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -448,7 +448,7 @@ def get_prefixes(file_paths, split_output):
448448
log_prefix += f'--{suffix}'
449449

450450
# Truncate file names that are too long
451-
max_length = 230
451+
max_length = 150
452452
if len(log_prefix) > max_length:
453453
log_prefix = log_prefix[:max_length-3] + '---'
454454

spine/post/reco/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@
1313
from .kinematics import *
1414
from .label import *
1515
from .shower import *
16+
from .topology import *

spine/post/reco/geometry.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ class ContainmentProcessor(PostBase):
2626

2727
def __init__(self, margin, cathode_margin=None, detector=None,
2828
geometry_file=None, mode='module',
29-
allow_multi_module=False, min_particle_sizes=0,
29+
allow_multi_module=False, skip_showers=False,
30+
min_particle_sizes=0,
3031
obj_type=('particle', 'interaction'),
3132
truth_point_mode='points', run_mode='both'):
3233
"""Initialize the containment conditions.
@@ -63,6 +64,8 @@ def __init__(self, margin, cathode_margin=None, detector=None,
6364
Note that this does not guarantee containment within the detector.
6465
allow_multi_module : bool, default False
6566
Whether to allow particles/interactions to span multiple modules
67+
skip_showers : bool, default False
68+
Whether to skip checking containment for showers
6669
min_particle_sizes : Union[int, dict], default 0
6770
When checking interaction containment, ignore particles below the
6871
size (in voxel count) specified by this parameter. If specified
@@ -87,6 +90,7 @@ def __init__(self, margin, cathode_margin=None, detector=None,
8790

8891
# Store parameters
8992
self.allow_multi_module = allow_multi_module
93+
self.skip_showers = skip_showers
9094

9195
# Store the particle size thresholds in a dictionary
9296
if np.isscalar(min_particle_sizes):
@@ -118,6 +122,12 @@ def process(self, data):
118122
for obj in data[k]:
119123
# Make sure the particle coordinates are expressed in cm
120124
self.check_units(obj)
125+
126+
# Skip showers for containment check if requested
127+
if self.skip_showers:
128+
if hasattr(obj, 'shape') and obj.shape == 0:
129+
obj.is_contained = True
130+
continue
121131

122132
# Get point coordinates
123133
points = self.get_points(obj)

0 commit comments

Comments
 (0)