From 7f37d1f9a2f0390be56ee465f58ae3c6120934d2 Mon Sep 17 00:00:00 2001
From: ralfulrich <ralf.ulrich@kit.edu>
Date: Tue, 4 Dec 2018 22:20:59 +0100
Subject: [PATCH] added particle lifetimes

---
 Framework/Particles/ParticleProperties.h |  8 ++++
 Framework/Particles/pdxml_reader.py      | 48 +++++++++++++++++-------
 Framework/Particles/testParticles.cc     |  8 ++++
 3 files changed, 51 insertions(+), 13 deletions(-)

diff --git a/Framework/Particles/ParticleProperties.h b/Framework/Particles/ParticleProperties.h
index 330048b3..4bb3cda1 100644
--- a/Framework/Particles/ParticleProperties.h
+++ b/Framework/Particles/ParticleProperties.h
@@ -26,6 +26,7 @@
 #include <corsika/units/PhysicalConstants.h>
 #include <corsika/units/PhysicalUnits.h>
 
+
 /**
  * @namespace particle
  *
@@ -36,6 +37,8 @@
 
 namespace corsika::particles {
 
+  using corsika::units::si::second;
+
   enum class Code : int16_t;
 
   using PDGCodeType = int16_t;
@@ -47,6 +50,7 @@ namespace corsika::particles {
   corsika::units::si::MassType constexpr GetMass(Code const);
   PDGCodeType constexpr GetPDG(Code const);
   constexpr std::string const& GetName(Code const);
+  corsika::units::si::TimeType constexpr GetLifetime(Code const);
 
 #include <corsika/particles/GeneratedParticleProperties.inc>
 
@@ -76,6 +80,10 @@ namespace corsika::particles {
     return names[static_cast<CodeIntType const>(p)];
   }
 
+  corsika::units::si::TimeType constexpr GetLifetime(Code const p) {
+    return lifetime[static_cast<CodeIntType const>(p)];
+  }
+
   namespace io {
 
     std::ostream& operator<<(std::ostream& stream, Code const p);
diff --git a/Framework/Particles/pdxml_reader.py b/Framework/Particles/pdxml_reader.py
index 96d50588..1483a6de 100755
--- a/Framework/Particles/pdxml_reader.py
+++ b/Framework/Particles/pdxml_reader.py
@@ -14,27 +14,37 @@ import pickle
 def parse(filename):
     tree = ET.parse(filename)
     root = tree.getroot()
-        
+
+    GeVfm = 0.19732696312541853
+    c_speed_of_light = 29.9792458e10  # mm / s
+    
     for particle in root.iter("particle"):
         name = particle.attrib["name"]
         antiName = "Unknown"
-        # print (str(particle.attrib))
         if ("antiName" in particle.attrib):
-            antiName = particle.attrib["antiName"]            
-#            print ("found anti: " + name + " " + antiName + "\n" )
+            antiName = particle.attrib["antiName"]
         pdg_id = int(particle.attrib["id"])
-        mass = float(particle.attrib["m0"]) # GeV
-        electric_charge = int(particle.attrib["chargeType"]) # in units of e/3
-        
-        decay_width = float(particle.attrib.get("mWidth", 0)) # GeV
-        lifetime = float(particle.attrib.get("tau0", math.inf)) # mm / c
+        mass = float(particle.attrib["m0"])  # GeV
+        electric_charge = int(particle.attrib["chargeType"])  # in units of e/3        
+        ctau = 0.
+        if pdg_id in (11, 12, 14, 16, 22, 2212):  # these are the stable particles !
+            ctau = float('Inf')
+        elif 'tau0' in particle.attrib:
+            ctau = float(particle.attrib['tau0'])  # mm / c
+        elif 'mWidth' in particle.attrib:
+            ctau = GeVfm / float(particle.attrib['mWidth']) * 1e-15 * 1000.0  # mm / s
+        elif pdg_id in (0, 423, 433, 4312, 4322, 5112, 5222):  # those are certainly not stable....
+            ctau = 0.
+        else:
+            print ("missing lifetime: " + str(pdg_id) + " " + str(name))
+            sys.exit(0)
         
-        yield (pdg_id, name, mass, electric_charge, antiName)
+        yield (pdg_id, name, mass, electric_charge, antiName, ctau/c_speed_of_light)
                 
         # TODO: read decay channels from child elements
         
         if "antiName" in particle.attrib:
-            yield (-pdg_id, antiName, mass, -electric_charge, name)
+            yield (-pdg_id, antiName, mass, -electric_charge, name, ctau/c_speed_of_light)
 
 
             
@@ -161,7 +171,7 @@ def build_pythia_db(filename, classnames):
     
     counter = itertools.count(0)
     
-    for (pdg, name, mass, electric_charge, antiName) in parse(filename):
+    for (pdg, name, mass, electric_charge, antiName, lifetime) in parse(filename):
 
         c_id = "Unknown"
         if pdg in classnames:
@@ -175,6 +185,7 @@ def build_pythia_db(filename, classnames):
             "pdg" : pdg,
             "mass" : mass, # in GeV
             "electric_charge" : electric_charge, # in e/3
+            "lifetime" : lifetime,
             "ngc_code" : next(counter)
         }
     
@@ -249,6 +260,15 @@ def gen_properties(pythia_db):
 #        string += "  {anti:d},\n".format(charge = p['anti_particle'])
 #    string += "};\n"
 
+    # lifetime
+    string += "static constexpr std::array<corsika::units::si::TimeType const, size> lifetime = {\n"
+    for p in pythia_db.values():
+        if p['lifetime'] == float("Inf") :
+            string += "  std::numeric_limits<double>::infinity() * corsika::units::si::second, \n"
+        else :
+            string += "  {tau:f} * corsika::units::si::second, \n".format(tau = p['lifetime'])
+    string += "};\n"
+    
     return string
 
 
@@ -335,10 +355,12 @@ if __name__ == "__main__":
         print("usage: {:s} <Pythia8.xml> <ClassNames.xml>".format(sys.argv[0]), file=sys.stderr)
         sys.exit(1)
         
+    print("\n       pdxml_reader.py: Automatically produce particle-properties from PYTHIA8 xml file\n")
+
     names = class_names(sys.argv[2])
     pythia_db = build_pythia_db(sys.argv[1], names)
 
-    print("\n       pdxml_reader.py: Automatically produce particle-properties from PYTHIA8 xml file\n")
+    print (str(pythia_db))
     
     with open("GeneratedParticleProperties.inc", "w") as f:
         print(inc_start(), file=f) 
diff --git a/Framework/Particles/testParticles.cc b/Framework/Particles/testParticles.cc
index 443d167c..283bb48c 100644
--- a/Framework/Particles/testParticles.cc
+++ b/Framework/Particles/testParticles.cc
@@ -53,4 +53,12 @@ TEST_CASE("ParticleProperties", "[Particles]") {
     REQUIRE(GetPDG(Code::NuE) == 12);
     REQUIRE(GetPDG(Code::MuMinus) == 13);
   }
+
+  SECTION("Lifetimes") {
+    REQUIRE(GetLifetime(Code::Electron) == std::numeric_limits<double>::infinity() * corsika::units::si::second);
+    REQUIRE(GetLifetime(Code::DPlus) <  GetLifetime(Code::Gamma));
+    //REQUIRE(GetLifetime(Code::RhoPlus)/corsika::units::si::second == (Approx(4.414566727909413e-24).epsilon(1e-3)));
+    //REQUIRE(GetLifetime(Code::SigmaMinusBar)/corsika::units::si::second == (Approx(8.018880848563575e-11).epsilon(1e-5)));
+    //REQUIRE(GetLifetime(Code::MuPlus)/corsika::units::si::second == (Approx(2.1970332555864364e-06).epsilon(1e-5)));
+  }
 }
-- 
GitLab