From a81e5bd6ed6173e9b6e92a603840ff155a1dbf29 Mon Sep 17 00:00:00 2001
From: FaserMC <fasermc@cern.ch>
Date: Thu, 9 Mar 2023 18:21:47 +0100
Subject: [PATCH] Consolodate s0010 script updates

---
 .../Generation/python/faser_parser.py         |  4 ++
 .../Generation/scripts/faser_particlegun.py   | 45 ++++++++++++---
 .../scripts/submit_faser_particlegun.sh       | 39 +++++++++++--
 Generators/ParticleGun/python/histsampling.py | 55 ++++++++++++++++---
 Generators/ParticleGun/python/samplers.py     | 23 +++++---
 5 files changed, 137 insertions(+), 29 deletions(-)

diff --git a/Control/CalypsoExample/Generation/python/faser_parser.py b/Control/CalypsoExample/Generation/python/faser_parser.py
index 055dd7200..03817efb8 100644
--- a/Control/CalypsoExample/Generation/python/faser_parser.py
+++ b/Control/CalypsoExample/Generation/python/faser_parser.py
@@ -44,6 +44,10 @@ def faser_pgparser():
                         help="Specify radius (in mm)")
     parser.add_argument("--angle", default=0.005, type=float_or_none,
                         help="Specify angular width (in Rad)")
+    parser.add_argument("--xpos", default=None, type=float,
+                        help="Specify x position of particles (in mm)")
+    parser.add_argument("--ypos", default=None, type=float,
+                        help="Specify y position of particles (in mm)")
     parser.add_argument("--zpos", default=None, type=float,
                         help="Specify z position of particles (in mm) (helpful to avoid FASERnu)")
 
diff --git a/Control/CalypsoExample/Generation/scripts/faser_particlegun.py b/Control/CalypsoExample/Generation/scripts/faser_particlegun.py
index 169badecf..7ec05e2aa 100755
--- a/Control/CalypsoExample/Generation/scripts/faser_particlegun.py
+++ b/Control/CalypsoExample/Generation/scripts/faser_particlegun.py
@@ -148,9 +148,15 @@ if __name__ == '__main__':
 
     # -1000 is safely upstream of detector (to be checked)
     # Note zpos is in mm!
-    if args.zpos:
+    if args.zpos != None:
         sg_dict["z"] = args.zpos
 
+    if args.xpos != None:
+        sg_dict["x"] = args.xpos
+
+    if args.ypos != None:
+        sg_dict["y"] = args.ypos
+
     # Determine energy sampling
     if args.sampler == "lin":
         sg_dict["energy"] = PG.UniformSampler(args.minE*GeV, args.maxE*GeV)
@@ -159,11 +165,28 @@ if __name__ == '__main__':
     elif args.sampler == "const":
         sg_dict["energy"] = PG.ConstSampler(args.maxE*GeV)
     elif args.sampler == "hist":
-        fname, hname = args.hist_name.split(":")
-        sg_dict["energy"] = PG.TH1Sampler(fname, hname)
+        nargs = len(args.hist_name.split(":"))
+        if nargs == 2:
+            fname, hname = args.hist_name.split(":")
+            sg_dict["energy"] = PG.TH1Sampler(fname, hname)
+        elif nargs == 3:
+            fname, hname, scale = args.hist_name.split(":")
+            sg_dict["energy"] = PG.TH1Sampler(fname, hname, scale)
+        else:
+            print(f"Can't parse histogram {args.hist_name}!")
+            sys.exit(1)
+
     elif args.sampler == "hist2D":
-        fname, hname = args.hist_name.split(":")
-        sg_dict["energy"] = PG.TH2Sampler(fname, hname)        
+        nargs = len(args.hist_name.split(":"))
+        if nargs == 2:
+            fname, hname = args.hist_name.split(":")
+            sg_dict["energy"] = PG.TH2Sampler(fname, hname)
+        elif nargs == 4:
+            fname, hname, scalex, scaley = args.hist_name.split(":")
+            sg_dict["energy"] = PG.TH2Sampler(fname, hname, scalex, scaley)
+        else:
+            print(f"Can't parse histogram {args.hist_name}!")
+            sys.exit(1)
     else:
         print(f"Sampler {args.sampler} not known!")
         sys.exit(1)
