Beispiel #1
0
    def DAQ(self, frame):
        """Process DAQ frame

        Parameters
        ----------
        frame : I3Frame
            The current Q-frame.
        """

        mc_tree = frame[self.mc_tree_name]

        # get in-ice muon:
        muon = mu_utils.get_muon_of_inice_neutrino(frame)

        if muon is not None:
            self.make_plots(mc_tree,
                            muon,
                            file=os.path.join(
                                self.plot_dir,
                                'energy_loss_cdf_{:08d}.png'.format(
                                    self.counter)))

        # push frame to next modules
        self.PushFrame(frame)
        self.counter += 1
Beispiel #2
0
    def Physics(self, frame):

        mese_dict = {
            'n_files': self._n_files,
            'n_events_per_run': self._n_events_per_run,
        }

        # get MC info
        energy_true = frame['MCPrimary'].energy
        zenith_true = frame['MCPrimary'].dir.zenith
        azimuth_true = frame['MCPrimary'].dir.azimuth

        # -------
        # NuGen
        # -------
        if self._dataset_type in ['nugen', 'genie']:
            # get oneweight / n_gen
            oneweight = frame['I3MCWeightDict']['OneWeight'] / self._ngen
            true_type = frame['I3MCWeightDict']['PrimaryNeutrinoType']
            is_tau = (np.abs(true_type) == 16).all()

            # calculate astrophysical weights
            mese_dict['weight_E269'] = 2.09e-18 * oneweight * (energy_true /
                                                               1e5)**-2.69
            mese_dict['weight_E250'] = 2.23e-18 * oneweight * (energy_true /
                                                               1e5)**-2.5

            # calculate atmospheric weights
            if is_tau:
                mese_dict['weight_conv'] = oneweight * atmosphericFlux(
                    neutrinoEnergy=energy_true,
                    neutrinoZenith=zenith_true,
                    neutrinoType=true_type,
                    atmFluxConv=None,
                    atmFluxPrompt=None,
                ) * 2. * self.conv_flux_multiplier

                mese_dict['weight_prompt'] = oneweight * atmosphericFlux(
                    neutrinoEnergy=energy_true,
                    neutrinoZenith=zenith_true,
                    neutrinoType=true_type,
                    atmFluxConv=None,
                    atmFluxPrompt=None,
                ) * 2. * self.prompt_flux_multiplier
            else:
                mese_dict['weight_conv'] = oneweight * atmosphericFlux(
                    neutrinoEnergy=energy_true,
                    neutrinoZenith=zenith_true,
                    neutrinoType=true_type,
                    atmFluxConv=self.honda,
                    atmFluxPrompt=None,
                ) * 2. * self.conv_flux_multiplier
                mese_dict['weight_prompt'] = oneweight * atmosphericFlux(
                    neutrinoEnergy=energy_true,
                    neutrinoZenith=zenith_true,
                    neutrinoType=true_type,
                    atmFluxConv=None,
                    atmFluxPrompt=self.enberg,
                ) * 2. * self.prompt_flux_multiplier

            # ---------------------
            # Atmospheric Self Veto
            # ---------------------
            # get true_depth
            if 'IntersectionPoint' in frame:
                true_depth = frame['IntersectionPoint'].z
            else:
                muon = mu_utils.get_muon_of_inice_neutrino(frame)
                tau = tau_utils.get_tau_of_inice_neutrino(frame)

                if muon is not None:
                    # found a muon
                    entry = self._get_muon_entry(frame, muon)
                    true_depth = entry.z

                elif tau is not None:
                    # found a tau
                    entry = self._get_particle_entry(tau)
                    true_depth = entry.z
                else:

                    # no muon or tau exists: cascade
                    cascade = get_cascade_of_primary_nu(frame,
                                                        frame['MCPrimary'],
                                                        convex_hull=None,
                                                        extend_boundary=800)[0]

                    if cascade is not None:
                        true_depth = cascade.pos.z
                    else:
                        cascade = get_cascade_of_primary_nu(
                            frame,
                            frame['MCPrimary'],
                            convex_hull=None,
                            extend_boundary=float('inf'))[0]

                        # Muon coming out of hadronic shower?
                        daughters = frame['I3MCTree'].get_daughters(cascade)

                        # collect possible muons from daughters of daughters
                        # e.g. Nu -> Nu + Hadrons -> Mu
                        muons = []
                        for d in daughters:
                            muons.extend([
                                m for m in frame['I3MCTree'].get_daughters(d)
                                if mu_utils.is_muon(m)
                            ])
                        if muons:
                            # pick highest energy muon
                            indices = np.argsort([m.energy for m in muons])
                            muon = muons[indices[-1]]
                            entry = self._get_muon_entry(frame, muon)
                            true_depth = entry.z
                        else:
                            true_depth = cascade.pos.z

            # apply self veto
            veto_args = (true_type, energy_true, np.cos(zenith_true),
                         1950. - true_depth)

            if 'IsHese' in frame:
                if frame['IsHese'].value:
                    mese_dict['veto_conv'] = self.honda_veto_hese(*veto_args)
                    mese_dict['veto_prompt'] = self.enberg_veto_hese(
                        *veto_args)
                else:
                    mese_dict['veto_conv'] = self.honda_veto_mese(*veto_args)
                    mese_dict['veto_prompt'] = self.enberg_veto_mese(
                        *veto_args)

            else:
                log_warn('WARNING: IsHese does not exist. Using MESE veto')
                mese_dict['veto_conv'] = self.honda_veto_mese(*veto_args)
                mese_dict['veto_prompt'] = self.honda_veto_mese(*veto_args)

            mese_dict['weight_conv'] *= mese_dict['veto_conv']
            mese_dict['weight_prompt'] *= mese_dict['veto_prompt']
            # ---------------------

        # -------
        # MuonGun
        # -------
        elif self._dataset_type == 'muongun':
            if 'MuonWeight_GaisserH4a' in frame:
                # --- Where does magic number of 1.6 come from? MuonMultiplier
                mese_dict['muon_weight'] = \
                    frame['MuonWeight_GaisserH4a'].value * 1.6 / self._ngen

        # -----------------
        # Experimental Data
        # -----------------
        elif self._dataset_type == 'data':
            mjd = frame['I3EventHeader'].start_time.mod_julian_day_double

        # -----------------------------------------------------
        # final track cut:
        # drop low energy downgoing tracks and duplicate events
        # -----------------------------------------------------
        try:
            # get TrackFit_zenith
            TrackFit_zenith = frame['TrackFit'].dir.zenith

            # get energy_millipede
            energy_millipede = frame['MillipedeDepositedEnergy'].value

            # mask events
            track_mask = data_dict['is_cascade_reco'] | \
                ~((np.cos(TrackFit_zenith) > 0.3) & (energy_millipede < 10e3))

            if self._dataset_type in ['muongun', 'nugen', 'genie']:
                uniq_mask = np.r_[True, np.diff(energy_true) != 0]
            else:
                uniq_mask = np.r_[True, np.diff(mjd) != 0]
            mese_dict['passed_final_track_cut'] = track_mask & uniq_mask
        except Exception as e:
            # log_warn(e)
            pass
        # -----------------------------------------------------
        for k, item in mese_dict.items():
            mese_dict[k] = float(item)
        frame[self._output_key] = dataclasses.I3MapStringDouble(mese_dict)

        self.PushFrame(frame)
