Utilities: recovering the energies of muons at depth

Muons are ranged particles, so both their position and energy are a function of time. The position is easy enough to represent in an I3Particle; by dint of special relativity, the position be approximated by speed-of-light displacement along the muon’s initial direction from an arbitrary reference point. Information about a muon’s true energy at any given moment, however, is scattered in multiple locations in each frame. These are:

  • The initial energy at the reference point, stored in the I3Particle that represents the muon itself

  • The energy the muon has when it enters and exits the MMC simulation volume (typically a 1600 x 800 meter upright cylinder), stored in the I3MMCTrack corresponding to the muon.

  • The sizes of the stochastic energy losses inside the MMC simulation volume. These are stored as I3Particle attached as daughters of the I3Particle represending the muon in the I3MCTree

While it is in principle straightforward to recover the energy of a simulated muon at any point along its path inside the MMC simulation volume from these scattered data structures, it is also extremely tedious. MuonGun includes a utility class, Track, to automate this task.

class icecube.MuonGun.Track

A subclass of I3Particle that includes the particle’s energy losses

classmethod harvest(frame)

Assemble a collection of Track from the MMCTrackList and I3MCTree in frame

get_energy(displacement)

Get the energy of the muon displacement meters from its starting position

This class can be used, for example, to find the total energy losses (stochastic and continuous) of all muons within some volume:

from icecube import MuonGun, simclasses

# A surface approximating the actual detector (make it smaller if you only care e.g. about DeepCore)
surface = MuonGun.Cylinder(1000,500)

edep = 0
for track in MuonGun.Track.harvest(frame['I3MCTree'], frame['MMCTrackList']):
    # Find distance to entrance and exit from sampling volume
    intersections = surface.intersection(track.pos, track.dir)
    # Get the corresponding energies
    e0, e1 = track.get_energy(intersections.first), track.get_energy(intersections.second)
    # Accumulate
    edep +=  (e0-e1)

There is also a convenience function, muons_at_surface(), that uses Track to “shift” all the muons in a frame forward so that their positions, times, and energies correspond to their intersections with a given surface.

icecube.MuonGun.muons_at_surface(frame, surface)
Parameters:
  • frame – an I3Frame

  • surface – a Surface

Returns:

a list of I3Particles with positions, times, and energies that correspond to their intersection with surface. Muons that range out before reaching surface are not included.

This can be used to quickly obtain the true multiplicity of a muon bundle when it enters the detector:

surface = MuonGun.Cylinder(1000,500)
multiplicity = len(MuonGun.muons_at_surface(frame, surface))