@@ -255,8 +278,12 @@ if __name__ == '__main__':
 
     b = time.time()
     log.info("Run G4FaserAlg in " + str(b-a) + " seconds")
-#
-# Success should be 0
-#
-    sys.exit(not sc.isSuccess())
+
+# Signal errors
+if sc.isSuccess():
+    log.info("Execution succeeded")
+    sys.exit(0)
+else:
+    log.info("Execution failed, return 1")
+    sys.exit(1)
 
diff --git a/Control/CalypsoExample/Generation/scripts/submit_faser_particlegun.sh b/Control/CalypsoExample/Generation/scripts/submit_faser_particlegun.sh
index 9b8676752..441ef2923 100755
--- a/Control/CalypsoExample/Generation/scripts/submit_faser_particlegun.sh
+++ b/Control/CalypsoExample/Generation/scripts/submit_faser_particlegun.sh
@@ -185,9 +185,12 @@ cd "${config_file_stem}-${seg_str}"
 # Run job
 if [[ -z "$tag" ]]; then
     faser_particlegun.py  "--conf=$config_path" "--segment=$seg_str"
+    gen_code=$?
 else
     faser_particlegun.py  "--conf=$config_path" "--segment=$seg_str" "--tag=$tag"
+    gen_code=$?
 fi
+echo "Return code: $gen_code"
 #
 # Print out ending time
 date
@@ -198,25 +201,51 @@ export EOS_MGM_URL=root://eospublic.cern.ch
 #
 if ! [ -z "$outdest" ]
 then
+    echo "Output directory:"
     ls -l
-    echo "copy *-HITS.root to $outdest"
-    mkdir -p $outdest
-    eos cp *-HITS.root ${outdest}/ || true
+    thefile=`ls *-HITS.root`
+    if [ $? -eq 0 ]; then
+	echo "copy $thefile to $outdest"
+	eos mkdir -p $outdest
+	eos cp $thefile ${outdest}/${thefile} || true
+
+	# Check that it worked
+	eos ls ${outdest}/${thefile} > /dev/null
+	if [ $? -eq 0 ]; then
+	    echo "file $thefile copied to $outdest"
+	    copy_code=0
+	else
+	    echo "didnt find $thefile in $outdest !"
+	    copy_code=1
+	fi
+    else
+	echo "ls *-xAOD.root returned nothing!"
+	copy_code=1
+    fi
+
 fi
 #
 # Also copy log file
 if ! [ -z "$logdest" ]
 then
     cd ..
+    echo "Working directory:"
     ls -l
     echo "copy $logfile to $logdest"
-    mkdir -p $logdest
+    eos mkdir -p $logdest
     eos cp $logfile $logdest/$logfile
 elif ! [ -z "$outdest" ]
 then 
     cd ..
     ls -l
     echo "copy $logfile to $outdest"
-    mkdir -p $outdest
+    eos mkdir -p $outdest
     eos cp $logfile $outdest/$logfile
 fi
+
+# Make sure to return an error is calypso failed
+if [ $gen_code -ne 0 ] || [ $copy_code -ne 0 ]; then
+  exit 1
+else
+  exit 0
+fi
diff --git a/Generators/ParticleGun/python/histsampling.py b/Generators/ParticleGun/python/histsampling.py
index 9f541da7c..10549ef0b 100644
--- a/Generators/ParticleGun/python/histsampling.py
+++ b/Generators/ParticleGun/python/histsampling.py
@@ -14,6 +14,7 @@ def load_hist(*args):
     """
     Load a histogram from a filename/TFile and histo name. If a single arg is
     provided, it has to be a histo object and will be cloned before return.
+
     """
     h = None
     if len(args) == 1 and issubclass(type(args[0]), ROOT.TH1):
@@ -25,6 +26,7 @@ def load_hist(*args):
             #f.Close()
         elif type(args[0]) is ROOT.TFile and type(args[1]) is str:
             h = args[0].Get(args[1]).Clone()
+
     if h is None:
         raise Exception("Error in histogram loading from " + args)
     else:
@@ -77,7 +79,7 @@ def get_random_bin(globalbins, cheights):
     raise Exception("Sample fell outside range of cumulative distribution?!?!")
 
 