Beispiel #3
0
def get_cascade_labels(frame,
                       primary,
                       convex_hull,
                       extend_boundary=0,
                       track_length_threshold=30):
    """Get general cascade labels.

    Parameters
    ----------
    frame : I3Frame
        Current I3Frame needed to retrieve I3MCTree
    primary : I3Particle
        Primary Nu Particle for which the cascade interaction is returned.
    convex_hull : scipy.spatial.ConvexHull, optional
        Defines the desired convex volume.
        Will be used to compute muon entry point for an entering muon.
    extend_boundary : float, optional
        Extend boundary of convex_hull by this distance [in meters].
    track_length_threshold : int, optional
        The miminum length (in meter) of a cascade/muon after which it is
        considered as a track event.

    Returns
    -------
    I3MapStringDouble
        Labels for cascade of primary neutrino.

    Raises
    ------
    ValueError
        Description
    """
    labels = dataclasses.I3MapStringDouble()

    labels['num_coincident_events'] = \
        general.get_num_coincident_events(frame)

    bundle_info = get_muon_bundle_information(frame=frame,
                                              convex_hull=convex_hull)
    for k in [
            'leading_energy_rel_entry', 'num_muons_at_entry',
            'num_muons_at_entry_above_threshold'
    ]:
        labels[k] = bundle_info[k]

    if not np.isfinite(labels['leading_energy_rel_entry']):
        labels['leading_energy_rel_entry'] = 0.

    labels['TotalDepositedEnergy'] = get_total_deposited_energy(
        frame, extend_boundary=300)
    labels['PrimaryEnergy'] = primary.energy
    labels['PrimaryAzimuth'] = primary.dir.azimuth
    labels['PrimaryZenith'] = primary.dir.zenith
    labels['PrimaryDirectionX'] = primary.dir.x
    labels['PrimaryDirectionY'] = primary.dir.y
    labels['PrimaryDirectionZ'] = primary.dir.z

    # set pid variables to false per default
    labels['p_starting'] = 0
    labels['p_starting_300m'] = 0
    labels['p_starting_glashow'] = 0
    labels['p_starting_nc'] = 0
    labels['p_starting_cc'] = 0
    labels['p_starting_cc_e'] = 0
    labels['p_starting_cc_mu'] = 0
    labels['p_starting_cc_tau'] = 0
    labels['p_starting_cc_tau_muon_decay'] = 0
    labels['p_starting_cc_tau_double_bang'] = 0

    labels['p_entering'] = 0
    labels['p_entering_muon_single'] = 0
    labels['p_entering_muon_single_stopping'] = 0
    labels['p_entering_muon_bundle'] = 0

    labels['p_outside_cascade'] = 0

    if primary.is_neutrino:
        # --------------------
        # NuGen dataset
        # --------------------
        mctree = frame['I3MCTree']
        cascade = get_cascade_of_primary_nu(frame,
                                            primary,
                                            convex_hull=None,
                                            extend_boundary=extend_boundary)[0]

        # ---------------------------
        # 300m detector boundary test
        # ---------------------------
        cascade_300 = get_cascade_of_primary_nu(frame,
                                                primary,
                                                convex_hull=None,
                                                extend_boundary=300)[0]
        if cascade_300 is not None:
            labels['p_starting_300m'] = 1
        # ---------------------------

        if cascade is None:
            # --------------------
            # not a starting event
            # --------------------
            muon = mu_utils.get_muon_of_inice_neutrino(frame)

            if muon is None:
                tau = tau_utils.get_tau_of_inice_neutrino(frame)

                if tau is None:
                    # --------------------
                    # Cascade interaction outside of defined volume
                    # Note: this could still be a muon created in a hadronic
                    # shower that can enter the detector and look like a
                    # normal track event! toDo: check this?
                    # --------------------
                    cascade = get_cascade_of_primary_nu(
                        frame,
                        primary,
                        convex_hull=None,
                        extend_boundary=float('inf'),
                        sanity_check=False)[0]

                    labels['p_outside_cascade'] = 1
                    labels['VertexX'] = cascade.pos.x
                    labels['VertexY'] = cascade.pos.y
                    labels['VertexZ'] = cascade.pos.z
                    labels['VertexTime'] = cascade.time
                    labels['EnergyVisible'] = cascade.energy
                    labels['Length'] = cascade.length
                    labels['LengthInDetector'] = \
                        mu_utils.get_muon_track_length_inside(cascade,
                                                              convex_hull)
                else:
                    # --------------------
                    # CC Tau interaction
                    # --------------------
                    entry, time, energy = get_tau_entry_info(
                        frame, tau, convex_hull)
                    labels['p_entering'] = 1
                    labels['VertexX'] = entry.x
                    labels['VertexY'] = entry.y
                    labels['VertexZ'] = entry.z
                    labels['VertexTime'] = time
                    labels['EnergyVisible'] = energy
                    labels['Length'] = tau.length
                    labels['LengthInDetector'] = \
                        mu_utils.get_muon_track_length_inside(tau, convex_hull)
            else:
                # ------------------------------
                # NuMu CC Muon entering detector
                # ------------------------------
                entry, time, energy = mu_utils.get_muon_entry_info(
                    frame, muon, convex_hull)
                labels['p_entering'] = 1
                labels['p_entering_muon_single'] = 1
                labels['p_entering_muon_single_stopping'] = \
                    mu_utils.is_stopping_muon(muon, convex_hull)
                labels['VertexX'] = entry.x
                labels['VertexY'] = entry.y
                labels['VertexZ'] = entry.z
                labels['VertexTime'] = time
                labels['EnergyVisible'] = energy
                labels['Length'] = muon.length
                labels['LengthInDetector'] = \
                    mu_utils.get_muon_track_length_inside(muon, convex_hull)

        else:
            # --------------------
            # starting NuGen event
            # --------------------
            labels['VertexX'] = cascade.pos.x
            labels['VertexY'] = cascade.pos.y
            labels['VertexZ'] = cascade.pos.z
            labels['VertexTime'] = cascade.time
            labels['EnergyVisible'] = cascade.energy
            labels['Length'] = cascade.length
            labels['LengthInDetector'] = \
                mu_utils.get_muon_track_length_inside(cascade, convex_hull)

            labels['p_starting'] = 1

            if frame['I3MCWeightDict']['InteractionType'] == 1:
                # charged current
                labels['p_starting_cc'] = 1

                if cascade.type_string[:3] == 'NuE':
                    # cc NuE
                    labels['p_starting_cc_e'] = 1

                elif cascade.type_string[:4] == 'NuMu':
                    # cc NuMu
                    labels['p_starting_cc_mu'] = 1

                elif cascade.type_string[:5] == 'NuTau':
                    # cc Tau
                    labels['p_starting_cc_tau'] = 1

                    nu_tau = get_interaction_neutrino(
                        frame,
                        primary,
                        convex_hull=None,
                        extend_boundary=extend_boundary)
                    tau = [
                        t for t in mctree.get_daughters(nu_tau)
                        if t.type_string in ['TauMinus', 'TauPlus']
                    ]

                    assert len(tau) == 1, 'Expected exactly 1 tau!'

                    mu = [
                        m for m in mctree.get_daughters(tau[0])
                        if m.type_string in ['MuMinus', 'MuPlus']
                    ]

                    if len(mu) > 0:
                        # tau decays into muon: No Double bang signature!
                        labels['p_starting_cc_tau_muon_decay'] = 1
                    else:
                        # Double bang signature
                        labels['p_starting_cc_tau_double_bang'] = 1

                else:
                    raise ValueError('Unexpected type: {!r}'.format(
                        cascade.type_string))

            elif frame['I3MCWeightDict']['InteractionType'] == 2:
                # neutral current (2)
                labels['p_starting_nc'] = 1

            elif frame['I3MCWeightDict']['InteractionType'] == 3:
                # glashow resonance (3)
                labels['p_starting_glashow'] = 1

            else:
                #  GN -- Genie
                print('InteractionType: {!r}'.format(
                    frame['I3MCWeightDict']['InteractionType']))

    elif (primary.type_string == 'unknown' and primary.pdg_encoding == 0) or \
            mu_utils.is_muon(primary):

        if mu_utils.is_muon(primary):
            muon = primary
            # -----------------------------
            # muon primary: MuonGun dataset
            # -----------------------------
            if len(frame['I3MCTree']) > 1:
                daughter = frame['I3MCTree'][1]
                if mu_utils.is_muon(daughter) and \
                    ((daughter.id == primary.id) and
                     (daughter.dir == primary.dir) and
                     (daughter.pos == primary.pos) and
                     (daughter.energy == primary.energy)):
                    muon = daughter

        else:
            daughters = frame['I3MCTree'].get_daughters(primary)
            muon = daughters[0]

            # Perform some safety checks to make sure that this is MuonGun
            assert len(daughters) == 1, \
                'Expected only 1 daughter for MuonGun, but got {!r}'.format(
                    daughters)
            assert mu_utils.is_muon(muon), \
                'Expected muon but got {!r}'.format(muon)

        entry, time, energy = mu_utils.get_muon_entry_info(
            frame, muon, convex_hull)
        labels['p_entering'] = 1
        labels['p_entering_muon_single'] = 1
        labels['p_entering_muon_single_stopping'] = \
            mu_utils.is_stopping_muon(muon, convex_hull)
        labels['VertexX'] = entry.x
        labels['VertexY'] = entry.y
        labels['VertexZ'] = entry.z
        labels['VertexTime'] = time
        labels['EnergyVisible'] = energy
        labels['Length'] = muon.length
        labels['LengthInDetector'] = \
            mu_utils.get_muon_track_length_inside(muon, convex_hull)

        # The primary particle for MuonGun simulation can have NaN energy
        # replace this if necessary
        # (Note: technically PrimarEnergy is not known for MuonGun)
        if not np.isfinite(labels['PrimaryEnergy']):
            labels['PrimaryEnergy'] = muon.energy

    else:
        # ---------------------------------------------
        # No neutrino or muon primary: Corsika dataset?
        # ---------------------------------------------
        muons = mu_utils.get_muons_inside(frame, convex_hull)
        if len(muons) == 0:
            muons = [m.particle for m in frame['MMCTrackList']]

        time_max = None
        entry_max = None
        energy_max = float('-inf')
        for m in muons:
            if mu_utils.is_muon(m):
                entry, time, energy = mu_utils.get_muon_entry_info(
                    frame, m, convex_hull)
                if energy > energy_max:
                    time_max = time
                    entry_max = entry
                    energy_max = energy
                    muon = m

        labels['p_entering'] = 1
        labels['VertexX'] = entry_max.x
        labels['VertexY'] = entry_max.y
        labels['VertexZ'] = entry_max.z
        labels['VertexTime'] = time_max
        labels['EnergyVisible'] = bundle_info['bundle_energy_at_entry']
        labels['Length'] = muon.length
        labels['LengthInDetector'] = \
            mu_utils.get_muon_track_length_inside(muon, convex_hull)

        if bundle_info['num_muons_at_entry_above_threshold'] > 0:
            bundle_key = 'num_muons_at_entry_above_threshold'
        elif bundle_info['num_muons_at_entry'] > 0:
            bundle_key = 'num_muons_at_entry'
        elif bundle_info['num_muons_at_cyl_above_threshold'] > 0:
            bundle_key = 'num_muons_at_cyl_above_threshold'
        elif bundle_info['num_muons_at_cyl'] > 0:
            bundle_key = 'num_muons_at_cyl'
        else:
            raise ValueError('Expected at least one muon!', frame['I3MCTree'])

        if bundle_info[bundle_key] == 1:
            labels['p_entering_muon_single'] = 1
            labels['p_entering_muon_single_stopping'] = \
                mu_utils.is_stopping_muon(muon, convex_hull)
        else:
            labels['p_entering_muon_bundle'] = 1

    # Check if event is track. Definition used here:
    #   Event is track if at least one muon exists in cylinder and the length
    #   of the shower/muon is greater than 20m
    if bundle_info['num_muons_at_cyl'] < 1:
        labels['p_is_track'] = 0
    else:
        if labels['Length'] > track_length_threshold:
            labels['p_is_track'] = 1
        else:
            labels['p_is_track'] = 0

    return labels
