FairShip
Loading...
Searching...
No Matches
makeMuonDIS.py
Go to the documentation of this file.
1#!/usr/bin/env python
2# SPDX-License-Identifier: LGPL-3.0-or-later
3# SPDX-FileCopyrightText: Copyright CERN for the benefit of the SHiP Collaboration
4
5"""Script to generate DIS events for muons in Pythia6, and save them to a ROOT file (along with the original muon's soft interactions)."""
6
7import argparse
8import logging
9import os
10import time
11from array import array
12
13import ROOT as r
14from tabulate import tabulate
15
16logging.basicConfig(level=logging.INFO)
17PDG = r.TDatabasePDG.Instance()
18PDG.AddParticle("C12", "Carbon-12", 12.0, True, 0, 6.0, "nucleus", 1000060120)
19PDG.AddParticle("C13", "Carbon-13", 13.003355, True, 0, 6.0, "nucleus", 1000060130)
20
21parser = argparse.ArgumentParser(description=__doc__)
22parser.add_argument("-f", "--inputFile", help="Input file to use", required=True)
23parser.add_argument(
24 "-i",
25 "--firstEvent",
26 dest="first_mu_event",
27 help="First event of muon file to use",
28 required=False,
29 default=0,
30 type=int,
31)
32parser.add_argument(
33 "-n",
34 "--nEvents",
35 dest="n_events",
36 help="Number of muons to generate DIS for",
37 required=False,
38 default=10,
39 type=int,
40)
41parser.add_argument(
42 "-nDISPerMuon",
43 "--nDIS",
44 help="Number of DIS per muon to generate",
45 required=False,
46 default=1000,
47 type=int,
48)
49
50args = parser.parse_args()
51n_events = args.n_events
52first_mu_event = args.first_mu_event
53
54
55def rotate(px, py, pz, theta, phi):
56 """Rotate the daughter particle momentum to align with respect to the muon's momentum."""
57 momentum = r.TVector3(px, py, pz)
58
59 rotation = r.TRotation()
60 rotation.RotateY(theta) # Rotate around the Y-axis
61 rotation.RotateZ(phi) # Rotate around the Z-axis
62
63 # Apply the rotation to the momentum vector
64 rotated_momentum = rotation * momentum
65
66 return rotated_momentum.X(), rotated_momentum.Y(), rotated_momentum.Z()
67
68
69def update_file(filename: str, final_xsec) -> None:
70 """Update the DIS cross section of the muon to the converged value from Pythia."""
71 file = r.TFile.Open(filename, "read")
72
73 original_tree = file.DIS
74
75 temp_filename = filename + ".tmp"
76 temp_file = r.TFile.Open(temp_filename, "recreate")
77
78 updated_tree = original_tree.CloneTree(0)
79
80 for i, event in enumerate(original_tree):
81 mu = event.InMuon[0]
82 mu[10] = final_xsec[int(first_mu_event + i / args.nDIS)]
83 updated_tree.Fill()
84
85 updated_tree.Write("DIS", r.TObject.kOverwrite)
86 temp_file.Close()
87 file.Close()
88
89 os.remove(filename)
90 os.rename(temp_filename, filename)
91 logging.info("Muon DIS events successfully updated with converged cross sections.")
92
93
94headers = [
95 "DIS_index",
96 "Fix_Target",
97 "nParticles in event",
98 "nSoftTracks_iMuon",
99 "nSBThits_iMuon",
100 "nUBThits_iMuon",
101 "cross_sec",
102]
103Fixtarget = {1: "p+", 0: "n0"}
104
105
106def inspect_file(filename: str) -> None:
107 """Inspect the contents of muonDis file."""
108 file = r.TFile.Open(filename, "READ")
109 tree = file.DIS
110
111 table_rows = []
112
113 for i, event in enumerate(tree):
114 muon = event.InMuon[0]
115 isProton = int(muon[9])
116 fix_target = Fixtarget.get(isProton, "unknown")
117 cross_sec = muon[10]
118
119 nParticles = len(event.DISParticles)
120 nSoftTracks = len(event.SoftParticles)
121 nSBThits = len(event.muon_vetoPoints)
122 nUBThits = len(event.muon_UpstreamTaggerPoints)
123
124 table_rows.append([i, fix_target, nParticles, nSoftTracks, nSBThits, nUBThits, cross_sec])
125
126 file.Close()
127 logging.info("\n" + tabulate(table_rows, headers=headers, tablefmt="grid"))
128
129
130def makeMuonDIS() -> None:
131 """Generate DIS events."""
132 final_xsec = {}
133
134 logging.info(f"Opening input file: {args.inputFile}")
135 muonFile = r.TFile.Open(args.inputFile, "read")
136
137 try:
138 muon_tree = muonFile.MuonAndSoftInteractions
139 except Exception as e:
140 logging.error(e)
141 muonFile.Close()
142 exit(1)
143
144 logging.debug(f"Total entries in the tree: {muon_tree.GetEntries()}")
145 last_mu_event = min(muon_tree.GetEntries(), first_mu_event + n_events)
146
147 logging.info("Creating output file: muonDis.root")
148
149 outputFile = r.TFile.Open("muonDis.root", "recreate")
150 output_tree = r.TTree("DIS", "muon DIS")
151
152 iMuon = r.TClonesArray("TVectorD")
153 output_tree.Branch("InMuon", iMuon, 32000, -1)
154
155 dPartDIS = r.TClonesArray("TVectorD")
156 output_tree.Branch("DISParticles", dPartDIS, 32000, -1)
157
158 dPartSoft = r.TClonesArray("TVectorD")
159 output_tree.Branch("SoftParticles", dPartSoft, 32000, -1)
160
161 muon_vetoPoints = r.TClonesArray("vetoPoint")
162 output_tree.Branch("muon_vetoPoints", muon_vetoPoints, 32000, -1)
163
164 muon_UpstreamTaggerPoints = r.TClonesArray("UpstreamTaggerPoint")
165 output_tree.Branch("muon_UpstreamTaggerPoints", muon_UpstreamTaggerPoints, 32000, -1)
166
167 myPythia = r.TPythia6()
168 myPythia.SetMSEL(2)
169 myPythia.SetPARP(2, 2)
170 for kf in [211, 321, 130, 310, 3112, 3122, 3222, 3312, 3322, 3334]:
171 kc = myPythia.Pycomp(kf)
172 myPythia.SetMDCY(kc, 1, 0)
173
174 seed = int(time.time() % 900000000)
175 myPythia.SetMRPY(1, seed)
176 mutype = {-13: "gamma/mu+", 13: "gamma/mu-"}
177
178 myPythia.SetMSTU(11, 11)
179 logging.info(f"Processing muon events from {first_mu_event} to {last_mu_event - 1}...")
180
181 nMade = 0
182
183 for k in range(first_mu_event, last_mu_event):
184 DIS_table = [] # debug
185 cross_sections = []
186
187 muon_tree.GetEvent(k)
188
189 imuondata = muon_tree.imuondata
190
191 pid = imuondata[0]
192 px = imuondata[1]
193 py = imuondata[2]
194 pz = imuondata[3]
195 x = imuondata[4]
196 y = imuondata[5]
197 z = imuondata[6]
198 w = imuondata[7]
199 time_muon = imuondata[8]
200 nmuons = imuondata[9] # number of muons in the original MuBack event
201
202 p = r.TMath.Sqrt(px**2 + py**2 + pz**2)
203 mass = PDG.GetParticle(abs(int(pid))).Mass()
204 E = r.TMath.Sqrt(mass**2 + p**2)
205
206 theta = r.TMath.ACos(pz / p)
207 phi = r.TMath.ATan2(py, px)
208
209 isProton = 1
210 xsec = 0
211
212 mu = array(
213 "d",
214 [
215 pid,
216 px,
217 py,
218 pz,
219 E,
220 x,
221 y,
222 z,
223 w,
224 isProton,
225 xsec,
226 time_muon,
227 args.nDIS,
228 nmuons,
229 ],
230 )
231 muPart = r.TVectorD(14, mu)
232 myPythia.Initialize("FIXT", mutype[pid], "p+", p) # target = "p+"
233 myPythia.Pylist(1)
234
235 for a in range(args.nDIS):
236 if a == args.nDIS // 2:
237 myPythia.Initialize("FIXT", mutype[pid], "n0", p) # target = "n0"
238 isProton = 0
239 # logging.debug("Switching to neutron interaction")
240
241 dPartDIS.Clear()
242 iMuon.Clear()
243 muPart[9] = isProton
244 iMuon[0] = muPart
245 myPythia.GenerateEvent()
246 myPythia.Pyedit(1)
247
248 for itrk in range(1, myPythia.GetN() + 1):
249 xsec = myPythia.GetPARI(1)
250
251 muPart[10] = xsec
252 did = myPythia.GetK(itrk, 2)
253 dpx, dpy, dpz = rotate(
254 myPythia.GetP(itrk, 1),
255 myPythia.GetP(itrk, 2),
256 myPythia.GetP(itrk, 3),
257 theta,
258 phi,
259 )
260 psq = dpx**2 + dpy**2 + dpz**2
261 masssq = PDG.GetParticle(did).Mass() ** 2
262 E = r.TMath.Sqrt(masssq + psq)
263 m = array("d", [did, dpx, dpy, dpz, E])
264 part = r.TVectorD(5, m)
265 nPart = len(dPartDIS)
266 if dPartDIS.GetSize() == nPart:
267 dPartDIS.Expand(nPart + 10)
268 # dPartDIS.ConstructedAt(nPart).Use(part) #to be adapted later
269 dPartDIS[nPart] = part
270
271 cross_sections.append(xsec)
272
273 dPartSoft.Clear()
274
275 for softTrack in muon_tree.tracks:
276 did = softTrack.GetPdgCode()
277 dpx = softTrack.GetPx()
278 dpy = softTrack.GetPy()
279 dpz = softTrack.GetPz()
280
281 psq = dpx**2 + dpy**2 + dpz**2
282 masssq = PDG.GetParticle(did).Mass() ** 2
283 E = r.TMath.Sqrt(masssq + psq)
284
285 softx = softTrack.GetStartX()
286 softy = softTrack.GetStartY()
287 softz = softTrack.GetStartZ()
288 time_ = softTrack.GetStartT()
289
290 m = array("d", [did, dpx, dpy, dpz, E, softx, softy, softz, time_])
291
292 part = r.TVectorD(9, m)
293 nPart = len(dPartSoft)
294 if dPartSoft.GetSize() == nPart:
295 dPartSoft.Expand(nPart + 10)
296 # dPartSoft.ConstructedAt(nPart).Use(part) #to be adapted later
297 dPartSoft[nPart] = part
298
299 muon_vetoPoints.Clear()
300
301 index = 0
302 for hit in muon_tree.muon_vetoPoints:
303 if muon_vetoPoints.GetSize() == index:
304 muon_vetoPoints.Expand(index + 1)
305 hit.SetTrackID(0) # Set TrackID to match for muon ID for new simulation
306 muon_vetoPoints[index] = hit
307 index += 1
308
309 muon_UpstreamTaggerPoints.Clear()
310
311 ubt_index = 0
312 for hit in muon_tree.muon_UpstreamTaggerPoints:
313 if muon_UpstreamTaggerPoints.GetSize() == ubt_index:
314 muon_UpstreamTaggerPoints.Expand(ubt_index + 1)
315 hit.SetTrackID(0) # Set TrackID to match for muon ID for new simulation
316 muon_UpstreamTaggerPoints[ubt_index] = hit
317 ubt_index += 1
318
319 output_tree.Fill()
320 DIS_table.append(
321 [
322 a,
323 Fixtarget[isProton],
324 myPythia.GetN(),
325 len(dPartSoft),
326 len(muon_vetoPoints),
327 len(muon_UpstreamTaggerPoints),
328 xsec,
329 ]
330 )
331
332 final_xsec[k] = xsec
333
334 nMade += 1
335 logging.debug(
336 f"\nMuon index:{k} \n\tPID = {pid}, weight = {w}, converged_cross_section= {final_xsec[k]}\n\tpx = {px}, py = {py}, pz = {pz}, E = {E},\n\tx = {x}, y = {y}, z = {z}\n\n\tDIS Events Summary\n{tabulate(DIS_table, headers=headers, tablefmt='grid')}"
337 )
338 if nMade % 10 == 0:
339 logging.info(f"Muons processed: {nMade}")
340
341 outputFile.cd()
342 output_tree.Write()
343 myPythia.SetMSTU(11, 6)
344 logging.info(
345 f"DIS generated for muons (index {first_mu_event} - {last_mu_event - 1}) , output saved in muonDis.root, nDISPerMuon = {args.nDIS}"
346 )
347 outputFile.Close()
348 muonFile.Close()
349
350 update_file("muonDis.root", final_xsec)
351
352
353if __name__ == "__main__":
355 inspect_file("muonDis.root")
None makeMuonDIS()
Definition: makeMuonDIS.py:130
def rotate(px, py, pz, theta, phi)
Definition: makeMuonDIS.py:55
None update_file(str filename, final_xsec)
Definition: makeMuonDIS.py:69
None inspect_file(str filename)
Definition: makeMuonDIS.py:106