From f4c9eb40df3d45590ab36fafb07735147d461ea6 Mon Sep 17 00:00:00 2001
From: Andy Buckley <a.g.buckley@gmail.com>
Date: Thu, 29 Jan 2015 19:47:32 +0100
Subject: [PATCH] 'Re-fix mass setting ;-) Previous version is mostly right but
 could produce out-of-sync vector and gen masses.' (ParticleGun-02-00-06)

	* Tagging: ParticleGun-02-00-06

	* Re-fix mass setting ;-) Previous version is mostly right but could produce out-of-sync vector and gen masses.

2015-01-29  Ewelina Lobodzinska  <ewelina@mail.desy.de>

        * Tagging: ParticleGun-02-00-05

        * implement fix provided by Zach for mass setting

2015-01-28  Andy Buckley  <andy.buckley@cern.ch>

	* Tagging: ParticleGun-02-00-04

	* Improve generated_mass setting and remove accidental print.

2015-01-25  Andy Buckley  <andy.buckley@cern.ch>

	* Tagging: ParticleGun-02-00-03
...
(Long ChangeLog diff - truncated)
---
 ...bOption.ParticleGun_constenergy_flateta.py |  3 +-
 .../jobOption.ParticleGun_corrhist.py         |  2 +-
 Generators/ParticleGun/python/__init__.py     | 49 ++++++++++++-------
 Generators/ParticleGun/python/histsampling.py | 30 +++++++++---
 Generators/ParticleGun/python/samplers.py     | 21 +++++---
 5 files changed, 69 insertions(+), 36 deletions(-)

diff --git a/Generators/ParticleGun/examples/jobOption.ParticleGun_constenergy_flateta.py b/Generators/ParticleGun/examples/jobOption.ParticleGun_constenergy_flateta.py
index 7b1d14e11f1..3c3cb0321b3 100644
--- a/Generators/ParticleGun/examples/jobOption.ParticleGun_constenergy_flateta.py
+++ b/Generators/ParticleGun/examples/jobOption.ParticleGun_constenergy_flateta.py
@@ -7,7 +7,8 @@ theApp.EvtMax = 100
 
 import ParticleGun as PG
 pg = PG.ParticleGun()
-pg.sampler.pid = 11
+pg.randomSeed = 123456
+pg.sampler.pid = {11,-11,211,111}
 pg.sampler.mom = PG.EEtaMPhiSampler(energy=10000, eta=[-2,2])
 topSeq += pg
 
diff --git a/Generators/ParticleGun/examples/jobOption.ParticleGun_corrhist.py b/Generators/ParticleGun/examples/jobOption.ParticleGun_corrhist.py
index 33ad1d9a118..f5539702d5d 100644
--- a/Generators/ParticleGun/examples/jobOption.ParticleGun_corrhist.py
+++ b/Generators/ParticleGun/examples/jobOption.ParticleGun_corrhist.py
@@ -26,7 +26,7 @@ class PtEtaHistParticleSampler(PG.ParticleSampler):
             ptrand *= 1000 # NB. This _particular_ histogram is in GeV, but Athena needs MeV!
             # TODO: Provide 4-mom construction functions to avoid building this one-time sampler
             pid = self.pid()
-            mom = PG.PtEtaMPhiSampler(pt=ptrand, eta=etarand, mass=PG.MASSES[pid])
+            mom = PG.PtEtaMPhiSampler(pt=ptrand, eta=etarand, mass=PG.MASSES[abs(pid)])
             p = PG.SampledParticle(pid, mom())
             #print p.mom.Pt(), "\t", p.mom.Eta(), "\t", p.mom.Phi(), "\t", p.mom.M()
             particles.append(p)
diff --git a/Generators/ParticleGun/python/__init__.py b/Generators/ParticleGun/python/__init__.py
index a5bc6a1564c..e6aaa7feac2 100644
--- a/Generators/ParticleGun/python/__init__.py
+++ b/Generators/ParticleGun/python/__init__.py
@@ -15,12 +15,12 @@ class ParticleGun(EvgenAlg):
     A simple but flexible algorithm for generating events from simple distributions.
     """
 
-    def __init__(self, name="ParticleGun", randomSvcName="AtRndmGenSvc", randomStream="ParticleGun"):
+    def __init__(self, name="ParticleGun", randomSvcName="AtRndmGenSvc", randomStream="ParticleGun", randomSeed=None):
         super(ParticleGun, self).__init__(name=name)
         self.samplers = [ParticleSampler()]
         self.randomStream = randomStream
         self.randomSvcName = randomSvcName
-
+        self.randomSeed = randomSeed
 
     @property
     def sampler(self):
@@ -35,20 +35,29 @@ class ParticleGun(EvgenAlg):
     def initialize(self):
         """
         Pass the AtRndmGenSvc seed to Python's random module (or fall back to a fixed value for reproducibility).
