FairShip
Loading...
Searching...
No Matches
ShipAna.py
Go to the documentation of this file.
1# SPDX-License-Identifier: LGPL-3.0-or-later
2# SPDX-FileCopyrightText: Copyright CERN for the benefit of the SHiP Collaboration
3
4# example for accessing smeared hits and fitted tracks
5from argparse import ArgumentParser
6
7import decorators
8import ROOT
9import rootUtils as ut
10import shipRoot_conf
11import shipunit as u
12from ShipGeoConfig import load_from_root_file
13
16PDG = ROOT.TDatabasePDG.Instance()
17
18chi2CutOff = 4.0
19fiducialCut = False
20measCutFK = 25
21measCutPR = 22
22docaCut = 2.0
23
24parser = ArgumentParser()
25
26parser.add_argument("-f", "--inputFile", dest="inputFile", help="Input file (MC simulation)", required=True)
27parser.add_argument(
28 "-r", "--recoFile", dest="recoFile", help="Reconstruction file (will be used as friend tree)", required=False
29)
30parser.add_argument(
31 "-n", "--nEvents", dest="nEvents", help="Number of events to analyze", required=False, default=999999, type=int
32)
33parser.add_argument("-g", "--geoFile", dest="geoFile", help="ROOT geofile", required=True)
34parser.add_argument("--Debug", dest="Debug", help="Switch on debugging", required=False, action="store_true")
35options = parser.parse_args()
36
37# Determine reconstruction file
38if not options.recoFile:
39 # Default: replace .root with _rec.root, or if already _rec.root use as-is
40 if options.inputFile.find("_rec.root") > 0:
41 options.recoFile = options.inputFile
42 options.inputFile = options.inputFile.replace("_rec.root", ".root")
43 else:
44 options.recoFile = options.inputFile.replace(".root", "_rec.root")
45
46# Open MC simulation file
47if not options.inputFile.find(",") < 0:
48 sTree = ROOT.TChain("cbmsim")
49 recoChain = ROOT.TChain("ship_reco_sim")
50 for x in options.inputFile.split(","):
51 sTree.AddFile(x)
52 for x in options.recoFile.split(","):
53 recoChain.AddFile(x)
54 sTree.AddFriend(recoChain)
55else:
56 f = ROOT.TFile(options.inputFile)
57 sTree = f["cbmsim"]
58 # Add reconstruction data as friend tree
59 sTree.AddFriend("ship_reco_sim", options.recoFile)
60
61if not options.geoFile:
62 options.geoFile = options.inputFile.replace("ship.", "geofile_full.").replace("_rec.", ".")
63else:
64 fgeo = ROOT.TFile(options.geoFile)
65
66# new geofile, load Shipgeo dictionary written by run_simScript.py
67ShipGeo = load_from_root_file(fgeo, "ShipGeo")
68dy = ShipGeo.Yheight / u.m
69
70# -----Create geometry----------------------------------------------
71import shipDet_conf
72
73run = ROOT.FairRunSim()
74run.SetName("TGeant4") # Transport engine
75run.SetSink(ROOT.FairRootFileSink(ROOT.TMemFile("output", "recreate"))) # Dummy output file
76run.SetUserConfig("g4Config_basic.C") # geant4 transport not used, only needed for the mag field
77rtdb = run.GetRuntimeDb()
78# -----Create geometry----------------------------------------------
79modules = shipDet_conf.configure(run, ShipGeo)
80
81import geomGeant4
82
83if hasattr(ShipGeo.Bfield, "fieldMap"):
84 fieldMaker = geomGeant4.addVMCFields(ShipGeo, "", True, withVirtualMC=False)
85else:
86 print("no fieldmap given, geofile too old, not anymore support")
87 exit(-1)
88sGeo = fgeo["FAIRGeom"]
89geoMat = ROOT.genfit.TGeoMaterialInterface()
90ROOT.genfit.MaterialEffects.getInstance().init(geoMat)
91bfield = ROOT.genfit.FairShipFields()
92bfield.setField(fieldMaker.getGlobalField())
93fM = ROOT.genfit.FieldManager.getInstance()
94fM.init(bfield)
95
96volDict = {}
97i = 0
98for x in ROOT.gGeoManager.GetListOfVolumes():
99 volDict[i] = x.GetName()
100 i += 1
101
102# prepare veto decisions
103import shipVeto
104
105veto = shipVeto.Task(sTree)
106vetoDets = {}
107h = {}
108ut.bookHist(h, "delPOverP", "delP / P", 400, 0.0, 200.0, 100, -0.5, 0.5)
109ut.bookHist(h, "pullPOverPx", "delPx / sigma", 400, 0.0, 200.0, 100, -3.0, 3.0)
110ut.bookHist(h, "pullPOverPy", "delPy / sigma", 400, 0.0, 200.0, 100, -3.0, 3.0)
111ut.bookHist(h, "pullPOverPz", "delPz / sigma", 400, 0.0, 200.0, 100, -3.0, 3.0)
112ut.bookHist(h, "delPOverP2", "delP / P chi2/nmeas<" + str(chi2CutOff), 400, 0.0, 200.0, 100, -0.5, 0.5)
113ut.bookHist(h, "delPOverPz", "delPz / Pz", 400, 0.0, 200.0, 100, -0.5, 0.5)
114ut.bookHist(h, "delPOverP2z", "delPz / Pz chi2/nmeas<" + str(chi2CutOff), 400, 0.0, 200.0, 100, -0.5, 0.5)
115ut.bookHist(h, "chi2", "chi2/nmeas after trackfit", 100, 0.0, 10.0)
116ut.bookHist(h, "prob", "prob(chi2)", 100, 0.0, 1.0)
117ut.bookHist(h, "IP", "Impact Parameter", 100, 0.0, 10.0)
118ut.bookHist(h, "Vzresol", "Vz reco - true [cm]", 100, -50.0, 50.0)
119ut.bookHist(h, "Vxresol", "Vx reco - true [cm]", 100, -10.0, 10.0)
120ut.bookHist(h, "Vyresol", "Vy reco - true [cm]", 100, -10.0, 10.0)
121ut.bookHist(h, "Vzpull", "Vz pull", 100, -5.0, 5.0)
122ut.bookHist(h, "Vxpull", "Vx pull", 100, -5.0, 5.0)
123ut.bookHist(h, "Vypull", "Vy pull", 100, -5.0, 5.0)
124ut.bookHist(h, "Doca", "Doca between two tracks", 100, 0.0, 10.0)
125for x in ["", "_pi0"]:
126 ut.bookHist(h, "IP0" + x, "Impact Parameter to target", 100, 0.0, 100.0)
127 ut.bookHist(h, "IP0/mass" + x, "Impact Parameter to target vs mass", 100, 0.0, 2.0, 100, 0.0, 100.0)
128 ut.bookHist(h, "HNL" + x, "reconstructed Mass", 500, 0.0, 2.0)
129ut.bookHist(h, "HNLw", "reconstructed Mass with weights", 500, 0.0, 2.0)
130ut.bookHist(h, "meas", "number of measurements", 40, -0.5, 39.5)
131ut.bookHist(h, "meas2", "number of measurements, fitted track", 40, -0.5, 39.5)
132ut.bookHist(h, "measVSchi2", "number of measurements vs chi2/meas", 40, -0.5, 39.5, 100, 0.0, 10.0)
133ut.bookHist(h, "distu", "distance to wire", 100, 0.0, 1.0)
134ut.bookHist(h, "distv", "distance to wire", 100, 0.0, 1.0)
135ut.bookHist(h, "disty", "distance to wire", 100, 0.0, 1.0)
136ut.bookHist(h, "meanhits", "mean number of hits / track", 50, -0.5, 49.5)
137
138ut.bookHist(h, "extrapTimeDetX", "extrapolation to TimeDet X", 100, -10.0, 10.0)
139ut.bookHist(h, "extrapTimeDetY", "extrapolation to TimeDet Y", 100, -10.0, 10.0)
140
141ut.bookHist(h, "oa", "cos opening angle", 100, 0.999, 1.0)
142# potential Veto detectors
143ut.bookHist(h, "nrtracks", "nr of tracks in signal selected", 10, -0.5, 9.5)
144ut.bookHist(h, "nrSBT", "nr of hits in SBT", 100, -0.5, 99.5)
145
146import TrackExtrapolateTool
147
148
149def VertexError(t1, t2, PosDir, CovMat, scalFac):
150 # with improved Vx x,y resolution
151 a, u = PosDir[t1]["position"], PosDir[t1]["direction"]
152 c, v = PosDir[t2]["position"], PosDir[t2]["direction"]
153 Vsq = v.Dot(v)
154 Usq = u.Dot(u)
155 UV = u.Dot(v)
156 ca = c - a
157 denom = Usq * Vsq - UV**2
158 tmp2 = Vsq * u - UV * v
159 Va = ca.Dot(tmp2) / denom
160 tmp2 = UV * u - Usq * v
161 Vb = ca.Dot(tmp2) / denom
162 X = (a + c + Va * u + Vb * v) * 0.5
163 l1 = a - X + u * Va # l2 = c - X + v*Vb
164 dist = 2.0 * ROOT.TMath.Sqrt(l1.Dot(l1))
165 T = ROOT.TMatrixD(3, 12)
166 for i in range(3):
167 for k in range(4):
168 for j in range(3):
169 KD = 0
170 if i == j:
171 KD = 1
172 if k == 0 or k == 2:
173 # cova and covc
174 temp = (u[j] * Vsq - v[j] * UV) * u[i] + (u[j] * UV - v[j] * Usq) * v[i]
175 sign = -1
176 if k == 2:
177 sign = +1
178 T[i][3 * k + j] = 0.5 * (KD + sign * temp / denom)
179 elif k == 1:
180 # covu
181 aNAZ = denom * (ca[j] * Vsq - v.Dot(ca) * v[j])
182 aZAN = (ca.Dot(u) * Vsq - ca.Dot(v) * UV) * 2 * (u[j] * Vsq - v[j] * UV)
183 bNAZ = denom * (ca[j] * UV + (u.Dot(ca) * v[j]) - 2 * ca.Dot(v) * u[j])
184 bZAN = (ca.Dot(u) * UV - ca.Dot(v) * Usq) * 2 * (u[j] * Vsq - v[j] * UV)
185 T[i][3 * k + j] = 0.5 * (
186 Va * KD + u[i] / denom**2 * (aNAZ - aZAN) + v[i] / denom**2 * (bNAZ - bZAN)
187 )
188 elif k == 3:
189 # covv
190 aNAZ = denom * (2 * ca.Dot(u) * v[j] - ca.Dot(v) * u[j] - ca[j] * UV)
191 aZAN = (ca.Dot(u) * Vsq - ca.Dot(v) * UV) * 2 * (v[j] * Usq - u[j] * UV)
192 bNAZ = denom * (ca.Dot(u) * u[j] - ca[j] * Usq)
193 bZAN = (ca.Dot(u) * UV - ca.Dot(v) * Usq) * 2 * (v[j] * Usq - u[j] * UV)
194 T[i][3 * k + j] = 0.5 * (
195 Vb * KD + u[i] / denom**2 * (aNAZ - aZAN) + v[i] / denom**2 * (bNAZ - bZAN)
196 )
197 transT = ROOT.TMatrixD(12, 3)
198 transT.Transpose(T)
199 CovTracks = ROOT.TMatrixD(12, 12)
200 tlist = [t1, t2]
201 for k in range(2):
202 for i in range(6):
203 for j in range(6):
204 xfac = 1.0
205 if i > 2:
206 xfac = scalFac[tlist[k]]
207 if j > 2:
208 xfac = xfac * scalFac[tlist[k]]
209 CovTracks[i + k * 6][j + k * 6] = CovMat[tlist[k]][i][j] * xfac
210 # if i==5 or j==5 : CovMat[tlist[k]][i][j] = 0 # ignore error on z-direction
211 tmp = ROOT.TMatrixD(3, 12)
212 tmp.Mult(T, CovTracks)
213 covX = ROOT.TMatrixD(3, 3)
214 covX.Mult(tmp, transT)
215 return X, covX, dist
216
217
218from array import array
219
220
221def dist2InnerWall(X, Y, Z):
222 dist = 0
223 # return distance to inner wall perpendicular to z-axis, if outside decayVolume return 0.
224 node = sGeo.FindNode(X, Y, Z)
225 if "DecayVacuum" not in node.GetName():
226 return dist
227 start = array("d", [X, Y, Z])
228 nsteps = 8
229 dalpha = 2 * ROOT.TMath.Pi() / nsteps
230 X**2 + Y**2
231 minDistance = 100 * u.m
232 for n in range(nsteps):
233 alpha = n * dalpha
234 sdir = array("d", [ROOT.TMath.Sin(alpha), ROOT.TMath.Cos(alpha), 0.0])
235 node = sGeo.InitTrack(start, sdir)
236 sGeo.FindNextBoundary()
237 distance = sGeo.GetStep()
238 if distance < minDistance:
239 minDistance = distance
240 return minDistance
241
242
243def isInFiducial(X, Y, Z) -> bool:
244 if not fiducialCut:
245 return True
246 if ShipGeo.TrackStation1.z < Z:
247 return False
248 # typical x,y Vx resolution for exclusive HNL decays 0.3cm,0.15cm (gaussian width)
249 return not dist2InnerWall(X, Y, Z) < 5 * u.cm
250
251
252#
253def ImpactParameter(point, tPos, tMom):
254 t = 0
255 if hasattr(tMom, "P"):
256 P = tMom.P()
257 else:
258 P = tMom.Mag()
259 for i in range(3):
260 t += tMom(i) / P * (point(i) - tPos(i))
261 dist = 0
262 for i in range(3):
263 dist += (point(i) - tPos(i) - t * tMom(i) / P) ** 2
264 dist = ROOT.TMath.Sqrt(dist)
265 return dist
266
267
268#
269def checkHNLorigin(sTree) -> bool:
270 flag = True
271 if not fiducialCut:
272 return flag
273 flag = False
274 # only makes sense for signal == HNL
275 hnlkey = -1
276 for n in range(len(sTree.MCTrack)):
277 mo = sTree.MCTrack[n].GetMotherId()
278 if mo < 0 or mo >= len(sTree.MCTrack):
279 continue
280 if abs(sTree.MCTrack[mo].GetPdgCode()) == 9900015:
281 hnlkey = n
282 break
283 if hnlkey < 0:
284 ut.reportError("ShipAna: checkHNLorigin, no HNL found")
285 elif hnlkey >= len(sTree.MCTrack):
286 ut.reportError("ShipAna: checkHNLorigin, HNL key out of bounds")
287 else:
288 # MCTrack after HNL should be first daughter
289 theHNLVx = sTree.MCTrack[hnlkey]
290 X, Y, Z = theHNLVx.GetStartX(), theHNLVx.GetStartY(), theHNLVx.GetStartZ()
291 if isInFiducial(X, Y, Z):
292 flag = True
293 return flag
294
295
296def checkFiducialVolume(sTree, tkey: int, dy) -> bool:
297 # extrapolate track to middle of magnet and check if in decay volume
298 inside = True
299 if not fiducialCut:
300 return True
301 fT = sTree.FitTracks[tkey]
302 rc, pos, _mom = TrackExtrapolateTool.extrapolateToPlane(fT, ShipGeo.Bfield.z)
303 if not rc:
304 return False
305 if not dist2InnerWall(pos.X(), pos.Y(), pos.Z()) > 0:
306 return False
307 return inside
308
309
310def getPtruthFirst(sTree, mcPartKey):
311 Ptruth, Ptruthx, Ptruthy, Ptruthz = -1.0, -1.0, -1.0, -1.0
312 for ahit in sTree.strawtubesPoint:
313 if ahit.GetTrackID() == mcPartKey:
314 Ptruthx, Ptruthy, Ptruthz = ahit.GetPx(), ahit.GetPy(), ahit.GetPz()
315 Ptruth = ROOT.TMath.Sqrt(Ptruthx**2 + Ptruthy**2 + Ptruthz**2)
316 break
317 return Ptruth, Ptruthx, Ptruthy, Ptruthz
318
319
320def access2SmearedHits(ev, TrackingHits, MCTracks) -> None:
321 key = 0
322 for ahit in ev.SmearedHits.GetObject():
323 print(ahit[0], ahit[1], ahit[2], ahit[3], ahit[4], ahit[5], ahit[6])
324 # follow link to true MCHit
325 mchit = TrackingHits[key]
326 mctrack = MCTracks[mchit.GetTrackID()]
327 print(mchit.GetZ(), mctrack.GetP(), mctrack.GetPdgCode())
328 key += 1
329
330
331def myVertex(t1, t2, PosDir):
332 # closest distance between two tracks
333 # d = |pq . u x v|/|u x v|
334 a = ROOT.TVector3(PosDir[t1][0](0), PosDir[t1][0](1), PosDir[t1][0](2))
335 u = ROOT.TVector3(PosDir[t1][1](0), PosDir[t1][1](1), PosDir[t1][1](2))
336 c = ROOT.TVector3(PosDir[t2][0](0), PosDir[t2][0](1), PosDir[t2][0](2))
337 v = ROOT.TVector3(PosDir[t2][1](0), PosDir[t2][1](1), PosDir[t2][1](2))
338 pq = a - c
339 uCrossv = u.Cross(v)
340 dist = pq.Dot(uCrossv) / (uCrossv.Mag() + 1e-8)
341 # u.a - u.c + s*|u|**2 - u.v*t = 0
342 # v.a - v.c + s*v.u - t*|v|**2 = 0
343 E = u.Dot(a) - u.Dot(c)
344 F = v.Dot(a) - v.Dot(c)
345 A, B = u.Mag2(), -u.Dot(v)
346 C, D = u.Dot(v), -v.Mag2()
347 t = -(C * E - A * F) / (B * C - A * D)
348 X = c.x() + v.x() * t
349 Y = c.y() + v.y() * t
350 Z = c.z() + v.z() * t
351 return X, Y, Z, abs(dist)
352
353
354def RedoVertexing(t1, t2):
355 PosDir = {}
356 for tr in [t1, t2]:
357 xx = sTree.FitTracks[tr].getFittedState()
358 PosDir[tr] = [xx.getPos(), xx.getDir()]
359 xv, yv, zv, doca = myVertex(t1, t2, PosDir)
360 # as we have learned, need iterative procedure
361 dz = 99999.0
362 reps, states, newPosDir = {}, {}, {}
363 ROOT.TVector3(0.0, 0.0, 1.0)
364 rc = True
365 step = 0
366 while dz > 0.1:
367 zBefore = zv
368 newPos = ROOT.TVector3(xv, yv, zv)
369 # make a new rep for track 1,2
370 for tr in [t1, t2]:
371 xx = sTree.FitTracks[tr].getFittedState()
372 reps[tr] = ROOT.genfit.RKTrackRep(xx.getPDG())
373 states[tr] = ROOT.genfit.StateOnPlane(reps[tr])
374 reps[tr].setPosMom(states[tr], xx.getPos(), xx.getMom())
375 try:
376 reps[tr].extrapolateToPoint(states[tr], newPos, False)
377 except Exception:
378 print("SHiPAna: extrapolation did not work")
379 rc = False
380 break
381 newPosDir[tr] = [reps[tr].getPos(states[tr]), reps[tr].getDir(states[tr])]
382 if not rc:
383 break
384 xv, yv, zv, doca = myVertex(t1, t2, newPosDir)
385 dz = abs(zBefore - zv)
386 step += 1
387 if step > 10:
388 print("abort iteration, too many steps, pos=", xv, yv, zv, " doca=", doca, "z before and dz", zBefore, dz)
389 rc = False
390 break
391 if not rc:
392 return xv, yv, zv, doca, -1 # extrapolation failed, makes no sense to continue
393 LV = {}
394 for tr in [t1, t2]:
395 mom = reps[tr].getMom(states[tr])
396 pid = abs(states[tr].getPDG())
397 if pid == 2212:
398 pid = 211
399 mass = PDG.GetParticle(pid).Mass()
400 E = ROOT.TMath.Sqrt(mass * mass + mom.Mag2())
401 LV[tr] = ROOT.TLorentzVector()
402 LV[tr].SetPxPyPzE(mom.x(), mom.y(), mom.z(), E)
403 HNLMom = LV[t1] + LV[t2]
404 return xv, yv, zv, doca, HNLMom
405
406
407def fitSingleGauss(x: str, ba: float | None = None, be: float | None = None) -> None:
408 name = "myGauss_" + x
409 myGauss = h[x].GetListOfFunctions().FindObject(name)
410 if not myGauss:
411 if not ba:
412 ba = h[x].GetBinCenter(1)
413 if not be:
414 be = h[x].GetBinCenter(h[x].GetNbinsX())
415 bw = h[x].GetBinWidth(1)
416 mean = h[x].GetMean()
417 sigma = h[x].GetRMS()
418 norm = h[x].GetEntries() * 0.3
419 myGauss = ROOT.TF1(name, "[0]*" + str(bw) + "/([2]*sqrt(2*pi))*exp(-0.5*((x-[1])/[2])**2)+[3]", 4)
420 myGauss.SetParameter(0, norm)
421 myGauss.SetParameter(1, mean)
422 myGauss.SetParameter(2, sigma)
423 myGauss.SetParameter(3, 1.0)
424 myGauss.SetParName(0, "Signal")
425 myGauss.SetParName(1, "Mean")
426 myGauss.SetParName(2, "Sigma")
427 myGauss.SetParName(3, "bckgr")
428 h[x].Fit(myGauss, "", "", ba, be)
429
430
431def match2HNL(p) -> bool:
432 matched = False
433 hnlKey = []
434 for t in [p.GetDaughter(0), p.GetDaughter(1)]:
435 mcp = sTree.fitTrack2MC[t]
436 while mcp > -0.5:
437 if mcp >= len(sTree.MCTrack):
438 break
439 mo = sTree.MCTrack[mcp]
440 if abs(mo.GetPdgCode()) == 9900015:
441 hnlKey.append(mcp)
442 break
443 mcp = mo.GetMotherId()
444 if len(hnlKey) == 2 and hnlKey[0] == hnlKey[1]:
445 matched = True
446 return matched
447
448
449def makePlots() -> None:
450 ut.bookCanvas(h, key="strawanalysis", title="Distance to wire and mean nr of hits", nx=1200, ny=600, cx=3, cy=1)
451 cv = h["strawanalysis"].cd(1)
452 h["disty"].Draw()
453 h["distu"].Draw("same")
454 h["distv"].Draw("same")
455 cv = h["strawanalysis"].cd(2)
456 h["meanhits"].Draw()
457 cv = h["strawanalysis"].cd(3)
458 h["meas2"].Draw()
459 ut.bookCanvas(h, key="fitresults", title="Fit Results", nx=1600, ny=1200, cx=2, cy=2)
460 cv = h["fitresults"].cd(1)
461 h["delPOverPz"].Draw("box")
462 cv = h["fitresults"].cd(2)
463 cv.SetLogy(1)
464 h["prob"].Draw()
465 cv = h["fitresults"].cd(3)
466 h["delPOverPz_proj"] = h["delPOverPz"].ProjectionY()
467 ROOT.gStyle.SetOptFit(11111)
468 h["delPOverPz_proj"].Draw()
469 h["delPOverPz_proj"].Fit("gaus")
470 cv = h["fitresults"].cd(4)
471 h["delPOverP2z_proj"] = h["delPOverP2z"].ProjectionY()
472 h["delPOverP2z_proj"].Draw()
473 fitSingleGauss("delPOverP2z_proj")
474 h["fitresults"].Print("fitresults.gif")
475 for x in ["", "_pi0"]:
476 ut.bookCanvas(h, key="fitresults2" + x, title="Fit Results " + x, nx=1600, ny=1200, cx=2, cy=2)
477 cv = h["fitresults2" + x].cd(1)
478 if x == "":
479 h["Doca"].SetXTitle("closest distance between 2 tracks [cm]")
480 h["Doca"].SetYTitle("N/1mm")
481 h["Doca"].Draw()
482 else:
483 h["pi0Mass"].SetXTitle("#gamma #gamma invariant mass [GeV/c^{2}]")
484 h["pi0Mass"].SetYTitle("N/" + str(int(h["pi0Mass"].GetBinWidth(1) * 1000)) + "MeV")
485 h["pi0Mass"].Draw()
486 h["pi0Mass"].Fit("gaus", "S", "", 0.08, 0.19)
487 cv = h["fitresults2" + x].cd(2)
488 h["IP0" + x].SetXTitle("impact parameter to p-target [cm]")
489 h["IP0" + x].SetYTitle("N/1cm")
490 h["IP0" + x].Draw()
491 cv = h["fitresults2" + x].cd(3)
492 h["HNL" + x].SetXTitle("inv. mass [GeV/c^{2}]")
493 h["HNL" + x].SetYTitle("N/4MeV/c2")
494 h["HNL" + x].Draw()
495 fitSingleGauss("HNL" + x, 0.9, 1.1)
496 cv = h["fitresults2" + x].cd(4)
497 h["IP0/mass" + x].SetXTitle("inv. mass [GeV/c^{2}]")
498 h["IP0/mass" + x].SetYTitle("IP [cm]")
499 h["IP0/mass" + x].Draw("colz")
500 h["fitresults2" + x].Print("fitresults2" + x + ".gif")
501 ut.bookCanvas(h, key="vxpulls", title="Vertex resol and pulls", nx=1600, ny=1200, cx=3, cy=2)
502 cv = h["vxpulls"].cd(4)
503 h["Vxpull"].Draw()
504 cv = h["vxpulls"].cd(5)
505 h["Vypull"].Draw()
506 cv = h["vxpulls"].cd(6)
507 h["Vzpull"].Draw()
508 cv = h["vxpulls"].cd(1)
509 h["Vxresol"].Draw()
510 cv = h["vxpulls"].cd(2)
511 h["Vyresol"].Draw()
512 cv = h["vxpulls"].cd(3)
513 h["Vzresol"].Draw()
514 ut.bookCanvas(h, key="trpulls", title="momentum pulls", nx=1600, ny=600, cx=3, cy=1)
515 cv = h["trpulls"].cd(1)
516 h["pullPOverPx_proj"] = h["pullPOverPx"].ProjectionY()
517 h["pullPOverPx_proj"].Draw()
518 cv = h["trpulls"].cd(2)
519 h["pullPOverPy_proj"] = h["pullPOverPy"].ProjectionY()
520 h["pullPOverPy_proj"].Draw()
521 cv = h["trpulls"].cd(3)
522 h["pullPOverPz_proj"] = h["pullPOverPz"].ProjectionY()
523 h["pullPOverPz_proj"].Draw()
524 ut.bookCanvas(h, key="vetodecisions", title="Veto Detectors", nx=1600, ny=600, cx=2, cy=1)
525 cv = h["vetodecisions"].cd(1)
526 cv.SetLogy(1)
527 h["nrtracks"].Draw()
528 cv = h["vetodecisions"].cd(2)
529 cv.SetLogy(1)
530 h["nrSBT"].Draw()
531 #
532 print("finished making plots")
533
534
535# start event loop
536def myEventLoop(n: int) -> None:
537 rc = sTree.GetEntry(n)
538 # check if tracks are made from real pattern recognition
539 measCut = measCutFK
540 if sTree.GetBranch("FitTracks_PR"):
541 sTree.FitTracks = sTree.FitTracks_PR
542 measCut = measCutPR
543 if sTree.GetBranch("fitTrack2MC_PR"):
544 sTree.fitTrack2MC = sTree.fitTrack2MC_PR
545 if sTree.GetBranch("Particles_PR"):
546 sTree.Particles = sTree.Particles_PR
547 if not checkHNLorigin(sTree):
548 return
549 if not len(sTree.MCTrack) > 1:
550 wg = 1.0
551 else:
552 wg = sTree.MCTrack[1].GetWeight()
553 if not wg > 0.0:
554 wg = 1.0
555
556 # make some straw hit analysis
557 hitlist = {}
558 for ahit in sTree.strawtubesPoint:
559 detID = ahit.GetDetectorID()
560 top = ROOT.TVector3()
561 bot = ROOT.TVector3()
562 modules["strawtubes"].StrawEndPoints(detID, bot, top)
563 dw = ahit.dist2Wire()
564 if detID < 50000000:
565 if abs(top.y()) == abs(bot.y()):
566 h["disty"].Fill(dw)
567 if abs(top.y()) > abs(bot.y()):
568 h["distu"].Fill(dw)
569 if abs(top.y()) < abs(bot.y()):
570 h["distv"].Fill(dw)
571 #
572 trID = ahit.GetTrackID()
573 if not trID < 0:
574 if trID in hitlist:
575 hitlist[trID] += 1
576 else:
577 hitlist[trID] = 1
578 for tr in hitlist:
579 h["meanhits"].Fill(hitlist[tr])
580 key = -1
581 fittedTracks = {}
582 for atrack in sTree.FitTracks:
583 key += 1
584 # kill tracks outside fiducial volume
585 if not checkFiducialVolume(sTree, key, dy):
586 continue
587 fitStatus = atrack.getFitStatus()
588 nmeas = fitStatus.getNdf()
589 h["meas"].Fill(nmeas)
590 if not fitStatus.isFitConverged():
591 continue
592 h["meas2"].Fill(nmeas)
593 if nmeas < measCut:
594 continue
595 fittedTracks[key] = atrack
596 # needs different study why fit has not converged, continue with fitted tracks
597 rchi2 = fitStatus.getChi2()
598 prob = ROOT.TMath.Prob(rchi2, int(nmeas))
599 h["prob"].Fill(prob)
600 chi2 = rchi2 / nmeas
601 fittedState = atrack.getFittedState()
602 h["chi2"].Fill(chi2, wg)
603 h["measVSchi2"].Fill(atrack.getNumPoints(), chi2)
604 P = fittedState.getMomMag()
605 Px, Py, Pz = fittedState.getMom().x(), fittedState.getMom().y(), fittedState.getMom().z()
606 cov = fittedState.get6DCov()
607 if len(sTree.fitTrack2MC) - 1 < key:
608 continue
609 mcPartKey = sTree.fitTrack2MC[key]
610 if mcPartKey < 0 or mcPartKey >= len(sTree.MCTrack):
611 continue
612 mcPart = sTree.MCTrack[mcPartKey]
613 mcPart.GetP()
614 mcPart.GetPz()
615 # get p truth from first strawpoint
616 Ptruth, Ptruthx, Ptruthy, Ptruthz = getPtruthFirst(sTree, mcPartKey)
617 delPOverP = (Ptruth - P) / Ptruth
618 h["delPOverP"].Fill(Ptruth, delPOverP)
619 delPOverPz = (1.0 / Ptruthz - 1.0 / Pz) * Ptruthz
620 h["pullPOverPx"].Fill(Ptruth, (Ptruthx - Px) / ROOT.TMath.Sqrt(cov[3][3]))
621 h["pullPOverPy"].Fill(Ptruth, (Ptruthy - Py) / ROOT.TMath.Sqrt(cov[4][4]))
622 h["pullPOverPz"].Fill(Ptruth, (Ptruthz - Pz) / ROOT.TMath.Sqrt(cov[5][5]))
623 h["delPOverPz"].Fill(Ptruthz, delPOverPz)
624 if chi2 > chi2CutOff:
625 continue
626 h["delPOverP2"].Fill(Ptruth, delPOverP)
627 h["delPOverP2z"].Fill(Ptruth, delPOverPz)
628 # try measure impact parameter
629 trackDir = fittedState.getDir()
630 trackPos = fittedState.getPos()
631 vx = ROOT.TVector3()
632 mcPart.GetStartVertex(vx)
633 t = 0
634 for i in range(3):
635 t += trackDir(i) * (vx(i) - trackPos(i))
636 dist = 0
637 for i in range(3):
638 dist += (vx(i) - trackPos(i) - t * trackDir(i)) ** 2
639 dist = ROOT.TMath.Sqrt(dist)
640 h["IP"].Fill(dist)
641 # ---
642 # loop over particles, 2-track combinations
643 for HNL in sTree.Particles:
644 t1, t2 = HNL.GetDaughter(0), HNL.GetDaughter(1)
645 # kill tracks outside fiducial volume, if enabled
646 if not checkFiducialVolume(sTree, t1, dy) or not checkFiducialVolume(sTree, t2, dy):
647 continue
648 checkMeasurements = True
649 # cut on nDOF
650 for tr in [t1, t2]:
651 fitStatus = sTree.FitTracks[tr].getFitStatus()
652 nmeas = fitStatus.getNdf()
653 if nmeas < measCut:
654 checkMeasurements = False
655 if not checkMeasurements:
656 continue
657 # check mc matching
658 if not match2HNL(HNL):
659 continue
660 HNLPos = ROOT.TLorentzVector()
661 HNL.ProductionVertex(HNLPos)
662 HNLMom = ROOT.TLorentzVector()
663 HNL.Momentum(HNLMom)
664 doca = HNL.GetDoca()
665 # redo doca calculation
666 # xv,yv,zv,doca,HNLMom = RedoVertexing(t1,t2)
667 # if HNLMom == -1: continue
668 # check if decay inside decay volume
669 if not isInFiducial(HNLPos.X(), HNLPos.Y(), HNLPos.Z()):
670 continue
671 h["Doca"].Fill(doca)
672 if doca > docaCut:
673 continue
674 tr = ROOT.TVector3(0, 0, ShipGeo.target.z0)
675
676 # look for pi0
677 """
678 for pi0 in pi0Reco.findPi0(sTree,HNLPos):
679 rc = h['pi0Mass'].Fill(pi0.M())
680 if abs(pi0.M()-0.135)>0.02: continue
681# could also be used for eta, by changing cut
682 HNLwithPi0 = HNLMom + pi0
683 dist = ImpactParameter(tr,HNLPos,HNLwithPi0)
684 mass = HNLwithPi0.M()
685 h['IP0_pi0'].Fill(dist)
686 h['IP0/mass_pi0'].Fill(mass,dist)
687 h['HNL_pi0'].Fill(mass)
688 """
689
690 dist = ImpactParameter(tr, HNLPos, HNLMom)
691 mass = HNLMom.M()
692 h["IP0"].Fill(dist)
693 h["IP0/mass"].Fill(mass, dist)
694 h["HNL"].Fill(mass)
695 h["HNLw"].Fill(mass, wg)
696 #
697 vetoDets["SBT"] = veto.SBT_decision()
698 vetoDets["UBT"] = veto.UBT_decision()
699 vetoDets["TRA"] = veto.Track_decision()
700 h["nrtracks"].Fill(vetoDets["TRA"][2])
701 h["nrSBT"].Fill(vetoDets["SBT"][2])
702 # HNL true
703 mcTrackIdx = sTree.fitTrack2MC[t1]
704 if mcTrackIdx < 0 or mcTrackIdx >= len(sTree.MCTrack):
705 continue
706 mctrack = sTree.MCTrack[mcTrackIdx]
707 h["Vzresol"].Fill((mctrack.GetStartZ() - HNLPos.Z()) / u.cm)
708 h["Vxresol"].Fill((mctrack.GetStartX() - HNLPos.X()) / u.cm)
709 h["Vyresol"].Fill((mctrack.GetStartY() - HNLPos.Y()) / u.cm)
710 PosDir, newPosDir, CovMat, _scalFac = {}, {}, {}, {}
711 # opening angle at vertex
712 newPos = ROOT.TVector3(HNLPos.X(), HNLPos.Y(), HNLPos.Z())
713 st1, st2 = sTree.FitTracks[t1].getFittedState(), sTree.FitTracks[t2].getFittedState()
714 PosDir[t1] = {"position": st1.getPos(), "direction": st1.getDir(), "momentum": st1.getMom()}
715 PosDir[t2] = {"position": st2.getPos(), "direction": st2.getDir(), "momentum": st2.getMom()}
716 CovMat[t1] = st1.get6DCov()
717 CovMat[t2] = st2.get6DCov()
718 rep1, rep2 = ROOT.genfit.RKTrackRep(st1.getPDG()), ROOT.genfit.RKTrackRep(st2.getPDG())
719 state1, state2 = ROOT.genfit.StateOnPlane(rep1), ROOT.genfit.StateOnPlane(rep2)
720 rep1.setPosMom(state1, st1.getPos(), st1.getMom())
721 rep2.setPosMom(state2, st2.getPos(), st2.getMom())
722 try:
723 rep1.extrapolateToPoint(state1, newPos, False)
724 rep2.extrapolateToPoint(state2, newPos, False)
725 mom1, mom2 = rep1.getMom(state1), rep2.getMom(state2)
726 except Exception:
727 mom1, mom2 = st1.getMom(), st2.getMom()
728 newPosDir[t1] = {"position": rep1.getPos(state1), "direction": rep1.getDir(state1), "momentum": mom1}
729 newPosDir[t2] = {"position": rep2.getPos(state2), "direction": rep2.getDir(state2), "momentum": mom2}
730 oa = mom1.Dot(mom2) / (mom1.Mag() * mom2.Mag())
731 h["oa"].Fill(oa)
732 #
733 covX = HNL.GetCovV()
734 dist = HNL.GetDoca()
735 h["Vzpull"].Fill((mctrack.GetStartZ() - HNLPos.Z()) / ROOT.TMath.Sqrt(covX[5]))
736 h["Vxpull"].Fill((mctrack.GetStartX() - HNLPos.X()) / ROOT.TMath.Sqrt(covX[0]))
737 h["Vypull"].Fill((mctrack.GetStartY() - HNLPos.Y()) / ROOT.TMath.Sqrt(covX[3]))
738
739 # check extrapolation to TimeDet if exists
740 if hasattr(ShipGeo, "TimeDet"):
741 for fT in sTree.FitTracks:
742 rc, pos, _mom = TrackExtrapolateTool.extrapolateToPlane(fT, ShipGeo.TimeDet.z)
743 if rc:
744 for aPoint in sTree.TimeDetPoint:
745 h["extrapTimeDetX"].Fill(pos.X() - aPoint.GetX())
746 h["extrapTimeDetY"].Fill(pos.Y() - aPoint.GetY())
747
748
749#
750def HNLKinematics() -> None:
751 HNLorigin = {}
752 ut.bookHist(h, "HNLmomNoW", "momentum unweighted", 100, 0.0, 300.0)
753 ut.bookHist(h, "HNLmom", "momentum", 100, 0.0, 300.0)
754 ut.bookHist(h, "HNLPtNoW", "Pt unweighted", 100, 0.0, 10.0)
755 ut.bookHist(h, "HNLPt", "Pt", 100, 0.0, 10.0)
756 ut.bookHist(h, "HNLmom_recTracks", "momentum", 100, 0.0, 300.0)
757 ut.bookHist(h, "HNLmomNoW_recTracks", "momentum unweighted", 100, 0.0, 300.0)
758 for n in range(sTree.GetEntries()):
759 sTree.GetEntry(n)
760 for hnlkey in [1, 2]:
761 if hnlkey >= len(sTree.MCTrack):
762 continue
763 if abs(sTree.MCTrack[hnlkey].GetPdgCode()) == 9900015:
764 theHNL = sTree.MCTrack[hnlkey]
765 wg = theHNL.GetWeight()
766 if not wg > 0.0:
767 wg = 1.0
768 if hnlkey - 1 < 0 or hnlkey - 1 >= len(sTree.MCTrack):
769 continue
770 idMother = abs(sTree.MCTrack[hnlkey - 1].GetPdgCode())
771 if idMother not in HNLorigin:
772 HNLorigin[idMother] = 0
773 HNLorigin[idMother] += wg
774 P = theHNL.GetP()
775 Pt = theHNL.GetPt()
776 h["HNLmom"].Fill(P, wg)
777 h["HNLmomNoW"].Fill(P)
778 h["HNLPt"].Fill(Pt, wg)
779 h["HNLPtNoW"].Fill(Pt)
780 for HNL in sTree.Particles:
781 t1, t2 = HNL.GetDaughter(0), HNL.GetDaughter(1)
782 for tr in [t1, t2]:
783 xx = sTree.FitTracks[tr].getFittedState()
784 Prec = xx.getMom().Mag()
785 h["HNLmom_recTracks"].Fill(Prec, wg)
786 h["HNLmomNoW_recTracks"].Fill(Prec)
787 theSum = 0
788 for x in HNLorigin:
789 theSum += HNLorigin[x]
790 for x in HNLorigin:
791 print("%4i : %5.4F relative fraction: %5.4F " % (x, HNLorigin[x], HNLorigin[x] / theSum))
792
793
794#
795sTree.GetEvent(0)
796options.nEvents = min(sTree.GetEntries(), options.nEvents)
797
798# import pi0Reco
799ut.bookHist(h, "pi0Mass", "gamma gamma inv mass", 100, 0.0, 0.5)
800
801for n in range(options.nEvents):
802 myEventLoop(n)
803 sTree.FitTracks.clear()
804makePlots()
805# output histograms
806hfile = options.inputFile.split(",")[0]
807if "_rec" in hfile:
808 hfile = hfile.replace("_rec", "_ana")
809else:
810 hfile = hfile.replace(".root", "_ana.root")
811if "/eos" in hfile or not options.inputFile.find(",") < 0:
812 # do not write to eos, write to local directory
813 tmp = hfile.split("/")
814 hfile = tmp[len(tmp) - 1]
815ROOT.gROOT.cd()
816ut.writeHists(h, hfile)
None access2SmearedHits(ev, TrackingHits, MCTracks)
Definition: ShipAna.py:320
None makePlots()
Definition: ShipAna.py:449
def RedoVertexing(t1, t2)
Definition: ShipAna.py:354
def VertexError(t1, t2, PosDir, CovMat, scalFac)
Definition: ShipAna.py:149
bool checkHNLorigin(sTree)
Definition: ShipAna.py:269
def myVertex(t1, t2, PosDir)
Definition: ShipAna.py:331
None HNLKinematics()
Definition: ShipAna.py:750
bool checkFiducialVolume(sTree, int tkey, dy)
Definition: ShipAna.py:296
def dist2InnerWall(X, Y, Z)
Definition: ShipAna.py:221
None myEventLoop(int n)
Definition: ShipAna.py:536
def getPtruthFirst(sTree, mcPartKey)
Definition: ShipAna.py:310
bool match2HNL(p)
Definition: ShipAna.py:431
def ImpactParameter(point, tPos, tMom)
Definition: ShipAna.py:253
bool isInFiducial(X, Y, Z)
Definition: ShipAna.py:243
None fitSingleGauss(str x, float|None ba=None, float|None be=None)
Definition: ShipAna.py:407
None apply_decorators()
Definition: decorators.py:99
def addVMCFields(shipGeo, str controlFile="", bool verbose=False, bool withVirtualMC=True)
Definition: geomGeant4.py:165
def configure(run, ship_geo)
None configure(darkphoton=None)