diff --git a/Control/CalypsoExample/Generation/python/faser_parser.py b/Control/CalypsoExample/Generation/python/faser_parser.py
index 43f98387e7089cfdf258531ec57aeea072b70f53..ff8f1d3792a666bf92dca51b16bf7adf58c7500d 100644
--- a/Control/CalypsoExample/Generation/python/faser_parser.py
+++ b/Control/CalypsoExample/Generation/python/faser_parser.py
@@ -4,6 +4,13 @@
 #
 # Parser function for particle gun samples
 #
+
+def float_or_none(arg):
+    if arg is None or arg == "None":
+        return None
+
+    return float(arg)
+
 def faser_pgparser():
 
     import sys
@@ -35,13 +42,16 @@ def faser_pgparser():
                         help="Specify particle mass (in MeV)")
     parser.add_argument("--radius", default=100., type=float,
                         help="Specify radius (in mm)")
-    parser.add_argument("--angle", default=0.005, type=float,
+    parser.add_argument("--angle", default=0.005, type=float_or_none,
                         help="Specify angular width (in Rad)")
     parser.add_argument("--zpos", default=None, type=float,
                         help="Specify z position of particles (in mm) (helpful to avoid FASERnu)")
 
     parser.add_argument("--sampler", default="log",
-                        help="Specify energy sampling (log, lin, const)")
+                        help="Specify energy sampling (log, lin, const, hist, hist2D)")
+    parser.add_argument("--hist_name", default="log",
+                        help="Specify energy sampling name for hist sampler file.root:hist")
+    
     parser.add_argument("--minE", default=10., type=float,
                         help="Minimum energy in GeV (for log or lin sampler)")
     parser.add_argument("--maxE", default=1000., type=float,
diff --git a/Generators/ForeseeGenerator/share/generate_forsee_events.py b/Generators/ForeseeGenerator/share/generate_forsee_events.py
index 3d84bb15354e0bd1ab8bf47eb1a380b27d08939a..0c3096f8abe6a640391999613442ce1a267f4aca 100644
--- a/Generators/ForeseeGenerator/share/generate_forsee_events.py
+++ b/Generators/ForeseeGenerator/share/generate_forsee_events.py
@@ -459,7 +459,7 @@ def production(args):
                 f.write()
 
             if args.outdir:
-                p = proc.run(["cp", filename, outdir + "/" + filename.split("/")[-1]])
+                p = proc.run(["cp", filename, outdir + "/" + filename.split("/")[-1].replace(".hepmc", f"{nrun}.hepmc")])
                 if not p.returncode:
                     proc.run(["rm", filename])
 
@@ -492,7 +492,7 @@ if __name__ == "__main__":
     parser.add_argument("--couplings", "-c", required = False, nargs = "+", help = "Couplings of mother (either single/mulitple values or tuple to pass to np.logspace)")
     parser.add_argument("--pid1", default = None, required = False, type = int, help = "PID of daughter 1")
     parser.add_argument("--pid2", default = None, type = int, help = "PID of daughter 2 (if not set then will be -PID1)")
-    parser.add_argument("--Ecom", default = "14", help = "Center of mass energy [TeV]")
+    parser.add_argument("--Ecom", default = "13.6", help = "Center of mass energy [TeV]")
     parser.add_argument("--outdir", "-o", default = None, help = "Output path")    
     parser.add_argument("--path", default = ".", help = "Path to foresee installation")
     parser.add_argument("--hepmc", action = "store_true", help = "Write HepMC events")
diff --git a/Generators/ForeseeGenerator/share/plot_validation.py b/Generators/ForeseeGenerator/share/plot_validation.py
index d4ff9c8b2cb9274a7aade2e8b0a0f6ceb6bf21e9..b3cfc2b55dd58ddd921c4833f7191fef16e618c3 100644
--- a/Generators/ForeseeGenerator/share/plot_validation.py
+++ b/Generators/ForeseeGenerator/share/plot_validation.py
@@ -65,6 +65,7 @@ def plotn(f, args, configs, x, y, outname):
         c._objs.append(plot(f, *cfg))
         
     c.Print(f"{args.output}/{outname}.eps")
+    c.Print(f"{args.output}/{outname}.png")    
 
     return
 
diff --git a/Generators/ForeseeGenerator/share/validate_grid.py b/Generators/ForeseeGenerator/share/validate_grid.py
index e3d5302dff75a44f6cc2a77497f625e6659706db..cf01468483c71f68b3687ec2a7a17a031fe84d7a 100644
--- a/Generators/ForeseeGenerator/share/validate_grid.py
+++ b/Generators/ForeseeGenerator/share/validate_grid.py
@@ -7,15 +7,15 @@ grid = "../calypso/Generators/ForeseeGenerator/share/points.json"
 with open(grid) as f:
     data = json.load(f)
 
-name = "DarkPhotonGrid2"
+name = "DarkPhotonGrid13p6"
 path = f"/bundle/data/FASER/Carl_Output/Foresee/{name}/"
-ecom = 14
+ecom = 13.6
 pid1 = 11
 pid2 = -11
 
 for coup, masses in data["samples"].items():
     for m in masses:
-        infile = f"{path}events_{ecom}TeV_m{m}GeV_c{float(coup):e}to_{pid1}_{pid2}_{name}.hepmc"
+        infile = f"{path}events_{ecom}TeV_m{m}GeV_c{float(coup):.1e}to_{pid1}_{pid2}_{name}.hepmc"
         outfile = infile.replace(".hepmc", ".EVNT.pool.root")
         valfile = infile.replace(".hepmc", ".VAL.pool.root")
         valdir = infile.replace(".hepmc", "")
diff --git a/Generators/ParticleGun/python/histsampling.py b/Generators/ParticleGun/python/histsampling.py
index c64112cc84e23963b63ea381817d1457f1c2a122..9f541da7c972ac01170b8acfafc8001b6eea5a14 100644
--- a/Generators/ParticleGun/python/histsampling.py
+++ b/Generators/ParticleGun/python/histsampling.py
@@ -27,6 +27,9 @@ def load_hist(*args):
             h = args[0].Get(args[1]).Clone()
     if h is None:
         raise Exception("Error in histogram loading from " + args)
+    else:
+        h.SetDirectory(0)
+
     return h
 
 
@@ -40,7 +43,10 @@ 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
-    if issubclass(type(h), ROOT.TH1):
+
+
+
+    if issubclass(type(h), ROOT.TH1) and not issubclass(type(h), ROOT.TH2):
         for ix in range(1, h.GetNbinsX()+1):
             iglobal = h.GetBin(ix)
             globalbins.append(iglobal)
@@ -109,10 +115,6 @@ class TH1(object):
             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"
-        return getattr(self.th1, attr)
-
 
 class TH2(object):
     "Minimal wrapper for ROOT TH2, for easy loading and to allow 2D sampling"
@@ -127,6 +129,4 @@ class TH2(object):
             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):