-
-        TODO: Also allow directly specifying the seed as a constructor/Athena property -> self.randomSeed=None?
         """
-        seed = "123456"
-        randomSvc = getattr(svcMgr, self.randomSvcName, None)
-        if randomSvc is not None:
-            for seedstr in randomSvc.Seeds:
-                if seedstr.startswith(self.randomStream):
-                    seed = seedstr
-                    self.msg.info("ParticleGun: Using random seed '%s'" % seed)
+        seed = None
+        ## Use self.randomSeed directly, or if it's None find a seed string from the ATLAS random number service
+        if self.randomSeed is not None:
+            seed = self.randomSeed
         else:
-            self.msg.warning("ParticleGun: Failed to find random number service called '%s'" % self.randomSvcName)
-        random.seed(seed)
-        return StatusCode.Success
+            randomSvc = getattr(svcMgr, self.randomSvcName, None)
+            if randomSvc is not None:
+                for seedstr in randomSvc.Seeds:
+                    if seedstr.startswith(self.randomStream):
+                        seed = seedstr
+                        self.msg.info("ParticleGun: Using random seed '%s'" % seed)
+                        break
+                if seed is None:
+                    self.msg.warning("ParticleGun: Failed to find a seed for the random stream named '%s'" % self.randomStream)
+            else:
+                self.msg.warning("ParticleGun: Failed to find random number service called '%s'" % self.randomSvcName)
+        ## Apply the seed
+        if seed is not None:
+            random.seed(seed)
+            return StatusCode.Success
+        else:
+            return StatusCode.Failure
 
 
     def fillEvent(self, evt):
@@ -64,10 +73,10 @@ class ParticleGun(EvgenAlg):
             particles = s.shoot()
             for p in particles:
                 ## Debug printout of particle properties
-                #print p.pid, p.mom.E(), p.mom.M()
-                #print "(px,py,pz,E) = (%0.2e, %0.2e, %0.2e, %0.2e)" % (p.mom.Px(), p.mom.Py(), p.mom.Pz(), p.mom.E())
-                #print "(eta,phi,pt,m) = (%0.2e, %0.2e, %0.2e, %0.2e)" % (p.mom.Eta(), p.mom.Phi(), p.mom.Pt(), p.mom.M())
-                #print "(x,y,z,t) = (%0.2e, %0.2e, %0.2e, %0.2e)" % (p.pos.X(), p.pos.Y(), p.pos.Z(), p.pos.T())
+                #print DEBUG0, p.pid, p.mom.E(), p.mom.Pt(), p.mom.M()
+                #print "DEBUG1 (px,py,pz,E) = (%0.2e, %0.2e, %0.2e, %0.2e)" % (p.mom.Px(), p.mom.Py(), p.mom.Pz(), p.mom.E())
+                #print "DEBUG2 (eta,phi,pt,m) = (%0.2e, %0.2e, %0.2e, %0.2e)" % (p.mom.Eta(), p.mom.Phi(), p.mom.Pt(), p.mom.M())
+                #print "DEBUG3 (x,y,z,t) = (%0.2e, %0.2e, %0.2e, %0.2e)" % (p.pos.X(), p.pos.Y(), p.pos.Z(), p.pos.T())
 
                 ## Make particle-creation vertex
                 # TODO: do something cleverer than one vertex per particle?
@@ -79,9 +88,11 @@ class ParticleGun(EvgenAlg):
                 ## Make particle with status == 1
                 mom = HepMC.FourVector(p.mom.Px(), p.mom.Py(), p.mom.Pz(), p.mom.E())
                 gp = HepMC.GenParticle()
+                gp.set_status(1)
                 gp.set_pdg_id(p.pid)
                 gp.set_momentum(mom)
-                gp.set_status(1)
+                if p.mass is not None:
+                    gp.set_generated_mass(p.mass)
                 ROOT.SetOwnership(gp, False)
                 gv.add_particle_out(gp)
 
diff --git a/Generators/ParticleGun/python/histsampling.py b/Generators/ParticleGun/python/histsampling.py
index e54e6ef0052..6d97ca2a929 100644
--- a/Generators/ParticleGun/python/histsampling.py
+++ b/Generators/ParticleGun/python/histsampling.py
@@ -41,12 +41,19 @@ def get_sampling_vars(h):
     globalbin_to_axisbin = {} # for reverse axis bin lookup to get edges
     globalbins = [] # because they aren't easily predicted, nor contiguous
     cheights = [0] # cumulative "histogram" from which to uniformly sample
-    for ix in xrange(1, h.GetNbinsX()+1):
-        for iy in xrange(1, h.GetNbinsY()+1):
-            iglobal = h.GetBin(ix, iy)
+    if issubclass(type(h), ROOT.TH1):
+        for ix in xrange(1, h.GetNbinsX()+1):
+            iglobal = h.GetBin(ix)
             globalbins.append(iglobal)
-            globalbin_to_axisbin[iglobal] = (ix, iy) if issubclass(type(h), ROOT.TH2) else tuple(ix,)
+            globalbin_to_axisbin[iglobal] = (ix,)
             cheights.append(cheights[-1] + h.GetBinContent(iglobal))
+    elif issubclass(type(h), ROOT.TH2):
+        for ix in xrange(1, h.GetNbinsX()+1):
+            for iy in xrange(1, h.GetNbinsY()+1):
+                iglobal = h.GetBin(ix, iy)
+                globalbins.append(iglobal)
+                globalbin_to_axisbin[iglobal] = (ix, iy)
+                cheights.append(cheights[-1] + h.GetBinContent(iglobal))
     return globalbins, globalbin_to_axisbin, cheights
 
 
@@ -74,7 +81,7 @@ def get_random_x(h, globalbins, cheights, globalbin_to_axisbin):
     irand = get_random_bin(globalbins, cheights)
     axisids = globalbin_to_axisbin.get(irand)
     assert axisids is not None
-    xrand = random.uniform(h2.GetXaxis().GetBinLowEdge(axisids[0]), h2.GetXaxis().GetBinUpEdge(axisids[0]))
+    xrand = random.uniform(h.GetXaxis().GetBinLowEdge(axisids[0]), h.GetXaxis().GetBinUpEdge(axisids[0]))
     return xrand
 
 
@@ -92,10 +99,17 @@ def get_random_xy(h2, globalbins, cheights, globalbin_to_axisbin):
 
 
 class TH1(object):
-    "Minimal wrapper for ROOT TH1, for consistency and easy loading"
+    "Minimal wrapper for ROOT TH1, for sampling consistency and easy loading"
 
     def __init__(self, *args):
         self.th1 = load_hist(*args)
+        self.globalbins, self.globalbin_to_axisbin, self.cheights = None, None, None
+
+    def GetRandom(self):
+        "A GetRandom that works for TH1s and uses Python random numbers"
+        if self.globalbins is None or self.globalbin_to_axisbin is None or self.cheights is None:
+            self.globalbins, self.globalbin_to_axisbin, self.cheights = get_sampling_vars(self.th1)
+        return get_random_x(self.th1, self.globalbins, self.cheights, self.globalbin_to_axisbin)
 
     def __getattr__(self, attr):
         "Forward all attributes to the contained TH1"
@@ -107,10 +121,12 @@ class TH2(object):
 
     def __init__(self, *args):
         self.th2 = load_hist(*args)
-        self.globalbins, self.globalbin_to_axisbin, self.cheights = get_sampling_vars(self.th2)
+        self.globalbins, self.globalbin_to_axisbin, self.cheights = None, None, None
 
     def GetRandom(self):
         "A GetRandom that works for TH2s"
+        if self.globalbins is None or self.globalbin_to_axisbin is None or self.cheights is None:
+            self.globalbins, self.globalbin_to_axisbin, self.cheights = get_sampling_vars(self.th2)
         return get_random_xy(self.th2, self.globalbins, self.cheights, self.globalbin_to_axisbin)
 
     def __getattr__(self, attr):
diff --git a/Generators/ParticleGun/python/samplers.py b/Generators/ParticleGun/python/samplers.py
index 94a38fa577c..d29078e82d1 100644
--- a/Generators/ParticleGun/python/samplers.py
+++ b/Generators/ParticleGun/python/samplers.py
@@ -204,7 +204,7 @@ class RandomSeqSampler(DiscreteSampler):
             self.sequence = args
 
     def shoot(self):
-        return self.sequence.random()
+        return random.choice(self.sequence)
 # Alias:
 RndmSeq = RandomSeqSampler
 
@@ -841,6 +841,7 @@ class SampledParticle(object):
         self.pid = pid
         self.mom = mom
         self.pos = pos
+        self.mass = None
 
 
 class ParticleSampler(Sampler):
@@ -877,16 +878,20 @@ class ParticleSampler(Sampler):
 
     def shoot(self):
         "Return a vector of sampled particles"
-
         numparticles = self.n()
         rtn = []
         for i in xrange(numparticles):
-            ## Sample the particle ID and pass mass info to the v4 sampler
+            ## Sample the particle ID and create a particle
             pid = self.pid()
+            p = SampledParticle(pid)
+            ## Pass mass info to the v4 sampler and set same generated mass
             if self.mass_override and self.massdict.has_key(abs(pid)):
-                self.mom.mass = self.massdict[abs(pid)]
-            ## Sample momentum and vertex positions
-            mom = self.mom()
-            pos = self.pos()
-            rtn.append( SampledParticle(pid, mom, pos) )
+                m = self.massdict[abs(pid)]
+                self.mom.mass = m
+                p.mass = m
+            ## Sample momentum and vertex positions into the particle
+            p.mom = self.mom()
+            p.pos = self.pos()
+            ## Add particle to output list
+            rtn.append(p)
         return rtn
-- 
GitLab