-def get_random_x(h, globalbins, cheights, globalbin_to_axisbin):
+def get_random_x(h, globalbins, cheights, globalbin_to_axisbin, scale=1.):
     """
     Choose a random bin via get_random_bin, then pick a uniform random x
     point in that bin (without any attempt at estimating the in-bin distribution).
@@ -86,10 +88,10 @@ def get_random_x(h, globalbins, cheights, globalbin_to_axisbin):
     axisids = globalbin_to_axisbin.get(irand)
     assert axisids is not None
     xrand = random.uniform(h.GetXaxis().GetBinLowEdge(axisids[0]), h.GetXaxis().GetBinUpEdge(axisids[0]))
-    return xrand
+    return scale * xrand
 
 
-def get_random_xy(h2, globalbins, cheights, globalbin_to_axisbin):
+def get_random_xy(h2, globalbins, cheights, globalbin_to_axisbin, xscale=1., yscale=1.):
     """
     Choose a random bin via get_random_bin, then pick a uniform random x,y
     point in that bin (without any attempt at estimating the in-bin distribution).
@@ -99,34 +101,71 @@ def get_random_xy(h2, globalbins, cheights, globalbin_to_axisbin):
     assert axisids is not None
     xrand = random.uniform(h2.GetXaxis().GetBinLowEdge(axisids[0]), h2.GetXaxis().GetBinUpEdge(axisids[0]))
     yrand = random.uniform(h2.GetYaxis().GetBinLowEdge(axisids[1]), h2.GetYaxis().GetBinUpEdge(axisids[1]))
-    return xrand, yrand
+    return (xscale*xrand), (yscale*yrand)
 
 
 class TH1(object):
     "Minimal wrapper for ROOT TH1, for sampling consistency and easy loading"
 
     def __init__(self, *args):
-        self.th1 = load_hist(*args)
+        """ Args can be variable length, but these are now kwargs, so order matters
+
+        fname - filename
+        hname - histogram name
+        xscale - scaling factor for x axis variable
+        """
+        if len(args) > 2:
+            self.th1 = load_hist(*(args[0:2]))
+        else:
+            self.th1 = load_hist(*args)
+
         self.globalbins, self.globalbin_to_axisbin, self.cheights = None, None, None
 
+        if len(args) >= 3:
+            self.xscale = float(args[2])
+        else:
+            self.xscale = 1.
+
     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)
+        return get_random_x(self.th1, self.globalbins, self.cheights, self.globalbin_to_axisbin, self.xscale)
 
 
 class TH2(object):
     "Minimal wrapper for ROOT TH2, for easy loading and to allow 2D sampling"
 
     def __init__(self, *args):
-        self.th2 = load_hist(*args)
+        """ Args can be variable length, but these are now kwargs, so order matters
+
+        fname - filename
+        hname - histogram name
+        xscale - scaling factor for x axis variable
+        yscale - scaling factor for y axis variable
+        """
+        if len(args) > 2:
+            self.th2 = load_hist(*(args[0:2]))
+        else:
+            self.th2 = load_hist(*args)
+
         self.globalbins, self.globalbin_to_axisbin, self.cheights = None, None, None
 
+        if len(args) >= 3:
+            self.xscale = float(args[2])
+        else:
+            self.xscale = 1.
+
+        if len(args) >= 4:
+            self.yscale = float(args[3])
+        else:
+            self.yscale = 1.
+
+
     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)
+        return get_random_xy(self.th2, self.globalbins, self.cheights, self.globalbin_to_axisbin, self.xscale, self.yscale)
 
 
diff --git a/Generators/ParticleGun/python/samplers.py b/Generators/ParticleGun/python/samplers.py
index d6e540ea6..9126b607c 100644
--- a/Generators/ParticleGun/python/samplers.py
+++ b/Generators/ParticleGun/python/samplers.py
@@ -633,14 +633,23 @@ class EThetaMPhiSampler(MomSampler):
         pt = p sin(theta)
         """
 
-        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 )
+
+        count = 0
+        e = -1
+        while (e < m and count < 5):
+            count += 1
+            if self._theta is None:
+                e,theta = self.energy()
+            else:            
+                e = self.energy()
+                theta = self.theta()
+
+        try:            
+            p = math.sqrt( e**2 - m**2 )
+        except Exception:
+            raise Exception(f"Error generating E: {e} m: {m}!")
+
         pz = p * math.cos(theta)
         pt = p * math.sin(theta)
         phi = self.phi()
-- 
GitLab