From a2afbd540d5844bfbb83fb2b5e925d7072145af2 Mon Sep 17 00:00:00 2001
From: Markus Frank <Markus.Frank@cern.ch>
Date: Wed, 22 Mar 2023 16:45:17 +0100
Subject: [PATCH] Allow users to add track user information to G4 tracks, which
 then moves to Geant4Particle for saving

---
 DDG4/include/DDG4/Geant4Handle.h              |   4 +-
 DDG4/include/DDG4/Geant4Particle.h            |  40 +++++--
 DDG4/include/DDG4/Geant4ParticleInformation.h | 107 ++++++++++++++++++
 DDG4/src/Geant4ParticleHandler.cpp            |  12 +-
 examples/DDG4_MySensDet/src/MyTrackerHit.h    |  18 +++
 .../DDG4_MySensDet/src/MyTrackerSDAction.cpp  |  13 ++-
 6 files changed, 177 insertions(+), 17 deletions(-)
 create mode 100644 DDG4/include/DDG4/Geant4ParticleInformation.h

diff --git a/DDG4/include/DDG4/Geant4Handle.h b/DDG4/include/DDG4/Geant4Handle.h
index 9386354e6..1fb061774 100644
--- a/DDG4/include/DDG4/Geant4Handle.h
+++ b/DDG4/include/DDG4/Geant4Handle.h
@@ -14,11 +14,11 @@
 #ifndef DDG4_GEANT4HANDLE_H
 #define DDG4_GEANT4HANDLE_H
 
-// Framework include files
+/// Framework include files
 #include "DD4hep/ComponentProperties.h"
 #include "DD4hep/Detector.h"
 
-// C/C++ include files
+/// C/C++ include files
 #include <string>
 #include <memory>
 
diff --git a/DDG4/include/DDG4/Geant4Particle.h b/DDG4/include/DDG4/Geant4Particle.h
index 2c3cb7171..06f714471 100644
--- a/DDG4/include/DDG4/Geant4Particle.h
+++ b/DDG4/include/DDG4/Geant4Particle.h
@@ -94,8 +94,6 @@ namespace dd4hep {
       G4PARTICLE_LAST_NOTHING = 1<<31
     };
 
