31logger = logging.getLogger(os.path.splitext(os.path.basename(os.sys.argv[0]))[0])
32logger.setLevel(logging.INFO)
38 host = socket.gethostname()
39 job_base_name = os.path.splitext(os.path.basename(os.sys.argv[0]))[0]
41 out_dir = f
"{host}_{job_base_name}_{run_number}_{tag}"
43 out_dir = f
"{host}_{job_base_name}_{run_number}"
47logger.info(
"SHiP proton-on-taget simulator (C) Thomas Ruf, 2017")
49ap = argparse.ArgumentParser(description=
'Run SHiP "pot" simulation')
50ap.add_argument(
"-d",
"--debug", action=
"store_true")
51ap.add_argument(
"-f",
"--force", action=
"store_true", help=
"force overwriting output directory")
52ap.add_argument(
"-r",
"--run-number", type=int, dest=
"runnr", default=1)
54 "-e",
"--ecut", type=float, help=
"energy cut", default=0.5
56ap.add_argument(
"-n",
"--num-events", type=int, help=
"number of events to generate", dest=
"nev", default=100)
60 action=argparse.BooleanOptionalAction,
62 help=
"Whether or not to use Geant4 directly, no Pythia8 (--no-G4only or --G4only). Default set to False.",
67 action=argparse.BooleanOptionalAction,
69 help=
"Whether or not to use Pythia8 for decays (--no-PythiaDecay or --PythiaDecay). Default set to False.",
71ap.add_argument(
"-t",
"--tau-only", action=argparse.BooleanOptionalAction, dest=
"tauOnly", default=
False)
72ap.add_argument(
"-J",
"--Jpsi-mainly", action=argparse.BooleanOptionalAction, dest=
"JpsiMainly", default=
False)
73ap.add_argument(
"-b",
"--boostDiMuon", type=float, default=1.0, help=
"boost Di-muon branching ratios")
74ap.add_argument(
"-X",
"--boostFactor", type=float, default=1.0, help=
"boost Di-muon prod cross sections")
75ap.add_argument(
"-C",
"--charm", action=argparse.BooleanOptionalAction, default=
False, help=
"generate charm decays")
76ap.add_argument(
"-B",
"--beauty", action=argparse.BooleanOptionalAction, default=
False, help=
"generate beauty decays")
80 action=argparse.BooleanOptionalAction,
82 help=
"store only muons, ignore neutrinos",
84ap.add_argument(
"-N",
"--skipNeutrinos", action=argparse.BooleanOptionalAction, default=
False, help=
"skip neutrinos")
88 action=argparse.BooleanOptionalAction,
91 help=
"enable ntuple production",
94ap.add_argument(
"-cc",
"--chicc", default=1.7e-3, help=
"ccbar over mbias cross section")
95ap.add_argument(
"-bb",
"--chibb", default=1.6e-7, help=
"bbbar over mbias cross section")
96ap.add_argument(
"-p",
"--pot", default=4e13, help=
"number of protons on target per spill to normalize on")
97ap.add_argument(
"-S",
"--nStart", type=int, help=
"first event of input file to start", dest=
"nStart", default=0)
102 dest=
"charmInputFile",
103 default=ROOT.gSystem.Getenv(
"EOSSHIP")
104 +
"/eos/experiment/ship/data/Charm/Cascade-parp16-MSTP82-1-MSEL4-76Mpot_1.root",
105 help=
"input file for charm/beauty decays",
107ap.add_argument(
"-o",
"--output", type=str, help=
"output directory", dest=
"work_dir", default=
None)
109 "-rs",
"--seed", type=int, help=
"random seed; default value is 0, see TRrandom::SetSeed documentation", default=0
112 "--DecayVolumeMedium",
113 help=
"Set Decay Volume Medium. Choices are helium (default) or vacuums.",
115 choices=[
"helium",
"vacuums"],
119 help=
"Name of the shield in the database. New_HA_Design or warm_opt.",
121 choices=[
"TRY_2025"],
125 help=
"Whether or not to add the muon shield. Default set to False.",
127 action=argparse.BooleanOptionalAction,
130 "--AddMuonShieldField",
131 help=
"Whether or not to add the muon shield magnetic field. Default set to False.",
133 action=argparse.BooleanOptionalAction,
136 "--AddHadronAbsorberOnly",
137 help=
"Whether to only add the hadron absorber part of the muon shield. Default set to True.",
139 action=argparse.BooleanOptionalAction,
143 "--z-offset", type=float, dest=
"z_offset", default=-84.0, help=
"z-offset for the FixedTargetGenerator [mm]"
146 "--x-offset", type=float, dest=
"x_offset", default=0.0, help=
"x-offset for the FixedTargetGenerator [mm]"
149 "--y-offset", type=float, dest=
"y_offset", default=0.0, help=
"y-offset for the FixedTargetGenerator [mm]"
152 "--beam-smear", type=float, dest=
"beam_smear", default=16.0, help=
"beam smearing for the FixedTargetGenerator [mm]"
159 help=
"beam painting radius for the FixedTargetGenerator [mm]",
164 help=
"File for target configuration",
165 default=os.path.expandvars(
"$FAIRSHIP/geometry/target_config.yaml"),
169 "--AddCylindricalSensPlane",
171 help=
"Whether or not to add cylindrical sensitive plane around the target. False by default.",
174 "--AddPostTargetSensPlane",
176 help=
"Whether or not to add sensitive plane after the target. False by default.",
179args = ap.parse_args()
181 logger.setLevel(logging.DEBUG)
188 args.pythiaDecay =
False
189elif args.pythiaDecay:
191 logger.info(
"use Pythia8 as primary decayer")
194 logger.info(
"use EvtGen as primary decayer")
196if args.charm
and args.beauty:
197 logger.warning(
"charm and beauty decays are set! Beauty gets priority")
199charmInputFile = args.charmInputFile
201if args.work_dir
is None:
209logger.debug(
"work_dir: %s" % args.work_dir)
210logger.debug(
"command line arguments: %s", args)
211if os.path.exists(args.work_dir):
212 logger.warning(
"output directory '%s' already exists." % args.work_dir)
214 logger.warning(
"...cleaning")
215 for root, dirs, files
in os.walk(args.work_dir):
217 os.unlink(os.path.join(root, f))
219 shutil.rmtree(os.path.join(root, d))
221 logger.warning(
"...use '-f' option to overwrite it")
223 os.makedirs(args.work_dir)
225os.chdir(args.work_dir)
231 ROOT.gRandom.SetSeed(0)
232 seed = ROOT.gRandom.GetSeed()
235 seed = seed % 900000000
236ROOT.gRandom.SetSeed(seed)
240 "DecayVolumeMedium": args.DecayVolumeMedium,
241 "shieldName": args.shieldName,
242 "TARGET_YAML": args.TARGET_YAML,
246txt =
"pythia8_Geant4_"
248 txt =
"pythia8_evtgen_Geant4_"
249outFile = f
"{outputDir}/{txt}{args.runnr}_{args.ecut}.root"
250parFile = f
"{outputDir}/ship.params.{txt}{args.runnr}_{args.ecut}.root"
253timer = ROOT.TStopwatch()
257run = ROOT.FairRunSim()
259run.SetSink(ROOT.FairRootFileSink(outFile))
260if args.boostFactor > 1:
262 os.environ[
"SET_GENERAL_PROCESS_TO_FALSE"] =
"1"
263run.SetUserConfig(
"g4Config.C")
264rtdb = run.GetRuntimeDb()
267run.SetMaterials(
"media.geo")
269cave = ROOT.ShipCave(
"CAVE")
270cave.SetGeometryFileName(
"caveWithAir.geo")
274TargetStation = ROOT.ShipTargetStation(
275 name=
"TargetStation",
276 tl=ship_geo.target.length,
277 tz=ship_geo.target.z,
278 nS=ship_geo.target.nS,
279 HeT=ship_geo.target.HeT,
281TargetStation.SetLayerPosMat(
282 d=ship_geo.target.xy,
283 L=ship_geo.target.slices_length,
284 G=ship_geo.target.slices_gap,
285 M=ship_geo.target.slices_material,
287run.AddModule(TargetStation)
290if args.AddPostTargetSensPlane:
291 sensPlanePostT = ROOT.exitHadronAbsorber()
292 sensPlanePostT.SetEnergyCut(args.ecut * u.GeV)
293 sensPlanePostT.SetVetoPointName(
"PlanePostT")
296 sensPlanePostT.SetZposition(
297 ship_geo.target.length + 7.6 * u.cm + 300 * u.mm
299 sensPlanePostT.SetUseCaveCoordinates()
301 if args.storeOnlyMuons:
302 sensPlanePostT.SetOnlyMuons()
303 if args.skipNeutrinos:
304 sensPlanePostT.SkipNeutrinos()
306 sensPlanePostT.SetOpt4DP()
307 run.AddModule(sensPlanePostT)
310if args.AddMuonShield
or args.AddHadronAbsorberOnly:
312 if not args.AddMuonShieldField:
313 for i
in range(ship_geo.muShield.nMagnets):
314 ship_geo.muShield.params[i * n_params + 14] = 0
315 if args.AddHadronAbsorberOnly:
316 ship_geo.muShield.params = ship_geo.muShield.params[:15]
318 MuonShield = ROOT.ShipMuonShield(
319 in_params=list(ship_geo.muShield.params),
320 z=ship_geo.muShield.z,
321 WithConstShieldField=
True,
322 SC_key=ship_geo.SC_mag,
325 run.AddModule(MuonShield)
328sensPlaneHA = ROOT.exitHadronAbsorber()
329sensPlaneHA.SetEnergyCut(args.ecut * u.GeV)
330sensPlaneHA.SetVetoPointName(
"PlaneHA")
332if args.AddCylindricalSensPlane:
333 sensPlaneT = ROOT.exitHadronAbsorber()
334 sensPlaneT.SetEnergyCut(args.ecut * u.GeV)
335 sensPlaneT.SetVetoPointName(
"PlaneT")
336 sensPlaneT.SetCylindricalPlane()
339 sensPlaneT.SetZposition(ship_geo.target.length)
341if args.storeOnlyMuons:
342 sensPlaneHA.SetOnlyMuons()
343 if args.AddCylindricalSensPlane:
344 sensPlaneT.SetOnlyMuons()
345if args.skipNeutrinos:
346 sensPlaneHA.SkipNeutrinos()
347 if args.AddCylindricalSensPlane:
348 sensPlaneT.SkipNeutrinos()
350 sensPlaneHA.SetOpt4DP()
351 if args.AddCylindricalSensPlane:
352 sensPlaneT.SetOpt4DP()
354run.AddModule(sensPlaneHA)
356if args.AddCylindricalSensPlane:
357 run.AddModule(sensPlaneT)
360primGen = ROOT.FairPrimaryGenerator()
361P8gen = ROOT.FixedTargetGenerator()
362P8gen.SetZoffset(args.z_offset * u.mm)
363P8gen.SetXoffset(args.x_offset * u.mm)
364P8gen.SetYoffset(args.y_offset * u.mm)
365P8gen.SetSmearBeam(args.beam_smear * u.mm)
366P8gen.SetPaintRadius(args.beam_paint * u.mm)
368P8gen.SetTargetCoordinates(ship_geo.target.z0, ship_geo.target.z0 + ship_geo.target.length)
369P8gen.SetMom(400.0 * u.GeV)
370P8gen.SetEnergyCut(args.ecut * u.GeV)
371P8gen.SetDebug(args.debug)
372P8gen.SetHeartBeat(100000)
376 P8gen.SetJpsiMainly()
381if args.boostDiMuon > 1:
390if args.charm
or args.beauty:
391 print(
"--- process heavy flavours ---")
392 P8gen.InitForCharmOrBeauty(charmInputFile, args.nev, args.pot, args.nStart)
393primGen.AddGenerator(P8gen)
395run.SetGenerator(primGen)
400gMC = ROOT.TVirtualMC.GetMC()
401fStack = gMC.GetStack()
402fStack.SetMinPoints(1)
403fStack.SetEnergyCut(-1.0)
405import AddDiMuonDecayChannelsToG4
410if args.boostFactor > 1:
411 ROOT.gROOT.ProcessLine(
'#include "Geant4/G4ProcessTable.hh"')
412 ROOT.gROOT.ProcessLine(
'#include "Geant4/G4AnnihiToMuPair.hh"')
413 ROOT.gROOT.ProcessLine(
'#include "Geant4/G4GammaConversionToMuons.hh"')
414 gProcessTable = ROOT.G4ProcessTable.GetProcessTable()
415 procAnnihil = gProcessTable.FindProcess(ROOT.G4String(
"AnnihiToMuPair"), ROOT.G4String(
"e+"))
416 procGMuPair = gProcessTable.FindProcess(ROOT.G4String(
"GammaToMuPair"), ROOT.G4String(
"gamma"))
417 procAnnihil.SetCrossSecFactor(args.boostFactor)
418 procGMuPair.SetCrossSecFactor(args.boostFactor)
425rtime = timer.RealTime()
426ctime = timer.CpuTime()
428print(
"Macro finished successfully.")
429print(f
"Output file is {outFile}")
430print(f
"Real time {rtime} s, CPU time {ctime} s")
432tmpFile = outFile +
"tmp"
433if ROOT.gROOT.GetListOfFiles().GetEntries() > 0:
434 fin = ROOT.gROOT.GetListOfFiles()[0]
436 fin = ROOT.TFile.Open(outFile)
437fHeader = fin[
"FileHeader"]
438fHeader.SetRunId(args.runnr)
439if args.charm
or args.beauty:
441 poteq = P8gen.GetPotForCharm()
442 info =
"POT equivalent = %7.3G" % (poteq)
444 info = f
"POT = {args.nev}"
446conditions =
" with ecut=" +
str(args.ecut)
453if args.boostDiMuon > 1:
454 conditions +=
" diMu" +
str(args.boostDiMuon)
455if args.boostFactor > 1:
456 conditions +=
" X" +
str(args.boostFactor)
459fHeader.SetTitle(info)
460print(f
"Data generated {fHeader.GetTitle()}")
465 tf = ROOT.TFile(
"FourDP.root",
"recreate")
466 tnt = nt.CloneTree(0)
467 for i
in range(nt.GetEntries()):
469 rc = tnt.Fill(nt.id, nt.px, nt.py, nt.pz, nt.x, nt.y, nt.z)
474fout = ROOT.TFile(tmpFile,
"recreate")
475sTree = t.CloneTree(0)
477for n
in range(t.GetEntries()):
480 (len(t.PlaneHAPoint) > 0)
481 or (args.AddCylindricalSensPlane
and len(t.PlaneTPoint) > 0)
482 or (args.AddPostTargetSensPlane
and len(t.PlanePostTPoint) > 0)
487for k
in fin.GetListOfKeys():
488 x = fin.Get(k.GetName())
489 className = x.Class().GetName()
490 if className.find(
"TTree") < 0
and className.find(
"TNtuple") < 0:
494ff = fin[
"FileHeader"].Clone(fout.GetName())
496ff.Write(
"FileHeader", ROOT.TObject.kSingleKey)
500rc1 = os.system(
"rm " + outFile)
501rc2 = os.system(
"mv " + tmpFile +
" " + outFile)
502print(
"removed out file, moved tmpFile to out file", rc1, rc2)
503fin.SetWritable(
False)
505print(f
"Number of events produced with activity after hadron absorber: {nEvents}")
508 sGeo = ROOT.gGeoManager
511 run.CreateGeometryFile(
"%s/geofile_full.root" % (outputDir))
512 import saveBasicParameters
def create_config(str DecayVolumeMedium="helium", float Yheight=6.0, int strawDesign=10, muShieldGeo=None, str shieldName="New_HA_Design", int nuTargetPassive=1, bool SND=True, SND_design=None, TARGET_YAML=None)
str get_work_dir(run_number, str|None tag=None)
def execute(f, ox, name="ShipGeo")
None configure(darkphoton=None)