def SFrame(self, frame): frame['MCDistanceCuts'] = dataclasses.I3VectorDouble(self.thresholds) frame['MCDomThresholds'] = dataclasses.I3VectorDouble(self.lim_doms) if self.relevance_dist is not None: frame['MCRelevanceDist'] = dataclasses.I3Double( self.relevance_dist) if self.oversize_factors is not None: frame['MCOversizing'] = dataclasses.I3VectorDouble( self.oversize_factors) self.PushFrame(frame)
def Physics(self, frame): track = frame[self.track] pulse_map = dataclasses.I3RecoPulseSeriesMap.from_frame( frame, self.pulses) dists, charges = [], [] for omkey, pulses in pulse_map: # Throw out Deep Core strings (want homogenized total charge) if (omkey.string < 79) and (omkey.om >= self.min_DOM) and ( omkey.om <= self.max_DOM): # Get distance of clostest approach to DOM from track dist = self.get_dist(track, self.geomap[omkey].position) dists.append(dist) # Get charge recorded in DOM charge = np.sum([pulse.charge for pulse in pulses]) charges.append(charge) # Ensure that both dists and charges have non-zero size if dists and charges: frame['inice_dom_dists_{}_{}'.format( self.min_DOM, self.max_DOM)] = dataclasses.I3VectorDouble(dists) frame['inice_dom_charges_{}_{}'.format( self.min_DOM, self.max_DOM)] = dataclasses.I3VectorDouble(charges) dists = np.asarray(dists) charges = np.asarray(charges) avg_dist = np.average(dists) median_dist = np.median(dists) std_dists = np.std(dists) one_std_mask = (dists > avg_dist + std_dists) | ( dists < avg_dist - std_dists) half_std_mask = (dists > avg_dist + 2 * std_dists) | ( dists < avg_dist - 2 * std_dists) frac_outside_one_std = dists[one_std_mask].shape[0] / dists.shape[0] frac_outside_two_std = dists[half_std_mask].shape[0] / dists.shape[ 0] # Add variables to frame frame['avg_inice_radius'] = dataclasses.I3Double(avg_dist) frame['median_inice_radius'] = dataclasses.I3Double(median_dist) frame['std_inice_radius'] = dataclasses.I3Double(std_dists) frame['frac_outside_one_std_inice_radius'] = dataclasses.I3Double( frac_outside_one_std) frame['frac_outside_two_std_inice_radius'] = dataclasses.I3Double( frac_outside_two_std) # frame['qweighted_inice_radius_{}_{}'.format(self.min_DOM, self.max_DOM)] = dataclasses.I3Double(np.average(dists, weights=charges)) # # frame['invqweighted_inice_radius_{}_{}'.format(self.min_DOM, self.max_DOM)] = dataclasses.I3Double(np.average(dists, weights=1/charges)) self.PushFrame(frame)
def Physics(self, frame): track = frame[self.track] frame[ 'I3RecoPulseSeriesMap_union'] = dataclasses.I3RecoPulseSeriesMapUnion( frame, self.pulses) pulse_map = dataclasses.I3RecoPulseSeriesMap.from_frame( frame, 'I3RecoPulseSeriesMap_union') tanks_dist, tanks_charge = [], [] for omkey, pulses in pulse_map: # Get distance of clostest approach to DOM from track dist = self.get_dist(track, self.geomap[omkey].position) tanks_dist.append(dist) # Get charge recorded in DOM charge = sum([pulse.charge for pulse in pulses]) tanks_charge.append(charge) # dist_bins = np.linspace(10, 1000, 100) dist_bins = np.logspace(2, 3, 25) if tanks_dist and tanks_charge: # ldf, _ = np.histogram(np.log10(tanks_dist), bins=dist_bins, # weights=tanks_charge) ldf, _ = np.histogram(tanks_dist, bins=dist_bins, weights=tanks_charge) else: ldf = np.zeros(len(dist_bins) - 1, dtype=float) frame['tank_charge_v_dist'] = dataclasses.I3VectorDouble(ldf) del frame['I3RecoPulseSeriesMap_union'] self.PushFrame(frame)
def testVectorDouble(self): vec = dataclasses.I3VectorDouble() for r in range(128): vec.append(r+3) field = 'double_vec' self.rows[field] = vec got = self.rows[field] for a,b in zip(vec,got): self.assertAlmostEqual(a,b)
def Physics(self, frame): pulses_key = 'I3RecoPulseSeriesMap_union' frame[pulses_key] = dataclasses.I3RecoPulseSeriesMapUnion( frame, self.pulses) pulse_map = dataclasses.I3RecoPulseSeriesMap.from_frame( frame, pulses_key) tank_x, tank_y, tank_charge = [], [], [] for omkey, pulses in pulse_map: x, y, z = self.geomap[omkey].position tank_x.append(x) tank_y.append(y) # Check for nan charges charge = 0 for pulse in pulses: if pulse.charge != NaN: charge += pulse.charge else: print('pulse.charge is NaN!!') # charge = sum([pulse.charge for pulse in pulses]) tank_charge.append(charge) if tank_x and tank_y and tank_charge: tank_charge = np.nan_to_num(tank_charge) frame['tank_x'] = dataclasses.I3VectorDouble(tank_x) frame['tank_y'] = dataclasses.I3VectorDouble(tank_y) frame['tank_charge'] = dataclasses.I3VectorDouble(tank_charge) # hist, _, _ = np.histogram2d(tanks_x, tanks_y, # bins=[self.xbins, self.ybins], # weights=tanks_charge) # self.hists.append(hist) # event_header = frame['I3EventHeader'] # event_id = '{}_{}_{}_{}'.format(self.sim, # event_header.run_id, # event_header.event_id, # event_header.sub_event_id) # self.event_ids.append(event_id) del frame[pulses_key] self.PushFrame(frame)
def RIDE(frame): T = frame['I3MCTree'].get_daughters(frame['I3MCTree'].get_primaries()[0]) if len(T) != 1: return False if not (str(T[0].type) == 'MuPlus' or str(T[0].type) == 'MuMinus'): return False muon = T[0] series = frame['SplitInIcePulses'].apply(frame) string = [] dom = [] charge = [] time = [] for i, pulse in enumerate(series): string.append(pulse[0].string) dom.append(pulse[0].om) charge.append(pulse[1][0].charge) time.append(pulse[1][0].time) domstr = np.array([string, dom]).T.astype(int).astype(str) domstr = np.char.zfill(domstr, 2).astype(object) domstr = np.sum(domstr, axis=1).astype(str) dombool = np.isin(domfile[:, 3], domstr) header = frame['I3EventHeader'] events = (np.ones(len(domstr)) * (header.event_id + header.sub_event_id)).astype(int) charge = np.array(charge) time = np.array(time) x = domfile[dombool, 5].astype(float) y = domfile[dombool, 6].astype(float) z = domfile[dombool, 7].astype(float) group = domfile[dombool, 4].astype(int) domstring = domfile[dombool, 3].astype(int) HQE = domfile[dombool, 2] HQE[HQE == 'NQE'] = 0 HQE[HQE == 'HQE'] = 1 HQE[HQE == 'BAD'] = 2 HQE = HQE.astype(int) coords = np.vstack((x, y, z)).T startp = np.array(muon.pos) endp = np.array((muon.pos) + muon.dir * muon.length) point_vector = coords - startp seg_vector = endp - startp t = np.dot(point_vector, seg_vector) / sum(seg_vector**2) close = startp + t[:, np.newaxis] * seg_vector close[t < 0] = startp close[t > 1] = endp domrad = np.linalg.norm(coords - close, axis=1) frame['events'] = dataclasses.I3VectorUInt64(events) frame['x'] = dataclasses.I3VectorDouble(x) frame['y'] = dataclasses.I3VectorDouble(y) frame['z'] = dataclasses.I3VectorDouble(z) frame['charge'] = dataclasses.I3VectorDouble(charge) frame['time'] = dataclasses.I3VectorDouble(time) frame['domstring'] = dataclasses.I3VectorUInt(domstring) frame['group'] = dataclasses.I3VectorUInt(group) frame['HQE'] = dataclasses.I3VectorUInt(HQE) frame['domrad'] = dataclasses.I3VectorDouble(domrad)
def Physics(self, frame): frame[ 'I3RecoPulseSeriesMap_union'] = dataclasses.I3RecoPulseSeriesMapUnion( frame, self.pulses) pulse_map = dataclasses.I3RecoPulseSeriesMap.from_frame( frame, 'I3RecoPulseSeriesMap_union') tanks_x, tanks_y, tanks_charge = [], [], [] for omkey, pulses in pulse_map: x, y, z = self.geomap[omkey].position tanks_x.append(x) tanks_y.append(y) charge = sum([pulse.charge for pulse in pulses]) tanks_charge.append(charge) if tanks_x and tanks_y and tanks_charge: frame['tanks_x'] = dataclasses.I3VectorDouble(tanks_x) frame['tanks_y'] = dataclasses.I3VectorDouble(tanks_y) frame['tanks_charge'] = dataclasses.I3VectorDouble(tanks_charge) del frame['I3RecoPulseSeriesMap_union'] self.PushFrame(frame)
def Physics(self, frame): if self.RunOnPulses: if frame.Has(self.PulseMapName): self.TuplePulseMask = dataclasses.I3RecoPulseSeriesMapMask(frame, self.PulseMapName) self.TuplePulseMask.set_none() else: print(("Error, cannot find pulse mask %s") % self.PulseMapName) return False self.TupleCosAlphaVector_Pulses = dataclasses.I3VectorDouble() self.TupleRelvVector_Pulses = dataclasses.I3VectorDouble() self.RunTaggerOnPulses(frame) frame['SLOPPulseMaskTuples'] = self.TuplePulseMask frame['SLOPTuples_CosAlpha_Pulses'] = self.TupleCosAlphaVector_Pulses frame['SLOPTuples_RelV_Pulses'] = self.TupleRelvVector_Pulses if self.RunOnLaunches: self.TupleLaunchMap = dataclasses.I3DOMLaunchSeriesMap() self.TupleCosAlphaVector_Launches = dataclasses.I3VectorDouble() self.TupleRelvVector_Launches = dataclasses.I3VectorDouble() self.RunTaggerOnLaunches(frame) frame['SLOPLaunchMapTuples'] = self.TupleLaunchMap frame['SLOPTuples_CosAlpha_Launches'] = self.TupleCosAlphaVector_Launches frame['SLOPTuples_RelV_Launches'] = self.TupleRelvVector_Launches self.PushFrame(frame)
def add_IceTop_tankXYcharge(frame, pulses): frame[ 'I3RecoPulseSeriesMap_union'] = dataclasses.I3RecoPulseSeriesMapUnion( frame, pulses) pulse_map = dataclasses.I3RecoPulseSeriesMap.from_frame( frame, 'I3RecoPulseSeriesMap_union') geomap = frame['I3Geometry'].omgeo tanks_x, tanks_y, tanks_charge = [], [], [] for omkey, pulses in pulse_map: x, y, z = geomap[omkey].position tanks_x.append(x) tanks_y.append(y) charge = sum([pulse.charge for pulse in pulses]) tanks_charge.append(charge) if tanks_x and tanks_y and tanks_charge: frame['tanks_x'] = dataclasses.I3VectorDouble(tanks_x) frame['tanks_y'] = dataclasses.I3VectorDouble(tanks_y) frame['tanks_charge'] = dataclasses.I3VectorDouble(tanks_charge) del frame['I3RecoPulseSeriesMap_union']
def Physics(self, frame): if not frame.Has(self.inputMapName): self.PushFrame(frame) return if type(frame[self.inputMapName]) == dataclasses.I3RecoPulseSeriesMapMask or \ type(frame[self.inputMapName]) == dataclasses.I3RecoPulseSeriesMap: workingOnPulses = True else: workingOnPulses = False if frame.Has(self.inputTrack): seed = frame[self.inputTrack] if type(frame[self.inputTrack]) == dataclasses.I3MCTree: seed = frame[self.inputTrack][0] initialPos = [seed.pos.x, seed.pos.y, seed.pos.z] initialDir = [ seed.speed * seed.dir.x, seed.speed * seed.dir.y, seed.speed * seed.dir.z ] initialState = initialPos + initialDir else: print("Kalman - Error: InputTrack not found") self.PushFrame(frame) return ## timewise sorted Pulses/DomLaunches - [(OMkey, Time, Pulse/DomLaunch)] hitlist = hitfilter(frame, self.inputMapName, self.IgnoreDC) if self.useAcceleration: stateDim = 9 else: stateDim = 6 ## Initialize Kalman filter kalman = Kalman(stateDim, 3, initialState, self.noiseQ, self.noiseR) for i in range(self.iterations): ## create variables for results prediction = dataclasses.I3VectorI3Particle() if self.useAcceleration: acceleration = dataclasses.I3VectorI3Particle() varianceVector = dataclasses.I3VectorDouble() if workingOnPulses: recoPulseSeriesMapMask = dataclasses.I3RecoPulseSeriesMapMask( frame, self.inputMapName) recoPulseSeriesMapMask.set_none() else: domLaunchSeriesMap = dataclasses.I3DOMLaunchSeriesMap() ## write seed to predictionlist seed = dataclasses.I3Particle() seed.pos = dataclasses.I3Position(initialState[0], initialState[1], initialState[2]) seed.dir = dataclasses.I3Direction(initialState[3], initialState[4], initialState[5]) seed.speed = np.linalg.norm(initialState[3:6]) seed.time = 0 prediction.append(seed) ## Write acceleration seed if self.useAcceleration: accel = dataclasses.I3Particle() accel.pos = dataclasses.I3Position(0, 0, 0) acceleration.append(accel) ## clean variables hitList = [] predList = [] time = [] chi2 = 0 lastTime = 0 p = kalman.Predict() ## loop over all hits in given map for hit in hitlist: ## extrapolate Kalman-Prediction pos = p[0:3] + p[3:6] * (hit.time - lastTime) om = np.array(self.geo.omgeo[hit.omkey].position) ## Distance to next hit d = np.linalg.norm(om - pos) if d <= self.cutRadius: dt = hit.time - lastTime lastTime = hit.time ## Update Kalman filter with new hit kalman.Update(om, dt) p = kalman.Predict() ## Uncomment to activate debug mode #kalman.Debug() ## Add Kalman-step to predictionlist pred = dataclasses.I3Particle() pred.pos = dataclasses.I3Position(p[0], p[1], p[2]) pred.dir = dataclasses.I3Direction(p[3], p[4], p[5]) pred.speed = np.linalg.norm(p[3:6]) pred.time = hit.time prediction.append(pred) if self.useAcceleration: accel = dataclasses.I3Particle() accel.pos = dataclasses.I3Position(p[6], p[7], p[8]) acceleration.append(accel) varianceVector.append(kalman.GetVariance()[0]) chi2 += d**2 ## write PulseMapMask/DOMLaunchMap if workingOnPulses: recoPulseSeriesMapMask.set(hit.omkey, hit.info, True) else: if hit.omkey in domLaunchSeriesMap: domLaunchSeriesMap[hit.omkey].append(hit.info) else: domLaunchSeries = dataclasses.I3DOMLaunchSeries() domLaunchSeries.append(hit.info) domLaunchSeriesMap[hit.omkey] = domLaunchSeries hitList += [om] predList += [p[0:3]] time += [hit.time] ## new initialization for the next global iteration if len(hitList) > 0: ## use last iteration as LineFit if self.iterationMethod == 1: x0 = p[0] - p[3] * hit.time y0 = p[1] - p[4] * hit.time z0 = p[2] - p[5] * hit.time particle = dataclasses.I3Particle() particle.pos = dataclasses.I3Position(x0, y0, z0) particle.dir = dataclasses.I3Direction(p[3], p[4], p[5]) particle.speed = np.linalg.norm([p[3:6]]) particle.time = 0 elif self.iterationMethod == 2: ## calculate the LineFit on selected pulses particle = LineFitCallKalman(hitList, time) elif self.iterationMethod == 3: ## calculate the LineFit on iteration steps particle = LineFitCallKalman(predList, time) else: raise NotImplementedError( "No IterationMethod with number %s" % str(self.IterationMethod)) ## Last Iteration ? if i < self.iterations - 1: initialPos = [ particle.pos.x, particle.pos.y, particle.pos.z ] initialDir = [ particle.speed * particle.dir.x, particle.speed * particle.dir.y, particle.speed * particle.dir.z ] initialState = initialPos + initialDir initialP = kalman.GetVarianceVector() kalman = Kalman(stateDim, 3, initialState, self.noiseQ, self.noiseR, initialP) else: ## Write everything to the frame frame["%s_LineFit" % (self.outputTrack)] = particle frame["%s_NHits" % (self.outputTrack)] = dataclasses.I3Double( len(hitList)) frame["%s_P" % (self.outputTrack)] = varianceVector frame["%s_Chi2Pred" % (self.outputTrack)] = dataclasses.I3Double( Chi2CallKalman(predList, time, particle)) if workingOnPulses: frame["%s_Map" % (self.outputTrack)] = recoPulseSeriesMapMask else: frame["%s_Map" % (self.outputTrack)] = domLaunchSeriesMap if self.additionalInformation: frame["%s_Pred" % (self.outputTrack)] = prediction if self.useAcceleration: frame["%s_Accel" % (self.outputTrack)] = acceleration self.PushFrame(frame)
def dom_data(frame, reco_fit, options): """ Analyze and save the per-dom data using the provided fit. Parameters ---------- reco_fit : str The key of the MPEFit reconstruction options : dict[str] Adds To Frame ------------- TotalCharge : I3VectorDouble String : I3VectorDouble OM : I3VectorDouble DistAboveEndpoint : I3VectorDouble ImpactAngle : I3VectorDouble RecoDistance : I3VectorDouble Returns ------- bool Whether any per-dom data was added to the frame. If no data was added, return False, because this frame contains no pertinent information. Otherwise return True. """ n_ice_group = I3Constants.n_ice_group n_ice_phase = I3Constants.n_ice_phase IC_strings = [26, 27, 37, 46, 45, 35, 17, 18, 19, 28, 38, 47, 56, 55, 54, 44, 34, 25] DC_strings = [81, 82, 83, 84, 85, 86] reco_endpoint = frame['RecoEndpoint'] # Get the pulse series pulse_series = frame[options['pulses_name']].apply(frame) mcpulses = None if frame.Has("I3MCPulseSeriesMap") : mcpulses = frame["I3MCPulseSeriesMap"] frame['DOM_MCPulses'] = dataclasses.I3VectorDouble() # Initialize the vectors containing the per DOM data frame['DOM_TotalCharge'] = dataclasses.I3VectorDouble() frame['DOM_TotalCharge_300ns'] = dataclasses.I3VectorDouble() frame['DOM_String'] = dataclasses.I3VectorDouble() frame['DOM_OM'] = dataclasses.I3VectorDouble() frame['DOM_DistAboveEndpoint'] = dataclasses.I3VectorDouble() frame['DOM_ImpactAngle'] = dataclasses.I3VectorDouble() frame['DOM_RecoDistance'] = dataclasses.I3VectorDouble() frame['DOM_TruthDistance'] = dataclasses.I3VectorDouble() frame['DOM_MinTimeResidual'] = dataclasses.I3VectorDouble() dom_geo = frame['I3Geometry'].omgeo.items() # Find all doms above the reconstructed z coord of endpoint and # within the specified distance interval of the track maxdist = 0.0 closez = 0.0 endpoint = 0.0 alldom = 0.0 wrongstring = 0.0 for dom, geo in dom_geo: # (OMKey, I3OMGeo) alldom += 1.0 # We want to get DOMs that are in the IC/DC strings and below the dust # layer (40 and below for IC, 11 and below for DC). if (dom.string in IC_strings and dom.om >= 40) or (dom.string in DC_strings and dom.om >= 11): dom_position = geo.position #partition_num = (dom.string + dom.om) % options['partitions'] #mpe = frame[reco_fit.format(partition_num)] # MPEFit0...4 #mpe = frame['MPEFit'] # Get the reconstructed track using the frame key passed to this function reco_track = frame[reco_fit] # Find cherenkov distance from track to DOM reco_dist = calc.cherenkov_distance(reco_track, dom_position, n_ice_group, n_ice_phase) truth_dist = 50000.0 true_track = None if frame.Has('MMCTrackList') : mctracks = frame['MMCTrackList'] for track in mctracks: temp_dist = calc.cherenkov_distance(track.GetI3Particle(), dom_position, n_ice_group, n_ice_phase) truth_dist = min(truth_dist,temp_dist) if truth_dist == temp_dist : true_track = track.GetI3Particle() # If the DOM is outside the maximum distance from the track or the track's position of # closest approach to the DOM is above the level of the DOM then skip this DOM if reco_dist > options['max_dist']: maxdist += 1.0 continue # Keep if track is below DOM clos_app_pos = calc.closest_approach_position(reco_track, dom_position) if clos_app_pos.z > dom_position.z: closez += 1.0 continue # Calculate the point at which direct Cherenkov light hitting the DOM would have been emitted # at and require that this be above the end point of the track cherenkov_pos = calc.cherenkov_position(reco_track, dom_position, n_ice_group, n_ice_phase) dist_above_endpoint = calc.distance_along_track(reco_track, reco_endpoint) - calc.distance_along_track(reco_track, cherenkov_pos) if dist_above_endpoint <= 0: endpoint += 1.0 continue # We have survived to this point in the loop so now we need to store the data for this # DOM in the arrays we created frame['DOM_RecoDistance'].append(reco_dist) frame['DOM_TruthDistance'].append(truth_dist) frame['DOM_DistAboveEndpoint'].append(dist_above_endpoint) frame['DOM_String'].append(dom.string) frame['DOM_OM'].append(dom.om) # Calculate the angle at which direct Cherenkov light would impact the DOM perp_position = dataclasses.I3Position(dom_position.x, dom_position.y, clos_app_pos.z) delta = perp_position - clos_app_pos impact_param = delta.magnitude impact_angle = math.asin(impact_param / calc.closest_approach_distance(reco_track, dom_position)) frame['DOM_ImpactAngle'].append(impact_angle) # Calculate the total charge and the time residual for the DOM total_charge = 0.0 totalmc_charge = 0.0 total_charge_300ns = 0.0 min_time_residual = 1000. # If there are pulses, sum the charge of the ones with a time residual less than 1000 ns. if dom in pulse_series.keys(): for pulse in pulse_series[dom]: time_res = calc.time_residual(reco_track, dom_position, pulse.time, n_ice_group, n_ice_phase) min_time_residual = min(min_time_residual,time_res) if time_res < 1000.: total_charge += pulse.charge if time_res > -100. and time_res < 300. : total_charge_300ns += pulse.charge if mcpulses : if dom in mcpulses.keys() : for pulse in mcpulses[dom]: time_res = calc.time_residual(true_track, dom_position, pulse.time, n_ice_group, n_ice_phase) if time_res < 1000.: if pulse.source == 10 : totalmc_charge += pulse.charge #print("appending vectors") frame['DOM_TotalCharge'].append(total_charge) if frame.Has('DOM_MCPulses'): frame['DOM_MCPulses'].append(totalmc_charge) frame['DOM_TotalCharge_300ns'].append(total_charge_300ns) frame['DOM_MinTimeResidual'].append(min_time_residual) else : wrongstring += 1.0 # After all that, if none of the DOMs made it through, get rid of this # frame. #print("maxdist = %f closez = %f and endpoint = %f wrongstring = %f total = %f" % (maxdist,closez,endpoint,wrongstring,alldom) ) return len(frame['DOM_TotalCharge']) != 0
def __call__(self, IceXModelNumber): # Calculate the Pertubed Series as the perturbed phase model of the pertubed amplitude model (These operations commute, of course) np.random.RandomState(int(IceXModelNumber)) if IceXModelNumber is 0: Rand_Amp_Shifts = np.zeros(len(Modes_to_Shift)) Rand_Phs_Shifts = np.zeros(len(Modes_to_Shift)) else: Rand_Amp_Shifts = np.random.normal(0, Rel_Amp_Sigmas, len(Modes_to_Shift)) Rand_Phs_Shifts = np.random.normal(0, Rel_Phs_Sigmas, len(Modes_to_Shift)) Perturbed_FS_Plus = PerturbPhases( PerturbAmplitudes(self.Central_FS_Plus, Modes_to_Shift, Rand_Amp_Shifts), Modes_to_Shift, Rand_Phs_Shifts) Perturbed_Sca = 10**(Perturbed_FS_Plus[1] - self.Central_Minus) Perturbed_Abs = 10**(Perturbed_FS_Plus[1] + self.Central_Minus) #Stitch Heads Back on Perturbed_Sca[-1] = self.Sca[-1] Perturbed_Abs[-1] = self.Abs[-1] Perturbed_Sca = np.insert(Perturbed_Sca, 0, self.Sca_Head) Perturbed_Abs = np.insert(Perturbed_Abs, 0, self.Abs_Head) # Make a new ice model object new_m = copy.deepcopy(self.m) for i in range(0, len(self.depths)): oldScat = new_m.GetScatteringLength(i) oldAbs = new_m.GetAbsorptionLength(i) newScat = clsim.I3CLSimFunctionScatLenIceCube( alpha=oldScat.alpha, b400=float(Perturbed_Sca[i])) newAbs = clsim.I3CLSimFunctionAbsLenIceCube( kappa=oldAbs.kappa, A=oldAbs.A, B=oldAbs.B, D=oldAbs.D, E=oldAbs.E, aDust400=float(Perturbed_Abs[i]), deltaTau=oldAbs.deltaTau) new_m.SetScatteringLength(i, newScat) new_m.SetAbsorptionLength(i, newAbs) # Store ice model in the frame frame = icetray.I3Frame('M') frame.Put("MediumProperties", new_m) # Store ice metadata in the frame IceModelDict = {} IceModelDict['MultisimAmplitudes'] = [] IceModelDict['MultisimPhases'] = [] IceModelDict['MultisimModes'] = [] for i in range(len(Modes_to_Shift)): IceModelDict['MultisimAmplitudes'].append(Rand_Amp_Shifts[i]) IceModelDict['MultisimPhases'].append(Rand_Phs_Shifts[i]) IceModelDict['MultisimModes'].append(Modes_to_Shift[i]) frame.Put( 'MultisimAmplitudes', dataclasses.I3VectorDouble(IceModelDict['MultisimAmplitudes'])) frame.Put('MultisimPhases', dataclasses.I3VectorDouble(IceModelDict['MultisimPhases'])) frame.Put('MultisimModes', dataclasses.I3VectorDouble(IceModelDict['MultisimModes'])) frame.Put('IceXModelNumber', dataclasses.I3String(str(IceXModelNumber))) return frame
def dom_data(frame, reco_fit, options): """ Analyze and save the per-dom data using the provided fit. Parameters ---------- reco_fit : str The key of the MPEFit reconstruction options : dict[str] Adds To Frame ------------- TotalCharge : I3VectorDouble String : I3VectorDouble OM : I3VectorDouble DistAboveEndpoint : I3VectorDouble ImpactAngle : I3VectorDouble RecoDistance : I3VectorDouble Returns ------- bool Whether any per-dom data was added to the frame. If no data was added, return False, because this frame contains no pertinent information. Otherwise return True. """ n_ice_group = I3Constants.n_ice_group n_ice_phase = I3Constants.n_ice_phase IC_strings = [ 26, 27, 37, 46, 45, 35, 17, 18, 19, 28, 38, 47, 56, 55, 54, 44, 34, 25 ] DC_strings = [81, 82, 83, 84, 85, 86] reco_endpoint = frame['RecoEndpoint'] # Get the pulse series # Initialize the vectors # frame['TotalCharge0'] = dataclasses.I3VectorDouble() # frame['TotalCharge1'] = dataclasses.I3VectorDouble() # frame['TotalCharge2'] = dataclasses.I3VectorDouble() # frame['TotalCharge3'] = dataclasses.I3VectorDouble() # frame['TotalCharge4'] = dataclasses.I3VectorDouble() frame['TotalChargeSpline'] = dataclasses.I3VectorDouble() frame['TotalChargeUnbiased'] = dataclasses.I3VectorDouble() #frame['String0'] = dataclasses.I3VectorDouble() #frame['String1'] = dataclasses.I3VectorDouble() #frame['String2'] = dataclasses.I3VectorDouble() #frame['String3'] = dataclasses.I3VectorDouble() #frame['String4'] = dataclasses.I3VectorDouble() #frame['StringSpline'] = dataclasses.I3VectorDouble() #frame['OM0'] = dataclasses.I3VectorDouble() #frame['OM1'] = dataclasses.I3VectorDouble() #frame['OM2'] = dataclasses.I3VectorDouble() #frame['OM3'] = dataclasses.I3VectorDouble() #frame['OM4'] = dataclasses.I3VectorDouble() #frame['OMSpline'] = dataclasses.I3VectorDouble() #frame['DistAboveEndpoint0'] = dataclasses.I3VectorDouble() #frame['DistAboveEndpoint1'] = dataclasses.I3VectorDouble() #frame['DistAboveEndpoint2'] = dataclasses.I3VectorDouble() #frame['DistAboveEndpoint3'] = dataclasses.I3VectorDouble() #frame['DistAboveEndpoint4'] = dataclasses.I3VectorDouble() #frame['DistAboveEndpointSpline'] = dataclasses.I3VectorDouble() # frame['ImpactAngle0'] = dataclasses.I3VectorDouble() # frame['ImpactAngle1'] = dataclasses.I3VectorDouble() # frame['ImpactAngle2'] = dataclasses.I3VectorDouble() # frame['ImpactAngle3'] = dataclasses.I3VectorDouble() # frame['ImpactAngle4'] = dataclasses.I3VectorDouble() # frame['ImpactAngleSpline'] = dataclasses.I3VectorDouble() #frame['RecoDistance0'] = dataclasses.I3VectorDouble() #frame['RecoDistance1'] = dataclasses.I3VectorDouble() #frame['RecoDistance2'] = dataclasses.I3VectorDouble() #frame['RecoDistance3'] = dataclasses.I3VectorDouble() #frame['RecoDistance4'] = dataclasses.I3VectorDouble() frame['RecoDistanceSpline'] = dataclasses.I3VectorDouble() frame['RecoDistanceUnbiased'] = dataclasses.I3VectorDouble() dom_geo = frame['I3Geometry'].omgeo.items() # We want to be able to also test the cherenkov distance to the Truth Track # as well as the truth endpoint, for sanity checks # Find all doms above the reconstructed z coord of endpoint and # within the specified distance interval of the track for dom, geo in dom_geo: # (OMKey, I3OMGeo) # We want to get DOMs that are in the IC/DC strings and below the dust # layer (40 and below for IC, 11 and below for DC). if (dom.string in IC_strings and dom.om >= 42) or (dom.string in DC_strings and dom.om >= 11): dom_position = geo.position #partition_num = (dom.string + dom.om) % options['partitions'] #mpe = frame[reco_fit.format(partition_num)] # MPEFit0...4 #mpe = frame['MPEFit'] mpe = frame['SplineMPE'] # Find cherenkov distance from track to DOM reco_dist = calc.cherenkov_distance(mpe, dom_position, n_ice_group, n_ice_phase) if reco_dist < options['max_dist']: # Keep if track is below DOM clos_app_pos = calc.closest_approach_position( mpe, dom_position) if clos_app_pos.z < dom_position.z: # Try cherenkov dist cherenkov_pos = calc.cherenkov_position( mpe, dom_position, n_ice_group, n_ice_phase) dist_above_endpoint = calc.distance_along_track( mpe, reco_endpoint) - calc.distance_along_track( mpe, cherenkov_pos) if dist_above_endpoint > 0: #frame['RecoDistanceSpline'].append(reco_dist) # frame['DistAboveEndpointSpline'].append(dist_above_endpoint) # frame['StringSpline'].append(dom.string) # frame['OMSpline'].append(dom.om) # perp_position = dataclasses.I3Position(dom_position.x, dom_position.y, clos_app_pos.z) # delta = perp_position - clos_app_pos # impact_param = delta.magnitude # impact_angle = math.asin(impact_param / calc.closest_approach_distance(mpe, dom_position)) # frame['ImpactAngleSpline'].append(impact_angle) # TotalCharge and TimeResidual total_charge = 0 # If there are pulses, sum the charge of the ones with a # time residual less than 1000 ns. pulse_series = frame[options['pulses_name']].apply( frame) if dom in pulse_series.keys(): frame['RecoDistanceSpline'].append(reco_dist) for pulse in pulse_series[dom]: time_res = calc.time_residual( mpe, dom_position, pulse.time, n_ice_group, n_ice_phase) if time_res < 1000: total_charge += pulse.charge frame['TotalChargeSpline'].append(total_charge) for i in range(5): mpe = frame['Spline' + str(i)] # Find cherenkov distance from track to DOM reco_dist = calc.cherenkov_distance(mpe, dom_position, n_ice_group, n_ice_phase) if reco_dist < options['max_dist']: # Keep if track is below DOM clos_app_pos = calc.closest_approach_position( mpe, dom_position) if clos_app_pos.z < dom_position.z: # Try cherenkov dist cherenkov_pos = calc.cherenkov_position( mpe, dom_position, n_ice_group, n_ice_phase) dist_above_endpoint = calc.distance_along_track( mpe, reco_endpoint) - calc.distance_along_track( mpe, cherenkov_pos) if dist_above_endpoint > 0: # frame['RecoDistance'+str(i)].append(reco_dist) #frame['DistAboveEndpoint'+str(i)].append(dist_above_endpoint) #frame['String'+str(i)].append(dom.string) #frame['OM'+str(i)].append(dom.om) #perp_position = dataclasses.I3Position(dom_position.x, dom_position.y, clos_app_pos.z) #delta = perp_position - clos_app_pos #impact_param = delta.magnitude #impact_angle = math.asin(impact_param / calc.closest_approach_distance(mpe, dom_position)) #frame['ImpactAngle'+str(i)].append(impact_angle) # TotalCharge and TimeResidual total_charge = 0 # If there are pulses, sum the charge of the ones with a # time residual less than 1000 ns. pulse_series = frame['DataPulses' + str(i)].apply(frame) if dom in pulse_series.keys(): frame['RecoDistanceUnbiased'].append(reco_dist) for pulse in pulse_series[dom]: time_res = calc.time_residual( mpe, dom_position, pulse.time, n_ice_group, n_ice_phase) if time_res < 1000: total_charge += pulse.charge frame['TotalChargeUnbiased'].append(total_charge) #Getting Photon Arrival Angle (SEBASTIANS FUNCTION) # n_ice_group = I3Constants.n_ice_group # n_ice_phase = I3Constants.n_ice_phase # frame['PhotonArrivalAngle'] = dataclasses.I3VectorDouble() # strings = frame['String'] # oms = frame['OM'] # cherenkov_distances = frame['RecoDistance'] # mpe = frame[reco_fit] # for i in range(0,len(strings)): # for om, geo in dom_geo: # if(om.string==strings[i] and om.om==oms[i]): # dom_position = geo.position # cherenkov_dist = cherenkov_distances[i] # cherenkov_pos = calc.cherenkov_position(mpe, dom_position, n_ice_group, n_ice_phase) # perp_position = dataclasses.I3Position(dom_position.x, dom_position.y, cherenkov_pos.z) # delta = perp_position - cherenkov_pos # impact_param = delta.magnitude # if cherenkov_pos.z < dom_position.z: # ph_angle = math.asin(impact_param / cherenkov_dist) # else: # There is one event that is failing, lets try to correct for it... \ # if((impact_param/cherenkov_dist) > 1): # print("Impact_param= {}".format(impact_param)) # print("Cherenkov_dist = {}".format(cherenkov_dist)) # impact_param = cherenkov_dist # ph_angle = math.pi - math.asin(impact_param / cherenkov_dist) # frame['PhotonArrivalAngle'].append(ph_angle) # After all that, if none of the DOMs made it through, get rid of this # frame. return len(frame['TotalChargeSpline']) != 0
def WaveformDerivatives(frame): frame["DoublePulseWaveforms"] = dataclasses.I3WaveformSeriesMap() frame["BinsToT1"] = dataclasses.I3VectorDouble( ) # bins with time over threshold (ToT) for the first pulse of a derivative waveform frame["BinsToT2"] = dataclasses.I3VectorDouble( ) # bins with time over threshold (ToT) for the second pulse of a derivative waveform frame["BinsTbT"] = dataclasses.I3VectorDouble( ) # bins with time below threshold (TbT) for the first trailing edge of a derivative waveform frame["Amp1"] = dataclasses.I3VectorDouble( ) # cumulative derivative amplitude of time over threshold (ToT) for the first pulse frame["Amp2"] = dataclasses.I3VectorDouble( ) # cumulative derivative amplitude of time over threshold (ToT) for the second pulse frame["AmpTrailing"] = dataclasses.I3VectorDouble( ) # cumulative derivative amplitude of time below threshold (ToT) for the trailing edge frame["DPWaveformQTot"] = dataclasses.I3VectorDouble() frame["DP_T1"] = dataclasses.I3VectorDouble() frame["DP_T2"] = dataclasses.I3VectorDouble() frame["DPWaveformPulse1Amp"] = dataclasses.I3VectorDouble() frame["DPWaveformPulse2Amp"] = dataclasses.I3VectorDouble() frame["DP_OMs"] = dataclasses.I3VectorOMKey() SDtag = False LCtag = False if frame.Has("CalibratedWaveformsHLCATWD"): wf_map = frame["CalibratedWaveformsHLCATWD"] dp_count = 0 #count number of dp waveforms from this event dp_om_keys = [] for om, wf_series in wf_map: for wf in wf_series: wf_vect = wf.waveform wf_status = wf.status wf_binwidth = wf.bin_width start_time = wf.time if wf_status == 0: wf_qtot = 0.0 for i in range(128): wf_vect[i] = wf_vect[ i] / I3Units.mV #transform wf unit to mV wf_qtot += wf_vect[ i] #individual wf integrated charge in mV #Calculate Sliding Time Window (STW) derivatives for waveforms stw_vect = [] stepsize = 2 for i in range(1, 127): stw_vect += [(wf_vect[i + 1] - wf_vect[i - 1]) / (wf_binwidth * stepsize)] der_wf_bins = len(stw_vect) der_flag = 0 for i in range(1, der_wf_bins - 6): # 3, 4, 5, 6 bins tried. With 6 bins required, all the manually-selected nice double pulse waveforms picked up by the algorithm. if (stw_vect[i - 1] > 0 and stw_vect[i] > 0 and stw_vect[i + 1] > 0 and stw_vect[i + 2] > 0 and stw_vect[i + 3] > 0 and stw_vect[i + 4] > 0): der_flag = i - 1 #Finding point where waveform first sees light. break #sum up the amplitude of the rising edge amp_rising = 0. for i1 in range(der_flag, der_flag + 6): amp_rising += stw_vect[i1] wf_diff = [] for i in range(der_flag, 128 - der_step, der_step): wf_diff += [(wf_vect[i + der_step] - wf_vect[i]) / (wf_binwidth * der_step)] diff_wf_bins = len(wf_diff) #search for the first and second pulses! pulse1 = False pulse2 = False trailing = False i_flag = 0 j_flag = 0 k_flag = 0 amp_p1 = 0 #amplitude of first rising edge in the derivative waveform amp_p2 = 0 #amplitude of second rising edge in the derivative waveform amp_trailing = 0 #amplitude of first trailing edge in the derivative waveform binsToT1 = 0 binsToT2 = 0 binsTbT = 0 j1_flag = 0 j2_flag = 0 j3_flag = 0 bins_p21 = 0 bins_p22 = 0 dp_t1 = 0 dp_t2 = 0 dp_amp1 = 0 # amplitude of first pulse dp_amp2 = 0 # amplitude of second pulse #first pulse search for i in range(diff_wf_bins - bins_p2 - bins_trailing): #if wf_diff[i]>0: #and wf_diff[i+1]>0: if np.prod([ True if wf_diff[n] > 0.0 else False for n in range(i, i + bins_p1) ]): pulse1 = True i_flag = i break #trailing edge search if pulse1: # firstly move the index out of the positive region for k in range(i_flag + bins_p1, diff_wf_bins - bins_trailing): # if np.prod([ True if wf_diff[n] < 0.0 else False for n in range(k, k + bins_trailing) ]): k_flag = k trailing = True l_flag = k_flag + bins_trailing break dp_t1 = start_time + der_flag * 422.4 / 128 #beginning time of the first pulse #second pulse search if pulse1 and trailing: for j in range(l_flag, (diff_wf_bins - bins_p2)): if np.prod([ True if wf_diff[n] > 0.0 else False for n in range(j, j + bins_p2) ]): pulse2 = True j_flag = j break for q in range( der_flag, der_flag + j_flag * der_step ): # transform the derivative waveform index back to the waveform vector basis dp_amp1 += wf_vect[ q] #calculate amplitude of first pulse #move the index out of the positive region, and do another round of big pulse search. #If the later pulse is bigger than the earlier one, point the index to the bigger one. #Having more than (including) two pulses with ToT of 3 derivetive waveform bins, which is #duration of 3*4*3.3 ns, is quite rare. Therefore, another round of searching should be sufficient enough. if j_flag and j_flag < (diff_wf_bins - bins_p2): for j1 in range(j_flag, (diff_wf_bins - bins_p2)): if wf_diff[j1] < 0: j1_flag = j1 break if j1_flag and j1_flag < (diff_wf_bins - bins_p2): for j2 in range(j1_flag, (diff_wf_bins - bins_p2)): if np.prod([ True if wf_diff[n] > 0.0 else False for n in range(j2, j2 + bins_p2) ]): j2_flag = j2 break #move the index out of the positive region if j2_flag and j2_flag < (diff_wf_bins - bins_p2): j3_flag = 0 for g in range(j2_flag + bins_p2, diff_wf_bins): if wf_diff[g] < 0: j3_flag = g break if j3_flag == 0: j3_flag = diff_wf_bins bins_p21 = j1_flag - j_flag bins_p22 = j3_flag - j2_flag pulse3 = True if bins_p22 > bins_p21: j_flag = j2_flag dp_t2 = start_time + ( der_flag + j_flag * der_step ) * 422.4 / 128 #starting time of second pulse for q in range( der_flag + j_flag * der_step, 128 ): # transform the derivative waveform index back to the waveform vector basis dp_amp2 += wf_vect[ q] #calculate amplitude of second pulse if pulse1 and trailing and pulse2: # calculate the cumulative derivative amplitude for potential first pulse for s in range(i_flag, k_flag): amp_p1 += wf_diff[s] binsToT1 = k_flag - i_flag #move the index out of the first trailing edge region r_flag = 0 for r in range(l_flag, (diff_wf_bins - bins_p2)): if wf_diff[r] > 0: r_flag = r break for q in range(k_flag, r_flag): amp_trailing += wf_diff[q] binsTbT = r_flag - k_flag # move the index out of the second positive region h_flag = 0 for h in range(j_flag + bins_p2, diff_wf_bins): if wf_diff[h] < 0: h_flag = h break if h_flag == 0: h_flag = diff_wf_bins # calculate the cumulative derivative amplitude for second pulse for t in range(j_flag, h_flag): amp_p2 += wf_diff[t] binsToT2 = h_flag - j_flag if wf_qtot > WfQtot and pulse1 and trailing and pulse2 and amp_p1 > Amp1LC and amp_p2 > Amp2LC and amp_trailing < AmpTrailingLC: # if this waveform has double peak feature. if amp_p1 > Amp1SD and amp_p2 > Amp2SD and amp_trailing < AmpTrailingSD and binsToT1 > 1: #if this waveform passes single dom double peak: SDtag = True dp_count += 1 dp_om_keys.append(om) frame["Amp1"].append(amp_p1) frame["Amp2"].append(amp_p2) frame["AmpTrailing"].append(amp_trailing) frame["BinsToT1"].append(binsToT1) frame["BinsTbT"].append(binsTbT) frame["BinsToT2"].append(binsToT2) frame["DPWaveformQTot"].append(wf_qtot) frame["DP_OMs"].append(om) frame["DoublePulseWaveforms"].update( {om: wf_series}) frame["DPWaveformPulse1Amp"].append(dp_amp1) frame["DPWaveformPulse2Amp"].append(dp_amp2) frame["DP_T1"].append(dp_t1) frame["DP_T2"].append(dp_t2) #end if #end for #end for #Finding waveforms that have neighboring DOMs that also see double pulses. If they do, use a lower threshold. for i in dp_om_keys: for j in dp_om_keys: if i[0] == j[0]: if 0 < np.abs(np.int(j[1]) - np.int(i[1])) < 3: LCtag = True break if LCtag: break frame["DPFlagSD"] = icetray.I3Int(SDtag) frame["DPFlagLC"] = icetray.I3Int(LCtag) else: return False #Keeping the double pulse events only if DiscardEvents: if LCtag or SDtag: return True return False
for dis in range(0, len(DistanceLims) - 1): if (DistanceLims[dis] < DistancesThisEvt[wf] and DistanceLims[dis + 1] > DistancesThisEvt[wf]) and len( WaveformsThisEvt[wf]) == 128: Kernel = KernelDictionary[DistanceLims[dis]] Filter = FilterDictionary[DistanceLims[dis]] break if (len(Kernel) == 0): continue if (Shift > WFLength): continue ToDeconvWF = numpy.zeros(WFLength) for w in range(0, min(128, (WFLength - Shift))): ToDeconvWF[w + Shift] += numpy.array(WaveformsThisEvt[wf][w]) ToDeconvFFT = numpy.fft.rfft(ToDeconvWF) FilteredFFT = ToDeconvFFT * Filter / Kernel TotalFilteredFFT += FilteredFFT TotalSumWF += ToDeconvWF TotalDeconvWF = numpy.fft.irfft(TotalFilteredFFT) fr["SumWF"] = dataclasses.I3VectorDouble(TotalSumWF) fr["DecWF"] = dataclasses.I3VectorDouble(TotalDeconvWF) fr["WeightDict"] = WeightDict # fr["I3MCWeightDict"]=WeightDict fout.push(fr) sigFile.close() fout.close()
def test(frame): if frame['I3EventHeader'].sub_event_stream != 'InIceSplit': return False if frame['I3EventHeader'].sub_event_id != 0: return False A = frame['I3MCTree'].get_primaries() if len(A) > 1: return False T = frame['I3MCTree'].get_daughters(frame['I3MCTree'].get_primaries()[0]) if len(T) > 1: #print('Cluster') return False if not (str(T[0].type) == 'MuPlus' or str(T[0].type) == 'MuMinus'): #print('Not a muon') return False mu = T[0] if reco == 1: mu == frame['MPEFitRIDE_Finite'] if str(mu.fit_status) != 'OK': print('Bad Fit') return False endp = np.array(mu.pos + mu.dir * mu.length) startp = np.array(mu.pos) end_x, end_y, end_z = endp[0], endp[1], endp[2] start_x, start_y, start_z = startp[0], startp[1], startp[2] theta = mu.dir.zenith * 180 / np.pi stopped_xy = border.contains_point((endp[0], endp[1]), radius=-100) stopped_z = endp[2] > -400 stopped_theta = (theta >= 40) * (theta <= 70) stoppedmuon = int(stopped_xy * stopped_z) if stoppedmuon == 0: return False dom = [] string = [] charge = [] #series = frame['SplitInIcePulses'].apply(frame) try: series = dataclasses.I3RecoPulseSeriesMap.from_frame( frame, "InIcePulses") except: return False for i, pulse in enumerate(series): for hit in pulse[1]: string.append(pulse[0].string) dom.append(pulse[0].om) charge.append(hit.charge) if len(charge) < 8: return False domstr = np.column_stack([string, dom]).astype(str) domstr = np.char.zfill(domstr, 2).astype(object) domstr = np.sum(domstr, axis=1).astype(str) domindex = np.array([np.where(domfile[:, 3] == i)[0] for i in domstr])[:, 0] x, y, z = domfile[:, 5], domfile[:, 6], domfile[:, 7] chargep = np.array(charge).astype(float) charge = np.zeros(len(x)) charge[domindex] = chargep #print('Accepted') coords = np.array([x, y, z]).T.astype(float) point_vector = coords - startp seg_vector = endp - startp t = np.dot(point_vector, seg_vector) / sum(seg_vector**2) close = startp + t[:, np.newaxis] * seg_vector close[t < 0] = startp close[t > 1] = endp domrad = np.linalg.norm(coords - close, axis=1) domrad_bool = (domrad < 200) grp2 = grp * domrad_bool if np.sum(grp2) == 0: return False charge = charge[grp2].astype(float) x = x[grp2].astype(float) y = y[grp2].astype(float) z = z[grp2].astype(float) domrad = domrad[grp2].astype(float) domstr = np.array(domfile[grp2, 3]).astype(float) HQE = domfile[grp2, 2].astype(str) HQE[HQE == 'NQE'] = 0 HQE[HQE == 'HQE'] = 1 HQE[HQE == 'BAD'] = 2 HQE = np.array(HQE).astype(float) tempid = event_run + str(frame['I3EventHeader'].event_id) eventid = np.ones(len(x)).astype(int) * int(tempid) frame['x'] = dataclasses.I3VectorDouble(x) frame['y'] = dataclasses.I3VectorDouble(y) frame['z'] = dataclasses.I3VectorDouble(z) frame['charge'] = dataclasses.I3VectorDouble(charge) frame['domrad'] = dataclasses.I3VectorDouble(domrad) frame['domstr'] = dataclasses.I3VectorDouble(domstr) frame['status'] = dataclasses.I3VectorDouble(HQE) frame['eventid'] = dataclasses.I3VectorDouble(eventid) frame['end_x'] = dataclasses.I3Double(end_x) frame['end_y'] = dataclasses.I3Double(end_y) frame['end_z'] = dataclasses.I3Double(end_z) frame['start_x'] = dataclasses.I3Double(start_x) frame['start_y'] = dataclasses.I3Double(start_y) frame['start_z'] = dataclasses.I3Double(start_z) frame['theta'] = dataclasses.I3Double(theta)