-
-
     /// Data structure to store the MC particle information
     /**
      *  \author  M.Frank
@@ -106,29 +104,47 @@ namespace dd4hep {
     public:
       typedef std::set<int> Particles;
       /// Reference counter
-      int ref = 0;           //! not persistent
-      int id  = 0;
-      int originalG4ID = 0;  //! not persistent
-      int g4Parent = 0, reason = 0, mask = 0;
-      int steps  = 0, secondaries = 0, pdgID = 0;
-      int status = 0, colorFlow[2] { 0, 0 };
+      int ref                      { 0 };           //! not persistent
+      int id                       { 0 };
+      int originalG4ID             { 0 };           //! not persistent
+      int g4Parent                 { 0 };
+      int reason                   { 0 };
+      int mask                     { 0 };
+      int steps                    { 0 };
+      int secondaries              { 0 };
+      int pdgID                    { 0 };
+      int status                   { 0 };
+      int colorFlow[2]             { 0, 0 };
       unsigned short genStatus     { 0 };
       char  charge                 { 0 };
       char  _spare[1]              { 0 };
       float spin[3]                { 0E0,0E0,0E0 };
       // 12 ints + 4 bytes + 3 floats should be aligned to 8 bytes....
+      /// The starting vertex
       double vsx  = 0E0, vsy  = 0E0, vsz = 0E0;
+      /// The end vertex
       double vex  = 0E0, vey  = 0E0, vez = 0E0;
+      /// The track momentum at the start vertex
       double psx  = 0E0, psy  = 0E0, psz = 0E0;
+      /// The track momentum at the end vertex
       double pex  = 0E0, pey  = 0E0, pez = 0E0;
-      double mass = 0E0, time = 0E0, properTime = 0E0;
-      /// The list of daughters of this MC particle
+      /// Particle mass
+      double mass       { 0E0 };
+      /// Particle creation time
+      double time       { 0E0 };
+      /// Proper time
+      double properTime { 0E0 };
+      /// The list of parents of this MC particle
       Particles parents;
+      /// The list of daughters of this MC particle
       Particles daughters;
 
       /// User data extension if required
-      dd4hep_ptr<ParticleExtension> extension;   //! not persisten. ROOT cannot handle
+      dd4hep_ptr<ParticleExtension> extension  { };
+      /// Reference to the G4VProcess, which created this track
       const G4VProcess *process = 0;             //! not persistent
+
+    public:
       /// Default constructor
       Geant4Particle();
       /// Constructor with ID initialization
@@ -169,6 +185,7 @@ namespace dd4hep {
     protected:
       /// Particle pointer
       Geant4Particle* particle;
+
     public:
       /// Default constructor
       Geant4ParticleHandle(Geant4Particle* part);
@@ -325,6 +342,7 @@ namespace dd4hep {
       /// Map associating the G4Track identifiers with identifiers of existing MCParticles
       TrackEquivalents equivalentTracks;
 
+    public:
       /// Default constructor
       Geant4ParticleMap() {}
       /// Default destructor
diff --git a/DDG4/include/DDG4/Geant4ParticleInformation.h b/DDG4/include/DDG4/Geant4ParticleInformation.h
new file mode 100644
index 000000000..f4404ed34
--- /dev/null
+++ b/DDG4/include/DDG4/Geant4ParticleInformation.h
@@ -0,0 +1,107 @@
+//==========================================================================
+//  AIDA Detector description implementation 
+//--------------------------------------------------------------------------
+// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
+// All rights reserved.
+//
+// For the licensing terms see $DD4hepINSTALL/LICENSE.
+// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
+//
+// Author     : M.Frank
+//
+//==========================================================================
+#ifndef DDG4_GEANT4PARTICLEINFORMATION_H
+#define DDG4_GEANT4PARTICLEINFORMATION_H
+
+/// Framework include files
+#include <DDG4/Geant4Particle.h>
+
+/// Geant4 include files
+#include <G4VUserTrackInformation.hh>
+
+/// C/C++ include files
+#include <string>
+#include <memory>
+
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit
+  namespace sim {
+
+    /// Wrapper to store user information in a G4Track.
+    /** Wrapper to store user information in a G4Track.
+     *  The data of type ParticleExtension is moved from the G4Track to the 
+     *  Geant4Particle in the Geant4ParticleHandler if present.
+     *  This automatically make the G4Track instance persistent as a 
+     *  Geant4Particle.
+     *  Hence: Be careful to not assign the entity by default!
+     *
+     *  The data in the subclass of ParticleExtension defined by the user
+     *  requires a dictionary to be stored to ROOT.
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_SIMULATION
+     */
+    class Geant4ParticleInformation : public G4VUserTrackInformation {
+      /// Keep track of the user data
+      std::unique_ptr<ParticleExtension> extension;
+
+    public:
+      /// Default Constructor
+      Geant4ParticleInformation() = default;
+      /// Initializing Constructor
+      template <typename EXTENSION_TYPE>
+      Geant4ParticleInformation(EXTENSION_TYPE* data);
+      /// Initializing Constructor
+      template <typename EXTENSION_TYPE>
+      Geant4ParticleInformation(std::unique_ptr<EXTENSION_TYPE>&& data);
+      /// Move constructor
+      Geant4ParticleInformation(Geant4ParticleInformation&& copy) = default;
+      /// Disable copy constructor
+      Geant4ParticleInformation(const Geant4ParticleInformation& copy) = delete;
+      /// Move assignemtn operator
+      Geant4ParticleInformation& operator=(Geant4ParticleInformation&& copy) = default;
+      /// Disable copy assignment
+      Geant4ParticleInformation& operator=(const Geant4ParticleInformation& copy) = delete;
+      /// Default destructor
+      virtual ~Geant4ParticleInformation() = default;
+      
+      /// Attach information
+      void set(ParticleExtension* data)   {
+	this->extension.reset(data);
+      }
+      /// Attach information
+      template <typename EXTENSION_TYPE> void set(std::unique_ptr<EXTENSION_TYPE>&& data)   {
+	this->extension = std::move(data);
+      }
+      template <typename EXTENSION_TYPE> EXTENSION_TYPE* get()   {
+	return dynamic_cast<EXTENSION_TYPE*>(this->extension.get());
+      }
+      ParticleExtension* get()   {
+	return this->extension.get();
+      }
+      ParticleExtension* release()   {
+	return this->extension.release();
+      }
+    };
+
+    /// Initializing Constructor
+    template <typename EXTENSION_TYPE> inline
+    Geant4ParticleInformation::Geant4ParticleInformation(std::unique_ptr<EXTENSION_TYPE>&& data)
+      : extension(std::move(data))
+    {
+    }
+
+    /// Initializing Constructor
+    template <typename EXTENSION_TYPE> inline
+    Geant4ParticleInformation::Geant4ParticleInformation(EXTENSION_TYPE* data)
+      : extension(data)
+    {
+    }
+
+  }    // End namespace sim
+}      // End namespace dd4hep
+#endif /* DDG4_GEANT4PARTICLEINFORMATION_H */
diff --git a/DDG4/src/Geant4ParticleHandler.cpp b/DDG4/src/Geant4ParticleHandler.cpp
index 79d4719ad..36e686e69 100644
--- a/DDG4/src/Geant4ParticleHandler.cpp
+++ b/DDG4/src/Geant4ParticleHandler.cpp
@@ -21,6 +21,7 @@
 #include <DDG4/Geant4TrackingAction.h>
 #include <DDG4/Geant4SteppingAction.h>
 #include <DDG4/Geant4ParticleHandler.h>
