Ejemplo n.º 1
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)
Ejemplo n.º 2
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
Ejemplo n.º 3
0
def get_muon_bundle_information(frame, convex_hull, energy_threshold=20):
    """Calculate muon bundle information:

    Number of muons for certain selections, relative leading muon energy,
    bundle energy.

    This will calculate all muons in MMCTrackList for 'cyl', but for 'entry'
    starting muons will not be considered.

    Parameters
    ----------
    frame : I3Frame
        Current I3Frame needed to retrieve MMCTrackList
    convex_hull : scipy.spatial.ConvexHull, optional
        Defines the desired convex volume.
    energy_threshold : int, optional
        Energy threshold in GeV at which to count muons.
        Muons below this threshold will be discarded.

    Returns
    -------
    dict
        A dictionary with the calculated labels.
    """
    bundle_info = {}

    energies_at_entry = []
    energies_at_cyl = []
    num_muons = 0

    for particle in frame['MMCTrackList']:
        muon = particle.particle
        # Check if particle is a muon
        if not mu_utils.is_muon(muon):
            continue

        # Determine entrance point into the convex hull
        initial_point = mu_utils.get_muon_initial_point_inside(
            muon, convex_hull)

        # Get energy at entry point
        if initial_point is not None:
            # check if it is a starting muon, e.g. if intial point inside
            # is the same as the vertex (Discard muon in this case)
            if (initial_point - muon.pos).magnitude > 1:
                entry_energy = mu_utils.get_muon_energy_at_position(
                    frame, muon, initial_point)
                energies_at_entry.append(entry_energy)

        cyl_energy = particle.Ei
        energies_at_cyl.append(cyl_energy)
        num_muons += 1

    energies_at_entry = np.array(energies_at_entry)
    energies_at_entry = energies_at_entry[np.isfinite(energies_at_entry)]
    mult_mask = energies_at_entry >= energy_threshold
    bundle_info['num_muons_at_entry'] = len(energies_at_entry)
    bundle_info['num_muons_at_entry_above_threshold'] = len(
        energies_at_entry[mult_mask])
    if len(energies_at_entry) > 0:
        bundle_info['leading_energy_rel_entry'] = np.max(
            energies_at_entry) / np.sum(energies_at_entry)
    else:
        bundle_info['leading_energy_rel_entry'] = float('NaN')

    energies_at_cyl = np.array(energies_at_cyl)
    energies_at_cyl = energies_at_cyl[np.isfinite(energies_at_cyl)]
    mult_mask = energies_at_cyl >= energy_threshold
    bundle_info['num_muons_at_cyl'] = len(energies_at_cyl)
    bundle_info['num_muons_at_cyl_above_threshold'] = len(
        energies_at_cyl[mult_mask])

    if len(energies_at_cyl) > 0:
        bundle_info['leading_energy_rel_cyl'] = np.max(
            energies_at_cyl) / np.sum(energies_at_cyl)
    else:
        bundle_info['leading_energy_rel_cyl'] = float('NaN')

    bundle_info['bundle_energy_at_entry'] = np.sum(energies_at_entry)
    bundle_info['bundle_energy_at_cyl'] = np.sum(energies_at_cyl)
    bundle_info['num_muons'] = num_muons

    return bundle_info
Ejemplo n.º 4
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)
    def __call__(self, bias_data):
        """Apply Bias Function

        Parameters
        ----------
        bias_data : dict
            Dictionary of bias input data.
            Contents may include:
            {
                'frame': the current I3Frame,
            }

        Returns
        -------
        float
            Keep probability: probability with which this event should be kept.
        """

        frame = bias_data['frame']

        # get primary
        mc_tree = frame[self.mctree_name]
        primaries = mc_tree.get_primaries()
        assert len(primaries) == 1, 'Expected only 1 Primary!'

        # get muon
        muon = mu_utils.get_muon(
            frame,
            primaries[0],
            detector.icecube_hull,
            mctree_name=self.mctree_name,
        )

        if muon is None:

            # if muon did not hit the convex hull, or if no muon exists,
            # it will be None. In this case we set default values
            found_muon = False
            cos_zen = np.cos(primaries[0].dir.zenith)
            track_length = 0.
            max_rel_loss = 0.

        else:
            found_muon = True
            cos_zen = np.cos(muon.dir.zenith)
            track_length = mu_utils.get_muon_track_length_inside(
                muon, detector.icecube_hull)

            # get muon energy losses
            losses = [
                loss for loss in mc_tree.get_daughters(muon)
                if not mu_utils.is_muon(loss) and
                geometry.is_in_detector_bounds(loss.pos, extend_boundary=60)
            ]

            # compute relative energy losses
            rel_losses = []
            loss_energies = []
            for loss in losses:

                # get energy of muon prior to energy loss
                distance = (muon.pos - loss.pos).magnitude
                energy = mu_utils.get_muon_energy_at_distance(
                    frame, muon, np.clip(distance - 1, 0., float('inf')))

                # If the loss is at the muon decay point, the returned energy
                # might be NaN, assert this and set default value of 1 GeV
                if not np.isfinite(energy):
                    assert np.abs(distance - muon.length) < 1, (energy, muon)
                    energy = 1

                rel_loss = loss.energy / energy
                if rel_loss > 1. or rel_loss < 0.:
                    msg = 'Found out of bounds rel_loss: {:3.3f}. '.format(
                        rel_loss)
                    msg += 'Clipping value to [0, 1]'
                    log_warn(msg)
                    rel_loss = np.clip(rel_loss, 0., 1.)

                loss_energies.append(loss.energy)
                rel_losses.append(rel_loss)
            if rel_losses:
                max_rel_loss = rel_losses[np.argmax(loss_energies)]
            else:
                max_rel_loss = 0.

        # bias based on zenith
        if self.cos_zenith_sigmoid_scale is None:
            zenith_keep_prob = 1.0
        else:
            zenith_keep_prob = self.sigmoid(
                -cos_zen,
                s=self.cos_zenith_sigmoid_scale,
                b=self.cos_zenith_sigmoid_bias,
            )

        # bias based on in detector track length
        if self.track_length_sigmoid_scale is None:
            track_length_prob = 1.0
        else:
            track_length_prob = self.sigmoid(
                track_length,
                s=self.track_length_sigmoid_scale,
                b=self.track_length_sigmoid_bias,
            )

        # bias based on maximum relative energy loss
        if self.muon_loss_sigmoid_scale is None:
            max_rel_loss_prob = 1.
        else:
            max_rel_loss_prob = self.sigmoid(
                max_rel_loss,
                s=self.muon_loss_sigmoid_scale,
                b=self.muon_loss_sigmoid_bias,
            )

        bias_info = {
            'found_muon': found_muon,
            'cos_zenith': cos_zen,
            'track_length_in_detector': track_length,
            'max_relative_energy_loss': max_rel_loss,
        }

        keep_prob = zenith_keep_prob * track_length_prob * max_rel_loss_prob
        return keep_prob, bias_info
Ejemplo n.º 6
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