Beispiel #4
0
def get_labels(frame,
               convex_hull,
               domPosDict,
               primary,
               pulse_map_string='InIcePulses',
               mcpe_series_map_name='I3MCPESeriesMap',
               is_muongun=False):
    '''Function to get extensive labels for muons, primary and general event
    data.

    Parameters
    ----------
    frame : frame

    convex_hull : scipy.spatial.ConvexHull
        defining the desired convex volume

    domPosDict : dict
        Dictionary of form (string,key) : (x,y,z)
        for all DOMs.
        string and key are of type int

    primary : I3Particle
        Primary particle

    pulse_map_string : key of pulse map in frame,
        of which the mask should be computed for

    mcpe_series_map_name : key of mcpe series map in frame

    is_muongun : bool
        In case of a MuonGun dataset, the primary neutrino has
        an unknown type and a pdg_encoding of 0.
        Therefore, the I3ParticleID of the primary needs to
        be passed along to sub-functions.
        Technically, this could be done implicity, by setting
        the primary id. However, this will loosen up sanity
        checks. Therefore, an explicit decision to use MuonGun
        is prefered.

    Returns
    -------
    labels : I3MapStringDouble
        Dictionary with all labels
    '''

    if primary is None:
        raise ValueError('Primary does not exist!')

    assert primary.id is not None, 'MuonGunFix will not work if this is not true'

    # Check if MuonGun dataset
    if is_muongun:
        # This loosens up sanity checks, therefore
        # better to use only if it is really a
        # MuonGun set.
        # Should work for all datasets though,
        # as long as a primary exists

        # make sure it is a MuonGun dataset
        assert primary.type_string == 'unknown', 'Expected unknown, got {}'.format(
            primary.type_string)
        assert primary.pdg_encoding == 0, 'Expected 0,got {}'.format(
            primary.pdg_encoding)

        # set primary particle id
        muongun_primary_neutrino_id = primary.id
    else:
        muongun_primary_neutrino_id = None

    # create empty labelDict
    labels = dataclasses.I3MapStringDouble()

    # get misc info
    misc_info = get_misc_information(frame,
                                     domPosDict,
                                     convex_hull,
                                     pulse_map_string=pulse_map_string,
                                     mcpe_series_map_name=mcpe_series_map_name)
    labels.update(misc_info)

    muons_inside = mu_utils.get_muons_inside(frame, convex_hull)
    labels['NoOfMuonsInside'] = len(muons_inside)

    # get muons
    mostEnergeticMuon = mu_utils.get_most_energetic_muon_inside(
        frame, convex_hull, muons_inside=muons_inside)
    highestEDepositMuon = mu_utils.get_highest_deposit_muon_inside(
        frame, convex_hull, muons_inside=muons_inside)
    mostVisibleMuon = mu_utils.get_most_visible_muon_inside(
        frame,
        convex_hull,
        pulse_map_string=pulse_map_string,
        mcpe_series_map_name=mcpe_series_map_name)
    primaryMuon = mu_utils.get_muon_of_inice_neutrino(
        frame, muongun_primary_neutrino_id=muongun_primary_neutrino_id)

    labels['PrimaryMuonExists'] = not (primaryMuon is None)
    labels['VisibleStartingTrack'] = False
    for m in [
            mostEnergeticMuon, highestEDepositMuon, mostVisibleMuon,
            primaryMuon
    ]:
        if m:
            if geometry.is_in_detector_bounds(m.pos, extend_boundary=60):
                labels['VisibleStartingTrack'] = True

    # get labels for most energetic muon
    mostEnergeticMuon_info = get_muon_information(
        frame,
        mostEnergeticMuon,
        domPosDict,
        convex_hull,
        pulse_map_string=pulse_map_string)
    for key in mostEnergeticMuon_info.keys():
        labels['MostEnergeticMuon' + key] = mostEnergeticMuon_info[key]

    # # get labels for highest deposit muon
    # if highestEDepositMuon == mostEnergeticMuon:
    #     highestEDepositMuon_info = mostEnergeticMuon_info
    # else:
    #     highestEDepositMuon_info = get_muon_information(frame,
    #             highestEDepositMuon, domPosDict, convex_hull,
    #             pulse_map_string=pulse_map_string)
    # for key in highestEDepositMuon_info.keys():
    #     labels['HighestEDepositMuon'+key] = highestEDepositMuon_info[key]

    # get labels for most visible muon
    if mostVisibleMuon == mostEnergeticMuon:
        mostVisibleMuon_info = mostEnergeticMuon_info
    else:
        mostVisibleMuon_info = get_muon_information(
            frame,
            mostVisibleMuon,
            domPosDict,
            convex_hull,
            pulse_map_string=pulse_map_string)
    for key in mostVisibleMuon_info.keys():
        labels['MostVisibleMuon' + key] = mostVisibleMuon_info[key]

    # get labels for muon from primary
    if primaryMuon == mostEnergeticMuon:
        primaryMuon_info = mostEnergeticMuon_info
    elif primaryMuon == mostVisibleMuon:
        primaryMuon_info = mostVisibleMuon_info
    else:
        primaryMuon_info = get_muon_information(
            frame,
            primaryMuon,
            domPosDict,
            convex_hull,
            pulse_map_string=pulse_map_string)
    for key in primaryMuon_info.keys():
        labels['PrimaryMuon' + key] = primaryMuon_info[key]

    # get labels for primary particle
    primary_info = get_primary_information(
        frame,
        primary,
        domPosDict,
        convex_hull,
        pulse_map_string=pulse_map_string,
        muongun_primary_neutrino_id=muongun_primary_neutrino_id)
    for key in primary_info.keys():
        labels['Primary' + key] = primary_info[key]

    return labels
