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
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)
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
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
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
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 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