diff --git a/DDG4/plugins/Geant4TCUserParticleHandler.cpp b/DDG4/plugins/Geant4TCUserParticleHandler.cpp
index 425ae268685ce977f29396ab45afed39177e7139..60fd6a7ead9f2a00fac1be8b5736ce39b43f7853 100644
--- a/DDG4/plugins/Geant4TCUserParticleHandler.cpp
+++ b/DDG4/plugins/Geant4TCUserParticleHandler.cpp
@@ -1,5 +1,5 @@
 //==========================================================================
-//  AIDA Detector description implementation 
+//  AIDA Detector description implementation
 //--------------------------------------------------------------------------
 // Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
 // All rights reserved.
@@ -72,7 +72,7 @@ namespace dd4hep {
 #endif // DD4HEP_DDG4_GEANT4TCUSERPARTICLEHANDLER_H
 
 //====================================================================
-//  AIDA Detector description implementation 
+//  AIDA Detector description implementation
 //--------------------------------------------------------------------
 //
 //  Author     : M.Frank
@@ -82,6 +82,7 @@ namespace dd4hep {
 //#include <DDG4/Geant4TCUserParticleHandler.h>
 #include <DDG4/Geant4Particle.h>
 #include <DDG4/Factories.h>
+#include "Geant4UserParticleHandlerHelper.h"
 
 
 using namespace dd4hep::sim;
@@ -106,16 +107,6 @@ void Geant4TCUserParticleHandler::end(const G4Track* /* track */, Particle& p)
     && z_prod <= m_zTrackerMax
   )  ;
 
-  dd4hep::detail::ReferenceBitMask<int> reason(p.reason);
-
-  if( reason.isSet(G4PARTICLE_PRIMARY) ) {
-    //do nothing
-  } else if( starts_in_trk_vol && ! reason.isSet(G4PARTICLE_ABOVE_ENERGY_THRESHOLD) )  {
-    // created in tracking volume but below energy cut
-    p.reason = 0;
-    return;
-  }
-
   double r_end  = std::sqrt(p.vex*p.vex + p.vey*p.vey);
   double z_end  = p.vez;
   bool ends_in_trk_vol =  ( r_end <= m_rTracker
@@ -123,41 +114,8 @@ void Geant4TCUserParticleHandler::end(const G4Track* /* track */, Particle& p)
      && z_end <= m_zTrackerMax
   ) ;
 
-  // created and ended in calo but not primary particle
-  //
-  // we can have particles from the generator only in the calo, if we have a
-  // long particle with preassigned decay, we need to keep the reason or the
-  // MChistory will not be updated later on
-  if( not reason.isSet(G4PARTICLE_PRIMARY) ) {
-    if( !starts_in_trk_vol ) {
-      if( !ends_in_trk_vol ){
-	p.reason = 0;
-      }
-      //fg: dont keep backscatter that did not create a tracker hit
-      else if( ! reason.isSet(G4PARTICLE_CREATED_TRACKER_HIT) ) {
-	p.reason = 0;
-      }
-    }
-  }
-
-  // Set the simulator status bits
-  dd4hep::detail::ReferenceBitMask<int> simStatus(p.status);
-
-  if( ends_in_trk_vol ) {
-    simStatus.set(G4PARTICLE_SIM_DECAY_TRACKER);
-  }
-
-  // if the particle doesn't end in the tracker volume it must have ended in the calorimeter
-  if( not ends_in_trk_vol && not simStatus.isSet(G4PARTICLE_SIM_LEFT_DETECTOR) ) {
-    simStatus.set(G4PARTICLE_SIM_DECAY_CALO);
-  }
-
-  if( not starts_in_trk_vol && ends_in_trk_vol ) {
-    simStatus.set(G4PARTICLE_SIM_BACKSCATTER);
-  }
-
-  return ;
-
+  setReason(p, starts_in_trk_vol, ends_in_trk_vol);
+  setSimulatorStatus(p, starts_in_trk_vol, ends_in_trk_vol);
 }
 
 /// Post-event action callback
diff --git a/DDG4/plugins/Geant4TVUserParticleHandler.cpp b/DDG4/plugins/Geant4TVUserParticleHandler.cpp
index 35b6a21657ef525a844f4273bfe4e5ba490f306c..46c1554462003de255c2f95cb66ddb971b97547b 100644
--- a/DDG4/plugins/Geant4TVUserParticleHandler.cpp
+++ b/DDG4/plugins/Geant4TVUserParticleHandler.cpp
@@ -85,6 +85,7 @@ namespace dd4hep {
 #include <DDG4/Factories.h>
 #include <DDG4/Geant4Particle.h>
 #include <DDG4/Geant4Kernel.h>
+#include "Geant4UserParticleHandlerHelper.h"
 
 
 using namespace dd4hep::sim;
@@ -103,54 +104,11 @@ void Geant4TVUserParticleHandler::end(const G4Track* /* track */, Particle& p)
   std::array<double, 3> start_point = {p.vsx, p.vsy, p.vsz};
   bool starts_in_trk_vol = m_trackingVolume.ptr()->Contains(start_point.data());
 
-  dd4hep::detail::ReferenceBitMask<int> reason(p.reason);
-
-  if( reason.isSet(G4PARTICLE_PRIMARY) ) {
-    //do nothing
-  } else if( starts_in_trk_vol && ! reason.isSet(G4PARTICLE_ABOVE_ENERGY_THRESHOLD) )  {
-    // created in tracking volume but below energy cut
-    p.reason = 0;
-    return;
-  }
-
   std::array<double, 3> end_point = {p.vex, p.vey, p.vez};
   bool ends_in_trk_vol = m_trackingVolume.ptr()->Contains(end_point.data());
 
-  // created and ended in calo but not primary particle
-  //
-  // we can have particles from the generator only in the calo, if we have a
-  // long particle with preassigned decay, we need to keep the reason or the
-  // MChistory will not be updated later on
-  if( not reason.isSet(G4PARTICLE_PRIMARY) ) {
-    if( !starts_in_trk_vol ) {
-      if( !ends_in_trk_vol ){
-	p.reason = 0;
-      }
-      //fg: dont keep backscatter that did not create a tracker hit
-      else if( ! reason.isSet(G4PARTICLE_CREATED_TRACKER_HIT) ) {
-	p.reason = 0;
-      }
-    }
-  }
-
-  // Set the simulator status bits
-  dd4hep::detail::ReferenceBitMask<int> simStatus(p.status);
-
-  if( ends_in_trk_vol ) {
-    simStatus.set(G4PARTICLE_SIM_DECAY_TRACKER);
-  }
-
-  // if the particle doesn't end in the tracker volume it must have ended in the calorimeter
-  if( not ends_in_trk_vol && not simStatus.isSet(G4PARTICLE_SIM_LEFT_DETECTOR) ) {
-    simStatus.set(G4PARTICLE_SIM_DECAY_CALO);
-  }
-
-  if( not starts_in_trk_vol && ends_in_trk_vol ) {
-    simStatus.set(G4PARTICLE_SIM_BACKSCATTER);
-  }
-
-  return ;
-
+  setReason(p, starts_in_trk_vol, ends_in_trk_vol);
+  setSimulatorStatus(p, starts_in_trk_vol, ends_in_trk_vol);
 }
 
 /// Post-event action callback