Beispiel #5
0
def get_cascade_parameters(frame,
                           primary,
                           convex_hull,
                           extend_boundary=200,
                           write_mc_cascade_to_frame=True):
    """Get cascade parameters.

    Parameters
    ----------
    frame : I3Frame
        Current I3Frame needed to retrieve I3MCTree
    primary : I3Particle
        Primary Nu Particle for which the cascade interaction is returned.
    convex_hull : scipy.spatial.ConvexHull, optional
        Defines the desired convex volume.
        Will be used to compute muon entry point for an entering muon.
    extend_boundary : float, optional
        Extend boundary of convex_hull by this distance [in meters].

    Returns
    -------
    I3MapStringDouble
        Cascade parameters of primary neutrino: x, y, z, t, azimuth, zenith, E
    """
    labels = dataclasses.I3MapStringDouble()

    cascade, e_em, e_hadron, e_track = get_cascade_of_primary_nu(
        frame,
        primary,
        convex_hull=None,
        extend_boundary=extend_boundary,
    )

    if cascade is None:
        # --------------------
        # not a starting event
        # --------------------
        muon = mu_utils.get_muon_of_inice_neutrino(frame)

        if muon is None:
            # --------------------
            # Cascade interaction outside of defined volume
            # --------------------
            cascade, e_em, e_hadron, e_track = get_cascade_of_primary_nu(
                frame,
                primary,
                convex_hull=None,
                extend_boundary=float('inf'),
                sanity_check=False,
            )

        else:
            # ------------------------------
            # NuMu CC Muon entering detector
            # ------------------------------
            # set cascade parameters to muon entry information
            entry, time, energy = mu_utils.get_muon_entry_info(
                frame, muon, convex_hull)
            length = mu_utils.get_muon_track_length_inside(muon, convex_hull)
            cascade = dataclasses.I3Particle()
            cascade.pos.x = entry.x
            cascade.pos.y = entry.y
            cascade.pos.z = entry.z
            cascade.time = time
            cascade.energy = energy
            cascade.dir = dataclasses.I3Direction(muon.dir)
            cascade.length = length
            e_em = 0.
            e_hadron = 0.
            e_track = energy

    if write_mc_cascade_to_frame:
        frame['MCCascade'] = cascade

    labels['cascade_x'] = cascade.pos.x
    labels['cascade_y'] = cascade.pos.y
    labels['cascade_z'] = cascade.pos.z
    labels['cascade_t'] = cascade.time
    labels['cascade_energy'] = cascade.energy
    labels['cascade_azimuth'] = cascade.dir.azimuth
    labels['cascade_zenith'] = cascade.dir.zenith
    labels['cascade_max_extension'] = cascade.length

    # compute fraction of energy for each component: EM, hadronic, track
    labels['energy_fraction_em'] = e_em / cascade.energy
    labels['energy_fraction_hadron'] = e_hadron / cascade.energy
    labels['energy_fraction_track'] = e_track / cascade.energy

    return labels