-        "Forward other attributes to the contained TH2"
-        return getattr(self.th2, attr)
+
diff --git a/Generators/ParticleGun/python/samplers.py b/Generators/ParticleGun/python/samplers.py
index 90e9676a5ce20dc1b52e48c79f0b36a7da49f639..d6e540ea6cf7a747d9152202cd488c135fa27099 100644
--- a/Generators/ParticleGun/python/samplers.py
+++ b/Generators/ParticleGun/python/samplers.py
@@ -1,7 +1,7 @@
 # Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
 
 import ROOT, math, random
-from ParticleGun.histsampling import TH1
+from ParticleGun.histsampling import TH1,TH2
 
 ## For convenience
 PI = math.pi
@@ -191,13 +191,26 @@ class TH1Sampler(ContinuousSampler):
 
     def __init__(self, *args):
         self.hist = TH1(*args)
-        if self.hist.GetEntries() < 1:
-            raise Exception("Histogram %s is EMPTY! Cannot sample" % self.hist.GetName())
+        if self.hist.th1.GetEntries() < 1:
+            raise Exception("Histogram %s is EMPTY! Cannot sample" % self.hist.th1.GetName())
 
     def shoot(self):
         return self.hist.GetRandom()
 
 
+class TH2Sampler(ContinuousSampler):
+    "Randomly sample from a 2D ROOT histogram."
+
+    def __init__(self, *args):
+        self.hist = TH2(*args)
+        if self.hist.th2.GetEntries() < 1:
+            raise Exception("Histogram %s is EMPTY! Cannot sample" % self.hist.th2.GetName())
+
+    def shoot(self):
+        return self.hist.GetRandom()
+    
+
+
 ########################################
 
 
@@ -572,9 +585,12 @@ class EThetaMPhiSampler(MomSampler):
 
     # TODO: ensure that E >= m!
 
-    def __init__(self, energy, theta, mass=0.0, phi=[0, TWOPI]):
+    def __init__(self, energy, theta=None, mass=0.0, phi=[0, TWOPI]):
         self.energy = energy
-        self.theta = theta
+        if theta is None:
+            self._theta = None
+        else:
+            self.theta = theta
         self.mass = mass
         self.phi = phi
 
@@ -616,10 +632,15 @@ class EThetaMPhiSampler(MomSampler):
         pz = p cos(theta)
         pt = p sin(theta)
         """
-        e = self.energy()
+
+        if self._theta is None:
+            e,theta = self.energy()
+        else:            
+            e = self.energy()
+            theta = self.theta()
+            
         m = self.mass()
         p = math.sqrt( e**2 - m**2 )
-        theta = self.theta()
         pz = p * math.cos(theta)
         pt = p * math.sin(theta)
         phi = self.phi()