diff --git a/DDG4/plugins/Geant4UserParticleHandlerHelper.cpp b/DDG4/plugins/Geant4UserParticleHandlerHelper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..de6f7920be2602f87b95d5a584c19340e4b1abac
--- /dev/null
+++ b/DDG4/plugins/Geant4UserParticleHandlerHelper.cpp
@@ -0,0 +1,71 @@
+
+//==========================================================================
+//  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
+//
+//==========================================================================
+
+#include "Geant4UserParticleHandlerHelper.h"
+#include <DDG4/Geant4Particle.h>
+#include <DDG4/Geant4UserParticleHandler.h>
+
+// using namespace dd4hep::sim;
+
+namespace dd4hep::sim {
+
+void setReason(Geant4Particle& p, bool starts_in_trk_vol, bool ends_in_trk_vol) {
+
+  dd4hep::detail::ReferenceBitMask<int> reason(p.reason);
+
+  if( reason.isSet(G4PARTICLE_PRIMARY) ) {
+    //do nothing
+  } else if( starts_in_trk_vol && ! reason.isSet(G4PARTICLE_ABOVE_ENERGY_THRESHOLD) )  {
+    // created in tracking volume but below energy cut
+    p.reason = 0;
+    return;
+  }
+
+  // created and ended in calo but not primary particle
+  //
+  // we can have particles from the generator only in the calo, if we have a
+  // long particle with preassigned decay, we need to keep the reason or the
+  // MChistory will not be updated later on
+  if( not reason.isSet(G4PARTICLE_PRIMARY) ) {
+    if( !starts_in_trk_vol ) {
+      if( !ends_in_trk_vol ){
+	p.reason = 0;
+      }
+      //fg: dont keep backscatter that did not create a tracker hit
+      else if( ! reason.isSet(G4PARTICLE_CREATED_TRACKER_HIT) ) {
+	p.reason = 0;
+      }
+    }
+  }
+}
+
+void setSimulatorStatus(Geant4Particle& p, bool starts_in_trk_vol, bool ends_in_trk_vol) {
+  // Set the simulator status bits
+  dd4hep::detail::ReferenceBitMask<int> simStatus(p.status);
+
+  if( ends_in_trk_vol ) {
+    simStatus.set(G4PARTICLE_SIM_DECAY_TRACKER);
+  }
+
+  // if the particle doesn't end in the tracker volume it must have ended in the calorimeter
+  if( not ends_in_trk_vol && not simStatus.isSet(G4PARTICLE_SIM_LEFT_DETECTOR) ) {
+    simStatus.set(G4PARTICLE_SIM_DECAY_CALO);
+  }
+
+  if( not starts_in_trk_vol && ends_in_trk_vol ) {
+    simStatus.set(G4PARTICLE_SIM_BACKSCATTER);
+  }
+}
+
+}
\ No newline at end of file
diff --git a/DDG4/plugins/Geant4UserParticleHandlerHelper.h b/DDG4/plugins/Geant4UserParticleHandlerHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..4497dc0de7b5de25960bf67bda0bafce0838bc88
--- /dev/null
+++ b/DDG4/plugins/Geant4UserParticleHandlerHelper.h
@@ -0,0 +1,36 @@
+
+//==========================================================================
+//  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_GEANT4USERPARTICLEHANDLERHELPER_H
+#define DDG4_GEANT4USERPARTICLEHANDLERHELPER_H
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Namespace for the Geant4 based simulation part of the AIDA detector description toolkit
+  namespace sim {
+
+    // Forward declarations
+    class Geant4Particle;
+
+    /// determines if particle should be kept and sets p.reason = 0 otherwise
+    void setReason(Geant4Particle& p, bool starts_in_trk_volume, bool ends_in_trk_volume);
+
+    /// determines if particle has ended in the tracker, calorimeter or if it is backscatter and sets simulator status accordingly
+    void setSimulatorStatus(Geant4Particle& p, bool starts_in_trk_volume, bool ends_in_trk_volume);
+
+  }
+}
+
+#endif // DDG4_GEANT4USERPARTICLEHANDLERHELPER_H
\ No newline at end of file