Beispiel #6
0
    def Physics(self, frame):

        veto_dict = {}

        # -------
        # NuGen
        # -------
        if self._dataset_type in ['nugen', 'genie']:

            # get MC info
            energy_true = frame['MCPrimary'].energy
            zenith_true = frame['MCPrimary'].dir.zenith
            azimuth_true = frame['MCPrimary'].dir.azimuth

            if self._dataset_type == 'nugen':
                true_type = frame['I3MCWeightDict']['PrimaryNeutrinoType']
            elif self._dataset_type == 'genie':
                true_type = frame['MCPrimary'].type

            # --------------------
            # Get muon entry depth
            # --------------------

            # Be more lenient with GENIE sets and catch assert
            if self._dataset_type == 'genie':
                try:
                    muon = mu_utils.get_muon_of_inice_neutrino(frame)
                except AssertionError as e:
                    print(e)
                    muon = None
                try:
                    tau = tau_utils.get_tau_of_inice_neutrino(frame)
                except AssertionError as e:
                    print(e)
                    tau = None

            # NuGen sets should not throw an assert
            else:
                tau = tau_utils.get_tau_of_inice_neutrino(frame)
                muon = mu_utils.get_muon_of_inice_neutrino(frame)

            # found a muon
            if muon is not None:
                entry = self._get_particle_entry(muon)
                entry_z = entry.z

            # found a tau
            elif tau is not None:
                entry = self._get_particle_entry(tau)
                entry_z = entry.z

            # no muon or tau exists: cascade
            else:
                cascade = get_cascade_of_primary_nu(frame,
                                                    frame['MCPrimary'],
                                                    convex_hull=None,
                                                    extend_boundary=800)[0]

                if cascade is not None:
                    entry_z = cascade.pos.z
                else:
                    cascade = get_cascade_of_primary_nu(
                        frame,
                        frame['MCPrimary'],
                        convex_hull=None,
                        extend_boundary=float('inf'))[0]

                    # Muon coming out of hadronic shower?
                    daughters = frame['I3MCTree'].get_daughters(cascade)

                    # collect possible muons from daughters of daughters
                    # e.g. Nu -> Nu + Hadrons -> Mu
                    muons = []
                    for d in daughters:
                        muons.extend([
                            m for m in frame['I3MCTree'].get_daughters(d)
                            if mu_utils.is_muon(m)
                        ])
                    if muons:
                        # pick highest energy muon
                        indices = np.argsort([m.energy for m in muons])
                        muon = muons[indices[-1]]
                        entry = self._get_particle_entry(muon)
                        entry_z = entry.z
                    else:
                        entry_z = cascade.pos.z

            # ---------------
            # apply self veto
            # ---------------
            veto_args = (true_type, energy_true, np.cos(zenith_true),
                         1950. - entry_z)

            veto_dict = {'depth': 1950. - entry_z}

            for i, threshold in enumerate(self.veto_thresholds):
                add = '_{:3.0f}GeV'.format(threshold)
                veto_dict['conv' + add] = float(self.conv_vetos[i](*veto_args))
                veto_dict['prompt' + add] = float(
                    self.prompt_vetos[i](*veto_args))
            # ---------------

            frame[self._output_key] = dataclasses.I3MapStringDouble(veto_dict)

        self.PushFrame(frame)