+#include <DDG4/Geant4ParticleInformation.h>
 #include <DDG4/Geant4UserParticleHandler.h>
 
 // Geant4 include files
@@ -351,15 +352,18 @@ void Geant4ParticleHandler::end(const G4Track* track)   {
   if ( m_userHandler )  {
     m_userHandler->end(track, m_currTrack);
   }
-
+ 
   // These are candidate tracks with a probability to be stored due to their properties:
   // - primary particle
   // - hits created
   // - secondaries
   // - above energy threshold
   // - to be kept due to creator process
+  // - to be kept due to user information of type 'Geant4ParticleInformation' stored in the G4Track
   //
-  if ( !mask.isNull() )   {
+  Geant4ParticleInformation* track_info =
+    dynamic_cast<Geant4ParticleInformation*>(track->GetUserInformation());
+  if ( !mask.isNull() || track_info )   {
     m_equivalentTracks[g4_id] = g4_id;
     ParticleMap::iterator ip = m_particleMap.find(g4_id);
     if ( mask.isSet(G4PARTICLE_PRIMARY) )   {
@@ -369,6 +373,10 @@ void Geant4ParticleHandler::end(const G4Track* track)   {
     Particle* part = 0;
     if ( ip==m_particleMap.end() ) part = m_particleMap[g4_id] = new Particle();
     else part = (*ip).second;
+    if ( track_info )  {
+      mask.set(G4PARTICLE_KEEP_USER);
+      part->extension.reset(track_info->release());
+    }
     part->get_data(m_currTrack);
   }
   else   {
diff --git a/examples/DDG4_MySensDet/src/MyTrackerHit.h b/examples/DDG4_MySensDet/src/MyTrackerHit.h
index 23d397077..18f7ad9e2 100644
--- a/examples/DDG4_MySensDet/src/MyTrackerHit.h
+++ b/examples/DDG4_MySensDet/src/MyTrackerHit.h
@@ -15,6 +15,7 @@
 
 /// Framework include files
 #include "DDG4/Geant4Data.h"
+#include "DDG4/Geant4Particle.h"
 
 namespace SomeExperiment {
 
@@ -56,6 +57,21 @@ namespace SomeExperiment {
     void copyFrom(const MyTrackerHit& c);
   };
 
+  /// User data to be attached to the output MC particle
+  /**
+   *  \author  M.Frank
+   *  \version 1.0
+   *  \ingroup DD4HEP_SIMULATION
+   */
+  class ParticleUserData : public dd4hep::sim::ParticleExtension   {
+  public:
+    /// Some data item to be attached to the Geant4 particle object
+    double absolute_momentum { 0e0 };
+
+  public:
+    using dd4hep::sim::ParticleExtension::ParticleExtension;
+  };
+  
   /// Helper to dump data file
   /**
    *  Usage:  
@@ -84,7 +100,9 @@ namespace SomeExperiment {
 #pragma link C++ namespace dd4hep::sim;
 #pragma link C++ namespace SomeExperiment;
 #pragma link C++ class     SomeExperiment::MyTrackerHit+;
+#pragma link C++ class     SomeExperiment::ParticleUserData+;
 #pragma link C++ class     SomeExperiment::Dump;
+
 #endif
 
 #endif // EXAMPLES_DDG4_MYSENSDET_SRC_MYTRACKERHIT_H
diff --git a/examples/DDG4_MySensDet/src/MyTrackerSDAction.cpp b/examples/DDG4_MySensDet/src/MyTrackerSDAction.cpp
index 7c34c2752..1d98b8edc 100644
--- a/examples/DDG4_MySensDet/src/MyTrackerSDAction.cpp
+++ b/examples/DDG4_MySensDet/src/MyTrackerSDAction.cpp
@@ -13,8 +13,9 @@
 
 // Framework include files
 #include "MyTrackerHit.h"
-#include "DDG4/Geant4SensDetAction.inl"
-#include "DDG4/Factories.h"
+#include <DDG4/Geant4SensDetAction.inl>
+#include <DDG4/Geant4ParticleInformation.h>
+#include <DDG4/Factories.h>
 
 
 namespace SomeExperiment {
@@ -86,6 +87,14 @@ namespace dd4hep {
       // Do something with my personal data (can be also something more clever ;-):
       m_userData.integratedDeposit += contrib.deposit;
       ++m_userData.mumDeposits;
+
+      /// Let's play with the Geant4TrackInformation
+      /// See issue https://github.com/AIDASoft/DD4hep/issues/1073
+      if ( nullptr == h.track->GetUserInformation() )   {
+	auto data = std::make_unique<ParticleUserData>();
+	data->absolute_momentum = h.track->GetMomentum().mag();
+	h.track->SetUserInformation(new Geant4ParticleInformation(std::move(data)));
+      }
       return true;
     }
 
-- 
GitLab