Beispiel #7
0
    def get_muon(self, frame, primary):
        """Get muon from frame

        Parameters
        ----------
        frame : I3Frame
            The current frame.
        primary : I3Particle
            The primary particle.

        Returns
        -------
        I3Particle
            The muon from the frame

        Raises
        ------
        ValueError
            If not muon is found.
        """

        # NuGen Dataset
        if primary.is_neutrino:
            muon = mu_utils.get_muon_of_inice_neutrino(frame)

            if muon is None:
                print(frame['I3MCTree'])
                raise ValueError('Did not find a muon!')

        # MuonGun Dataset
        elif ((primary.type_string == 'unknown' and primary.pdg_encoding == 0)
                or mu_utils.is_muon(primary)):

            if mu_utils.is_muon(primary):
                muon = primary

                # -----------------------------
                # muon primary: MuonGun dataset
                # -----------------------------
                daugters = frame['I3MCTree'].get_daughters(muon)
                if len(daugters) == 1:
                    daughter = daugters[0]
                    if mu_utils.is_muon(daughter) and \
                        ((daughter.id == primary.id) and
                         (daughter.dir == primary.dir) and
                         (daughter.pos == primary.pos) and
                         (daughter.energy == primary.energy)):
                            muon = daughter

            else:
                daughters = frame['I3MCTree'].get_daughters(primary)
                muon = daughters[0]

                # Perform some safety checks to make sure that this is MuonGun
                assert len(daughters) == 1, \
                    'Expected 1 daughter for MuonGun, but got {!r}'.format(
                        daughters)
                assert mu_utils.is_muon(muon), \
                    'Expected muon but got {!r}'.format(muon)

        return muon