diff --git a/DDCore/include/DD4hep/Callback.h b/DDCore/include/DD4hep/Callback.h
index 2eed558655e6ed26434057b530fe70107225d6e2..3c48d9391dc586eff3025483d45cf799a7563784 100644
--- a/DDCore/include/DD4hep/Callback.h
+++ b/DDCore/include/DD4hep/Callback.h
@@ -365,6 +365,10 @@ namespace dd4hep {
       else
         callbacks.insert(callbacks.end(),cb);
     }
+    /// Generically Add a new callback to the sequence depending on the location arguments
+    void add(const Callback& cb) {
+      callbacks.insert(callbacks.end(),cb);
+    }
     /// Execution overload for callbacks with no arguments
     void operator()() const;
     /// Execution overload for callbacks with 1 argument
diff --git a/DDCore/include/DD4hep/PluginCreators.h b/DDCore/include/DD4hep/PluginCreators.h
index f18783ae40ee9363a55e62a056def67049142d93..b045158aa5683597610b64432786f6faba554253 100644
--- a/DDCore/include/DD4hep/PluginCreators.h
+++ b/DDCore/include/DD4hep/PluginCreators.h
@@ -16,7 +16,7 @@
 #define DD4HEP_PLUGINCREATORS_H 1
 
 // Framework include files
-#include "DD4hep/Primitives.h"
+#include <DD4hep/Primitives.h>
 
 // C/C++ include files
 #include <string>
diff --git a/DDCore/include/DD4hep/Plugins.h b/DDCore/include/DD4hep/Plugins.h
index e29df7410aaae288e446817939a21c8488fac8aa..143628f9bb07727a8be2bc12012663a5cc009b0f 100644
--- a/DDCore/include/DD4hep/Plugins.h
+++ b/DDCore/include/DD4hep/Plugins.h
@@ -14,7 +14,7 @@
 #define DD4HEP_PLUGINS_H
 
 // Framework include files
-#include "DD4hep/config.h"
+#include <DD4hep/config.h>
 
 // ROOT include files
 #ifndef __CINT__
@@ -36,7 +36,7 @@ namespace std {
 #endif
 
 #ifndef DD4HEP_PARSERS_NO_ROOT
-#include "RVersion.h"
+#include <RVersion.h>
 #endif
 
 /// Namespace for the AIDA detector description toolkit
diff --git a/DDCore/src/PluginCreators.cpp b/DDCore/src/PluginCreators.cpp
index 262a986f7b95121cbe2fe7dbdc3bb3728be94aea..1c9f48a1175ddc820a46b8729bdecc2d7f7c3ff5 100644
--- a/DDCore/src/PluginCreators.cpp
+++ b/DDCore/src/PluginCreators.cpp
@@ -12,10 +12,10 @@
 //==========================================================================
 
 // Framework include files
-#include "DD4hep/PluginCreators.h"
-#include "DD4hep/Primitives.h"
-#include "DD4hep/Printout.h"
-#include "DD4hep/Plugins.h"
+#include <DD4hep/PluginCreators.h>
+#include <DD4hep/Primitives.h>
+#include <DD4hep/Printout.h>
+#include <DD4hep/Plugins.h>
 
 // C/C++ include files
 #include <cstring>
@@ -73,7 +73,7 @@ namespace dd4hep {
       PluginDebug dbg;
       object = PluginService::Create<void*>(factory, &description, argc, argv);
       if ( !object )  {
-        except("ConditionsManager","dd4hep-plugins: Failed to locate plugin %s. \n%s.",
+        except("createPlugin","dd4hep-plugins: Failed to locate plugin %s. \n%s.",
                factory.c_str(), dbg.missingFactory(factory).c_str());
       }
     }
diff --git a/DDDigi/ddg4/DigiDDG4Input.cpp b/DDDigi/ddg4/DigiDDG4Input.cpp
index a50fbdaa0564e9c1a2826eea760c5e60dda16f76..5366b7ba25f758ae96ca69697e1779d4487a8a64 100644
--- a/DDDigi/ddg4/DigiDDG4Input.cpp
+++ b/DDDigi/ddg4/DigiDDG4Input.cpp
@@ -27,8 +27,9 @@
 #include <any>
 
 using namespace dd4hep;
+using namespace dd4hep::digi;
 
-typedef std::function<std::any(int, const char*, void*)> func_t;
+typedef std::function<void(DataSegment& segment, int, const char*, void*)> func_t;
 
 namespace  {
 
@@ -40,30 +41,39 @@ namespace  {
 
   template <typename T> void* ddg4_hit_convert_function(const char* desc)   {
     std::string tag = desc;
-    func_t* cnv = new func_t([tag] (int mask, const char* name, void* ptr)  {
+    func_t* cnv = new func_t([tag] (DataSegment& segment, int mask, const char* name, void* ptr)  {
 	using wrap_t = std::shared_ptr<sim::Geant4HitData>;
 	std::size_t len = 0;
-	digi::DepositMapping out(name, mask);
+	std::string nam = name;
+	std::vector<wrap_t> particles;
+	DepositVector out(name, mask);
 	if ( ptr )   {
+	  Key history_key;
 	  input_data<T> data(ptr);
-	  digi::Key::mask_type msk = digi::Key::mask_type(mask);
-	  for( auto* p : *data.items )   {
-	    auto it = out.find(p->cellID);
-	    if ( it == out.end() )   {
-	      digi::EnergyDeposit dep(p->energyDeposit);
-	      dep.history.emplace_back(std::any(wrap_t(p)), msk);
-	      out.emplace(p->cellID, std::move(dep));
-	      continue;
-	    }
-	    (*it).second.history.emplace_back(std::any(wrap_t(p)), msk);
-	    (*it).second.deposit += p->energyDeposit;
+	  history_key.set_mask(Key::mask_type(mask));
+	  for(size_t i=0; i < data.items->size(); ++i)   {
+	    auto* p = (*data.items)[i];
+	    EnergyDeposit dep { };
+	    dep.flag = p->flag;
+	    dep.deposit = p->energyDeposit;
+	    dep.position = p->position;
+
+	    history_key.set_item(i);
+	    dep.hit_history.emplace_back(history_key, 1.0);
+	    history_key.set_item(p->g4ID);
+	    dep.particle_history.emplace_back(history_key, 1.0);
+	    out.emplace(p->cellID, std::move(dep));
+	    particles.emplace_back(wrap_t(p));
 	  }
 	  len = data.items->size();
 	  data.items->clear();
 	}
 	printout(DEBUG,"DDG4Converter","++ Converted %ld %s to %ld cell deposits",
 		 len, tag.c_str(), out.size());
-	return std::make_any<digi::DepositMapping>(std::move(out));
+	Key depo_key(nam, mask);
+	segment.emplace(depo_key, std::make_any<DepositVector>(std::move(out)));
+	Key src_key(nam+".ddg4", mask);
+	segment.emplace(src_key, std::make_any<std::vector<wrap_t>>(std::move(particles)));
       });
     return cnv;
   }
@@ -80,23 +90,31 @@ static void* convert_sim_geant4tracker_hits()     {
 DECLARE_CREATE(DD4hep_DDDigiConverter_vector_dd4hep_sim_Geant4Tracker_Hit_,convert_sim_geant4tracker_hits);
 
 static void* convert_sim_geant4particles()     {
-  func_t* cnv = new func_t([] (int mask, const char* name, void* ptr)  {
-      std::any res = std::make_any<digi::ParticleMapping>(name, mask);
-      digi::ParticleMapping* out = std::any_cast<digi::ParticleMapping>(&res);
+  func_t* cnv = new func_t([] (DataSegment& segment, int mask, const char* name, void* ptr)  {
+      std::any res = std::make_any<ParticleMapping>(name, mask);
+      ParticleMapping* out = std::any_cast<ParticleMapping>(&res);
       if ( ptr )   {
 	using wrap_t = std::shared_ptr<sim::Geant4Particle>;
 	auto* items = (std::vector<sim::Geant4Particle*>*)ptr;
 	for( auto* p : *items )   {
-	  digi::Key key(0);
-	  key.values.mask = mask;
-	  key.values.item = out->size();
+	  Key key(0);
+	  Particle part;
+	  key.set_mask(mask);
+	  key.set_item(out->size());
 	  p->mask = mask;
-	  out->push(key.key, {std::make_any<wrap_t>(p)});
+	  part.start_position = Position(p->vsx, p->vsy, p->vsz);
+	  part.end_position   = Position(p->vex, p->vey, p->vez);
+	  part.momentum       = Direction(p->psx, p->psy, p->psz);
+	  part.charge         = p->charge;
+	  part.mass           = p->mass;
+	  part.history        = std::make_any<wrap_t>(p);
+	  out->push(key.key, std::move(part));
 	}
 	items->clear();
       }
       printout(DEBUG,"DDG4Converter","++ Converted %ld DDG4 particles", out->size());
-      return res;
+      Key part_key(name, mask);
+      segment.emplace(part_key, std::move(res));
     });
   return cnv;
 }
diff --git a/DDDigi/include/DDDigi/DigiAction.h b/DDDigi/include/DDDigi/DigiAction.h
index 7c6daae6439260fe15380a90c012dabf5c73f833..dcb1ed3bf22317ed2d38483cc5e5b92195599fa6 100644
--- a/DDDigi/include/DDDigi/DigiAction.h
+++ b/DDDigi/include/DDDigi/DigiAction.h
@@ -15,7 +15,6 @@
 
 // Framework include files
 #include <DD4hep/Printout.h>
-#include <DD4hep/Callback.h>
 #include <DD4hep/ComponentProperties.h>
 #include <DDDigi/DigiContext.h>
 
@@ -48,15 +47,6 @@ namespace dd4hep {
   /// Namespace for the Digitization part of the AIDA detector description toolkit
   namespace digi {
 
-    /// Cast operator
-    template <typename TO, typename FROM> TO fast_cast(FROM from) {
-#ifdef USE_FASTCAST
-      return static_cast<TO>(from);
-#else
-      return dynamic_cast<TO>(from);
-#endif
-    }
-
     /// Helper class to handle strings of the format "type/name"
     /**
      *  \author  M.Frank
@@ -90,7 +80,7 @@ namespace dd4hep {
      *
      *  \author  M.Frank
      *  \version 1.0
-     *  \ingroup DD4HEP_SIMULATION
+     *  \ingroup DD4HEP_DIGITIZATION
      */
     class DigiAction {
       friend class DigiKernel;
@@ -117,121 +107,6 @@ namespace dd4hep {
       int                m_outputLevel = 3;
 
     protected:
-
-      /// Functor to access elements by name
-      struct FindByName  {
-        std::string _n;
-        FindByName(const std::string& n) : _n(n) {}
-        bool operator()(const DigiAction* a) { return a->name() == _n; }
-      };
-      /// Actor class to manipulate action groups
-      /**
-       *  \author  M.Frank
-       *  \version 1.0
-       *  \ingroup DD4HEP_SIMULATION
-       */
-      template <typename T> class Actors {
-      public:
-        typedef typename std::vector<T*> _V;
-        _V m_v;
-        Actors() = default;
-        ~Actors()  = default;
-        size_t size()    const        { return m_v.size();              }
-        void clear()                  { m_v.clear();                    }
-        void add(T* obj)              { m_v.emplace_back(obj);          }
-        void add_front(T* obj)        { m_v.insert(m_v.begin(), obj);   }
-        const typename _V::value_type& operator[](size_t i)  const
-        {  return m_v[i];}
-        typename _V::value_type& operator[](size_t i)  {  return m_v[i];}
-        operator const _V&() const    { return m_v;                     }
-        operator _V&()                { return m_v;                     }
-        const _V* operator->() const  { return &m_v;                    }
-        _V* operator->()              { return &m_v;                    }
-        typename _V::iterator begin() { return m_v.begin();             }
-        typename _V::iterator end()   { return m_v.end();               }
-        typename _V::const_iterator begin() const { return m_v.begin(); }
-        typename _V::const_iterator end()   const { return m_v.end();   }
-        
-        /// Context updates
-        void updateContext(DigiContext* ctxt)  {
-          (*this)(&T::updateContext,ctxt);
-        }
-        /// Element access by name
-        template <typename F> typename _V::value_type get(const F& f)  const   {
-          if (!m_v.empty())  {
-            typename _V::const_iterator i=std::find_if(m_v.begin(),m_v.end(),f);
-            return i==m_v.end() ? 0 : (*i);
-          }
-          return 0;
-        }
-        /// NON-CONST actions
-        template <typename R, typename Q> void operator()(R (Q::*pmf)()) {
-          if (m_v.empty())
-            return;
-          for (typename _V::iterator i = m_v.begin(); i != m_v.end(); ++i)
-            ((*i)->*pmf)();
-        }
-        template <typename R, typename Q, typename A0> void operator()(R (Q::*pmf)(A0&), A0& a0) {
-          if (m_v.empty())
-            return;
-          for (typename _V::iterator i = m_v.begin(); i != m_v.end(); ++i)
-            ((*i)->*pmf)(a0);
-        }
-        template <typename R, typename Q, typename A0, typename A1> void operator()(R (Q::*pmf)(A0, A1), A0 a0, A1 a1) {
-          if (m_v.empty())
-            return;
-          for (typename _V::iterator i = m_v.begin(); i != m_v.end(); ++i)
-            ((*i)->*pmf)(a0, a1);
-        }
-        /// CONST actions
-        template <typename R, typename Q> void operator()(R (Q::*pmf)() const) const {
-          if (m_v.empty())
-            return;
-          for (typename _V::const_iterator i = m_v.begin(); i != m_v.end(); ++i)
-            ((*i)->*pmf)();
-        }
-        template <typename R, typename Q, typename A0> void operator()(R (Q::*pmf)(A0&) const, A0& a0) const {
-          if (m_v.empty())
-            return;
-          for (typename _V::const_iterator i = m_v.begin(); i != m_v.end(); ++i)
-            ((*i)->*pmf)(a0);
-        }
-        template <typename R, typename Q, typename A0, typename A1> void operator()(R (Q::*pmf)(A0, A1) const, A0 a0,
-                                                                                    A1 a1) const {
-          if (m_v.empty())
-            return;
-          for (typename _V::const_iterator i = m_v.begin(); i != m_v.end(); ++i)
-            ((*i)->*pmf)(a0, a1);
-        }
-        /// CONST filters
-        template <typename Q> bool filter(bool (Q::*pmf)() const) const {
-          if (!m_v.empty())
-            return true;
-          for (typename _V::const_iterator i = m_v.begin(); i != m_v.end(); ++i)
-            if (!((*i)->*pmf)())
-              return false;
-          return true;
-        }
-        template <typename Q, typename A0> bool filter(bool (Q::*pmf)(A0) const, A0 a0) const {
-          if (m_v.empty())
-            return true;
-          for (typename _V::const_iterator i = m_v.begin(); i != m_v.end(); ++i)
-            if (!((*i)->*pmf)(a0))
-              return false;
-          return true;
-        }
-        template <typename Q, typename A0, typename A1> bool filter(bool (Q::*pmf)(A0, A1) const, A0 a0, A1 a1) const {
-          if (m_v.empty())
-            return true;
-          for (typename _V::const_iterator i = m_v.begin(); i != m_v.end(); ++i)
-            if (!((*i)->*pmf)(a0, a1))
-              return false;
-          return true;
-        }
-      };
-
-    protected:
-
       /// Define standard assignments and constructors
       DDDIGI_DEFINE_ACTION_CONSTRUCTORS(DigiAction);
 
@@ -276,17 +151,9 @@ namespace dd4hep {
       bool hasProperty(const std::string& name) const;
       /// Access single property
       Property& property(const std::string& name);
+
       /// Support for messages with variable output level using output level
       void print(const char* fmt, ...) const;
-      /// Support for messages with variable output level using output level-1
-      void printM1(const char* fmt, ...) const;
-      /// Support for messages with variable output level using output level-2
-      void printM2(const char* fmt, ...) const;
-      /// Support for messages with variable output level using output level+1
-      void printP1(const char* fmt, ...) const;
-      /// Support for messages with variable output level using output level+2
-      void printP2(const char* fmt, ...) const;
-
       /// Support for building formatted messages
       std::string format(const char* fmt, ...) const;
       /// Support of debug messages.
@@ -303,9 +170,6 @@ namespace dd4hep {
       void fatal(const char* fmt, ...) const;
       /// Support of exceptions: Print fatal message and throw runtime_error.
       void except(const char* fmt, ...) const;
-
-      /// Optional action initialization if required
-      virtual void initialize();
     };
 
     /// Declare property
diff --git a/DDDigi/include/DDDigi/DigiActionSequence.h b/DDDigi/include/DDDigi/DigiActionSequence.h
index cefd19fac792d1a930a0062789bb9146d864cb84..548294422c86619d02fa64bffd26df94f8d0fcd5 100644
--- a/DDDigi/include/DDDigi/DigiActionSequence.h
+++ b/DDDigi/include/DDDigi/DigiActionSequence.h
@@ -14,6 +14,7 @@
 #define DDDIGI_DIGIACTIONSEQUENCE_H
 
 // Framework include files
+#include <DD4hep/Callback.h>
 #include <DDDigi/DigiSynchronize.h>
 
 /// Namespace for the AIDA detector description toolkit
diff --git a/DDDigi/include/DDDigi/DigiSegmentAction.h b/DDDigi/include/DDDigi/DigiAttenuatorSequenceAction.h
similarity index 50%
rename from DDDigi/include/DDDigi/DigiSegmentAction.h
rename to DDDigi/include/DDDigi/DigiAttenuatorSequenceAction.h
index 95bf318cf52f73bba1332d6049fb2c1cc9511d1b..3e27c14bb596027546355a444e38606f83a0ea45 100644
--- a/DDDigi/include/DDDigi/DigiSegmentAction.h
+++ b/DDDigi/include/DDDigi/DigiAttenuatorSequenceAction.h
@@ -10,13 +10,11 @@
 // Author     : M.Frank
 //
 //==========================================================================
-#ifndef DDDIGI_DIGISEGMENTACTION_H
-#define DDDIGI_DIGISEGMENTACTION_H
+#ifndef DDDIGI_DIGIATTENUATORSEQUENCEACTION_H
+#define DDDIGI_DIGIATTENUATORSEQUENCEACTION_H
 
 // Framework include files
-#include <DDDigi/DigiData.h>
 #include <DDDigi/DigiEventAction.h>
-#include <DDDigi/DigiSegmentationTool.h>
 
 /// Namespace for the AIDA detector description toolkit
 namespace dd4hep {
@@ -24,11 +22,6 @@ namespace dd4hep {
   /// Namespace for the Digitization part of the AIDA detector description toolkit
   namespace digi {
 
-    /// Forward declarations
-    class DigiSegmentAction;
-    class DigiSegmentContext;
-    class DigiSegmentSplitter;
-
     /// Default base class for all Digitizer actions and derivates thereof.
     /**
      *  This is a utility class supporting properties, output and access to
@@ -36,27 +29,38 @@ namespace dd4hep {
      *
      *  \author  M.Frank
      *  \version 1.0
-     *  \ingroup DD4HEP_SIMULATION
+     *  \ingroup DD4HEP_DIGITIZATION
      */
-    class DigiSegmentAction : virtual public DigiAction   {
+    class DigiAttenuatorSequenceAction : public DigiEventAction   {
     protected:
-      friend class DigiSegmentSplitter;
+      /// Property: Input data segment name
+      std::string                    m_processor_name { };
+      /// Property: Input data segment name
+      std::string                    m_input_segment  { };
+      /// Property: Container names to be loaded
+      std::map<std::string, double>  m_container_attenuation  { };
+      /// Property: event mask to be handled
+      int                            m_mask   { 0 };
+      /// Property: T0 with respect to central crossing
+      double                         m_t0     { 0e0 };
+
+      /// Keys of all containers to be manipulated
+      std::map<Key, double> m_attenuation  { };
 
-      /// Segmentation split context
-      DigiSegmentContext    segment  { };
-  
+    protected:
       /// Define standard assignments and constructors
-      DDDIGI_DEFINE_ACTION_CONSTRUCTORS(DigiSegmentAction);
+      DDDIGI_DEFINE_ACTION_CONSTRUCTORS(DigiAttenuatorSequenceAction);
+      /// Default destructor
+      virtual ~DigiAttenuatorSequenceAction();
 
     public:
       /// Standard constructor
-      DigiSegmentAction(const DigiKernel& kernel, const std::string& name);
-      /// Default destructor
-      virtual ~DigiSegmentAction();
-      /// Main functional callback. Default implementnation is noop.
-      virtual DepositVector handleSegment(DigiContext&              context,
-					  const DepositMapping&     deposits)  const;
+      DigiAttenuatorSequenceAction(const DigiKernel& kernel, const std::string& nam);
+      /// Initialization callback
+      virtual void initialize();
+      /// Main functional callback
+      virtual void execute(DigiContext& context)  const;
     };
   }    // End namespace digi
 }      // End namespace dd4hep
-#endif // DDDIGI_DIGISEGMENTACTION_H
+#endif // DDDIGI_DIGIATTENUATORSEQUENCEACTION_H
diff --git a/DDDigi/include/DDDigi/DigiContainerCombine.h b/DDDigi/include/DDDigi/DigiContainerCombine.h
index a4bc569068d72196f43a597f2061ae86974d5c25..60393ba2d84c00e15f7262e77dfd7452099a49a8 100644
--- a/DDDigi/include/DDDigi/DigiContainerCombine.h
+++ b/DDDigi/include/DDDigi/DigiContainerCombine.h
@@ -13,8 +13,12 @@
 #ifndef DDDIGI_DIGICONTAINERCOMBINE_H
 #define DDDIGI_DIGICONTAINERCOMBINE_H
 
-// Framework include files
+/// Framework include files
 #include <DDDigi/DigiEventAction.h>
+#include <DDDigi/DigiParallelWorker.h>
+
+/// C/C++ include files
+#include <functional>
 
 /// Namespace for the AIDA detector description toolkit
 namespace dd4hep {
@@ -29,15 +33,38 @@ namespace dd4hep {
      *
      *  \author  M.Frank
      *  \version 1.0
-     *  \ingroup DD4HEP_SIMULATION
+     *  \ingroup DD4HEP_DIGITIZATION
      */
     class DigiContainerCombine : public DigiEventAction   {
+    public:
+      class work_definition_t;
+      using self_t  = DigiContainerCombine;
+      using Worker  = DigiParallelWorker<self_t,work_definition_t>;
+      using Workers = DigiParallelWorkers<Worker>;
+
     protected:
-      /// Implementation declaration
-      class internals_t;
+      /// Property: Container names to be loaded
+      std::vector<std::string>       m_containers   { };
+      /// Property: Output container dressing
+      std::string                    m_output_name_flag;
+      /// Property: Input data segment name
+      std::string                    m_input;
+      /// Property: event masks to be handled
+      std::vector<int>               m_input_masks  { };
+      /// Property: Output data segment name
+      std::string                    m_output;
+      /// Property: mask of the deposit
+      int                            m_deposit_mask { 0 };
+      /// Property: Flag to erase already combined containers (not thread-safe!!!)
+      bool                           m_erase_combined { false };
+
+      /// Fully qualified keys of all containers to be manipulated
+      std::set<Key::key_type>        m_keys  { };
+      /// Container keys of all containers to be manipulated
+      std::set<Key::key_type>        m_cont_keys  { };
 
-      /// Reference to the implementation
-      std::unique_ptr<internals_t> internals;
+      /// Worker objects to be submitted to TBB each performing part of the job
+      Workers m_workers;
 
     protected:
       /// Define standard assignments and constructors
@@ -46,12 +73,19 @@ namespace dd4hep {
       /// Default destructor
       virtual ~DigiContainerCombine();
 
+      /// Initializing function: compute values which depend on properties
+      void initialize();
+
+      /// Check if we have sufficient workers
+      void have_workers(size_t len)  const;
+
       /// Combine selected containers to one single deposit container
-      template <typename PREDICATE> 
-	std::size_t combine_containers(DigiEvent& event,
-				       DataSegment& inputs,
-				       DataSegment& outputs,
-				       const PREDICATE& predicate)  const;
+      std::size_t combine_containers(DigiEvent& event,
+				     DataSegment& inputs,
+				     DataSegment& outputs)  const;
+
+      /// Decide if a continer is to merged based on the properties
+      virtual bool use_key(Key key)  const;
 
     public:
       /// Standard constructor
diff --git a/DDDigi/include/DDDigi/DigiContainerProcessor.h b/DDDigi/include/DDDigi/DigiContainerProcessor.h
new file mode 100644
index 0000000000000000000000000000000000000000..d90741c311f833da7c4ca7a56bc5f3bb580ff9a3
--- /dev/null
+++ b/DDDigi/include/DDDigi/DigiContainerProcessor.h
@@ -0,0 +1,248 @@
+//==========================================================================
+//  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 DDDIGI_DIGIMULTICONTAINERPROCESSOR_H
+#define DDDIGI_DIGIMULTICONTAINERPROCESSOR_H
+
+// Framework include files
+#include <DDDigi/DigiData.h>
+#include <DDDigi/DigiEventAction.h>
+#include <DDDigi/DigiParallelWorker.h>
+
+/// C/C++ include files
+#include <set>
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Namespace for the Digitization part of the AIDA detector description toolkit
+  namespace digi {
+
+    /// Forward declarations
+    class DigiContainerProcessor;
+    class DigiContainerSequence;
+    class DigiContainerSequenceAction;
+    class DigiMultiContainerProcessor;
+
+    /// Worker base class to analyse containers from the input segment in parallel
+    /**
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_DIGITIZATION
+     */
+    class DigiContainerProcessor : public DigiAction   {
+    public:
+      struct work_t  {
+	/// Event processing context
+	DigiContext&    context;
+	/// Input data key
+	Key             key;
+	/// Input deposits
+	std::any&       input;
+	/// Lock for secure output merging
+	std::mutex&     output_lock;
+	/// Handle to output 
+	std::any&       output;
+
+	/// Merge output data (thread safe, locked)
+	void merge_output(DepositVector&& data);
+	/// Merge output data (thread safe, locked)
+	void emplace_output(CellID cell, EnergyDeposit&& deposit);
+      };
+    protected:
+      /// Define standard assignments and constructors
+      DDDIGI_DEFINE_ACTION_CONSTRUCTORS(DigiContainerProcessor);
+
+    private:
+      /// Main functional callback if specific work is known.
+      virtual void execute(DigiContext& context, Key key, std::any& data)  const;
+
+    public:
+      /// Standard constructor
+      DigiContainerProcessor(const DigiKernel& kernel, const std::string& name);
+      /// Default destructor
+      virtual ~DigiContainerProcessor();
+      /// Main functional callback adapter
+      virtual void execute(DigiContext& context, work_t& work)  const;
+    };
+
+    /// Worker class act on containers in an event identified by input masks and container name
+    /**
+     *  The sequencer calls all registered processors for the contaiers registered.
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_DIGITIZATION
+     */
+    class DigiContainerSequence : public DigiContainerProcessor  {
+    protected:
+      /// Property to steer parallel processing
+      bool m_parallel { false };
+
+    protected:
+      using self_t = DigiContainerSequence;
+      using processor_t = DigiContainerProcessor;
+      using worker_t  = DigiParallelWorker<processor_t,work_t>;
+      using workers_t = DigiParallelWorkers<worker_t>;
+      friend class DigiParallelWorker<processor_t,work_t>;
+
+      /// Array of sub-workers
+      workers_t         m_workers;
+
+      /// Lock for output merging
+      mutable std::mutex        m_output_lock;
+
+    protected:
+      /// Define standard assignments and constructors
+      DDDIGI_DEFINE_ACTION_CONSTRUCTORS(DigiContainerSequence);
+      /// Default destructor
+      virtual ~DigiContainerSequence();
+
+    public:
+      /// Standard constructor
+      DigiContainerSequence(const DigiKernel& kernel, const std::string& name);
+      /// Adopt new parallel worker
+      virtual void adopt_processor(DigiContainerProcessor* action);
+      /// Main functional callback adapter
+      virtual void execute(DigiContext& context, work_t& work)  const;
+    };
+
+    /// Worker base class to analyse containers from the input segment in parallel
+    /**
+     *  Depending on the adopted processors, the full input record is scanned and
+     *  the registered processors are called.
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_DIGITIZATION
+     */
+    class DigiContainerSequenceAction : public DigiEventAction  {
+
+    protected:
+      /// Argument structure for client calls
+      using self_t = DigiContainerSequenceAction;
+      using processor_t = DigiContainerProcessor;
+      struct work_item_t  {
+	Key key;
+	std::any* data;
+      };
+      struct work_t  {
+	DigiContext&             context;
+	std::vector<work_item_t> input_items;
+	/// Lock for secure output merging
+	std::mutex&              output_lock;
+	std::any                 output;
+	const self_t*            parent;
+      };
+      using worker_t         = DigiParallelWorker<processor_t, work_t>;
+      using workers_t        = DigiParallelWorkers<worker_t>;
+      using reg_workers_t    = std::map<Key, worker_t*>;
+      using reg_processors_t = std::map<Key, processor_t*>;
+      friend class DigiParallelWorker<processor_t, work_t>;
+
+      /// Array of sub-workers
+      workers_t         m_workers;
+      /// Registered action map
+      reg_processors_t  m_registered_processors;
+      /// Registered worker map
+      reg_workers_t     m_registered_workers;
+
+      /// Property: Input data segment name
+      std::string       m_input_segment { "inputs" };
+      /// Property: Input mask to be handled
+      int               m_mask { 0x0 };
+
+      /// Lock for output merging
+      mutable std::mutex        m_output_lock;
+
+    protected:
+      /// Define standard assignments and constructors
+      DDDIGI_DEFINE_ACTION_CONSTRUCTORS(DigiContainerSequenceAction);
+
+      /// Default destructor
+      virtual ~DigiContainerSequenceAction();
+      /// Initialization callback
+      void initialize();
+
+    public:
+      /// Standard constructor
+      DigiContainerSequenceAction(const DigiKernel& kernel, const std::string& name);
+      /// Adopt new parallel worker
+      void adopt_processor(DigiContainerProcessor* action, const std::string& container);
+      /// Main functional callback if specific work is known
+      virtual void execute(DigiContext& context)  const override;
+    };
+
+    /// Sequencer class to analyse containers from the input segment in parallel
+    /**
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_DIGITIZATION
+     */
+    class DigiMultiContainerProcessor : virtual public DigiEventAction   {
+    protected:
+      using self_t       = DigiMultiContainerProcessor;
+      using processor_t  = DigiContainerProcessor;
+      using work_items_t = std::vector<std::pair<Key, std::any*> >;
+      /// Argument structure for client calls
+      struct work_t  {
+	DigiContext&    context;
+	work_items_t&   items;
+	std::mutex&     output_lock;
+	std::any        output;
+	const self_t*   parent;
+      };
+      using worker_t  = DigiParallelWorker<processor_t, work_t>;
+      using workers_t = DigiParallelWorkers<worker_t>;
+      friend class DigiParallelWorker<processor_t, work_t>;
+
+    protected:
+      /// Property: Input data segment name
+      std::string       m_input_segment { "inputs" };
+      /// Property: event masks to be handled
+      std::vector<int>  m_input_masks  { };
+
+      /// Set of work items to be processed and passed to clients
+      std::set<Key>     m_work_items;
+
+      std::vector<std::vector<Key> > m_worker_keys;
+      /// Array of sub-workers
+      workers_t         m_workers;
+
+      /// Lock for output merging
+      mutable std::mutex        m_output_lock;
+
+    protected:
+       /// Define standard assignments and constructors
+      DDDIGI_DEFINE_ACTION_CONSTRUCTORS(DigiMultiContainerProcessor);
+      /// Default destructor
+      virtual ~DigiMultiContainerProcessor();
+
+    public:
+      /// Standard constructor
+      DigiMultiContainerProcessor(const DigiKernel& kernel, const std::string& name);
+      const std::vector<Key>& worker_keys(size_t worker_id)  const  {
+	return this->m_worker_keys.at(worker_id);
+      }
+      const std::vector<int>& input_masks()  const   {
+	return this->m_input_masks;
+      }
+      /// Adopt new parallel worker
+      void adopt_processor(DigiContainerProcessor* action, const std::vector<std::string>& containers);
+      /// Main functional callback
+      virtual void execute(DigiContext& context)  const;
+    };
+  }    // End namespace digi
+}      // End namespace dd4hep
+#endif // DDDIGI_DIGIMULTICONTAINERPROCESSOR_H
diff --git a/DDDigi/include/DDDigi/DigiData.h b/DDDigi/include/DDDigi/DigiData.h
index 55d30ca12a16a14447f1122941b0ec7977b405cb..519f9210a78b668e0e99440350f1ebc18dfe8cd0 100644
--- a/DDDigi/include/DDDigi/DigiData.h
+++ b/DDDigi/include/DDDigi/DigiData.h
@@ -14,7 +14,7 @@
 #define DDDIGI_DIGIDATA_H
 
 /// Framework include files
-#include <DD4hep/Primitives.h>
+#include <DD4hep/Objects.h>
 
 /// C/C++ include files
 #include <cstdint>
@@ -38,6 +38,9 @@ namespace dd4hep {
     class DigiEvent;
     class DataSegment;
 
+    using Position = dd4hep::Position;
+    using Direction = dd4hep::Direction;
+
     ///  Key defintion to access the event data
     /**
      *  Helper to convert item and mask to a 64 bit integer
@@ -71,8 +74,10 @@ namespace dd4hep {
       Key(const Key&);
       /// Initializaing constructor (fast)
       Key(key_type full_mask);
-      /// Initializaing constructor with key generation using hash algorithm
-      Key(mask_type mask, const std::string& item);
+      /// Initializing constructor with key generation using hash algorithm
+      Key(const char* item, mask_type mask);
+      /// Initializing constructor with key generation using hash algorithm
+      Key(const std::string& item, mask_type mask);
       /// Assignment operator
       Key& operator = (const Key&);
       /// Move assignment operator
@@ -117,6 +122,10 @@ namespace dd4hep {
       itemkey_type item()  {
 	return this->values.item;
       }
+      /// Set key item identifier
+      void set_item(itemkey_type i)  {
+	this->values.item = i;
+      }
       /// Project the mask part of the key
       static itemkey_type item(key_type k)  {
 	return Key(k).values.item;
@@ -150,7 +159,12 @@ namespace dd4hep {
     }
 
     /// Initializaing constructor with key generation using hash algorithm
-    inline Key::Key(mask_type mask, const std::string& item)  {
+    inline Key::Key(const char* item, mask_type mask)  {
+      this->set(item, mask);
+    }
+
+    /// Initializaing constructor with key generation using hash algorithm
+    inline Key::Key(const std::string& item, mask_type mask)  {
       this->set(item, mask);
     }
 
@@ -181,6 +195,34 @@ namespace dd4hep {
       return this->key > other.key;
     }
 
+    class SegmentEntry   {
+    public:
+      std::string      name { };
+      Key              key  { 0x0 };
+    public:
+      /// Initializing constructor
+      SegmentEntry(const std::string& name, Key::mask_type mask);
+      /// Default constructor
+      SegmentEntry() = default;
+      /// Disable move constructor
+      SegmentEntry(SegmentEntry&& copy) = default;
+      /// Disable copy constructor
+      SegmentEntry(const SegmentEntry& copy) = default;      
+      /// Default destructor
+      virtual ~SegmentEntry() = default;
+      /// Disable move assignment
+      SegmentEntry& operator=(SegmentEntry&& copy) = default;
+      /// Disable copy assignment
+      SegmentEntry& operator=(const SegmentEntry& copy) = default;      
+    };
+
+    /// Initializing constructor
+    inline SegmentEntry::SegmentEntry(const std::string& nam, Key::mask_type msk)
+      : name(nam)
+    {
+      key.set_mask(msk);
+    }
+
     /// Particle definition for digitization
     /** Particle definition for digitization
      *
@@ -190,6 +232,11 @@ namespace dd4hep {
      */
     class Particle   {
     public:
+      Position  start_position { };
+      Position  end_position   { };
+      Direction momentum       { };
+      double    mass           { 0e0 };
+      char      charge         { 0 };
       /// Source contributing
       std::any history;
 
@@ -223,10 +270,15 @@ namespace dd4hep {
      *  \version 1.0
      *  \ingroup DD4HEP_DIGITIZATION
      */
-    class ParticleMapping : public std::map<Key::key_type, Particle>   {
-    public:
-      std::string      name { };
-      Key::mask_type   mask { 0x0 };
+    class ParticleMapping : public SegmentEntry   {
+      using container_t = std::map<Key::key_type, Particle>;
+      using value_type = container_t::value_type;
+      using mapped_type = container_t::mapped_type;
+      using key_type = container_t::key_type;
+      using iterator = container_t::iterator;
+      using const_iterator = container_t::const_iterator;
+
+      container_t data;
 
     public: 
       /// Initializing constructor
@@ -248,11 +300,29 @@ namespace dd4hep {
       std::size_t merge(ParticleMapping&& updates);
       /// Add new entry to the particle mapping (not thread safe!)
       void push(Key key, Particle&& particle);
+
+      /// Access container size
+      std::size_t size()  const           { return this->data.size();        }
+      /// Check container if empty
+      bool        empty() const           { return this->data.empty();       }
+      /// Insert new entry
+      //std::pair<iterator, bool> emplace(key_type entry_key, mapped_type&& entry_data);
+      void emplace(Key entry_key, Particle&& entry_data);
+
+      /** Iteration support */
+      /// Begin iteration
+      iterator begin()                    { return this->data.begin();       }
+      /// End iteration
+      iterator end()                      { return this->data.end();         }
+      /// Begin iteration (CONST)
+      const_iterator begin() const        { return this->data.begin();       }
+      /// End iteration (CONST)
+      const_iterator end()   const        { return this->data.end();         }
     };
 
     /// Initializing constructor
-    inline ParticleMapping::ParticleMapping(const std::string& n, Key::mask_type m)
-      : name(n), mask(m)
+    inline ParticleMapping::ParticleMapping(const std::string& nam, Key::mask_type msk)
+      : SegmentEntry(nam, msk)
     {
     }
 
@@ -265,14 +335,23 @@ namespace dd4hep {
      */
     class EnergyDeposit   {
     public:
+      /// Hit position
+      Position       position    { };
+      /// Hit direction
+      Direction      momentum    { };
+      /// Length of the track segment contributing to this hit
+      double         length      { 0 };
       /// Total energy deposit
-      double deposit  { 0 };
+      double         deposit     { 0 };
+
+      long           flag        { 0 };
+      Key::mask_type mask        { 0 };
+
       /// Sources contributing to this deposit
-      std::vector<std::pair<std::any, Key::mask_type> > history;
+      std::vector<std::pair<Key, double> > hit_history;
+      std::vector<std::pair<Key, double> > particle_history;
 
     public:
-      /// Initializing constructor
-      EnergyDeposit(double ene);
       /// Default constructor
       EnergyDeposit() = default;
       /// Disable move constructor
@@ -288,12 +367,6 @@ namespace dd4hep {
     };
 
 
-    /// Initializing constructor
-    inline EnergyDeposit::EnergyDeposit(double ene)
-      : deposit(ene)
-    {
-    }
-
     /// Energy deposit vector definition for digitization
     /** Energy deposit vector definition for digitization
      *
@@ -301,10 +374,13 @@ namespace dd4hep {
      *  \version 1.0
      *  \ingroup DD4HEP_DIGITIZATION
      */
-    class DepositVector : public std::vector<std::pair<CellID, EnergyDeposit> >  {
+    class DepositVector : public SegmentEntry  {
     public: 
-      std::string    name { };
-      Key            key  { 0x0 };
+      using container_t = std::vector<std::pair<CellID, EnergyDeposit> >;
+      using iterator = container_t::iterator;
+      using const_iterator = container_t::const_iterator;
+
+      container_t    data { };
 
     public: 
       /// Initializing constructor
@@ -323,14 +399,38 @@ namespace dd4hep {
       DepositVector& operator=(const DepositVector& copy) = default;      
       /// Merge new deposit map onto existing map (not thread safe!)
       std::size_t merge(DepositVector&& updates);
+      /// Merge new deposit map onto existing map (keep inputs. not thread safe!)
+      std::size_t insert(const DepositVector& updates);
+      /// Emplace entry
+      void emplace(CellID cell, EnergyDeposit&& deposit);
+
+      /// Access container size
+      std::size_t size()  const           { return this->data.size();        }
+      /// Check container if empty
+      bool        empty() const           { return this->data.empty();       }
+
+      /** Iteration support */
+      /// Begin iteration
+      iterator begin()                    { return this->data.begin();       }
+      /// End iteration
+      iterator end()                      { return this->data.end();         }
+      /// Begin iteration (CONST)
+      const_iterator begin() const        { return this->data.begin();       }
+      /// End iteration (CONST)
+      const_iterator end()   const        { return this->data.end();         }
     };
 
     /// Initializing constructor
-    inline DepositVector::DepositVector(const std::string& n, Key::mask_type mask)
-      : name(n), key(mask, n)
+    inline DepositVector::DepositVector(const std::string& nam, Key::mask_type msk)
+      : SegmentEntry(nam, msk)
     {
     }
 
+    /// Emplace entry
+    inline void DepositVector::emplace(CellID cell, EnergyDeposit&& deposit)   {
+      this->data.emplace_back(cell, std::move(deposit));
+    }
+
     /// Energy deposit mapping definition for digitization
     /** Energy deposit mapping definition for digitization
      *
@@ -338,10 +438,13 @@ namespace dd4hep {
      *  \version 1.0
      *  \ingroup DD4HEP_DIGITIZATION
      */
-    class DepositMapping : public std::map<CellID, EnergyDeposit>   {
+    class DepositMapping : public SegmentEntry  {
     public: 
-      std::string    name { };
-      Key            key  { 0x0 };
+      using container_t = std::multimap<CellID, EnergyDeposit>;
+      using iterator = container_t::iterator;
+      using const_iterator = container_t::const_iterator;
+
+      container_t    data { };
 
     public: 
       /// Initializing constructor
@@ -362,11 +465,36 @@ namespace dd4hep {
       std::size_t merge(DepositMapping&& updates);
       /// Merge new deposit map onto existing map (not thread safe!)
       std::size_t merge(DepositVector&& updates);
+      /// Merge new deposit map onto existing map (not thread safe!)
+      std::size_t insert(const DepositMapping& updates);
+      /// Merge new deposit map onto existing map (not thread safe!)
+      std::size_t insert(const DepositVector& updates);
+
+      /// Access container size
+      std::size_t size()  const           { return this->data.size();        }
+      /// Check container if empty
+      bool        empty() const           { return this->data.empty();       }
+
+      /** Iteration support */
+      /// Begin iteration
+      iterator begin()                    { return this->data.begin();       }
+      /// End iteration
+      iterator end()                      { return this->data.end();         }
+      /// Begin iteration (CONST)
+      const_iterator begin() const        { return this->data.begin();       }
+      /// End iteration (CONST)
+      const_iterator end()   const        { return this->data.end();         }
+
+      /** Direct element access by key */
+      /// Find entry by cell
+      //iterator find(CellID cell)          { return this->data.find(cell);    }
+      /// Find entry by cell
+      //const_iterator find(CellID cell) const { return this->data.find(cell); }
     };
 
     /// Initializing constructor
-    inline DepositMapping::DepositMapping(const std::string& n, Key::mask_type mask)
-      : name(n), key(mask, n)
+    inline DepositMapping::DepositMapping(const std::string& nam, Key::mask_type msk)
+      : SegmentEntry(nam, msk)
     {
     }
 
@@ -425,6 +553,11 @@ namespace dd4hep {
       void print_keys()   const;
       
       /** Unlocked operations */
+      /// Access data by key. If not existing, nullptr is returned
+      std::any* entry(Key key)              { return this->get_item(key, false); }
+      /// Access data by key. If not existing, nullptr is returned
+      const std::any* entry(Key key)  const { return this->get_item(key, false); }
+
       /// Access data as reference by key. If not existing, an exception is thrown
       template<typename T> T& get(Key key);
       /// Access data as reference by key. If not existing, an exception is thrown
diff --git a/DDDigi/include/DDDigi/DigiEventAction.h b/DDDigi/include/DDDigi/DigiEventAction.h
index 9611312e48565fe1634db13ad13e4c3cd886c690..313b7e6104c14a5775403ac8ccc6cc48aead2abd 100644
--- a/DDDigi/include/DDDigi/DigiEventAction.h
+++ b/DDDigi/include/DDDigi/DigiEventAction.h
@@ -32,7 +32,7 @@ namespace dd4hep {
      *
      *  \author  M.Frank
      *  \version 1.0
-     *  \ingroup DD4HEP_SIMULATION
+     *  \ingroup DD4HEP_DIGITIZATION
      */
     class DigiEventAction : public DigiAction   {
       friend class DigiKernel;
diff --git a/DDDigi/include/DDDigi/DigiFactories.h b/DDDigi/include/DDDigi/DigiFactories.h
index 9116f745c653739e83f189eb77c3592927b4bcae..f64ba2e51873e31e211e4a07dee2e01c60df1505 100644
--- a/DDDigi/include/DDDigi/DigiFactories.h
+++ b/DDDigi/include/DDDigi/DigiFactories.h
@@ -36,9 +36,10 @@ namespace dd4hep {
     class DigiAction;
     class DigiInputAction;
     class DigiEventAction;
-    class DigiSegmentAction;
     class DigiCellScanner;
     class DigiSignalProcessor;
+    class DigiSegmentProcessor;
+    class DigiContainerProcessor;
   }
 }
 
@@ -48,12 +49,15 @@ namespace {
   /// Factory to create Digi action objects
   DD4HEP_PLUGIN_FACTORY_ARGS_2(DS::DigiAction*,const DS::DigiKernel*, std::string)
   {    return new P(*a0,a1);  }
+#if 0
   /// Factory to create Digi action objects
   DD4HEP_PLUGIN_FACTORY_ARGS_2(DS::DigiEventAction*,const DS::DigiKernel*, std::string)
   {    return new P(*a0,a1);  }
   /// Factory to create Digi action objects
-  DD4HEP_PLUGIN_FACTORY_ARGS_2(DS::DigiSegmentAction*,const DS::DigiKernel*, std::string)
+  DD4HEP_PLUGIN_FACTORY_ARGS_2(DS::DigiSegmentProcessor*,const DS::DigiKernel*, std::string)
   {    return new P(*a0,a1);  }
+#endif
+
   /// Factory to create Digi signal processor objects
   DD4HEP_PLUGIN_FACTORY_ARGS_2(DS::DigiSignalProcessor*,const DS::DigiKernel*, std::string)
   {    return new P(*a0,a1);  }
@@ -69,6 +73,8 @@ namespace {
 /// Plugin defintion to create DigiAction objects
 #define DECLARE_DIGIACTION(name)   DECLARE_DIGIACTION_NS(dd4hep::digi,name)
 
+
+#if 0
 /// Plugin defintion to create DigiAction objects
 #define DECLARE_DIGIEVENTACTION_NS(name_space,name)  namespace {\
   using name_space::name;                                               \
@@ -77,11 +83,14 @@ namespace {
 #define DECLARE_DIGIEVENTACTION(name)     DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,name)
 
 /// Plugin defintion to create DigiAction objects
-#define DECLARE_DIGISEGMENTACTION_NS(name_space,name)  namespace {\
+#define DECLARE_DIGISEGMENTPROCESSOR_NS(name_space,name)  namespace {\
   using name_space::name;                                               \
-  DD4HEP_PLUGINSVC_FACTORY(name,name,dd4hep::digi::DigiSegmentAction*(const DS::DigiKernel*,std::string),__LINE__) }
+  DD4HEP_PLUGINSVC_FACTORY(name,name,dd4hep::digi::DigiSegmentProcessor*(const DS::DigiKernel*,std::string),__LINE__) }
 /// Plugin defintion to create DigiAction objects
-#define DECLARE_DIGISEGMENTACTION(name)   DECLARE_DIGISEGMENTACTION_NS(dd4hep::digi,name)
+#define DECLARE_DIGISEGMENTPROCESSOR(name)   DECLARE_DIGISEGMENTPROCESSOR_NS(dd4hep::digi,name)
+#endif
+
+
 
 #define DECLARE_DIGISIGNALPROCESSOR_NS(name_space,name)  namespace {     \
   using name_space::name;                                               \
diff --git a/DDDigi/include/DDDigi/DigiHandle.h b/DDDigi/include/DDDigi/DigiHandle.h
index 80ebf49f28541ca0502b350f21b8c1bf9491b3b7..b2fedc28350560b5f7ba0232ae6d4e14d24ab1ad 100644
--- a/DDDigi/include/DDDigi/DigiHandle.h
+++ b/DDDigi/include/DDDigi/DigiHandle.h
@@ -37,7 +37,7 @@ namespace dd4hep {
     /**
      *  \author  M.Frank
      *  \version 1.0
-     *  \ingroup DD4HEP_SIMULATION
+     *  \ingroup DD4HEP_DIGITIZATION
      */
     class KernelHandle {
     public:
@@ -67,7 +67,7 @@ namespace dd4hep {
     /**
      *  \author  M.Frank
      *  \version 1.0
-     *  \ingroup DD4HEP_SIMULATION
+     *  \ingroup DD4HEP_DIGITIZATION
      */
     template <typename TYPE> class DigiHandle {
     protected:
diff --git a/DDDigi/include/DDDigi/DigiHitAttenuatorExp.h b/DDDigi/include/DDDigi/DigiHitAttenuatorExp.h
index 598962b0e202024165f3215721e1e8c9099bf04b..faadc0bff55f10b80ccee1402e8cbc0978253d78 100644
--- a/DDDigi/include/DDDigi/DigiHitAttenuatorExp.h
+++ b/DDDigi/include/DDDigi/DigiHitAttenuatorExp.h
@@ -16,7 +16,6 @@
 // Framework include files
 #include <DDDigi/DigiEventAction.h>
 
-
 /// Namespace for the AIDA detector description toolkit
 namespace dd4hep {
 
@@ -30,25 +29,35 @@ namespace dd4hep {
      *
      *  \author  M.Frank
      *  \version 1.0
-     *  \ingroup DD4HEP_SIMULATION
+     *  \ingroup DD4HEP_DIGITIZATION
      */
     class DigiHitAttenuatorExp : public DigiEventAction   {
     protected:
-      /// Implementation declaration
-      class internals_t;
-      /// Reference to the actual implementation
-      std::unique_ptr<internals_t> internals;
+      /// Property: Input data segment name
+      std::string                    m_input_segment  { };
+      /// Property: Container names to be loaded
+      std::map<std::string, double>  m_container_attenuation  { };
+      /// Property: event masks to be handled
+      std::vector<int>               m_masks  { };
+      /// Property: T0 with respect to central crossing
+      double                         m_t0     { 0e0 };
+
+      /// Keys of all containers to be manipulated
+      std::map<unsigned long, double> m_attenuation  { };
 
     protected:
       /// Define standard assignments and constructors
       DDDIGI_DEFINE_ACTION_CONSTRUCTORS(DigiHitAttenuatorExp);
-
       /// Default destructor
       virtual ~DigiHitAttenuatorExp();
+      /// Attenuator callback for single container
+      template <typename T> std::size_t attenuate(T* cont, double factor) const;
 
     public:
       /// Standard constructor
       DigiHitAttenuatorExp(const DigiKernel& kernel, const std::string& nam);
+      /// Initialization callback
+      virtual void initialize();
       /// Main functional callback
       virtual void execute(DigiContext& context)  const;
     };
diff --git a/DDDigi/include/DDDigi/DigiKernel.h b/DDDigi/include/DDDigi/DigiKernel.h
index 6cc5b9129d3d646810606cf19e4b34808d62509d..e5984b99a8f2a6b07ed9e08acaf15f44f69f7654 100644
--- a/DDDigi/include/DDDigi/DigiKernel.h
+++ b/DDDigi/include/DDDigi/DigiKernel.h
@@ -14,7 +14,9 @@
 #define DDDIGI_DIGIKERNEL_H
 
 // Framework include files
+#include <DD4hep/Callback.h>
 #include <DDDigi/DigiEventAction.h>
+#include <DDDigi/DigiParallelWorker.h>
 
 // C/C++ include files
 #include <mutex>
@@ -43,18 +45,6 @@ namespace dd4hep {
       typedef std::map<std::string,int>                 ClientOutputLevels;
       typedef std::pair<void*, const std::type_info*>   UserFramework;
 
-      class ParallelCall     {
-      public:
-        ParallelCall(ParallelCall* p, void* a);
-        ParallelCall() = default;
-	ParallelCall(ParallelCall&& copy) = default;
-	ParallelCall(const ParallelCall& copy) = default;
-	ParallelCall& operator=(ParallelCall&& copy) = default;
-	ParallelCall& operator=(const ParallelCall& copy) = default;
-	virtual ~ParallelCall() = default;
-	virtual void execute(void* args) const = 0;
-      };
-
     private:
       class Internals;
       class Processor;
@@ -141,18 +131,29 @@ namespace dd4hep {
       /// Access current number of events processing (events in flight)
       std::size_t events_processing()  const;
 
+      /// Register configure callback. Signature:   (function)()
+      void register_configure(const Callback& callback)   const;
+      /// Register initialize callback. Signature:  (function)()
+      void register_initialize(const Callback& callback)   const;
+      /// Register terminate callback. Signature:   (function)()
+      void register_terminate(const Callback& callback)   const;
+      /// Register start event callback. Signature: (function)(DigiContext*)
+      void register_start_event(const Callback& callback)   const;
+      /// Register end event callback. Signature:   (function)(DigiContext*)
+      void register_end_event(const Callback& callback)   const;
+
       /// Construct detector geometry using description plugin
       virtual void loadGeometry(const std::string& compact_file);
       /// Load XML file 
       virtual void loadXML(const char* fname);
 
-      /// Run the simulation: Configure Digi
+      /// Configure the digitization: call all registered configurators
       virtual int configure();
-      /// Run the simulation: Initialize Digi
+      /// Initialize the digitization: call all registered initializers
       virtual int initialize();
-      /// Run the simulation: Simulate the number of events given by the property "NumEvents"
+      /// Run the digitization sequence over the requested number of events
       virtual int run();
-      /// Run the simulation: Terminate Digi
+      /// Terminate the digitization: call all registered terminators and release the allocated resources
       virtual int terminate();
 
       /// Access to the main input action sequence from the kernel object
@@ -163,15 +164,11 @@ namespace dd4hep {
       DigiActionSequence& outputAction() const;
 
       /// Submit a bunch of actions to be executed in parallel
-      virtual void submit (const std::vector<ParallelCall*>& algorithms, void* data)  const;
-      /// Submit a bunch of actions to be executed serially
-      virtual void execute(const std::vector<ParallelCall*>& algorithms, void* data)  const;
-#if 0
+      virtual void submit (ParallelCall*const algorithms[], std::size_t count, void* data, bool parallel=true)  const;
+
       /// Submit a bunch of actions to be executed in parallel
-      virtual void submit (const DigiAction::Actors<DigiEventAction>& algorithms, DigiContext& context)  const;
-      /// Submit a bunch of actions to be executed serially
-      virtual void execute(const DigiAction::Actors<DigiEventAction>& algorithms, DigiContext& context)  const;
-#endif
+      virtual void submit (const std::vector<ParallelCall*>& algorithms, void* data, bool parallel=true)  const;
+
       /// If running multithreaded: wait until the thread-group finished execution
       virtual void wait(DigiContext& context)   const;
 
diff --git a/DDDigi/include/DDDigi/DigiMultiContainerProcessor.h b/DDDigi/include/DDDigi/DigiMultiContainerProcessor.h
deleted file mode 100644
index 4784853e5b4507b11367295daa2ab0ca8f5dfa6a..0000000000000000000000000000000000000000
--- a/DDDigi/include/DDDigi/DigiMultiContainerProcessor.h
+++ /dev/null
@@ -1,96 +0,0 @@
-//==========================================================================
-//  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 DDDIGI_DIGIMULTICONTAINERPROCESSOR_H
-#define DDDIGI_DIGIMULTICONTAINERPROCESSOR_H
-
-// Framework include files
-#include <DDDigi/DigiData.h>
-#include <DDDigi/DigiEventAction.h>
-#include <DDDigi/DigiParallelWorker.h>
-
-/// C/C++ include files
-#include <set>
-
-/// Namespace for the AIDA detector description toolkit
-namespace dd4hep {
-
-  /// Namespace for the Digitization part of the AIDA detector description toolkit
-  namespace digi {
-
-    /// Worker base class to analyse containers from the input segment in parallel
-    class DigiContainerProcessor : public DigiAction   {
-
-    public:
-      using WorkItems = std::vector<std::pair<Key, std::any*> >;
-      
-    public:
-      /// Property: Input data segment name
-      std::string      m_input_segment   { "inputs" };
-      /// Property: event masks to be handled
-      std::vector<int> m_input_masks     { };
-      /// Item keys required by this worker
-      std::vector<Key> m_container_keys  { };
-
-    public:
-      /// Standard constructor
-      DigiContainerProcessor(const DigiKernel& kernel, const std::string& name);
-      /// Main functional callback if specific work is known
-      virtual void execute(DigiContext& context, WorkItems& work)  const;
-      /// Check if the work item is for us
-      bool use_container(Key key)  const;
-    };
-
-    /// Sequencer class to analyse containers from the input segment in parallel
-    /**
-     *
-     *  \author  M.Frank
-     *  \version 1.0
-     *  \ingroup DD4HEP_SIMULATION
-     */
-    class DigiMultiContainerProcessor : public DigiEventAction   {
-    public:
-      using WorkItems = DigiContainerProcessor::WorkItems;
-      struct CallData  {
-	DigiContext& context;
-	WorkItems&   work;
-      };
-      using Worker    = DigiParallelWorker<DigiContainerProcessor,CallData,int>;
-      using Workers   = std::vector<DigiKernel::ParallelCall*>;
-
-    protected:
-      /// Property: Input data segment name
-      std::string       m_input_segment { "inputs" };
-      /// Property: event masks to be handled
-      std::vector<int>  m_input_masks  { };
-
-      std::set<Key>     m_work_items;
-      /// Array of sub-workers
-      Workers           m_workers;
-
-    protected:
-       /// Define standard assignments and constructors
-      DDDIGI_DEFINE_ACTION_CONSTRUCTORS(DigiMultiContainerProcessor);
-      /// Default destructor
-      virtual ~DigiMultiContainerProcessor();
-
-    public:
-      /// Standard constructor
-      DigiMultiContainerProcessor(const DigiKernel& kernel, const std::string& name);
-      /// Adopt new parallel worker
-      void adopt_processor(DigiContainerProcessor* action, const std::vector<std::string>& containers);
-      /// Main functional callback
-      virtual void execute(DigiContext& context)  const;
-    };
-  }    // End namespace digi
-}      // End namespace dd4hep
-#endif // DDDIGI_DIGIMULTICONTAINERPROCESSOR_H
diff --git a/DDDigi/include/DDDigi/DigiParallelWorker.h b/DDDigi/include/DDDigi/DigiParallelWorker.h
index 5319335bedcc22e9d0d21942f43475ca05b170c1..e22cba7800e0ffe179fc0e390f72a980abcd8fb4 100644
--- a/DDDigi/include/DDDigi/DigiParallelWorker.h
+++ b/DDDigi/include/DDDigi/DigiParallelWorker.h
@@ -13,9 +13,6 @@
 #ifndef DDDIGI_DIGIPARALLELWORKER_H
 #define DDDIGI_DIGIPARALLELWORKER_H
 
-/// Framework include files
-#include <DDDigi/DigiKernel.h>
-
 /// Namespace for the AIDA detector description toolkit
 namespace dd4hep {
 
@@ -27,45 +24,64 @@ namespace dd4hep {
      *
      *  \author  M.Frank
      *  \version 1.0
-     *  \ingroup DD4HEP_SIMULATION
+     *  \ingroup DD4HEP_DIGITIZATION
      */
-    template <typename ACTION_TYPE, typename CALLDATA, typename OPTIONS>
-      class DigiParallelWorker : public DigiKernel::ParallelCall   {
+    class ParallelWorker     {
     public:
-      using action_t = ACTION_TYPE;
-      using options_t = OPTIONS;
-      using calldata_t = CALLDATA;
+      /// Default constructor
+      ParallelWorker() = default;
+      ParallelWorker(ParallelWorker&& copy) = default;
+      ParallelWorker(const ParallelWorker& copy) = default;
+      ParallelWorker& operator=(ParallelWorker&& copy) = default;
+      ParallelWorker& operator=(const ParallelWorker& copy) = default;
+      virtual ~ParallelWorker() = default;
+      virtual void execute(void* args) const = 0;
+    };
 
-      action_t* action;
-      options_t options;
+    /// Wrapper class to submit bulk actions
+    /**
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_DIGITIZATION
+     */
+    template <typename ACTION_TYPE, typename CALLDATA, typename OPTIONS=std::size_t>
+      class DigiParallelWorker : public ParallelWorker   {
+    public:
+    using action_t = ACTION_TYPE;
+    using options_t = OPTIONS;
+    using calldata_t = CALLDATA;
+    
+    action_t* action   { nullptr };
+    options_t options  { nullptr };
 
     public:
-      /// Initializing constructor
-      DigiParallelWorker(ACTION_TYPE* p, const OPTIONS& opts);
-      /// Default constructor
-      DigiParallelWorker() = delete;
-      /// Move constructor
-      DigiParallelWorker(DigiParallelWorker&& copy) = default;
-      /// Inhibit copy constructor
-      DigiParallelWorker(const DigiParallelWorker& copy) = default;
-      /// Inhibit move assignment
-      DigiParallelWorker& operator=(DigiParallelWorker&& copy) = delete;
-      /// Inhibit copy assignment
-      DigiParallelWorker& operator=(const DigiParallelWorker& copy) = delete;
-      /// Default destructor
-      virtual ~DigiParallelWorker();
-      /// Access to processor name
-      const char* name()  const {  return action->name().c_str();   }
-      /// Callback on data
-      virtual void execute(void* data) const override;
+    /// Initializing constructor
+    DigiParallelWorker(ACTION_TYPE* p, const OPTIONS& opts);
+    /// Default constructor
+    DigiParallelWorker() = delete;
+    /// Move constructor
+    DigiParallelWorker(DigiParallelWorker&& copy) = default;
+    /// Inhibit copy constructor
+    DigiParallelWorker(const DigiParallelWorker& copy) = default;
+    /// Inhibit move assignment
+    DigiParallelWorker& operator=(DigiParallelWorker&& copy) = delete;
+    /// Inhibit copy assignment
+    DigiParallelWorker& operator=(const DigiParallelWorker& copy) = delete;
+    /// Default destructor
+    virtual ~DigiParallelWorker();
+    /// Access to processor name
+    const char* name()  const {  return action->name().c_str();   }
+    /// Callback on data
+    virtual void execute(void* data) const override;
     };
 
     /// Initializing constructor
     template <typename ACTION_TYPE, typename CALLDATA, typename OPTIONS>
       DigiParallelWorker<ACTION_TYPE, CALLDATA, OPTIONS>::DigiParallelWorker(ACTION_TYPE* proc, const OPTIONS& opts)
-      : ParallelCall(), action(proc), options(opts)
+      : ParallelWorker(), action(proc), options(opts)
     {
-      action->addRef();
+      if ( action ) action->addRef();
     }
 
     /// Default destructor
@@ -76,6 +92,12 @@ namespace dd4hep {
 	action = nullptr;
       }
     }
+
+    typedef ParallelWorker ParallelCall;
   }    // End namespace digi
 }      // End namespace dd4hep
+
+#include <DDDigi/DigiParallelWorkers.h>
+#include <DDDigi/DigiParallelWorkerGroup.h>
+
 #endif // DDDIGI_DIGIPARALLELWORKER_H
diff --git a/DDDigi/include/DDDigi/DigiParallelWorkerGroup.h b/DDDigi/include/DDDigi/DigiParallelWorkerGroup.h
new file mode 100644
index 0000000000000000000000000000000000000000..ebebd2a56049dc907fd5998dede35c73d43d49e9
--- /dev/null
+++ b/DDDigi/include/DDDigi/DigiParallelWorkerGroup.h
@@ -0,0 +1,78 @@
+//==========================================================================
+//  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 DDDIGI_DIGIPARALLELWORKERGROUP_H
+#define DDDIGI_DIGIPARALLELWORKERGROUP_H
+
+/// Framework include files
+
+/// C/C++ include files
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Namespace for the Digitization part of the AIDA detector description toolkit
+  namespace digi {
+
+    /// Forward declarations
+    template <typename T> class DigiParallelWorkers;
+    template <typename T> class DigiParallelWorkerGroup;
+
+    /// Wrapper class to submit bulk actions
+    /**
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_DIGITIZATION
+     */
+    template <typename T> class DigiParallelWorkerGroup  {
+      /// Reference to worker collection with semaphore
+      const DigiParallelWorkers<T>& workers;
+
+    public:
+      /// Initializin constructor: aquire worker's semaphore
+      DigiParallelWorkerGroup(const DigiParallelWorkers<T>& workers);
+      /// Inhibit Move constructor
+      DigiParallelWorkerGroup(DigiParallelWorkerGroup&& copy) = delete;
+      /// Inhibit copy constructor
+      DigiParallelWorkerGroup(const DigiParallelWorkerGroup& copy) = delete;
+      /// Inhibit move assignment
+      DigiParallelWorkerGroup& operator=(DigiParallelWorkerGroup&& copy) = delete;
+      /// Inhibit copy assignment
+      DigiParallelWorkerGroup& operator=(const DigiParallelWorkerGroup& copy) = delete;
+      /// Default destructor. Releasing semaphore
+      ~DigiParallelWorkerGroup();
+      /// Access the worker array. As long as the object persists it shall not be altered
+      operator ParallelCall*const* ();
+    };
+    
+    /// Initializin constructor: aquire worker's semaphore
+    template <typename T> inline 
+      DigiParallelWorkerGroup<T>::DigiParallelWorkerGroup(const DigiParallelWorkers<T>& w)
+      : workers(w) 
+      {
+	workers.semaphore.aquire();
+      }
+
+    /// Default destructor. Releasing semaphore
+    template <typename T> inline DigiParallelWorkerGroup<T>::~DigiParallelWorkerGroup()   {
+      workers.semaphore.release();
+    }
+
+    /// Access the worker array. As long as the object persists the array stays intact
+    template <typename T> inline DigiParallelWorkerGroup<T>::operator ParallelCall*const* ()   {
+      return (ParallelCall**)&this->workers.actors.at(0);
+    }
+
+  }    // End namespace digi
+}      // End namespace dd4hep
+#endif // DDDIGI_DIGIPARALLELWORKERGROUP_H
diff --git a/DDDigi/include/DDDigi/DigiParallelWorkers.h b/DDDigi/include/DDDigi/DigiParallelWorkers.h
new file mode 100644
index 0000000000000000000000000000000000000000..ea2cbd76f204599ea30a5228ba2cb55ba5e1a94b
--- /dev/null
+++ b/DDDigi/include/DDDigi/DigiParallelWorkers.h
@@ -0,0 +1,121 @@
+//==========================================================================
+//  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 DDDIGI_DIGIPARALLELWORKERS_H
+#define DDDIGI_DIGIPARALLELWORKERS_H
+
+/// Framework include files
+#include <DDDigi/DigiSemaphore.h>
+#include <DDDigi/DigiParallelWorkerGroup.h>
+
+/// C/C++ include files
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Namespace for the Digitization part of the AIDA detector description toolkit
+  namespace digi {
+
+    /// Forward declarations
+    class ParallelWorker;
+    template <typename T, typename A, typename O> class DigiParallelWorker;
+    template <typename T> class DigiParallelWorkers;
+    template <typename T> class DigiParallelWorkerGroup;
+
+    /// Wrapper class to submit bulk actions
+    /**
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_DIGITIZATION
+     */
+    template <typename T> class DigiParallelWorkers   {
+    public:
+      typedef T worker_t;
+      typedef DigiParallelWorkers<T> self_t;
+      typedef DigiParallelWorkerGroup<T> group_t;
+
+      friend class DigiParallelWorkerGroup<worker_t>;
+
+    private:
+      mutable std::vector<worker_t*>   actors;
+      mutable DigiSemaphore            semaphore;
+
+    public:
+      /// Default constructor
+      DigiParallelWorkers() = default;
+      /// Inhibit Move constructor
+      DigiParallelWorkers(DigiParallelWorkers&& copy) = delete;
+      /// Inhibit copy constructor
+      DigiParallelWorkers(const DigiParallelWorkers& copy) = delete;
+      /// Inhibit move assignment
+      DigiParallelWorkers& operator=(DigiParallelWorkers&& copy) = delete;
+      /// Inhibit copy assignment
+      DigiParallelWorkers& operator=(const DigiParallelWorkers& copy) = delete;
+      /// Default destructor
+      virtual ~DigiParallelWorkers();
+
+      std::unique_lock<std::mutex> can_modify()  const;
+      /// Return array protected worker group
+      group_t get_group()  const;
+
+      /// NOT thread-safe stuff. Do not use during event processing unless you are sequential
+      const std::vector<worker_t*>& get() const { return actors; }
+      std::size_t size()  const;
+      bool empty()  const;
+      bool insert(worker_t* entry)  const;
+      ParallelCall*const* get_calls()   const;
+    };
+
+    template <typename T> inline
+      DigiParallelWorkers<T>::~DigiParallelWorkers()  {
+      for(auto* w : actors)
+	delete w;
+      actors.clear();
+    }
+
+    template <typename T> inline 
+      std::unique_lock<std::mutex> DigiParallelWorkers<T>::can_modify()  const  {
+      return this->semaphore.wait_null();
+    }
+
+    template <typename T> inline
+      bool DigiParallelWorkers<T>::insert(worker_t* entry)   const {
+      if ( entry )  {
+	actors.emplace_back(entry);
+	return true;
+      }
+      return false;
+    }
+
+    template <typename T> inline
+      std::size_t DigiParallelWorkers<T>::size() const   {
+      return actors.size();
+    }
+
+    template <typename T> inline
+      bool DigiParallelWorkers<T>::empty() const   {
+      return actors.empty();
+    }
+
+    template <typename T> inline
+      ParallelWorker*const* DigiParallelWorkers<T>::get_calls()  const  {
+      return (ParallelWorker**)&this->actors.at(0);
+    }
+
+    template <typename T> inline typename DigiParallelWorkers<T>::group_t
+      DigiParallelWorkers<T>::get_group()  const  {
+      return group_t(*this);
+    }
+  }    // End namespace digi
+}      // End namespace dd4hep
+#endif // DDDIGI_DIGIPARALLELWORKERS_H
diff --git a/DDDigi/include/DDDigi/DigiPlugins.h b/DDDigi/include/DDDigi/DigiPlugins.h
new file mode 100644
index 0000000000000000000000000000000000000000..5e36625ab8a32337ef1255c2c3e362199071e2be
--- /dev/null
+++ b/DDDigi/include/DDDigi/DigiPlugins.h
@@ -0,0 +1,46 @@
+//==========================================================================
+//  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 DDDIGI_DIGIPLUGINS_H
+#define DDDIGI_DIGIPLUGINS_H
+
+/// Framework include files
+#include <DD4hep/Primitives.h>
+
+/// C/C++ include files
+#include <string>
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Namespace for the Digitization part of the AIDA detector description toolkit
+  namespace digi {
+
+    /// Forward declarations
+    class DigiKernel;
+
+    void* create_action(const std::string& factory,
+			const DigiKernel& kernel,
+			const std::string& arg,
+			void* (*cast)(void*));
+
+    /// Handler for factories of type: ConstructionFactory with casted return type
+    template <typename T> T* createAction(const std::string& factory,
+					  const DigiKernel& kernel,
+					  const std::string& arg)
+      {
+	struct __cast{ static void* cast(void* p) { return &dynamic_cast<T&>(*(T*)p); } };
+	return (T*)create_action(factory, kernel, arg, __cast::cast);
+      }
+  }    // End namespace digi
+}      // End namespace dd4hep
+#endif // DDDIGI_DIGIPLUGINS_H
diff --git a/DDDigi/include/DDDigi/DigiSegmentProcessor.h b/DDDigi/include/DDDigi/DigiSegmentProcessor.h
new file mode 100644
index 0000000000000000000000000000000000000000..cde4ceb93553372d9db67fa464c0703f9bd83179
--- /dev/null
+++ b/DDDigi/include/DDDigi/DigiSegmentProcessor.h
@@ -0,0 +1,105 @@
+//==========================================================================
+//  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 DDDIGI_DIGISEGMENTPROCESSOR_H
+#define DDDIGI_DIGISEGMENTPROCESSOR_H
+
+// Framework include files
+#include <DDDigi/DigiData.h>
+#include <DDDigi/DigiContainerProcessor.h>
+#include <DDDigi/DigiSegmentationTool.h>
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Namespace for the Digitization part of the AIDA detector description toolkit
+  namespace digi {
+
+    /// Forward declarations
+    class DigiSegmentProcessor;
+    class DigiSegmentSequence;
+
+    /// Default base class to process parts of a subdetector segmentation data
+    /**
+     *  This is a utility class supporting properties, output and access to
+     *  event and run objects through the context.
+     *
+     *  Default base class to process parts of a subdetector segmentation data.
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_DIGITAZATION
+     */
+    class DigiSegmentProcessor : public DigiContainerProcessor   {
+    public:
+      /// Segmentation split context
+      DigiSegmentContext    segment  { };
+
+    protected:
+      /// Define standard assignments and constructors
+      DDDIGI_DEFINE_ACTION_CONSTRUCTORS(DigiSegmentProcessor);
+
+    public:
+      /// Standard constructor
+      DigiSegmentProcessor(const DigiKernel& kernel, const std::string& name);
+      /// Default destructor
+      virtual ~DigiSegmentProcessor();
+      /// Main functional callback if specific work is known
+      virtual void handle_segment(DigiContext& context, work_t& data)  const;
+    };
+
+    /// Sequencer class to process parts of a subdetector segmentation data
+    /**
+     *  This is a utility class supporting properties, output and access to
+     *  event and run objects through the context.
+     *
+     *  The sequencer calls all registered processors for the contaiers registered.
+     *  The sequencer manages a set of workers all acting on the same data segment
+     *  being a part of the data of a collection
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_DIGITIZATION
+     */
+    class DigiSegmentSequence : public DigiSegmentProcessor  {
+    protected:
+      /// Property to steer parallel processing
+      bool m_parallel { false };
+
+    protected:
+      using self_t = DigiSegmentSequence;
+      using processor_t = DigiSegmentProcessor;
+      using worker_t  = DigiParallelWorker<processor_t,work_t,VolumeID>;
+      using workers_t = DigiParallelWorkers<worker_t>;
+      friend class DigiParallelWorker<processor_t,work_t,VolumeID>;
+
+      /// Array of sub-workers
+      workers_t         m_workers;
+
+    protected:
+      /// Define standard assignments and constructors
+      DDDIGI_DEFINE_ACTION_CONSTRUCTORS(DigiSegmentSequence);
+      /// Default destructor
+      virtual ~DigiSegmentSequence();
+
+    public:
+      /// Standard constructor
+      DigiSegmentSequence(const DigiKernel& kernel, const std::string& name);
+      /// Adopt new parallel worker
+      virtual void adopt_processor(DigiContainerProcessor* action);
+      /// Main functional callback if specific work is known
+      virtual void handle_segment(DigiContext& context, work_t& work)  const;
+    };
+
+  }    // End namespace digi
+}      // End namespace dd4hep
+#endif // DDDIGI_DIGISEGMENTPROCESSOR_H
diff --git a/DDDigi/include/DDDigi/DigiSegmentSplitter.h b/DDDigi/include/DDDigi/DigiSegmentSplitter.h
index 54afcd398fe45e7163c91b59c011294150770243..25c7a9fd971b3dfd68df20487e4fe4519a4744ce 100644
--- a/DDDigi/include/DDDigi/DigiSegmentSplitter.h
+++ b/DDDigi/include/DDDigi/DigiSegmentSplitter.h
@@ -15,7 +15,7 @@
 
 // Framework include files
 #include <DDDigi/DigiEventAction.h>
-#include <DDDigi/DigiSegmentAction.h>
+#include <DDDigi/DigiSegmentProcessor.h>
 #include <DDDigi/DigiSegmentationTool.h>
 #include <DDDigi/DigiParallelWorker.h>
 
@@ -32,28 +32,20 @@ namespace dd4hep {
      *
      *  \author  M.Frank
      *  \version 1.0
-     *  \ingroup DD4HEP_SIMULATION
+     *  \ingroup DD4HEP_DIGITIZATION
      */
-    class DigiSegmentSplitter : public DigiEventAction   {
-    public:
-
-      struct CallData  {
-	DigiContext&    context;
-	DepositMapping& input;
-	DepositMapping* output;
-	const DigiSegmentSplitter* parent;
-      };
-
-      using Worker  = DigiParallelWorker<DigiSegmentAction,CallData,int>;
-      using Workers = std::vector<DigiKernel::ParallelCall*>;
-      using Splits  = std::map<VolumeID, std::pair<DetElement, VolumeID> >;
+    class DigiSegmentSplitter : public DigiContainerSequence   {
+    protected:
+      using self_t      = DigiSegmentSplitter;
+      using processor_t = DigiSegmentProcessor;
+      using work_t      = processor_t::work_t;
+      using worker_t    = DigiParallelWorker<processor_t, work_t, VolumeID>;
+      using workers_t   = DigiParallelWorkers<worker_t>;
+      using split_t     = std::pair<DetElement, VolumeID>;
+      using splits_t    = std::map<VolumeID, split_t>;
+      friend class DigiParallelWorker<processor_t, work_t, VolumeID>;
       
     protected:
-      /// Implementation declaration
-      class internals_t;
-      /// Reference to the implementation
-      std::unique_ptr<internals_t> internals;
-
       /// Property: Split element of the ID descriptor
       std::string          m_processor_type;
       /// Name of the subdetector to be handed
@@ -63,43 +55,34 @@ namespace dd4hep {
       /// Property: Flag if processors should be shared
       bool                 m_share_processor   { true };
 
-      /// Property: Identifier of the input repository
-      std::string          m_input_id;
-      /// Property: Input mask in the repository
-      int                  m_input_mask;
-      /// Property: Identifier of the input repository
-      std::string          m_output_id;
-      /// Property: Input mask in the repository
-      int                  m_output_mask;
-
+      /**  Member variables  */
       /// Segmentation too instance
       DigiSegmentationTool m_split_tool;
       /// Segmentation split context
       DigiSegmentContext   m_split_context;
-
+      /// Data keys from the readout collection names
+      std::vector<Key>     m_keys;
       /// Split elements used to parallelize the processing
-      Splits               m_splits;
-      /// Input data keys: depend on dd4hep::Readout and the input mask(s)
-      std::vector<Key>     m_data_keys;
-
+      splits_t             m_splits;
       /// Array of sub-workers
-      Workers              m_workers;
-
+      workers_t            m_workers;
+      /// Need a lock for possible output merging
       mutable std::mutex   m_output_lock;
 
     protected:
       /// Default destructor
       virtual ~DigiSegmentSplitter();
+
       /// Initialization function
       void initialize();
+      /// Adopt new parallel worker: INHIBITED: will throw exception
+      virtual void adopt_processor(DigiContainerProcessor* action) override final;
 
     public:
       /// Standard constructor
       DigiSegmentSplitter(const DigiKernel& kernel, const std::string& name);
-      /// Handle result from segment callbacks
-      void register_output(DepositMapping& result, DepositVector&& output)  const;
       /// Main functional callback
-      virtual void execute(DigiContext& context)  const;
+      virtual void execute(DigiContext& context, work_t& work)  const  override;
     };
   }    // End namespace digi
 }      // End namespace dd4hep
diff --git a/DDDigi/include/DDDigi/DigiSegmentationTool.h b/DDDigi/include/DDDigi/DigiSegmentationTool.h
index 0e4858ab1b19b32e54b21ae48324815ea6154c68..eb2702b343c7f5a2aba4d2f5aaef1e48e1118e7d 100644
--- a/DDDigi/include/DDDigi/DigiSegmentationTool.h
+++ b/DDDigi/include/DDDigi/DigiSegmentationTool.h
@@ -35,7 +35,7 @@ namespace dd4hep {
      *
      *  \author  M.Frank
      *  \version 1.0
-     *  \ingroup DD4HEP_SIMULATION
+     *  \ingroup DD4HEP_DIGITIZATION
      */
     class DigiSegmentContext  {
     public:
@@ -80,7 +80,7 @@ namespace dd4hep {
      *
      *  \author  M.Frank
      *  \version 1.0
-     *  \ingroup DD4HEP_SIMULATION
+     *  \ingroup DD4HEP_DIGITIZATION
      */
     class DigiSegmentationTool  {
     public:
diff --git a/DDDigi/include/DDDigi/DigiSemaphore.h b/DDDigi/include/DDDigi/DigiSemaphore.h
new file mode 100644
index 0000000000000000000000000000000000000000..21fc04eb7ac3c200cbd6c0dc049a25a4d728c6c0
--- /dev/null
+++ b/DDDigi/include/DDDigi/DigiSemaphore.h
@@ -0,0 +1,75 @@
+//==========================================================================
+//  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 DDDIGI_DIGISEMAPHORE_H
+#define DDDIGI_DIGISEMAPHORE_H
+
+/// Framework include files
+
+/// C/C++ include files
+#include <mutex>
+#include <atomic>
+#include <condition_variable>
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Namespace for the Digitization part of the AIDA detector description toolkit
+  namespace digi {
+
+    /// Forward declarations
+    class DigiSemaphore;
+
+    /// Semaphore implementation, where object get notified when the reference count reaches NULL
+    /** Semaphore implementation
+     *  object get notified when the reference count reaches NULL
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_DIGITIZATION
+     */
+    class DigiSemaphore  final {
+    private:
+      /// Reference count
+      std::atomic<std::size_t> reference_count { 0UL };
+      /// Condition variable to wait until reference count is NULL
+      std::condition_variable  reference_count_is_null;
+      /// Lock to handle condition variable
+      std::mutex lock;
+
+    public:
+      /// Default constructor: the one and only one
+      DigiSemaphore() = default;
+      /// Inhibit Move constructor
+      DigiSemaphore(DigiSemaphore&& copy) = delete;
+      /// Inhibit copy constructor
+      DigiSemaphore(const DigiSemaphore& copy) = delete;
+      /// Inhibit move assignment
+      DigiSemaphore& operator=(DigiSemaphore&& copy) = delete;
+      /// Inhibit copy assignment
+      DigiSemaphore& operator=(const DigiSemaphore& copy) = delete;
+      /// Default destructor. Decreasing reference count.
+      ~DigiSemaphore() = default;
+
+    public:
+      /// Wait until reference count is NULL.
+      /** @return unique_lock  [out] The semaphore is locks as long as the returned lock is held
+       */
+      std::unique_lock<std::mutex> wait_null();
+      /// Aquire semaphore count
+      void aquire();
+      /// Release semaphore count
+      void release();
+    };
+  }    // End namespace digi
+}      // End namespace dd4hep
+#endif // DDDIGI_DIGISEMAPHORE_H
diff --git a/DDDigi/include/DDDigi/DigiStoreDump.h b/DDDigi/include/DDDigi/DigiStoreDump.h
index ea814049ab33de08ec8274f5708ed3f1b2422b68..507b434d383138f25fdeaee14fcf6f145a0333fd 100644
--- a/DDDigi/include/DDDigi/DigiStoreDump.h
+++ b/DDDigi/include/DDDigi/DigiStoreDump.h
@@ -32,7 +32,7 @@ namespace dd4hep {
      *
      *  \author  M.Frank
      *  \version 1.0
-     *  \ingroup DD4HEP_SIMULATION
+     *  \ingroup DD4HEP_DIGITIZATION
      */
     class DigiStoreDump : public DigiEventAction   {
     protected:
diff --git a/DDDigi/include/DDDigi/DigiSynchronize.h b/DDDigi/include/DDDigi/DigiSynchronize.h
index 8b3f4827de73f1538ccee32ac62906fe85077841..3c4c565b8b3176a1f5f95dceb72322df464ccc5d 100644
--- a/DDDigi/include/DDDigi/DigiSynchronize.h
+++ b/DDDigi/include/DDDigi/DigiSynchronize.h
@@ -34,10 +34,9 @@ namespace dd4hep {
      */
     class DigiSynchronize : public DigiEventAction {
     protected:
-      /// The list of action objects to be called
-      //Actors<DigiEventAction> m_actors;
       using Worker    = DigiParallelWorker<DigiEventAction,DigiContext,int>;
-      using Workers   = std::vector<DigiKernel::ParallelCall*>;
+      using Workers   = DigiParallelWorkers<Worker>;
+      /// The list of action objects to be called
       Workers m_actors;
 
     protected:
diff --git a/DDDigi/include/DDDigi/noise/DigiSignalProcessorSequence.h b/DDDigi/include/DDDigi/noise/DigiSignalProcessorSequence.h
index 3a137477ee8b1725968f8d010647ee6575f6c289..4dd7f980cc703967db07d46db7a6916f532a98d8 100644
--- a/DDDigi/include/DDDigi/noise/DigiSignalProcessorSequence.h
+++ b/DDDigi/include/DDDigi/noise/DigiSignalProcessorSequence.h
@@ -14,6 +14,7 @@
 #define DDDIGI_NOISE_DIGISIGNALPROCESSORSEQUENCE_H
 
 // Framework include files
+#include <DDDigi/DigiParallelWorker.h>
 #include <DDDigi/DigiSignalProcessor.h>
 
 /// Namespace for the AIDA detector description toolkit
@@ -41,9 +42,16 @@ namespace dd4hep {
      *  \ingroup DD4HEP_DIGITIZATION
      */
     class DigiSignalProcessorSequence : public DigiSignalProcessor {
+    public:
+      struct CallData {
+	DigiCellContext& context;
+	double value;
+      };
     protected:
+      using Worker    = DigiParallelWorker<DigiSignalProcessor,CallData,int>;
+      using Workers   = DigiParallelWorkers<Worker>;
       /// The list of action objects to be called
-      Actors<DigiSignalProcessor> m_actors;
+      Workers m_actors;
 
     protected:
       /// Define standard assignments and constructors
diff --git a/DDDigi/include/DDDigi/noise/DigiSubdetectorSequence.h b/DDDigi/include/DDDigi/noise/DigiSubdetectorSequence.h
index c2a68bdafeb14b60fe90fba24f2364aa104049ec..4a11413ee3f86c32fff239dc8be1b476acba5728 100644
--- a/DDDigi/include/DDDigi/noise/DigiSubdetectorSequence.h
+++ b/DDDigi/include/DDDigi/noise/DigiSubdetectorSequence.h
@@ -103,7 +103,7 @@ namespace dd4hep {
       /// Default destructor
       virtual ~DigiSubdetectorSequence();
       /// Iniitalize subdetector sequencer
-      virtual void initialize()  override;
+      virtual void initialize();
       /// Begin-of-event callback
       virtual void execute(DigiContext& context)  const override;
     };
diff --git a/DDDigi/plugins/Components.cpp b/DDDigi/plugins/Components.cpp
index f982f9cfaf461be4c42fe8a87412df3e1d34d2f2..30a0234786375778fc8a63b2cdbd12e7c2b47ce2 100644
--- a/DDDigi/plugins/Components.cpp
+++ b/DDDigi/plugins/Components.cpp
@@ -22,44 +22,49 @@
 #include <sstream>
 
 #include <DDDigi/DigiInputAction.h>
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiInputAction)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiInputAction)
 
 #include <DDDigi/DigiROOTInput.h>
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiROOTInput)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiROOTInput)
 
 #include <DDDigi/DigiSynchronize.h>
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiSynchronize)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiSynchronize)
 
 #include <DDDigi/DigiActionSequence.h>
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiActionSequence)
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiParallelActionSequence)
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiSequentialActionSequence)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiActionSequence)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiParallelActionSequence)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiSequentialActionSequence)
 
 //#include <DDDigi/DigiSubdetectorSequence.h>
 // DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiSubdetectorSequence)
 
 #include <DDDigi/DigiLockedAction.h>
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiLockedAction)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiLockedAction)
 
 //#include <DDDigi/DigiSignalProcessorSequence.h>
 // DECLARE_DIGISIGNALPROCESSOR_NS(dd4hep::digi,DigiSignalProcessorSequence)
 
 #include <DDDigi/DigiStoreDump.h>
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiStoreDump)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiStoreDump)
 
 #include <DDDigi/DigiHitAttenuatorExp.h>
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiHitAttenuatorExp)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiHitAttenuatorExp)
 
 #include <DDDigi/DigiContainerCombine.h>
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiContainerCombine)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiContainerCombine)
+
+#include <DDDigi/DigiSegmentProcessor.h>
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiSegmentProcessor)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiSegmentSequence)
 
 #include <DDDigi/DigiSegmentSplitter.h>
-DECLARE_DIGISEGMENTACTION_NS(dd4hep::digi,DigiSegmentAction)
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiSegmentSplitter)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiSegmentSplitter)
 
-#include <DDDigi/DigiMultiContainerProcessor.h>
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiMultiContainerProcessor)
+#include <DDDigi/DigiContainerProcessor.h>
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiMultiContainerProcessor)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiContainerSequence)
 DECLARE_DIGIACTION_NS(dd4hep::digi,DigiContainerProcessor)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiContainerSequenceAction)
 
 /// Basic entry point 
 static long dummy(dd4hep::Detector&, int, char**) {
diff --git a/DDDigi/plugins/DigiHitHistoryDrop.cpp b/DDDigi/plugins/DigiHitHistoryDrop.cpp
index c3dd1b30675d99ccec490d465d0d847a032d5c1c..b4da3d172a1ce476cd96a860d4494e0fcdfcf923 100644
--- a/DDDigi/plugins/DigiHitHistoryDrop.cpp
+++ b/DDDigi/plugins/DigiHitHistoryDrop.cpp
@@ -31,7 +31,7 @@ namespace dd4hep {
      *
      *  \author  M.Frank
      *  \version 1.0
-     *  \ingroup DD4HEP_SIMULATION
+     *  \ingroup DD4HEP_DIGITIZATION
      */
     class DigiHitHistoryDrop : public DigiEventAction   {
     protected:
@@ -62,27 +62,36 @@ namespace dd4hep {
       /// Main functional callback
       virtual void execute(DigiContext& context)  const  final  {
 	auto& inputs = context.event->get_segment(m_input);
-	std::size_t num_drop = 0;
+	std::size_t num_drop_hit = 0;
+	std::size_t num_drop_particle = 0;
 	for ( auto& i : inputs )     {
 	  Key key(i.first);
-	  auto im = std::find(m_masks.begin(), m_masks.end(), key.values.mask);
+	  auto im = std::find(m_masks.begin(), m_masks.end(), key.mask());
 	  if ( im != m_masks.end() )   {
-	    std::any& obj = i.second;
-	    DepositMapping* m = std::any_cast<DepositMapping>(&obj);
-	    if ( m )    {
+	    if ( DepositMapping* m = std::any_cast<DepositMapping>(&i.second) )    {
 	      for( auto& c : *m )    {
-		num_drop += c.second.history.size();
-		c.second.history.clear();
+		num_drop_hit += c.second.hit_history.size();
+		c.second.hit_history.clear();
+		num_drop_particle += c.second.particle_history.size();
+		c.second.particle_history.clear();
+	      }
+	    }
+	    if ( DepositVector* m = std::any_cast<DepositVector>(&i.second) )    {
+	      for( auto& c : *m )    {
+		num_drop_hit += c.second.hit_history.size();
+		c.second.hit_history.clear();
+		num_drop_particle += c.second.particle_history.size();
+		c.second.particle_history.clear();
 	      }
 	    }
 	  }
 	}
-	info("%s+++ Dropped history of %6ld hits", context.event->id(), num_drop);
+	info("%s+++ Dropped history of %6ld hits %6ld particles", 
+	     context.event->id(), num_drop_hit, num_drop_particle);
       }
     };
   }    // End namespace digi
 }      // End namespace dd4hep
 
-
 #include <DDDigi/DigiFactories.h>
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiHitHistoryDrop)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiHitHistoryDrop)
diff --git a/DDDigi/plugins/DigiIPRandomizer.cpp b/DDDigi/plugins/DigiIPRandomizer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..39ec5180874262474c9e1adfa71ad81dd40ddff5
--- /dev/null
+++ b/DDDigi/plugins/DigiIPRandomizer.cpp
@@ -0,0 +1,131 @@
+//==========================================================================
+//  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
+//
+//==========================================================================
+
+// Framework include files
+#include <DD4hep/InstanceCount.h>
+#include <DDDigi/DigiData.h>
+#include <DDDigi/DigiContext.h>
+#include <DDDigi/DigiEventAction.h>
+#include <DDDigi/DigiParallelWorker.h>
+
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Namespace for the Digitization part of the AIDA detector description toolkit
+  namespace digi {
+
+    /// Default base class for all Digitizer actions and derivates thereof.
+    /**
+     *  This is a utility class supporting properties, output and access to
+     *  event and run objects through the context.
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_DIGITIZATION
+     */
+    class DigiIPRandomizer : public DigiEventAction   {
+    protected:
+      /// Property: Masks to act on
+      int              m_mask   { 0 };
+      /// Property: Input data segment name
+      std::string      m_input  { };
+
+      struct worker_args  {
+	std::size_t num_hit  { 0 };
+	std::size_t num_particle  { 0 };
+	std::any*   container { nullptr };
+	worker_args(std::any* p) : container(p) {}
+      };
+      struct work_t {
+	DigiContext& context;
+	Position     ip_move;
+	std::vector<worker_args> args;
+      };
+      class work_definition_t;
+      using self_t  = DigiIPRandomizer;
+      using Worker  = DigiParallelWorker<self_t,work_t>;
+      using Workers = DigiParallelWorkers<Worker>;
+
+      /// Worker objects to be submitted to the kernel
+      Workers m_workers;
+
+    protected:
+      /// Define standard assignments and constructors
+      DDDIGI_DEFINE_ACTION_CONSTRUCTORS(DigiIPRandomizer);
+
+      /// Default destructor
+      virtual ~DigiIPRandomizer()  {
+	InstanceCount::decrement(this);
+      }
+
+      Position ip_displacement()  const  {
+	return Position(0,0,0);
+      }
+
+    public:
+      /// Standard constructor
+      DigiIPRandomizer(const DigiKernel& krnl, const std::string& nam)
+	: DigiEventAction(krnl, nam)
+      {
+	declareProperty("mask", m_mask);
+	declareProperty("input_segment", m_input = "inputs");
+	InstanceCount::increment(this);
+      }
+      /// Main functional callback
+      virtual void execute(DigiContext& context)  const  final  {
+	auto& inputs = context.event->get_segment(m_input);
+	std::vector<worker_args> collection_args;
+	collection_args.reserve(inputs.size());
+	for ( auto& i : inputs )     {
+	  Key key(i.first);
+	  if ( key.mask() == m_mask )   {
+	    collection_args.emplace_back(worker_args(&i.second));
+	  }
+	}
+	work_t data  { context, ip_displacement(), collection_args };
+	for( auto& c : collection_args )   {
+	  if ( DepositMapping* m = std::any_cast<DepositMapping>(c.container) )    {
+	    for( auto& dep : *m )
+	      dep.second.position += data.ip_move;
+	    c.num_hit += m->size();
+	  }
+	  else if ( DepositVector* v = std::any_cast<DepositVector>(c.container) )    {
+	    for( auto& dep : *v )
+	      dep.second.position += data.ip_move;
+	    c.num_hit += v->size();
+	  }
+	  else if ( ParticleMapping* parts = std::any_cast<ParticleMapping>(c.container) )    {
+	    for( auto& p : *parts )   {
+	      auto& part = p.second;
+	      part.end_position   += data.ip_move;
+	      part.start_position += data.ip_move;
+	    }
+	    c.num_particle += parts->size();
+	  }
+	}
+	std::size_t num_hit = 0;
+	std::size_t num_particle = 0;
+	for( const auto& c : collection_args )   {
+	  num_hit += c.num_hit;
+	  num_particle += c.num_particle;
+	}
+	info("%s+++ Moved coordinates of %6ld hits ans %6ld particles", 
+	     context.event->id(), num_hit, num_particle);
+      }
+    };
+  }    // End namespace digi
+}      // End namespace dd4hep
+
+#include <DDDigi/DigiFactories.h>
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiIPRandomizer)
diff --git a/DDDigi/plugins/DigiRandomNoise.cpp b/DDDigi/plugins/DigiRandomNoise.cpp
index 7055249a9d57de28ecb8c3f1f4f2edc076e40747..72b435c80ac93d1010cbb332bb2d242c1c324ea7 100644
--- a/DDDigi/plugins/DigiRandomNoise.cpp
+++ b/DDDigi/plugins/DigiRandomNoise.cpp
@@ -85,7 +85,7 @@ namespace dd4hep {
 using namespace std;
 using namespace dd4hep::digi;
 
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiRandomNoise)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiRandomNoise)
 
 /// Standard constructor
 DigiRandomNoise::DigiRandomNoise(const DigiKernel& kernel, const string& nam)
diff --git a/DDDigi/plugins/DigiSegmentDepositExtractor.cpp b/DDDigi/plugins/DigiSegmentDepositExtractor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2f0999ddfeb1531b5e6cfb7e9afdc5276d02662d
--- /dev/null
+++ b/DDDigi/plugins/DigiSegmentDepositExtractor.cpp
@@ -0,0 +1,55 @@
+//==========================================================================
+//  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
+//
+//==========================================================================
+
+// Framework include files
+#include <DDDigi/DigiContext.h>
+#include <DDDigi/DigiSegmentProcessor.h>
+
+/// Namespace for the AIDA detector description toolkit
+namespace dd4hep {
+
+  /// Namespace for the Digitization part of the AIDA detector description toolkit
+  namespace digi {
+
+    /// Actor to select energy deposits according to the supplied segmentation
+    /** Actor to select energy deposits according to the supplied segmentation
+     *
+     *  The selected deposits are placed in the output container
+     *  supplied by the arguments.
+     *
+     *  \author  M.Frank
+     *  \version 1.0
+     *  \ingroup DD4HEP_DIGITIZATION
+     */
+    class DigiSegmentDepositExtractor : public DigiSegmentProcessor   {
+    public:
+      /// Standard constructor
+      DigiSegmentDepositExtractor(const DigiKernel& kernel, const std::string& nam)
+	: DigiSegmentProcessor(kernel, nam) {}
+
+      /// Main functional callback
+      virtual void handle_segment(DigiContext&, work_t& work)  const override final  {
+	for( const auto& d : work.input )   {
+	  if ( segment.matches(d.first) )   {
+	    CellID        cell = d.first;
+	    EnergyDeposit depo = d.second;
+	    work.emplace_output(cell, std::move(depo));
+	  }
+	}
+      }
+    };
+  }    // End namespace digi
+}      // End namespace dd4hep
+
+#include <DDDigi/DigiFactories.h>
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiSegmentDepositExtractor)
diff --git a/DDDigi/plugins/DigiSegmentDepositPrint.cpp b/DDDigi/plugins/DigiSegmentDepositPrint.cpp
index 9c4d7313bee6b070bab121e1e04209e9a0b7aa8d..6a5bd23cfc07b5ae6cc139031e55701cddd623ba 100644
--- a/DDDigi/plugins/DigiSegmentDepositPrint.cpp
+++ b/DDDigi/plugins/DigiSegmentDepositPrint.cpp
@@ -13,7 +13,7 @@
 
 // Framework include files
 #include <DDDigi/DigiContext.h>
-#include <DDDigi/DigiSegmentSplitter.h>
+#include <DDDigi/DigiSegmentProcessor.h>
 
 /// Namespace for the AIDA detector description toolkit
 namespace dd4hep {
@@ -28,32 +28,43 @@ namespace dd4hep {
      *  \version 1.0
      *  \ingroup DD4HEP_SIMULATION
      */
-    class DigiSegmentDepositPrint : virtual public DigiSegmentAction   {
+    class DigiSegmentDepositPrint : public DigiSegmentProcessor   {
     public:
       /// Standard constructor
       DigiSegmentDepositPrint(const DigiKernel& kernel, const std::string& nam)
-	: DigiAction(kernel, nam), DigiSegmentAction(kernel, nam) {}
+	: DigiSegmentProcessor(kernel, nam) {}
 
+      void print_deposit(const char* format, CellID cell, const EnergyDeposit& depo)  const   {
+	info(format, segment.split_id(cell), cell,
+	     depo.hit_history.size(), 
+	     depo.particle_history.size(),
+	     depo.deposit);
+      }
       /// Main functional callback
-      virtual DepositVector 
-      handleSegment(DigiContext&              context,
-		    const DepositMapping&     deposits)  const override final   {
+      virtual void handle_segment(DigiContext& context, work_t& work)  const override final  {
 	char format[256];
 	::snprintf(format, sizeof(format), 
-		   "%s[%s] %s-id: %%d [processor:%d] Cell: %%016lX mask: %016lX  hist:%%4ld entries deposit: %%f", 
+		   "%s[%s] %s-id: %%d [processor:%d] Cell: %%016lX mask: %016lX  hist:%%4ld hits %%4ld parts. entries deposit: %%f", 
 		   context.event->id(), segment.idspec.name(), segment.cname(), segment.id, segment.split_mask);
-	for( const auto& d : deposits )   {
-	  if ( segment.matches(d.first) )   {
-	    auto  cell = d.first;
-	    auto& depo = d.second;
-	    info(format, segment.split_id(cell), cell, depo.history.size(), depo.deposit);
+	if ( const auto* m = std::any_cast<DepositMapping>(&work.input) )   {
+	  for( const auto& d : *m )   {
+	    if ( segment.matches(d.first) )
+	      print_deposit(format, d.first, d.second);
+	  }
+	}
+	else if ( const auto* v = std::any_cast<DepositVector>(&work.input) )   {
+	  for( const auto& d : *v )   {
+	    if ( segment.matches(d.first) )
+	      print_deposit(format, d.first, d.second);
 	  }
 	}
-	return {};
+	else   {
+	  error("+++ Request to dump an invalid container %s", Key::key_name(work.key.item()));
+	}
       }
     };
   }    // End namespace digi
 }      // End namespace dd4hep
 
 #include <DDDigi/DigiFactories.h>
-DECLARE_DIGISEGMENTACTION_NS(dd4hep::digi,DigiSegmentDepositPrint)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiSegmentDepositPrint)
diff --git a/DDDigi/python/DDDigiDict.C b/DDDigi/python/DDDigiDict.C
index 4eb4e9a4c2b79aec77d5f138c1a809584f7458e2..83f196af506c48b90180ea77d618630db5a018f1 100644
--- a/DDDigi/python/DDDigiDict.C
+++ b/DDDigi/python/DDDigiDict.C
@@ -36,13 +36,8 @@
 #include <DDDigi/DigiKernel.h>
 #include <DDDigi/DigiContext.h>
 #include <DDDigi/DigiAction.h>
-#include <DDDigi/DigiSynchronize.h>
 #include <DDDigi/DigiEventAction.h>
-#include <DDDigi/DigiInputAction.h>
-#include <DDDigi/DigiSegmentAction.h>
-#include <DDDigi/DigiActionSequence.h>
-#include <DDDigi/DigiMultiContainerProcessor.h>
-#include <DDDigi/DigiSignalProcessor.h>
+#include <DDDigi/DigiContainerProcessor.h>
 
 struct DDDigiDict  {};
 
@@ -62,20 +57,14 @@ namespace dd4hep {
       operator dd4hep::digi::Digi##x* () const         { return action;                 } \
       Digi##x* operator->() const                      { return action;                 } \
       Digi##x* get() const                             { return action;                 } \
+      Digi##x* I_am_a_ROOT_interface_handle() const    { return action;                 } \
       KernelHandle kernel()  const                     {		\
 	auto* k = const_cast<DigiKernel*>(action->kernel());		\
 	return KernelHandle(k);						\
       }									\
     }
 
-    ACTIONHANDLE(SignalProcessor);
     ACTIONHANDLE(Action);
-    ACTIONHANDLE(EventAction);
-    ACTIONHANDLE(InputAction);
-    ACTIONHANDLE(SegmentAction);
-    ACTIONHANDLE(ContainerProcessor);
-    ACTIONHANDLE(ActionSequence);
-    ACTIONHANDLE(Synchronize);
 
     struct PropertyResult  {
       std::string data;
@@ -93,12 +82,65 @@ namespace dd4hep {
         H handle(action.get());
         return handle;
       }
+      template <typename T,typename H> static H* cst(T* in)  {
+	auto* out = dynamic_cast<H*>(in);
+	if ( out ) return out;
+	if ( in )
+	  except("DigiAction","Invalid cast of action '%s' [type:%s] to type %s!",
+		 in->c_name(), typeName(typeid(T)).c_str(), typeName(typeid(H)).c_str());
+	except("DigiAction","Invalid cast of NULL [type:%s] to type %s!",
+	       typeName(typeid(T)).c_str(), typeName(typeid(H)).c_str());
+	return nullptr;
+      }
+
+      static PropertyResult getProperty(DigiAction* action, const std::string& name)  {
+        if ( action->hasProperty(name) )  {
+          return PropertyResult(action->property(name).str(),1);
+        }
+        return PropertyResult("",0);
+      }
+      static int setProperty(DigiAction* action, const std::string& name, const std::string& value)  {
+        if ( action->hasProperty(name) )  {
+          action->property(name).str(value);
+          return 1;
+        }
+        return 0;
+      }
+      static PropertyResult getPropertyKernel(DigiKernel* kernel, const std::string& name)  {
+        if ( kernel->hasProperty(name) )  {
+          return PropertyResult(kernel->property(name).str(),1);
+        }
+        return PropertyResult("",0);
+      }
+      static int setPropertyKernel(DigiKernel* kernel, const std::string& name, const std::string& value)  {
+        if ( kernel->hasProperty(name) )  {
+          kernel->property(name).str(value);
+          return 1;
+        }
+        return 0;
+      }
+
       static KernelHandle createKernel(DigiAction* action)   {
 	auto* k = const_cast<DigiKernel*>(action->kernel());
 	return KernelHandle(k);
       }
       static ActionHandle createAction(KernelHandle& kernel, const std::string& name_type)   
       { return cr<ActionHandle,DigiHandle<DigiAction> >(kernel,name_type);                           }
+      static DigiAction* toAction(DigiAction* f)                   { return f;                       }
+      static DigiAction* toAction(ActionHandle f)                  { return f.action;                }
+
+      static DigiEventAction* toEventAction(DigiAction* a)        { return cst<DigiAction,DigiEventAction>(a); }
+      static DigiContainerProcessor* toContainerProcessor(DigiAction* a) { return cst<DigiAction,DigiContainerProcessor>(a); }
+
+#if 0
+      static DigiEventAction* toEventAction(DigiEventAction* a)   { return a; }
+      ACTIONHANDLE(SignalProcessor);
+      ACTIONHANDLE(EventAction);
+      ACTIONHANDLE(InputAction);
+      ACTIONHANDLE(SegmentProcessor);
+      ACTIONHANDLE(ContainerProcessor);
+      ACTIONHANDLE(ActionSequence);
+      ACTIONHANDLE(Synchronize);
 
       static EventActionHandle createEventAction(KernelHandle& kernel, const std::string& name_type)   
       { return cr<EventActionHandle,DigiHandle<DigiEventAction> >(kernel,name_type);                 }
@@ -106,8 +148,8 @@ namespace dd4hep {
       static InputActionHandle createInputAction(KernelHandle& kernel, const std::string& name_type)   
       { return cr<InputActionHandle,DigiHandle<DigiInputAction> >(kernel,name_type);                 }
 
-      static SegmentActionHandle createSegmentAction(KernelHandle& kernel, const std::string& name_type)   
-      { return cr<SegmentActionHandle,DigiHandle<DigiSegmentAction> >(kernel,name_type);              }
+      static SegmentProcessorHandle createSegmentProcessor(KernelHandle& kernel, const std::string& name_type)   
+      { return cr<SegmentProcessorHandle,DigiHandle<DigiSegmentProcessor> >(kernel,name_type);              }
 
       static ActionSequenceHandle createSequence(KernelHandle& kernel, const std::string& name_type)   
       { return cr<ActionSequenceHandle,DigiHandle<DigiActionSequence> >(kernel,name_type);           }
@@ -115,54 +157,31 @@ namespace dd4hep {
       static SynchronizeHandle createSync(KernelHandle& kernel, const std::string& name_type)
       { return cr<SynchronizeHandle,DigiHandle<DigiSynchronize> >(kernel,name_type);                 }
 
-      static DigiAction* toAction(DigiAction* f)                   { return f;                       }
-#if 0
       static DigiAction* toAction(DigiEventAction* f)              { return f;                       }
       static DigiAction* toAction(DigiInputAction* f)              { return f;                       }
-      static DigiAction* toAction(DigiSegmentAction* f)            { return f;                       }
+      static DigiAction* toAction(DigiSegmentProcessor* f)            { return f;                       }
       static DigiAction* toAction(DigiContainerProcessor* f)       { return f;                       }
       static DigiAction* toAction(DigiActionSequence* f)           { return f;                       }
       static DigiAction* toAction(DigiSynchronize* f)              { return f;                       }
       static DigiAction* toAction(DigiSignalProcessor* f)          { return f;                       }
-#endif
-      static DigiAction* toAction(ActionHandle f)                  { return f.action;                }
+
       static DigiAction* toAction(EventActionHandle f)             { return f.action;                }
       static DigiAction* toAction(InputActionHandle f)             { return f.action;                }
-      static DigiAction* toAction(SegmentActionHandle f)           { return f.action;                }
+      static DigiAction* toAction(SegmentProcessorHandle f)           { return f.action;                }
       static DigiAction* toAction(ActionSequenceHandle f)          { return f.action;                }
       static DigiAction* toAction(SynchronizeHandle f)             { return f.action;                }
       static DigiAction* toAction(SignalProcessorHandle f)         { return f.action;                }
-
-      static PropertyResult getProperty(DigiAction* action, const std::string& name)  {
-        if ( action->hasProperty(name) )  {
-          return PropertyResult(action->property(name).str(),1);
-        }
-        return PropertyResult("",0);
-      }
-      static int setProperty(DigiAction* action, const std::string& name, const std::string& value)  {
-        if ( action->hasProperty(name) )  {
-          action->property(name).str(value);
-          return 1;
-        }
-        return 0;
-      }
-      static PropertyResult getPropertyKernel(DigiKernel* kernel, const std::string& name)  {
-        if ( kernel->hasProperty(name) )  {
-          return PropertyResult(kernel->property(name).str(),1);
-        }
-        return PropertyResult("",0);
-      }
-      static int setPropertyKernel(DigiKernel* kernel, const std::string& name, const std::string& value)  {
-        if ( kernel->hasProperty(name) )  {
-          kernel->property(name).str(value);
-          return 1;
-        }
-        return 0;
-      }
+#endif
     };
   }
 }
 
+#include <DDDigi/DigiSynchronize.h>
+#include <DDDigi/DigiInputAction.h>
+#include <DDDigi/DigiSegmentProcessor.h>
+#include <DDDigi/DigiActionSequence.h>
+#include <DDDigi/DigiSignalProcessor.h>
+
 // CINT configuration
 #if defined(__CINT__) || defined(__MAKECINT__) || defined(__CLING__) || defined(__ROOTCLING__)
 
@@ -175,39 +194,37 @@ using namespace std;
 #pragma link C++ namespace dd4hep;
 #pragma link C++ namespace dd4hep::digi;
 
+#pragma link C++ class dd4hep::digi::KernelHandle;
+#pragma link C++ class dd4hep::digi::ActionHandle;
+
 #pragma link C++ class dd4hep::digi::DigiActionCreation;
 #pragma link C++ class dd4hep::digi::DigiContext;
-
 #pragma link C++ class dd4hep::digi::DigiKernel;
-#pragma link C++ class dd4hep::digi::KernelHandle;
-
 #pragma link C++ class dd4hep::digi::DigiAction;
-#pragma link C++ class dd4hep::digi::ActionHandle;
-
 #pragma link C++ class dd4hep::digi::DigiEventAction;
-#pragma link C++ class dd4hep::digi::EventActionHandle;
-
 #pragma link C++ class dd4hep::digi::DigiInputAction;
-#pragma link C++ class dd4hep::digi::InputActionHandle;
-
-#pragma link C++ class dd4hep::digi::DigiSegmentAction;
-#pragma link C++ class dd4hep::digi::SegmentActionHandle;
-
+#pragma link C++ class dd4hep::digi::DigiSegmentProcessor;
 #pragma link C++ class dd4hep::digi::DigiActionSequence;
-#pragma link C++ class dd4hep::digi::ActionSequenceHandle;
-
 #pragma link C++ class dd4hep::digi::DigiSynchronize;
-#pragma link C++ class dd4hep::digi::SynchronizeHandle;
-
 #pragma link C++ class dd4hep::digi::DigiSignalProcessor;
-#pragma link C++ class dd4hep::digi::SignalProcessorHandle;
-
 #pragma link C++ class dd4hep::digi::DigiContainerProcessor;
+#pragma link C++ class dd4hep::digi::DigiContainerSequence;
 #pragma link C++ class dd4hep::digi::DigiMultiContainerProcessor;
 
 /// Digi data item wrappers
-#pragma link C++ class dd4hep::digi::DigiEvent;
+#pragma link C++ class dd4hep::digi::Particle+;
 #pragma link C++ class dd4hep::digi::EnergyDeposit+;
+#pragma link C++ class dd4hep::digi::ParticleMapping+;
 #pragma link C++ class dd4hep::digi::DepositMapping+;
+#pragma link C++ class dd4hep::digi::DepositVector+;
+
+#pragma link C++ class dd4hep::digi::DigiEvent;
+
+//#pragma link C++ class dd4hep::digi::EventActionHandle;
+//#pragma link C++ class dd4hep::digi::InputActionHandle;
+//#pragma link C++ class dd4hep::digi::SegmentProcessorHandle;
+//#pragma link C++ class dd4hep::digi::ActionSequenceHandle;
+//#pragma link C++ class dd4hep::digi::SignalProcessorHandle;
+//#pragma link C++ class dd4hep::digi::SynchronizeHandle;
 
 #endif
diff --git a/DDDigi/python/dddigi.py b/DDDigi/python/dddigi.py
index cd22c7a73741d3e17865d8b75d9e37ade555808c..1e45a0dbf47b2a7c358d4d61c1c13ed29b289759 100644
--- a/DDDigi/python/dddigi.py
+++ b/DDDigi/python/dddigi.py
@@ -186,36 +186,45 @@ def Action(kernel, nam):
 # ---------------------------------------------------------------------------
 
 
-def EventAction(kernel, nam, parallel=False):
-  obj = Interface.createEventAction(kernel, str(nam))
-  obj.parallel = parallel
-  return obj
+def _default_adopt(self, action):
+  getattr(self, '__adopt')(action.get())
 # ---------------------------------------------------------------------------
 
 
-def SegmentAction(kernel, nam, parallel=False):
-  obj = Interface.createSegmentAction(kernel, str(nam))
-  obj.parallel = parallel
-  return obj
+def _adopt_event_action(self, action):
+  " Helper to convert DigiActions objects to DigiEventAction "
+  getattr(self, '__adopt')(Interface.toEventAction(action.get()))
 # ---------------------------------------------------------------------------
 
 
-def ActionSequence(kernel, nam, parallel=False):
-  obj = Interface.createSequence(kernel, str(nam))
-  obj.parallel = parallel
-  return obj
+def _adopt_processor_action(self, action, container):
+  " Helper to convert DigiActions objects to DigiEventAction "
+  print(str(action.__class__))
+  attr = getattr(self, 'adopt_processor')
+  if hasattr(action,'I_am_a_ROOT_interface_handle'):
+    print('Entering handle branch')
+    proc = Interface.toContainerProcessor(action.get())
+    attr(proc, container)
+  else:
+    attr(action, container)
+  print('ContainerProcessor succesfully adopted')
 # ---------------------------------------------------------------------------
 
 
-def Synchronize(kernel, nam, parallel=False):
-  obj = Interface.createSync(kernel, str(nam))
-  obj.parallel = parallel
-  return obj
+def _adopt_sequence_action(self, name, **options):
+  " Helper to adopt DigiAction objects for DigiSynchronize "
+  kernel = Interface.createKernel(Interface.toAction(self))
+  action = Action(kernel, name)
+  for option in options.items():
+    setattr(action, option[0], option[1])
+  self.adopt(action)
+  return action
 # ---------------------------------------------------------------------------
 
 
-def _default_adopt(self, action):
-  getattr(self, '__adopt')(action.get())
+def _adopt_processor(self, action, containers):
+  getattr(self, '__adopt_processor')(action.get(), containers)
+# ---------------------------------------------------------------------------
 
 
 def _setup(obj, call='adopt', py_call=_default_adopt):
@@ -224,6 +233,7 @@ def _setup(obj, call='adopt', py_call=_default_adopt):
   setattr(cls, '__' + call, getattr(cls, call))
   setattr(cls, call, py_call)
   return cls
+# ---------------------------------------------------------------------------
 
 
 def _get(self, name):
@@ -233,10 +243,11 @@ def _get(self, name):
     return ret.data
   elif hasattr(self.action, name):
     return getattr(self.action, name)
-  elif hasattr(a, name):
+  elif a.__class__ != self.__class__ and hasattr(a, name):
     return getattr(a, name)
   msg = 'DDDigiAction::GetProperty [Unhandled]: Cannot access property ' + a.name() + '.' + name
   raise KeyError(msg)
+# ---------------------------------------------------------------------------
 
 
 def _set(self, name, value):
@@ -249,6 +260,7 @@ def _set(self, name, value):
     return
   msg = 'DDDigiAction::SetProperty [Unhandled]: Cannot set ' + a.name() + '.' + name + ' = ' + value
   raise KeyError(msg)
+# ---------------------------------------------------------------------------
 
 
 def _props(obj, **extensions):
@@ -259,42 +271,33 @@ def _props(obj, **extensions):
   cls.__getattr__ = _get
   cls.__setattr__ = _set
   return cls
+# ---------------------------------------------------------------------------
+
+
+def _props2(obj, **extensions):
+  cls = getattr(current, obj)
+  for extension in extensions.items():
+    setattr(cls, extension[0], extension[1])
+  cls.__getattr__ = _get
+  cls.__setattr__ = _set
+  return cls
+# ---------------------------------------------------------------------------
 
 
-_setup('DigiActionSequence')
-_setup('DigiSynchronize')
+_setup('DigiSynchronize', py_call=_adopt_event_action)
+_setup('DigiActionSequence', py_call=_adopt_event_action)
 
 _import_class('digi', 'DigiKernel')
 _import_class('digi', 'DigiContext')
 _import_class('digi', 'DigiAction')
 _import_class('digi', 'DigiEventAction')
 _import_class('digi', 'DigiInputAction')
-
 _props('ActionHandle')
-_props('EventActionHandle')
-_props('InputActionHandle')
-_props('ActionSequenceHandle')
-_props('SynchronizeHandle')
-
-
-def adopt_sequence_action(self, name, **options):
-  kernel = Interface.createKernel(Interface.toAction(self))
-  action = EventAction(kernel, name)
-  for option in options.items():
-    setattr(action, option[0], option[1])
-  self.adopt(action)
-  return action
-
-
-def _adopt_processor(self, action, containers):
-  getattr(self, '__adopt_processor')(action.get(), containers)
-
-
-_props('DigiSynchronize')
-_props('DigiActionSequence', adopt_action=adopt_sequence_action)
-_props('DigiParallelActionSequence', adopt_action=adopt_sequence_action)
-_props('DigiSequentialActionSequence', adopt_action=adopt_sequence_action)
-
+_props('DigiSynchronize', adopt_action=_adopt_sequence_action)
+_props('DigiActionSequence', adopt_action=_adopt_sequence_action)
+_props('DigiParallelActionSequence', adopt_action=_adopt_sequence_action)
+_props('DigiSequentialActionSequence', adopt_action=_adopt_sequence_action)
+_props('DigiContainerSequenceAction', adopt_container_processor=_adopt_processor_action)
 
 _setup('DigiMultiContainerProcessor', call='adopt_processor', py_call=_adopt_processor)
 
diff --git a/DDDigi/python/digitize.py b/DDDigi/python/digitize.py
index b77dfe6cd5a33b4d562836a3c391af930e2e3720..b22c00dbc25c45274fb4aaca323619b0df2a5e69 100644
--- a/DDDigi/python/digitize.py
+++ b/DDDigi/python/digitize.py
@@ -48,23 +48,20 @@ class Digitize(dd4hep.Logger):
   def kernel(self):
     return self._kernel
 
-  def main_sequencer(self):
-    if not self._main_processor:
-      self._main_processor = dddigi.Synchronize(self._kernel, 'DigiSynchronize/MainDigitizer', self._parallel)
-      self._main_processor.parallel = self._parallel
-    return self._main_processor
-
   def create_action(self, name, **options):
     action = dddigi.Action(self._kernel, name)
     for option in options.items():
       setattr(action, option[0], option[1])
     return action
 
-  def new_action(self, name, **options):
-    action = dddigi.EventAction(self._kernel, name)
-    for option in options.items():
-      setattr(action, option[0], option[1])
-    return action
+  def main_sequencer(self):
+    """
+    Create main digitization sequencer
+    """
+    if not self._main_processor:
+      self._main_processor = self.create_action('DigiSynchronize/MainDigitizer', parallel=self._parallel)
+      self._main_processor.parallel = self._parallel
+    return self._main_processor
 
   def input_action(self, name=None, **options):
     """
@@ -77,7 +74,7 @@ class Digitize(dd4hep.Logger):
     if not name:
       return self._input_processor
 
-    act = self.new_action(name, **options)
+    act = self.create_action(name, **options)
     self._input_processor.adopt(act)
     return act
 
@@ -92,7 +89,7 @@ class Digitize(dd4hep.Logger):
     if not name:
       return self._event_processor
 
-    action = self.new_action(name, **options)
+    action = self.create_action(name, **options)
     if register:
       self._event_processor.adopt(action)
     return action
@@ -108,7 +105,7 @@ class Digitize(dd4hep.Logger):
     if not name:
       return self._output_processor
 
-    act = self.new_action(name, **options)
+    act = self.create_action(name, **options)
     self._output_processor.adopt(act)
     return act
 
@@ -139,8 +136,9 @@ class Digitize(dd4hep.Logger):
     self.kernel().configure()
     self.kernel().initialize()
     self.kernel().run()
+    done = krnl.events_done()
     self.kernel().terminate()
-    return self
+    return done
 
   def activeDetectors(self):
     detectors = []
@@ -183,5 +181,7 @@ class Digitize(dd4hep.Logger):
     krnl.numEvents = num_events
     krnl.numThreads = num_threads   # = number of concurrent threads
     krnl.maxEventsParallel = parallel
+    krnl.configure()
+    krnl.initialize()
     krnl.run()
     return krnl.events_done()
diff --git a/DDDigi/src/DigiAction.cpp b/DDDigi/src/DigiAction.cpp
index 4e1aaee7ab5492ed41a7c64bb8c7440d94fc6cee..4443ba1379fd4f514eb54e513903c2001c97cabe 100644
--- a/DDDigi/src/DigiAction.cpp
+++ b/DDDigi/src/DigiAction.cpp
@@ -59,8 +59,8 @@ long DigiAction::addRef() {
 long DigiAction::release() {
   long count = --m_refCount;
   if (m_refCount <= 0) {
-    printM1("DigiAction: Deleting object %s of type %s Pointer:%p",
-            m_name.c_str(),typeName(typeid(*this)).c_str(),(void*)this);
+    info("Deleting object %s of type %s Pointer:%p",
+	 m_name.c_str(),typeName(typeid(*this)).c_str(),(void*)this);
     delete this;
   }
   return count;
@@ -94,50 +94,6 @@ void DigiAction::print(const char* fmt, ...) const   {
   }
 }
 
-/// Support for messages with variable output level using output level-1
-void DigiAction::printM1(const char* fmt, ...) const   {
-  int level = std::max(outputLevel()-1,(int)VERBOSE);
-  if ( level >= printLevel() )  {
-    va_list args;
-    va_start(args, fmt);
-    dd4hep::printout((PrintLevel)level, m_name.c_str(), fmt, args);
-    va_end(args);
-  }
-}
-
-/// Support for messages with variable output level using output level-2
-void DigiAction::printM2(const char* fmt, ...) const   {
-  int level = std::max(outputLevel()-2,(int)VERBOSE);
-  if ( level >= printLevel() )  {
-    va_list args;
-    va_start(args, fmt);
-    dd4hep::printout((PrintLevel)level, m_name.c_str(), fmt, args);
-    va_end(args);
-  }
-}
-
-/// Support for messages with variable output level using output level-1
-void DigiAction::printP1(const char* fmt, ...) const   {
-  int level = std::min(outputLevel()+1,(int)FATAL);
-  if ( level >= printLevel() )  {
-    va_list args;
-    va_start(args, fmt);
-    dd4hep::printout((PrintLevel)level, m_name.c_str(), fmt, args);
-    va_end(args);
-  }
-}
-
-/// Support for messages with variable output level using output level-2
-void DigiAction::printP2(const char* fmt, ...) const   {
-  int level = std::min(outputLevel()+2,(int)FATAL);
-  if ( level >= printLevel() )  {
-    va_list args;
-    va_start(args, fmt);
-    dd4hep::printout((PrintLevel)level, m_name.c_str(), fmt, args);
-    va_end(args);
-  }
-}
-
 /// Support of debug messages.
 std::string DigiAction::format(const char* fmt, ...) const {
   va_list args;
@@ -205,8 +161,3 @@ void DigiAction::except(const char* fmt, ...) const {
   va_end(args);
   throw std::runtime_error(err);
 }
-
-/// Optional action initialization if required
-void DigiAction::initialize()   {
-}
-
diff --git a/DDDigi/src/DigiAttenuatorSequenceAction.cpp b/DDDigi/src/DigiAttenuatorSequenceAction.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8df1448b9300e75ab723c71da5cb51bcf498f9eb
--- /dev/null
+++ b/DDDigi/src/DigiAttenuatorSequenceAction.cpp
@@ -0,0 +1,75 @@
+//==========================================================================
+//  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
+//
+//==========================================================================
+
+
+///             TODO !
+
+
+// Framework include files
+#include <DD4hep/InstanceCount.h>
+
+#include <DDDigi/DigiData.h>
+#include <DDDigi/DigiKernel.h>
+#include <DDDigi/DigiContext.h>
+#include <DDDigi/DigiAttenuatorSequenceAction.h>
+
+/// C/C++ include files
+#include <cmath>
+
+/// Standard constructor
+dd4hep::digi::DigiAttenuatorSequenceAction::DigiAttenuatorSequenceAction(const DigiKernel& krnl, const std::string& nam)
+  : DigiEventAction(krnl, nam)
+{
+  declareProperty("input",      m_input_segment = "inputs");
+  declareProperty("processor",  m_processor_name);
+  declareProperty("containers", m_container_attenuation);
+  declareProperty("mask",       m_mask);
+  declareProperty("t0",         m_t0);
+  m_kernel.register_initialize(Callback(this).make(&DigiAttenuatorSequenceAction::initialize));
+  InstanceCount::increment(this);
+}
+
+/// Default destructor
+dd4hep::digi::DigiAttenuatorSequenceAction::~DigiAttenuatorSequenceAction() {
+  InstanceCount::decrement(this);
+}
+
+/// Initialization callback
+void dd4hep::digi::DigiAttenuatorSequenceAction::initialize()   {
+  for ( const auto& c : m_container_attenuation )   {
+    double factor = std::exp(-1e0 * m_t0/c.second);
+    m_attenuation.emplace(Key(c.first, m_mask), factor);
+  }
+}
+
+/// Main functional callback
+void dd4hep::digi::DigiAttenuatorSequenceAction::execute(DigiContext& context)  const    {
+  std::size_t count = 0, cnt = 0, cont = 0;
+  auto& event  = *context.event;
+  auto& inputs = event.get_segment(m_input_segment);
+  for ( const auto& k : m_attenuation )     {
+    Key key = k.first;
+    auto* data = inputs.entry(key);
+    if ( auto* m = std::any_cast<DepositMapping>(data) )
+      ++cont;
+    else if ( auto* v = std::any_cast<DepositVector>(data) )
+      cnt++, ++cont;
+    count += cnt;
+    std::string nam = Key::key_name(key)+":";
+    debug("%s+++ %-32s mask:%04X item: %08X Attenuated exponentially %6ld hits by %8.5f",
+	  event.id(), nam.c_str(), key.mask(), key.item(), cnt, k.second); 
+  }
+  info("%s+++ Attenuated exponentially %6ld hits from %4ld containers from segment %s",
+       event.id(), count, cont, m_input_segment.c_str());
+}
+
diff --git a/DDDigi/src/DigiContainerCombine.cpp b/DDDigi/src/DigiContainerCombine.cpp
index 0bfdd89e8e6073fd8261282d0ce62b4e7bf75efd..0de723b764f6c285e59ffbe63b8678789cb1b7c3 100644
--- a/DDDigi/src/DigiContainerCombine.cpp
+++ b/DDDigi/src/DigiContainerCombine.cpp
@@ -13,212 +13,226 @@
 
 // Framework include files
 #include <DD4hep/InstanceCount.h>
-
 #include <DDDigi/DigiData.h>
+#include <DDDigi/DigiKernel.h>
 #include <DDDigi/DigiContext.h>
 #include <DDDigi/DigiContainerCombine.h>
 
 /// C/C++ include files
 #include <set>
 
-using dd4hep::digi::DigiContainerCombine;
+using namespace dd4hep::digi;
 
-class DigiContainerCombine::internals_t   {
+class DigiContainerCombine::work_definition_t  {
 public:
-  /// Property: Container names to be loaded
-  std::vector<std::string>       containers { };
-  /// Property: event masks to be handled
-  std::vector<int>               input_masks  { };
-  /// Property: Output container dressing
-  std::string                    output_name_flag;
-  /// Property: Input data segment name
-  std::string                    input;
-  /// Property: Output data segment name
-  std::string                    output;
-  /// Property: mask of the deposit
-  int                            deposit_mask { 0 };
-  /// Property: Flag to erase already combined containers (not thread-safe!!!)
-  bool                           erase_combined { false };
-
-  /// Fully qualified keys of all containers to be manipulated
-  std::set<Key::key_type>        keys  { };
-  /// Container keys of all containers to be manipulated
-  std::set<Key::key_type>        cont_keys  { };
-
-  /// Predicate function to select containers for merging
-  std::function<bool(Key::key_type)>  container_selector;
-  /// Flag to check the initialization
-  bool inited  { false };
-  /// Initializing function: compute values which depend on properties
-  void initialize(DigiContext& context)   {
-    if ( !this->inited )   {
-      std::lock_guard<std::mutex> lock(context.initializer_lock());
-      if ( !this->inited )   {
-	this->inited = true;
-
-	for ( const auto& c : this->containers )   {
-	  Key key(0x0, c);
-	  this->cont_keys.emplace(key.key);
-	  if ( this->input_masks.empty() )   {
-	    this->keys.emplace(key.key);
-	    continue;
-	  }
-	  for ( int m : this->input_masks )    {
-	    key.values.mask = m;
-	    this->keys.emplace(key.key);
-	  }
-	}
-
-	this->container_selector = [this](Key::key_type k)  {
-	  const auto& m = this->input_masks;
-	  bool use = m.empty() || this->keys.empty();
-	  if ( !use )  {
-	    if ( !this->cont_keys.empty() )
-	      return this->cont_keys.find(Key::item(k)) != this->cont_keys.end();
-	    return std::find(m.begin(), m.end(), Key::mask(k)) != m.end();
-	  }
-	  return true;
-	};
+  const DigiContainerCombine* combine;
+  std::size_t cnt_conts = 0;
+  std::size_t cnt_depos = 0;
+  std::size_t cnt_parts = 0;
+  /// Printout format string
+  char        format[128];
+
+  std::vector<Key>            keys;
+  std::vector<std::any*>      work;
+  std::set<Key::itemkey_type> items;
+
+  DigiEvent&                  event;
+  DataSegment&                inputs;
+  DataSegment&                outputs;
+
+  /// Initializing constructor
+  work_definition_t(const DigiContainerCombine* c, DigiEvent& ev, DataSegment& in, DataSegment& out)
+    : combine(c), event(ev), inputs(in), outputs(out)
+  {
+    keys.reserve(inputs.size());
+    work.reserve(inputs.size());
+    for( auto& i : inputs )   {
+      Key  key = i.first;
+      if ( combine->use_key(key) )   {
+	keys.emplace_back(key);
+	work.emplace_back(&i.second);
+	items.insert(key.item());
+      }
+    }
+    ::snprintf(format, sizeof(format),
+	       "%s+++%%2d++ %%-32s Mask: $%04X Input: $%%04X Merged %%6ld %%s",
+	       event.id(), combine->m_deposit_mask);
+    format[sizeof(format)-1] = 0;
+  }
+
+  template<typename T> void merge_depos(DepositMapping& output, T& input, int thr)  {
+    std::size_t cnt = 0;
+    if ( combine->m_erase_combined )
+      cnt = output.merge(std::move(input));
+    else
+      cnt = output.insert(input);
+    combine->info(this->format, thr, input.name.c_str(), input.key.values.mask, cnt, "deposits"); 
+    this->cnt_depos += cnt;
+    this->cnt_conts++;
+  }
+
+  void merge(const std::string& nam, size_t start, int thr)  {
+    Key key = keys[start];
+    DepositMapping out(nam, combine->m_deposit_mask);
+    for( std::size_t j = start; j < keys.size(); ++j )   {
+      if ( keys[j].item() == key.item() )   {
+	if ( DepositMapping* m = std::any_cast<DepositMapping>(work[j]) )
+	  merge_depos(out, *m, thr);
+	else if ( DepositVector* v = std::any_cast<DepositVector>(work[j]) )
+	  merge_depos(out, *v, thr);
+	else 
+	  continue;
+	this->work[j]->reset();
+      }
+    }
+    key.set_mask(combine->m_deposit_mask);
+    outputs.emplace(key, std::move(out));
+  }
+
+  void merge_parts(const std::string& nam, size_t start, int thr)  {
+    Key key = keys[start];
+    ParticleMapping out(nam, combine->m_deposit_mask);
+    for( std::size_t j=start; j < keys.size(); ++j )   {
+      if ( keys[j].item() == key.item() )   {
+	ParticleMapping* next = std::any_cast<ParticleMapping>(work[j]);
+	std::size_t cnt = out.merge(std::move(*next));
+	combine->info(format, thr, next->name.c_str(), keys[j].mask(), cnt, "particles"); 
+	cnt_parts += cnt;
+	cnt_conts++;
+	work[j]->reset();
+      }
+    }
+    key.set_mask(combine->m_deposit_mask);
+    outputs.emplace(key, std::move(out));
+  }
+
+  void merge_one(Key::itemkey_type itm, int thr)   {
+    const std::string& opt = combine->m_output_name_flag;
+    for( std::size_t i=0; i < keys.size(); ++i )   {
+      if ( keys[i].values.item != itm )
+	continue;
+      /// Merge deposit mapping
+      if ( DepositMapping* depom = std::any_cast<DepositMapping>(work[i]) )   {
+	merge(depom->name+opt, i, thr);
+	break;
+      }
+      /// Merge deposit vector
+      else if ( DepositVector* depov = std::any_cast<DepositVector>(work[i]) )   {
+	merge(depov->name+opt, i, thr);
+	break;
+      }
+      /// Merge particle container
+      else if ( ParticleMapping* parts = std::any_cast<ParticleMapping>(work[i]) )   {
+	merge_parts(parts->name+opt, i, thr);
+	break;
       }
     }
   }
+
+  void merge_all()   {
+    for( auto itm : items )
+      merge_one(itm, 0);
+  }
 };
 
+template <> void DigiParallelWorker<DigiContainerCombine,
+				    DigiContainerCombine::work_definition_t,
+				    std::size_t>::execute(void* data) const  {
+  calldata_t* args = reinterpret_cast<calldata_t*>(data);
+  std::size_t cnt = 0;
+  for( auto itm : args->items )  {
+    if ( cnt == this->options )   {
+      args->merge_one(itm, this->options);
+      return;
+    }
+    ++cnt;
+  }
+}
+
 /// Standard constructor
-dd4hep::digi::DigiContainerCombine::DigiContainerCombine(const DigiKernel& krnl, const std::string& nam)
+DigiContainerCombine::DigiContainerCombine(const DigiKernel& krnl, const std::string& nam)
   : DigiEventAction(krnl, nam)
 {
-  this->internals = std::make_unique<internals_t>();
-  this->declareProperty("containers",       this->internals->containers);
-  this->declareProperty("input_masks",      this->internals->input_masks);
-  this->declareProperty("input_segment",    this->internals->input  = "inputs");
-  this->declareProperty("output_segment",   this->internals->output = "deposits");
-  this->declareProperty("deposit_mask",     this->internals->deposit_mask);
-  this->declareProperty("output_name_flag", this->internals->output_name_flag);
-  this->declareProperty("erase_combined",   this->internals->erase_combined);
+  declareProperty("containers",       m_containers);
+  declareProperty("input_masks",      m_input_masks);
+  declareProperty("input_segment",    m_input  = "inputs");
+  declareProperty("output_segment",   m_output = "deposits");
+  declareProperty("deposit_mask",     m_deposit_mask);
+  declareProperty("output_name_flag", m_output_name_flag);
+  declareProperty("erase_combined",   m_erase_combined);
+  m_kernel.register_initialize(Callback(this).make(&DigiContainerCombine::initialize));
   InstanceCount::increment(this);
 }
 
 /// Default destructor
-dd4hep::digi::DigiContainerCombine::~DigiContainerCombine() {
+DigiContainerCombine::~DigiContainerCombine() {
   InstanceCount::decrement(this);
 }
 
-struct work_definition  {
-  using Key = dd4hep::digi::Key;
-  using DigiEvent = dd4hep::digi::DigiEvent;
-  using DataSegment = dd4hep::digi::DataSegment;
-  std::size_t cnt_depos = 0;
-  std::size_t cnt_parts = 0;
-  std::size_t cnt_conts = 0;
-  std::vector<Key> keys;
-  std::vector<std::any*> work;
-  std::set<Key::itemkey_type> items;
-  Key depo_key;
-  char format[128];
-  DigiEvent& event;
-  DataSegment& inputs;
-  DataSegment& outputs;
-  work_definition(DigiEvent& e, DataSegment& i, DataSegment& o)
-    : event(e), inputs(i), outputs(o) {}
-};
-struct DigiCombineAction : public dd4hep::digi::DigiEventAction   {
-public:
-  work_definition work;
-};
-
-/// Combine selected containers to one single deposit container
-template <typename PREDICATE> std::size_t 
-dd4hep::digi::DigiContainerCombine::combine_containers(DigiEvent&   event,
-						       DataSegment& inputs,
-						       DataSegment& outputs,
-						       const PREDICATE& predicate)  const
-{
-  std::set<Key::itemkey_type> items;
-  work_definition def(event, inputs, outputs);
-  def.depo_key.values.mask = internals->deposit_mask;
-  def.keys.reserve(inputs.size());
-  def.work.reserve(inputs.size());
-  for( auto& i : def.inputs )   {
-    if ( predicate(i.first) )   {
-      def.keys.emplace_back(i.first);
-      def.work.emplace_back(&i.second);
-      items.insert(Key(i.first).values.item);
+/// Initializing function: compute values which depend on properties
+void DigiContainerCombine::initialize()    {
+  for ( const auto& cont : m_containers )   {
+    Key key(cont, 0x0);
+    m_cont_keys.emplace(key.key);
+    if ( m_input_masks.empty() )   {
+      m_keys.emplace(key.key);
+      continue;
+    }
+    for ( int m : m_input_masks )    {
+      key.values.mask = m;
+      m_keys.emplace(key.key);
     }
   }
+  if ( !m_output_name_flag.empty() )
+    m_output_name_flag += '/';
+}
 
-  ::snprintf(def.format, sizeof(def.format),
-	     "%s+++ %%-32s Mask: $%04X Input: $%%04X Merged %%6ld %%s",
-	     event.id(), def.depo_key.values.mask);
+/// Initializing function: compute values which depend on properties
+void DigiContainerCombine::have_workers(size_t count)  const   {
+  if ( m_workers.size() < count )   {
+    auto lock = m_workers.can_modify();
+    for(size_t i=m_workers.size(); i <= count; ++i)
+      m_workers.insert(new Worker(nullptr, i));
+  }
+}
 
-  for(auto itm : items)   {
-    for( std::size_t i=0; i < def.keys.size(); ++i )   {
-      if ( def.keys[i].values.item != itm )
-	continue;
-      auto* output = def.work[i];
-      def.depo_key.values.item = itm;
+/// Decide if a continer is to merged based on the properties
+bool DigiContainerCombine::use_key(Key key)  const   {
+  const auto& m = m_input_masks;
+  bool use = m.empty() || m_keys.empty();
+  if ( !use )  {
+    if ( !m_cont_keys.empty() )
+      return m_cont_keys.find(key.item()) != m_cont_keys.end();
+    return std::find(m.begin(), m.end(), key.mask()) != m.end();
+  }
+  return true;
+}
 
-      /// Merge deposit mapping
-      if ( DepositMapping* depos = std::any_cast<DepositMapping>(output) )   {
-	if ( !internals->output_name_flag.empty() )
-	  depos->name = depos->name+"/"+internals->output_name_flag;
-	def.cnt_depos += depos->size();
-	def.cnt_conts++;
-	this->info(def.format, depos->name.c_str(), 
-		   def.keys[i].values.mask, depos->size(), "deposits"); 
-	for( std::size_t j=i+1; j < def.keys.size(); ++j )   {
-	  if ( def.keys[j].values.item == itm )   {
-	    DepositMapping* next = std::any_cast<DepositMapping>(def.work[j]);
-	    std::size_t cnt = depos->merge(std::move(*next));
-	    this->info(def.format, next->name.c_str(), 
-		       def.keys[j].values.mask, cnt, "deposits"); 
-	    def.cnt_depos += cnt;
-	    def.cnt_conts++;
-	    def.work[j]->reset();
-	  }
-	}
-	def.outputs.emplace(def.depo_key, std::move(*output));
-	break;
-      }
-      /// Merge particle container
-      else if ( ParticleMapping* parts = std::any_cast<ParticleMapping>(output) )   {
-	if ( !internals->output_name_flag.empty() )
-	  parts->name = parts->name+"/"+internals->output_name_flag;
-	def.cnt_parts += parts->size();
-	def.cnt_conts++;
-
-	this->info(def.format, parts->name.c_str(), 
-		   def.keys[i].values.mask, parts->size(), "particles"); 
-	for( std::size_t j=i+1; j < def.keys.size(); ++j )   {
-	  if ( def.keys[j].values.item == itm )   {
-	    ParticleMapping* next = std::any_cast<ParticleMapping>(def.work[j]);
-	    std::size_t cnt = parts->merge(std::move(*next));
-	    this->info(def.format, next->name.c_str(), 
-		       def.keys[j].values.mask, cnt, "particles"); 
-	    def.cnt_parts += cnt;
-	    def.cnt_conts++;
-	    def.work[j]->reset();
-	  }
-	}
-	def.outputs.emplace(def.depo_key, std::move(*output));
-	break;
-      }
-    }
+/// Combine selected containers to one single deposit container
+std::size_t DigiContainerCombine::combine_containers(DigiEvent&   event,
+						     DataSegment& inputs,
+						     DataSegment& outputs)  const
+{
+  work_definition_t def(this, event, inputs, outputs);
+  if ( m_parallel )  {
+    have_workers(def.items.size());
+    m_kernel.submit(m_workers.get_group(), def.items.size(), &def);
+  }
+  else  {
+    def.merge_all();
   }
-  if ( this->internals->erase_combined )   {
+  if ( m_erase_combined )   {
     inputs.erase(def.keys);
   }
-  this->info("%s+++ Merged %ld particles and %ld deposits from %ld containers",
-	     event.id(), def.cnt_parts, def.cnt_depos, def.cnt_conts);
+  info("%s+++ Merged %ld particles and %ld deposits from segment '%s' to segment '%s'",
+       event.id(), def.cnt_parts, def.cnt_depos, m_input.c_str(), m_output.c_str());
   return def.cnt_depos;
 }
 
 /// Main functional callback
-void dd4hep::digi::DigiContainerCombine::execute(DigiContext& context)  const    {
-  this->internals->initialize(context);
+void DigiContainerCombine::execute(DigiContext& context)  const    {
   auto& event    = *context.event;
-  auto& inputs   = event.get_segment(this->internals->input);
-  auto& outputs  = event.get_segment(this->internals->output);
-  auto& selector = this->internals->container_selector;
-  this->combine_containers(event, inputs, outputs, selector);
+  auto& inputs   = event.get_segment(m_input);
+  auto& outputs  = event.get_segment(m_output);
+  combine_containers(event, inputs, outputs);
 }
diff --git a/DDDigi/src/DigiContainerProcessor.cpp b/DDDigi/src/DigiContainerProcessor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b3eaa9f10ce690952d4f77de43185f69f7256a24
--- /dev/null
+++ b/DDDigi/src/DigiContainerProcessor.cpp
@@ -0,0 +1,237 @@
+//==========================================================================
+//  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
+//
+//==========================================================================
+
+// Framework include files
+#include <DD4hep/InstanceCount.h>
+#include <DDDigi/DigiData.h>
+#include <DDDigi/DigiKernel.h>
+#include <DDDigi/DigiContext.h>
+#include <DDDigi/DigiContainerProcessor.h>
+
+/// C/C++ include files
+#include <sstream>
+
+using namespace dd4hep::digi;
+
+/// Standard constructor
+DigiContainerProcessor::DigiContainerProcessor(const DigiKernel& kernel, const std::string& name)   
+  : DigiAction(kernel, name)
+{
+  InstanceCount::increment(this);
+}
+
+/// Default destructor
+DigiContainerProcessor::~DigiContainerProcessor() {
+  InstanceCount::decrement(this);
+}
+
+/// Main functional callback 
+void DigiContainerProcessor::execute(DigiContext& context, work_t& work)  const  {
+  this->execute(context, work.key, work.input);
+}
+
+/// Main functional callback if specific work is known
+void DigiContainerProcessor::execute(DigiContext& context, Key key, std::any& /* data */)  const    {
+  info("%s+++ %p Using container: %016lX  --> %04X %08X %s",
+       context.event->id(), (void*)this, key.key, key.mask(), key.item(), Key::key_name(key).c_str());
+}
+
+/// Standard constructor
+DigiContainerSequence::DigiContainerSequence(const DigiKernel& krnl, const std::string& nam)
+  : DigiContainerProcessor(krnl, nam)
+{
+  declareProperty("parallel", m_parallel = false);
+  InstanceCount::increment(this);
+}
+
+/// Default destructor
+DigiContainerSequence::~DigiContainerSequence() {
+  InstanceCount::decrement(this);
+}
+
+/// Adopt new parallel worker
+void DigiContainerSequence::adopt_processor(DigiContainerProcessor* action)   {
+  if ( !action )  {
+    except("+++ FAILED: attempt to add invalid processor!");
+    return;
+  }
+  m_workers.insert(new worker_t(action, m_workers.size()));
+}
+
+/// Main functional callback if specific work is known
+void DigiContainerSequence::execute(DigiContext& /* context */, work_t& work)  const   {
+  m_kernel.submit(m_workers.get_calls(), m_workers.size(), &work, m_parallel);
+}
+
+/// Worker adaptor for caller DigiContainerSequence
+template <> void DigiParallelWorker<DigiContainerProcessor,
+				    DigiContainerSequence::work_t,
+				    std::size_t>::execute(void* data) const  {
+  calldata_t* args  = reinterpret_cast<calldata_t*>(data);
+  action->execute(args->context, *args);
+}
+
+/// Standard constructor
+DigiContainerSequenceAction::DigiContainerSequenceAction(const DigiKernel& krnl, const std::string& nam)
+  : DigiEventAction(krnl, nam)
+{
+  declareProperty("mask",          m_mask);
+  declareProperty("input_segment", m_input_segment);
+  m_kernel.register_initialize(Callback(this).make(&DigiContainerSequenceAction::initialize));
+  InstanceCount::increment(this);
+}
+
+/// Default destructor
+DigiContainerSequenceAction::~DigiContainerSequenceAction() {
+  InstanceCount::decrement(this);
+}
+
+/// Initialization callback
+void dd4hep::digi::DigiContainerSequenceAction::initialize()   {
+  size_t count = 0;
+  for( auto& ent : m_registered_processors )   {
+    Key key = ent.first;
+    key.set_mask(m_mask);
+    worker_t* w = new worker_t(ent.second, count++);
+    m_registered_workers.emplace(key, w);
+    m_workers.insert(w);
+  }
+}
+
+/// Adopt new parallel worker
+void DigiContainerSequenceAction::adopt_processor(DigiContainerProcessor* action, const std::string& container)   {
+  Key key(container, 0x0);
+  auto it = m_registered_processors.find(key);
+  if ( it != m_registered_processors.end() )   {
+    except("+++ The action %s was already registered to mask:%04X container:%s!",
+	   action->c_name(), m_mask, container.c_str());
+    return;
+  }
+  action->addRef();
+  m_registered_processors.emplace(key, action);
+}
+
+/// Main functional callback if specific work is known
+void DigiContainerSequenceAction::execute(DigiContext& context)  const   {
+  auto& event  = *context.event;
+  auto& inputs = event.get_segment(m_input_segment);
+  std::vector<ParallelWorker*> event_workers;
+  work_t args { context, {}, m_output_lock, {}, this };
+
+  args.input_items.resize(m_workers.size(), {0, nullptr});
+  event_workers.reserve(inputs.size());
+  for( auto& i : inputs )   {
+    Key key(i.first);
+    auto it = m_registered_workers.find(key);
+    if ( it != m_registered_workers.end() )  {
+      worker_t* w = it->second;
+      //work_item_t itm { key, &(i.second) };
+      event_workers.emplace_back(w);
+      args.input_items[w->options] = { key, &(i.second) };
+    }
+  }
+  m_kernel.submit(&event_workers.at(0), event_workers.size(), &args, m_parallel);
+}
+
+/// Worker adaptor for caller DigiContainerSequenceAction
+template <> void DigiParallelWorker<DigiContainerProcessor,
+				    DigiContainerSequenceAction::work_t,
+				    std::size_t>::execute(void* data) const  {
+  calldata_t* args  = reinterpret_cast<calldata_t*>(data);
+  auto& item = args->input_items[this->options];
+  DigiContainerProcessor::work_t work { args->context, item.key, *item.data, args->output_lock, args->output};
+  action->execute(args->context, work);
+}
+
+/// Standard constructor
+DigiMultiContainerProcessor::DigiMultiContainerProcessor(const DigiKernel& krnl, const std::string& nam)
+  : DigiEventAction(krnl, nam)
+{
+  this->declareProperty("input_masks",      m_input_masks);
+  this->declareProperty("input_segment",    m_input_segment);
+  InstanceCount::increment(this);
+}
+
+/// Default destructor
+DigiMultiContainerProcessor::~DigiMultiContainerProcessor() {
+  InstanceCount::decrement(this);
+}
+
+void DigiMultiContainerProcessor::adopt_processor(DigiContainerProcessor* action, const std::vector<std::string>& containers)    {
+  if ( !action )   {
+    except("+++ Attempt to use invalid processor. Request FAILED.");
+  }
+  else if ( containers.empty() )   {
+    except("+++ Processor %s is defined, but no workload was assigned. Request FAILED.");
+  }
+  std::stringstream str;
+  std::vector<Key> keys;
+  str << "[ ";
+  for(const auto& cont : containers)    {
+    Key key(cont, 0x0);
+    keys.push_back(key);
+    m_work_items.insert(key.item());
+    str << cont << " ";
+  }
+  str << " ";
+  m_workers.insert(new worker_t(action, m_workers.size()));
+  m_worker_keys.emplace_back(std::move(keys));
+  info("+++ Use processor: %-32s for processing: %s", action->c_name(), str.str().c_str());
+}
+
+/// Main functional callback
+void DigiMultiContainerProcessor::execute(DigiContext& context)  const  {
+  work_items_t work_items;
+  auto& msk    = m_input_masks;
+  auto& event  = *context.event;
+  auto& inputs = event.get_segment(m_input_segment);
+
+  work_items.reserve(inputs.size());
+  for( auto& i : inputs )   {
+    Key in_key(i.first);
+    bool use = msk.empty() || std::find(msk.begin(), msk.end(), in_key.mask()) != msk.end();
+    if ( use )   {
+      use = m_work_items.empty() || m_work_items.find(in_key.item()) != m_work_items.end();
+      if ( use )   {
+	work_items.emplace_back(std::make_pair(i.first, &i.second));
+      }
+    }
+  }
+  if ( !work_items.empty() )   {
+    work_t args { context, work_items, m_output_lock, {}, this };
+    m_kernel.submit(m_workers.get_calls(), m_workers.size(), &args, m_parallel);
+  }
+}
+
+/// Worker adaptor for caller DigiMultiContainerProcessor
+template <> void DigiParallelWorker<DigiContainerProcessor,
+				    DigiMultiContainerProcessor::work_t,
+				    std::size_t>::execute(void* data) const  {
+  calldata_t* arg  = reinterpret_cast<calldata_t*>(data);
+  const auto& keys  = arg->parent->worker_keys(this->options);
+  const auto& masks = arg->parent->input_masks();
+  for( const auto& item : arg->items )  {
+    Key key = item.first;
+    if ( masks.empty() || std::find(masks.begin(), masks.end(), key.mask()) != masks.end() )  {
+      if ( keys.empty() )  {
+	DigiContainerProcessor::work_t work {arg->context, key, *item.second, arg->output_lock, arg->output };
+	action->execute(arg->context, work);
+      }
+      else if ( std::find(keys.begin(), keys.end(), Key(key.item())) != keys.end() )    {
+	DigiContainerProcessor::work_t work {arg->context, key, *item.second, arg->output_lock, arg->output };
+	action->execute(arg->context, work);
+      }
+    }
+  }
+}
+
diff --git a/DDDigi/src/DigiData.cpp b/DDDigi/src/DigiData.cpp
index 4c65d7b5d00538b885971ec78f3678406295c5b8..2caea66de57ec34771106d2e7a2042d19915ccdb 100644
--- a/DDDigi/src/DigiData.cpp
+++ b/DDDigi/src/DigiData.cpp
@@ -66,7 +66,16 @@ std::string Key::key_name(const Key& k)    {
 std::size_t DepositVector::merge(DepositVector&& updates)    {
   std::size_t update_size = updates.size();
   for( auto& c : updates )    {
-    this->emplace_back(c);
+    data.emplace_back(c);
+  }
+  return update_size;
+}
+
+/// Merge new deposit map onto existing map (keep inputs)
+std::size_t DepositVector::insert(const DepositVector& updates)    {
+  std::size_t update_size = updates.size();
+  for( const auto& c : updates )    {
+    data.emplace_back(c);
   }
   return update_size;
 }
@@ -75,16 +84,24 @@ std::size_t DepositVector::merge(DepositVector&& updates)    {
 std::size_t DepositMapping::merge(DepositVector&& updates)    {
   std::size_t update_size = updates.size();
   for( auto& c : updates )    {
+    data.emplace(std::move(c));
+#if 0
     CellID         cell = c.first;
     EnergyDeposit& depo = c.second;
-    auto iter = this->find(cell);
-    if ( iter == this->end() )   {
-      this->emplace(cell, std::move(depo));
+    auto iter = data.find(cell);
+    if ( iter == data.end() )   {
+      data.emplace(cell, std::move(depo));
       continue;
     }
     auto& to_update = iter->second;
     to_update.deposit += depo.deposit;
-    to_update.history.insert(to_update.history.end(), depo.history.begin(), depo.history.end());
+    to_update.hit_history.insert(to_update.hit_history.end(),
+				 depo.hit_history.begin(),
+				 depo.hit_history.end());
+    to_update.particle_history.insert(to_update.particle_history.end(),
+				      depo.particle_history.begin(),
+				      depo.particle_history.end());
+#endif
   }
   return update_size;
 }
@@ -93,16 +110,42 @@ std::size_t DepositMapping::merge(DepositVector&& updates)    {
 std::size_t DepositMapping::merge(DepositMapping&& updates)    {
   std::size_t update_size = updates.size();
   for( auto& c : updates )    {
+    data.emplace(std::move(c));
+#if 0
     CellID         cell = c.first;
     EnergyDeposit& depo = c.second;
-    auto iter = this->find(cell);
-    if ( iter == this->end() )   {
-      this->emplace(cell, std::move(depo));
+    auto iter = data.find(cell);
+    if ( iter == data.end() )   {
+      data.emplace(cell, std::move(depo));
       continue;
     }
     auto& to_update = iter->second;
     to_update.deposit += depo.deposit;
-    to_update.history.insert(to_update.history.end(), depo.history.begin(), depo.history.end());
+    to_update.hit_history.insert(to_update.hit_history.end(),
+				 depo.hit_history.begin(),
+				 depo.hit_history.end());
+    to_update.particle_history.insert(to_update.particle_history.end(),
+				      depo.particle_history.begin(),
+				      depo.particle_history.end());
+#endif
+  }
+  return update_size;
+}
+
+/// Merge new deposit map onto existing map (keep inputs)
+std::size_t DepositMapping::insert(const DepositVector& updates)    {
+  std::size_t update_size = updates.size();
+  for( const auto& c : updates )    {
+    data.emplace(c);
+  }
+  return update_size;
+}
+
+/// Merge new deposit map onto existing map (keep inputs)
+std::size_t DepositMapping::insert(const DepositMapping& updates)    {
+  std::size_t update_size = updates.size();
+  for( const auto& c : updates )    {
+    data.emplace(c);
   }
   return update_size;
 }
@@ -119,19 +162,28 @@ std::size_t ParticleMapping::merge(ParticleMapping&& updates)    {
   return update_size;
 }
 
-void ParticleMapping::push(Key key, Particle&& part)  {
+void ParticleMapping::push(Key particle_key, Particle&& particle_data)  {
 #if defined(__GNUC__) && (__GNUC__ < 10)
   /// Lower compiler version have a bad implementation of std::any
   bool ret = false;
 #else
-  bool ret = this->emplace(key.key, std::move(part)).second;
+  bool ret = data.emplace(particle_key.key, std::move(particle_data)).second;
 #endif
   if ( !ret )   {
     except("ParticleMapping","Error in particle map. Duplicate ID: mask:%04X Number:%d History:%s",
-	   key.values.mask, key.values.item, yes_no(part.history.has_value()));
+	   particle_key.mask(), particle_key.item(), yes_no(particle_data.history.has_value()));
   }
 }
 
+/// Insert new entry
+void ParticleMapping::emplace(Key particle_key, Particle&& particle_data)  {
+#if defined(__GNUC__) && (__GNUC__ < 10)
+  //return std::make_pair(false);
+#else
+  data.emplace(particle_key.key, std::move(particle_data)).second;
+#endif
+}
+
 /// Initializing constructor
 DataSegment::DataSegment(std::mutex& l) : lock(l)
 {
diff --git a/DDDigi/src/DigiHandle.cpp b/DDDigi/src/DigiHandle.cpp
index 25159394cf5da747ce2cb7d7f38c698d8614ad67..64aefbab59217828a77bcd755a13c4564999b6c4 100644
--- a/DDDigi/src/DigiHandle.cpp
+++ b/DDDigi/src/DigiHandle.cpp
@@ -20,7 +20,7 @@
 #include <DDDigi/DigiKernel.h>
 #include <DDDigi/DigiInputAction.h>
 #include <DDDigi/DigiEventAction.h>
-#include <DDDigi/DigiSegmentAction.h>
+#include <DDDigi/DigiSegmentProcessor.h>
 #include <DDDigi/DigiSignalProcessor.h>
 
 // C/C++ include files
@@ -69,8 +69,8 @@ namespace dd4hep {
       return PluginService::Create<DigiAction*>(t, &kernel, n);
     }
 
-    template <> DigiSegmentAction* _raw_create<DigiSegmentAction>(const std::string& t, const DigiKernel& kernel, const std::string& n)    {
-      return PluginService::Create<DigiSegmentAction*>(t, &kernel, n);
+    template <> DigiSegmentProcessor* _raw_create<DigiSegmentProcessor>(const std::string& t, const DigiKernel& kernel, const std::string& n)    {
+      return PluginService::Create<DigiSegmentProcessor*>(t, &kernel, n);
     }
 
     template <> DigiSignalProcessor* _raw_create<DigiSignalProcessor>(const std::string& t, const DigiKernel& kernel, const std::string& n)    {
@@ -202,7 +202,7 @@ namespace dd4hep {
     template class DigiHandle<DigiAction>;
     template class DigiHandle<DigiInputAction>;
     template class DigiHandle<DigiEventAction>;
-    template class DigiHandle<DigiSegmentAction>;
+    template class DigiHandle<DigiSegmentProcessor>;
     template class DigiHandle<DigiSynchronize>;
     template class DigiHandle<DigiActionSequence>;
     template class DigiHandle<DigiSignalProcessor>;
diff --git a/DDDigi/src/DigiHitAttenuatorExp.cpp b/DDDigi/src/DigiHitAttenuatorExp.cpp
index 16ae3b2be864ae804e16711f7e05359bd6169697..ffdae21a5cca9b1c3bfe382564193210a18d7ca0 100644
--- a/DDDigi/src/DigiHitAttenuatorExp.cpp
+++ b/DDDigi/src/DigiHitAttenuatorExp.cpp
@@ -15,54 +15,22 @@
 #include <DD4hep/InstanceCount.h>
 
 #include <DDDigi/DigiData.h>
+#include <DDDigi/DigiKernel.h>
 #include <DDDigi/DigiContext.h>
 #include <DDDigi/DigiHitAttenuatorExp.h>
 
 /// C/C++ include files
 #include <cmath>
 
-
-
-class dd4hep::digi::DigiHitAttenuatorExp::internals_t   {
-public:
-  /// Property: Input data segment name
-  std::string                    input  { };
-  /// Property: Container names to be loaded
-  std::map<std::string, double>  container_attenuation  { };
-  /// Property: event masks to be handled
-  std::vector<int>               masks  { };
-  /// Property: T0 with respect to central crossing
-  double                         t0     { 0e0 };
-
-  /// Keys of all containers to be manipulated
-  std::map<unsigned long, double> attenuation  { };
-
-  void initialize(DigiContext& context)   {
-    if ( this->attenuation.empty() )   {
-      std::lock_guard<std::mutex> lock(context.initializer_lock());
-      if ( this->attenuation.empty() )   {
-	for ( const auto& c : this->container_attenuation )   {
-	  Key key(0x0, c.first);
-	  for ( int m : this->masks )    {
-	    double factor = std::exp(-1e0 * this->t0/c.second);
-	    key.values.mask = m;
-	    this->attenuation.emplace(key.key, factor);
-	  }
-	}
-      }
-    }
-  }
-};
-
 /// Standard constructor
 dd4hep::digi::DigiHitAttenuatorExp::DigiHitAttenuatorExp(const DigiKernel& krnl, const std::string& nam)
   : DigiEventAction(krnl, nam)
 {
-  internals = std::make_unique<internals_t>();
-  declareProperty("input",      internals->input = "inputs");
-  declareProperty("containers", internals->container_attenuation);
-  declareProperty("masks",      internals->masks);
-  declareProperty("t0",         internals->t0);
+  declareProperty("input",      m_input_segment = "inputs");
+  declareProperty("containers", m_container_attenuation);
+  declareProperty("masks",      m_masks);
+  declareProperty("t0",         m_t0);
+  m_kernel.register_initialize(Callback(this).make(&DigiHitAttenuatorExp::initialize));
   InstanceCount::increment(this);
 }
 
@@ -71,27 +39,44 @@ dd4hep::digi::DigiHitAttenuatorExp::~DigiHitAttenuatorExp() {
   InstanceCount::decrement(this);
 }
 
+/// Initialization callback
+void dd4hep::digi::DigiHitAttenuatorExp::initialize()   {
+  for ( const auto& c : m_container_attenuation )   {
+    Key key(c.first, 0x0);
+    for ( int m : m_masks )    {
+      double factor = std::exp(-1e0 * m_t0/c.second);
+      key.set_mask(m);
+      m_attenuation.emplace(key.key, factor);
+    }
+  }
+}
+
+/// Attenuator callback for single container
+template <typename T> std::size_t 
+dd4hep::digi::DigiHitAttenuatorExp::attenuate(T* cont, double factor) const {
+  for( auto& c : *cont )
+    c.second.deposit *= factor;
+  return cont->size();
+}
+
 /// Main functional callback
 void dd4hep::digi::DigiHitAttenuatorExp::execute(DigiContext& context)  const    {
-  internals->initialize(context);
-  std::size_t count = 0;
+  std::size_t count = 0, cnt = 0, cont = 0;
   auto& event  = *context.event;
-  auto& inputs = event.get_segment(internals->input);
-  for ( const auto& k : internals->attenuation )     {
-    DepositMapping* m = inputs.pointer<DepositMapping>(k.first);
-    if ( m )    {
-      double factor = k.second;
-      for( auto& c : *m )    {
-	c.second.deposit *= factor;
-      }
-      count += m->size();
-      std::string nam = Key::key_name(k.first)+":";
-      debug("%s+++ %-32s mask:%04X item: %08X Attenuated exponentially %6ld hits by %8.5f",
-	    event.id(), nam.c_str(), m->key.values.mask, m->key.values.item, 
-	    m->size(), factor); 
-      continue;
-    }
+  auto& inputs = event.get_segment(m_input_segment);
+  for ( const auto& k : m_attenuation )     {
+    Key key = k.first;
+    auto* data = inputs.entry(key);
+    if ( auto* m = std::any_cast<DepositMapping>(data) )
+      cnt += this->attenuate(m, k.second), ++cont;
+    else if ( auto* v = std::any_cast<DepositVector>(data) )
+      cnt += this->attenuate(v, k.second), ++cont;
+    count += cnt;
+    std::string nam = Key::key_name(key)+":";
+    debug("%s+++ %-32s mask:%04X item: %08X Attenuated exponentially %6ld hits by %8.5f",
+	  event.id(), nam.c_str(), key.mask(), key.item(), cnt, k.second); 
   }
-  info("%s+++ Attenuated exponentially %6ld hits", event.id(), count);
+  info("%s+++ Attenuated exponentially %6ld hits from %4ld containers from segment %s",
+       event.id(), count, cont, m_input_segment.c_str());
 }
 
diff --git a/DDDigi/src/DigiKernel.cpp b/DDDigi/src/DigiKernel.cpp
index 979865d50bbf1cb37480566af611190998bdea75..3c480bedcf8e09aed575f14e985556a7591c2c10 100644
--- a/DDDigi/src/DigiKernel.cpp
+++ b/DDDigi/src/DigiKernel.cpp
@@ -71,14 +71,25 @@ public:
   /// Lock for global output logging
   std::mutex            global_output_lock  { };
 
+  /// Configure callbacks
+  CallbackSequence      configurators       { };
+  /// Initialize callbacks
+  CallbackSequence      initializers        { };
+  //// Termination callback
+  CallbackSequence      terminators         { };
+      /// Register start event callback
+  CallbackSequence      start_event         { };
+  /// Register end event callback
+  CallbackSequence      end_event           { };
+
   /// The main data input action sequence
-  DigiActionSequence*   inputAction = 0;
+  DigiActionSequence*   inputAction         { nullptr };
   /// The main event action sequence
-  DigiActionSequence*   eventAction = 0;
+  DigiActionSequence*   eventAction         { nullptr };
   /// The main data output action sequence
-  DigiActionSequence*   outputAction = 0;
+  DigiActionSequence*   outputAction        { nullptr };
   /// TBB initializer (If TBB is used)
-  void*                 tbbInit = 0;
+  void*                 tbbInit             { nullptr };
   /// Property: Output level
   int                   outputLevel;
   /// Property: maximum number of events to be processed (if < 0: infinite)
@@ -292,12 +303,44 @@ void DigiKernel::loadXML(const char* fname) {
   m_detDesc->apply("DD4hep_XMLLoader", 1, (char**) args);
 }
 
+/// Configure the digitization: call all registered configurators
 int DigiKernel::configure()   {
-  return 1;//DigiExec::configure(*this);
+  internals->configurators();
+  return 1;
 }
 
+/// Initialize the digitization: call all registered initializers
 int DigiKernel::initialize()   {
-  return 1;//DigiExec::initialize(*this);
+  internals->initializers();
+  return 1;
+}
+
+/// Register configure callback
+void DigiKernel::register_configure(const Callback& callback)   const  {
+  std::lock_guard<std::mutex> lock(initializer_lock());
+  internals->configurators.add(callback);
+}
+
+/// Register initialize callback
+void DigiKernel::register_initialize(const Callback& callback)   const  {
+  std::lock_guard<std::mutex> lock(initializer_lock());
+  internals->initializers.add(callback);
+}
+
+/// Register terminate callback
+void DigiKernel::register_terminate(const Callback& callback)   const  {
+  std::lock_guard<std::mutex> lock(initializer_lock());
+  internals->terminators.add(callback);
+}
+
+/// Register start event callback
+void DigiKernel::register_start_event(const Callback& callback)   const  {
+  internals->start_event.add(callback);
+}
+
+/// Register end event callback
+void DigiKernel::register_end_event(const Callback& callback)   const  {
+  internals->end_event.add(callback);
 }
 
 /// Access to the main input action sequence from the kernel object
@@ -316,54 +359,27 @@ DigiActionSequence& DigiKernel::outputAction() const    {
 }
 
 /// Submit a bunch of actions to be executed in parallel
-void DigiKernel::submit (const std::vector<ParallelCall*>& algorithms, void* context)  const    {
+void DigiKernel::submit (ParallelCall*const algorithms[], std::size_t count, void* context, bool parallel)  const    {
 #ifdef DD4HEP_USE_TBB
-  bool parallel = 0 != internals->tbbInit && internals->numThreads>0;
-  if ( parallel )   {
+  bool para = parallel && (0 != internals->tbbInit && internals->numThreads>0);
+  if ( para )   {
     tbb::task_group que;
-    for( auto* algo : algorithms )
-      que.run( Wrapper<ParallelCall,void*>(algo, context) );
+    printout(INFO,"DigiKernel","+++ Executing chunk of %ld execution entries in parallel", count);
+    for( std::size_t i=0; i<count; ++i)
+      que.run( Wrapper<ParallelCall,void*>(algorithms[i], context) );
     que.wait();
     return;
   }
 #endif
-  for( auto* algo : algorithms )
-    algo->execute(context);
+  printout(INFO,"DigiKernel","+++ Executing chunk of %ld execution entries sequentially", count);
+  for( std::size_t i=0; i<count; ++i)
+    algorithms[i]->execute(context);
 }
 
-/// Submit a bunch of actions to be executed serially
-void DigiKernel::execute(const std::vector<ParallelCall*>& algorithms, void* context)  const   {
-  for( auto* algo : algorithms )
-    algo->execute(context);
-}
-
-#if 0
-void DigiKernel::submit(const DigiAction::Actors<DigiEventAction>& actions, DigiContext& context)   const  {
-  std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
-  bool parallel = 0 != internals->tbbInit && internals->numThreads>0;
-#ifdef DD4HEP_USE_TBB
-  if ( parallel )   {
-    tbb::task_group que;
-    for ( auto* i : actions )
-      que.run( Wrapper<DigiEventAction,DigiContext&>(i, context) );
-    que.wait();
-    goto print_stamp;
-  }
-#endif
-  actions(&DigiEventAction::execute,context);
-  goto print_stamp;
-
- print_stamp:
-  std::chrono::duration<double> secs = std::chrono::system_clock::now() - start;
-  printout(DEBUG, "DigiKernel", "+++ Event: %8d Executed %s task group with %3ld members [%8.3g sec]",
-           context.event->eventNumber, parallel ? "parallel" : "serial", actions.size(),
-           secs.count());
-}
-
-void DigiKernel::execute(const DigiAction::Actors<DigiEventAction>& actions, DigiContext& context)   const  {
-  actions(&DigiEventAction::execute,context);
+/// Submit a bunch of actions to be executed in parallel
+void DigiKernel::submit (const std::vector<ParallelCall*>& algorithms, void* data, bool parallel)  const  {
+  this->submit(&algorithms[0], algorithms.size(), data, parallel);
 }
-#endif
 
 void DigiKernel::wait(DigiContext& context)   const  {
   if ( context.event ) {}
@@ -373,9 +389,11 @@ void DigiKernel::wait(DigiContext& context)   const  {
 void DigiKernel::executeEvent(std::unique_ptr<DigiContext>&& context)    {
   DigiContext& refContext = *context;
   try {
+    internals->start_event(&refContext);
     inputAction().execute(refContext);
     eventAction().execute(refContext);
     outputAction().execute(refContext);
+    internals->end_event(&refContext);
     notify(std::move(context));
   }
   catch(const std::exception& e)   {
@@ -383,6 +401,7 @@ void DigiKernel::executeEvent(std::unique_ptr<DigiContext>&& context)    {
   }
 }
 
+/// Notify kernel that the execution of one single event finished
 void DigiKernel::notify(std::unique_ptr<DigiContext>&& context)   {
   if ( context )   {
     context->event.reset();
@@ -391,6 +410,7 @@ void DigiKernel::notify(std::unique_ptr<DigiContext>&& context)   {
   ++internals->eventsFinished;
 }
 
+/// Notify kernel that the execution of one single event finished
 void DigiKernel::notify(std::unique_ptr<DigiContext>&& context, const std::exception& e)   {
   internals->stop = true;
   printout(ERROR,"DigiKernel","+++ Exception during event processing [Shall stop the event loop]");
@@ -398,6 +418,7 @@ void DigiKernel::notify(std::unique_ptr<DigiContext>&& context, const std::excep
   notify(std::move(context));
 }
 
+/// Run the digitization sequence over the requested number of events
 int DigiKernel::run()   {
   std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
   internals->stop = false;
@@ -438,8 +459,10 @@ int DigiKernel::run()   {
   return 1;
 }
 
+/// Terminate the digitization: call all registered terminators and release the allocated resources
 int DigiKernel::terminate() {
   printout(INFO, "DigiKernel", "++ Terminate Digi and delete associated actions.");
+  internals->terminators();
   m_detDesc->destroyInstance();
   m_detDesc = 0;
   return 1;
diff --git a/DDDigi/src/DigiMultiContainerProcessor.cpp b/DDDigi/src/DigiMultiContainerProcessor.cpp
deleted file mode 100644
index 454cc862034a62d5d6ac9c6ebacf30ef91f2e866..0000000000000000000000000000000000000000
--- a/DDDigi/src/DigiMultiContainerProcessor.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-//==========================================================================
-//  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
-//
-//==========================================================================
-
-// Framework include files
-#include <DD4hep/InstanceCount.h>
-
-#include <DDDigi/DigiData.h>
-#include <DDDigi/DigiContext.h>
-#include <DDDigi/DigiMultiContainerProcessor.h>
-
-/// C/C++ include files
-#include <set>
-#include <sstream>
-
-using namespace dd4hep::digi;
-
-template <> void DigiParallelWorker<DigiContainerProcessor,
-				    DigiMultiContainerProcessor::CallData,
-				    int>::execute(void* data) const  {
-  calldata_t* args = reinterpret_cast<calldata_t*>(data);
-  action->execute(args->context, args->work);
-}
-
-/// Standard constructor
-DigiMultiContainerProcessor::DigiMultiContainerProcessor(const DigiKernel& krnl, const std::string& nam)
-  : DigiEventAction(krnl, nam)
-{
-  this->declareProperty("input_masks",      m_input_masks);
-  this->declareProperty("input_segment",    m_input_segment);
-  InstanceCount::increment(this);
-}
-
-/// Default destructor
-DigiMultiContainerProcessor::~DigiMultiContainerProcessor() {
-  for(auto* w : m_workers ) detail::deletePtr(w);
-  m_workers.clear();
-  InstanceCount::decrement(this);
-}
-
-void DigiMultiContainerProcessor::adopt_processor(DigiContainerProcessor* action, const std::vector<std::string>& containers)    {
-  if ( !action )   {
-    except("+++ Attempt to use invalid processor. Request FAILED.");
-  }
-  else if ( containers.empty() )   {
-    except("+++ Processor %s is defined, but no workload was assigned. Request FAILED.");
-  }
-  std::stringstream str;
-  std::vector<Key> keys;
-  for(const auto& c : containers)    {
-    Key key(0x0, c);
-    keys.push_back(key);
-    m_work_items.insert(key.item());
-    str << c << " ";
-  }
-  action->m_container_keys = std::move(keys);
-  m_workers.emplace_back(new Worker(action, 0));
-  info("+++ Use processor: %-32s for processing: %s", action->c_name(), str.str().c_str());
-}
-
-/// Main functional callback
-void DigiMultiContainerProcessor::execute(DigiContext& context)  const    {
-  WorkItems work_items;
-  auto& msk    = m_input_masks;
-  auto& event  = *context.event;
-  auto& inputs = event.get_segment(m_input_segment);
-
-  work_items.reserve(inputs.size());
-  for( auto& i : inputs )   {
-    Key in_key(i.first);
-    bool use = msk.empty() || std::find(msk.begin(), msk.end(), in_key.mask()) != msk.end();
-    if ( use )   {
-      use = m_work_items.find(in_key.item()) != m_work_items.end();
-      if ( use )   {
-	work_items.emplace_back(std::make_pair(i.first, &i.second));
-      }
-    }
-  }
-  if ( !work_items.empty() )   {
-    CallData data { context, work_items };
-    m_kernel.submit(m_workers, &data);
-  }
-}
-
-/// Standard constructor
-DigiContainerProcessor::DigiContainerProcessor(const DigiKernel& kernel, const std::string& name)   
-  : DigiAction(kernel, name)
-{
-  this->declareProperty("input_masks",      m_input_masks);
-  this->declareProperty("input_segment",    m_input_segment);
-}
-
-/// Check if the work item is for us
-bool DigiContainerProcessor::use_container(Key key)   const    {
-  auto key_iter = std::find(m_container_keys.begin(), m_container_keys.end(), Key(key.item()));
-  if ( m_container_keys.empty() || key_iter != m_container_keys.end() )    {
-    auto mask_iter = std::find(m_input_masks.begin(), m_input_masks.end(), key.mask());
-    return m_input_masks.empty() || mask_iter != m_input_masks.end();
-  }
-  return false;
-}
-
-/// Main functional callback if specific work is known
-void DigiContainerProcessor::execute(DigiContext& context, WorkItems& work)  const    {
-  for( const auto& item : work )  {
-    if ( use_container(item.first) )   {
-      Key key = item.first;
-      info("%s+++ %p Using container: %016lX  --> %04X %08X %s",
-	   context.event->id(), (void*)this, key.key, key.mask(), key.item(), Key::key_name(key).c_str());
-    }
-  }
-}
diff --git a/DDDigi/src/DigiPlugins.cpp b/DDDigi/src/DigiPlugins.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2f9199b6316f8a275bf024119f73f757b64c506e
--- /dev/null
+++ b/DDDigi/src/DigiPlugins.cpp
@@ -0,0 +1,43 @@
+//==========================================================================
+//  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
+//
+//==========================================================================
+
+// Framework include files
+#include <DD4hep/Printout.h>
+#include <DD4hep/Primitives.h>
+#include <DD4hep/Plugins.h>
+#include <DDDigi/DigiKernel.h>
+#include <DDDigi/DigiAction.h>
+#include <DDDigi/DigiPlugins.h>
+
+static inline dd4hep::ComponentCast* component(void* p) { return (dd4hep::ComponentCast*)p; }
+
+void* dd4hep::digi::create_action(const std::string& factory,
+				  const dd4hep::digi::DigiKernel& kernel,
+				  const std::string& arg, void* (*cast)(void*))   {
+  void* object = PluginService::Create<DigiAction*>(factory, &kernel, arg);
+  if ( !object ) {
+    PluginDebug dbg;
+    object = PluginService::Create<DigiAction*>(factory, &kernel, arg);
+    if ( !object )  {
+      except("createPlugin","dd4hep-plugins: Failed to locate plugin %s. \n%s.",
+	     factory.c_str(), dbg.missingFactory(factory).c_str());
+    }
+  }
+  if ( cast )   {
+    void* obj = cast(object);
+    if ( obj ) return obj;
+    ComponentCast* c = component(object);
+    invalidHandleAssignmentError(typeid(cast),typeid(*c));
+  }
+  return object;
+}
diff --git a/DDDigi/src/DigiROOTInput.cpp b/DDDigi/src/DigiROOTInput.cpp
index 18a1bea03a87edb7b96a07d0b0ffe64934ee38c9..e3f0cfd9a644e0026725ba38a8012889c48c3bee 100644
--- a/DDDigi/src/DigiROOTInput.cpp
+++ b/DDDigi/src/DigiROOTInput.cpp
@@ -37,7 +37,7 @@ using namespace dd4hep::digi;
  */
 class DigiROOTInput::internals_t   {
 public:
-  typedef std::function<std::any(int, const char*, void*)> func_t;
+  typedef std::function<void(DataSegment&, int, const char*, void*)> func_t;
 
   class converter_t   {
   public:
@@ -48,6 +48,8 @@ public:
 
   /// Mutex to allow re-use of a single source for multiple input streams
   std::mutex m_lock        { };
+
+  /// Branches present in the current file
   std::map<unsigned long, converter_t>  branches;
   /// Reference to the current ROOT file to be read
   TFile*     file   { };
@@ -127,7 +129,7 @@ void DigiROOTInput::open_new_file()   const  {
 	TClass* cls = gROOT->GetClass( b->GetClassName(), kTRUE );
 	/// If there are no required branches, we convert everything
 	if ( this->m_containers.empty() )    {
-	  Key key(this->m_mask, b->GetName());
+	  Key key(b->GetName(), this->m_mask);
 	  b->SetAutoDelete( kFALSE );
 	  imp->branches[key.key] = {b, cls, imp->input_converter(cls)};
 	  continue;
@@ -135,7 +137,7 @@ void DigiROOTInput::open_new_file()   const  {
 	/// Otherwise only the entities asked for
 	for( const auto& bname : this->m_containers )    {
 	  if ( bname == b->GetName() )   {
-	    Key key(this->m_mask, b->GetName());
+	    Key key(b->GetName(), this->m_mask);
 	    b->SetAutoDelete( kFALSE );
 	    imp->branches[key.key] = {b, cls, imp->input_converter(cls)};
 	    break;
@@ -201,8 +203,7 @@ void DigiROOTInput::execute(DigiContext& context)  const   {
     input_len += nb;
     const auto&  func = *br.second.call;
     void** addr = (void**)b->GetAddress();
-    auto data = func(this->m_mask, b->GetName(), *addr);
-    segment.emplace(br.first, std::move(data));
+    func(segment, this->m_mask, b->GetName(), *addr);
   }
   info("%s+++ Read event %6ld [%ld bytes] from tree %s file: %s",
        event->id(), imp->entry, input_len, imp->tree->GetName(), imp->file->GetName());
diff --git a/DDDigi/src/DigiSegmentProcessor.cpp b/DDDigi/src/DigiSegmentProcessor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..07064d0cb6d64207fe2fe691278670d6d4116ba0
--- /dev/null
+++ b/DDDigi/src/DigiSegmentProcessor.cpp
@@ -0,0 +1,96 @@
+//==========================================================================
+//  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
+//
+//==========================================================================
+
+// Framework include files
+#include <DD4hep/InstanceCount.h>
+#include <DDDigi/DigiKernel.h>
+#include <DDDigi/DigiSegmentProcessor.h>
+
+using namespace dd4hep::digi;
+
+void DigiSegmentProcessor::work_t::merge_output(DepositVector&& data)   {
+  std::lock_guard<std::mutex> lock(output_lock);
+#if 0
+  if ( !output.get() )   {
+    output.reset(new DepositVector(std::move(data)));
+  }
+  else   {
+    output->merge(std::move(data));
+  }
+#endif
+}
+
+void DigiSegmentProcessor::work_t::emplace_output(CellID cell, EnergyDeposit&& data)   {
+  std::lock_guard<std::mutex> lock(output_lock);
+#if 0
+  if ( !output.get() )   {
+    output.reset(new DepositVector());
+  }
+  output->emplace(cell, std::move(data));
+#endif
+}
+
+/// Standard constructor
+DigiSegmentProcessor::DigiSegmentProcessor(const DigiKernel& krnl, const std::string& nam)
+  : DigiContainerProcessor(krnl, nam)
+{
+  InstanceCount::increment(this);
+}
+
+/// Default destructor
+DigiSegmentProcessor::~DigiSegmentProcessor() {
+  InstanceCount::decrement(this);
+}
+
+/// Main functional callback if specific work is known
+void DigiSegmentProcessor::handle_segment(DigiContext& /* context */, work_t& /* args */)  const   {
+}
+
+template <> void DigiParallelWorker<DigiSegmentProcessor,
+				    DigiSegmentSequence::work_t,
+				    dd4hep::VolumeID>::execute(void* args) const  {
+  calldata_t* work = reinterpret_cast<calldata_t*>(args);
+  action->handle_segment(work->context, *work);
+}
+
+/// Standard constructor
+DigiSegmentSequence::DigiSegmentSequence(const DigiKernel& krnl, const std::string& nam)
+  : DigiSegmentProcessor(krnl, nam)
+{
+  InstanceCount::increment(this);
+}
+
+/// Default destructor
+DigiSegmentSequence::~DigiSegmentSequence() {
+  InstanceCount::decrement(this);
+}
+
+/// Adopt new parallel worker
+void DigiSegmentSequence::adopt_processor(DigiContainerProcessor* action)   {
+  if ( !action )  {
+    except("+++ FAILED: attempt to add invalid processor!");
+  }
+  auto* proc = dynamic_cast<DigiSegmentProcessor*>(action);
+  if ( !proc )   {
+    error("+++ FAILED: Attempt to add processor %s of type %s",
+	  action->c_name(), typeName(typeid(*action)).c_str());
+    except("+++ DigiSegmentSplitter do ONLY accept processors of type DigiSegmentProcessor!");
+  }
+  proc->segment = segment;
+  m_workers.insert(new worker_t(proc, proc->segment.id));
+}
+
+/// Main functional callback
+void DigiSegmentSequence::handle_segment(DigiContext& /* context */, work_t& work) const {
+  m_kernel.submit(m_workers.get_group(), m_workers.size(), &work, m_parallel);
+}
diff --git a/DDDigi/src/DigiSegmentSplitter.cpp b/DDDigi/src/DigiSegmentSplitter.cpp
index d6578d6a9d7fec19ddfbe1e6762c42b1a99404a0..7fbb9a5c332b2ea41be81f6eb84f205003c56fd4 100644
--- a/DDDigi/src/DigiSegmentSplitter.cpp
+++ b/DDDigi/src/DigiSegmentSplitter.cpp
@@ -14,125 +14,105 @@
 // Framework include files
 #include <DD4hep/Plugins.h>
 #include <DD4hep/InstanceCount.h>
-
 #include <DDDigi/DigiKernel.h>
 #include <DDDigi/DigiContext.h>
+#include <DDDigi/DigiPlugins.h>
 #include <DDDigi/DigiSegmentSplitter.h>
 
 using namespace dd4hep::digi;
 
-class DigiSegmentSplitter::internals_t   {
-public:
-  /// Reference to master
-  DigiSegmentSplitter* split { nullptr };
-  /// Flag to check the initialization
-  bool inited  { false };
-  /// Default constructor
-  internals_t(DigiSegmentSplitter* s) : split(s)  {}
-  /// Initializing function: compute values which depend on properties
-  void initialize(DigiContext& context)   {
-    if ( !this->inited )   {
-      std::lock_guard<std::mutex> lock(context.initializer_lock());
-      if ( !this->inited )   {
-	this->split->initialize();
-	this->inited = true;
-      }
-    }
-  }
-};
-
-template <> void DigiParallelWorker<DigiSegmentAction,
-				    DigiSegmentSplitter::CallData,
-				    int>::execute(void* data) const  {
-  calldata_t* args = reinterpret_cast<calldata_t*>(data);
-  auto res = action->handleSegment(args->context, args->input);
-  if ( args->output && !res.empty() )   {
-    args->parent->register_output(*args->output, std::move(res));
-  }
-}
-
 /// Standard constructor
 DigiSegmentSplitter::DigiSegmentSplitter(const DigiKernel& kernel, const std::string& nam)
-  : DigiEventAction(kernel, nam),
+  : DigiContainerSequence(kernel, nam),
     m_split_tool(kernel.detectorDescription())
 {
-  this->internals = std::make_unique<internals_t>(this);
   declareProperty("detector",        m_detector_name);
   declareProperty("split_by",        m_split_by);
   declareProperty("processor_type",  m_processor_type);
   declareProperty("share_processor", m_share_processor = false);
-
-  declareProperty("input_segment",   m_input_id = "deposits");
-  declareProperty("input_mask",      m_input_mask);
-  declareProperty("output_segment",  m_output_id);
-  declareProperty("output_mask",     m_output_mask);
+  m_kernel.register_initialize(Callback(this).make(&DigiSegmentSplitter::initialize));
   InstanceCount::increment(this);
 }
 
 /// Default destructor
 DigiSegmentSplitter::~DigiSegmentSplitter() {
-  for(auto* w : m_workers ) detail::deletePtr(w);
-  m_workers.clear();
   InstanceCount::decrement(this);
 }
 
-
 /// Initialization function
 void DigiSegmentSplitter::initialize()   {
   char text[256];
   std::size_t count = 0;
 
   m_split_tool.set_detector(m_detector_name);
+  m_keys          = m_split_tool.collection_keys();
   m_split_context = m_split_tool.split_context(m_split_by);
-  m_data_keys = m_split_tool.collection_keys(m_input_mask);
-  m_splits = m_split_tool.split_segmentation(m_split_by);
+  m_splits        = m_split_tool.split_segmentation(m_split_by);
 
-  /// Create the processors:
+  /// 1) Check if the workers were pre-configured
+  if ( !m_workers.empty() )    {
+    bool bad = false;
+    const auto& workers = m_workers.get();
+    /// Create the processors:
+    for( auto& p : m_splits )   {
+      auto split_id = p.second.second;
+      bool ok = false;
+      for( const auto* w : workers )   {
+	if ( w->options == split_id )  { ok = true; break; }
+      }
+      if ( !ok )   {
+	error("+++ Missing processor for plit ID: %08ld", split_id);
+	bad = true;
+      }
+    }
+    if ( bad )    {
+      except("+++ If you add processors by hand, do it properly! "
+	     "Otherwise use the property 'processor_type'. "
+	     "This setup is invalid.");
+    }
+    return;
+  }
+  /// IF NOT:
+  /// 2) Create the processors using the 'processor_type' option
   for( auto& p : m_splits )   {
     ::snprintf(text, sizeof(text), "_%05X", m_split_context.split_id(p.first));
     std::string nam = this->name() + text;
-    auto* proc = PluginService::Create<DigiSegmentAction*>(m_processor_type, &m_kernel, nam);
+    auto* proc = createAction<DigiSegmentProcessor>(m_processor_type, m_kernel, nam);
     if ( !proc )   {
       except("+++ Failed to create split worker: %s/%s", m_processor_type.c_str(), nam.c_str());
     }
     proc->segment          = m_split_context;
     proc->segment.detector = p.second.first;
     proc->segment.id       = p.second.second;
-    m_workers.emplace_back(new Worker(proc, 0));
+    m_workers.insert(new worker_t(proc, proc->segment.id));
     ++count;
   }
   info("+++ Detector splitter is now fully initialized!");
 }
 
-/// Main functional callback
-void DigiSegmentSplitter::execute(DigiContext& context)  const    {
-  auto& input = context.event->get_segment(m_input_id);
-  this->internals->initialize(context);
-  for( auto k : m_data_keys )   {
-    auto* hits = input.pointer<DepositMapping>(k);
-    if ( hits )    {
-      /// prepare processors for execution
-      info("+++ Got hit collection %04X %08X. Prepare processors.", Key::mask(k), Key::item(k));
-      /// Now submit them
-      if ( m_output_id.empty() )   {
-	CallData data { context, *hits, nullptr, this };
-	m_kernel.submit(m_workers, &data);
-      }
-      else   {
-	DepositMapping result (m_name+"."+hits->name, m_output_mask);
-	CallData data { context, *hits, &result, this };
-	m_kernel.submit(m_workers, &data);
-	auto& output = context.event->get_segment(m_output_id);
-	output.emplace(result.key, std::move(result));
-      }
-    }
+/// Adopt new parallel worker
+void DigiSegmentSplitter::adopt_processor(DigiContainerProcessor* action)   {
+  if ( !action )  {
+    except("+++ FAILED: attempt to add invalid processor!");
+  }
+  auto* proc = dynamic_cast<DigiSegmentProcessor*>(action);
+  if ( !proc )   {
+    error("+++ FAILED: Attempt to add processor %s of type %s",
+	  action->c_name(), typeName(typeid(*action)).c_str());
+    except("+++ DigiSegmentSplitter do ONLY accept processors of type DigiSegmentProcessor!");
   }
+  m_workers.insert(new worker_t(proc, m_workers.size()));
 }
 
-/// Handle result from segment callbacks
-void DigiSegmentSplitter::register_output(DepositMapping& result,
-					       DepositVector&& output)  const  {
-  std::lock_guard<std::mutex> lock(m_output_lock);
-  result.merge(std::move(output));
+/// Main functional callback
+void DigiSegmentSplitter::execute(DigiContext& /* context */, work_t& work)  const    {
+  Key unmasked_key(work.key.item());
+  if ( std::find(m_keys.begin(), m_keys.end(), unmasked_key) != m_keys.end() )   {
+    if ( work.input.has_value() )   {
+      Key key = work.key;
+      info("+++ Got hit collection %04X %08X. Prepare processors for %sparallel execution.",
+	   key.mask(), key.item(), m_parallel ? "" : "NON-");
+      m_kernel.submit(m_workers.get_group(), m_workers.size(), &work, m_parallel);
+    }
+  }
 }
-
diff --git a/DDDigi/src/DigiSegmentationTool.cpp b/DDDigi/src/DigiSegmentationTool.cpp
index e2aea1bab5089a2b8f3c0507c99530e8af18e908..37026cfffad3f36f0b7f70378605dbdabcf5ec5b 100644
--- a/DDDigi/src/DigiSegmentationTool.cpp
+++ b/DDDigi/src/DigiSegmentationTool.cpp
@@ -89,10 +89,10 @@ vector<Key> DigiSegmentationTool::collection_keys(Key::mask_type mask)   const
   vector<Key> keys;
   if ( this->sensitive.isValid() )    {
     Readout rd = this->sensitive.readout();
-    auto colls = rd.collectionNames();
-    if ( colls.empty() ) colls.emplace_back(rd.name());
-    for( const auto& c : colls )
-      keys.emplace_back(Key(mask, c));
+    auto collection_names = rd.collectionNames();
+    if ( collection_names.empty() ) collection_names.emplace_back(rd.name());
+    for( const auto& collection : collection_names )
+      keys.emplace_back(Key(collection, mask));
     return keys;
   }
   except("DigiSegmentationTool",
diff --git a/DDDigi/src/DigiSegmentAction.cpp b/DDDigi/src/DigiSemaphore.cpp
similarity index 51%
rename from DDDigi/src/DigiSegmentAction.cpp
rename to DDDigi/src/DigiSemaphore.cpp
index 66e1b723094237c9e10acbe0f2658ca9a19a3472..5da4a444fbfa544e933877bc9fa682c687a9a846 100644
--- a/DDDigi/src/DigiSegmentAction.cpp
+++ b/DDDigi/src/DigiSemaphore.cpp
@@ -12,26 +12,25 @@
 //==========================================================================
 
 // Framework include files
-#include <DD4hep/InstanceCount.h>
-#include <DDDigi/DigiSegmentAction.h>
+#include <DDDigi/DigiSemaphore.h>
 
 using namespace dd4hep::digi;
 
-/// Standard constructor
-DigiSegmentAction::DigiSegmentAction(const DigiKernel& krnl, const std::string& nam)
-  : DigiAction(krnl, nam)
-{
-  InstanceCount::increment(this);
+/// Wait until reference count is NULL
+std::unique_lock<std::mutex> DigiSemaphore::wait_null()  {
+  std::unique_lock<std::mutex> protect(this->lock);
+  this->reference_count_is_null.wait(protect, [this] { return this->reference_count == 0; } );
+  return protect;
 }
 
-/// Default destructor
-DigiSegmentAction::~DigiSegmentAction() {
-  InstanceCount::decrement(this);
+/// Aquire semaphore count
+void DigiSemaphore::aquire()   {
+  std::lock_guard guard(this->lock);
+  ++this->reference_count;
 }
 
-/// Main functional callback
-DepositVector
-DigiSegmentAction::handleSegment(DigiContext&              /* context */,
-				 const DepositMapping&     /* depos   */) const {
-  return {};
+/// Release semaphore count
+void DigiSemaphore::release()  {
+  if ( --this->reference_count == 0 )
+    this->reference_count_is_null.notify_one();
 }
diff --git a/DDDigi/src/DigiStoreDump.cpp b/DDDigi/src/DigiStoreDump.cpp
index b93ed8a26f2464a26c9f3909002d7ee834d9c934..168cc9002232492b1939731bc6e39a3e41521579 100644
--- a/DDDigi/src/DigiStoreDump.cpp
+++ b/DDDigi/src/DigiStoreDump.cpp
@@ -52,9 +52,10 @@ void dd4hep::digi::DigiStoreDump::dump(const std::string& tag,
     const std::any& data = e.second;
     std::string nam = Key::key_name(key);
     std::string typ = typeName(data.type());
-    std::size_t idx = typ.find(", std::less<long long>, std::allocator<std::pair");
+    std::size_t idx = typ.find(", std::allocator<std::");
     if ( idx != std::string::npos ) typ = str_replace(typ, typ.substr(idx), ">");
-    typ = str_replace(str_replace(typ,"std::",""),"dd4hep::digi::","");
+    typ = str_replace(str_replace(typ,"std::",""),"dd4hep::","");
+    typ = str_replace(str_replace(typ,"sim::",""),"digi::","");
     if ( const auto* mapping = std::any_cast<DepositMapping>(&data) )   {
       str = this->format("%s|---- %4X %08X %-32s: %6ld hits      [%s]", 
 			 event.id(), key.values.mask, key.values.item,
diff --git a/DDDigi/src/DigiSynchronize.cpp b/DDDigi/src/DigiSynchronize.cpp
index bf01a5283f457ec5e8c91cf83fc8a27a1889b09f..536e0941489d1296f7d97479be4450eba55a9d33 100644
--- a/DDDigi/src/DigiSynchronize.cpp
+++ b/DDDigi/src/DigiSynchronize.cpp
@@ -39,18 +39,15 @@ DigiSynchronize::DigiSynchronize(const DigiKernel& kernel, const string& nam)
 
 /// Default destructor
 DigiSynchronize::~DigiSynchronize() {
-  for(auto* w : m_actors ) detail::deletePtr(w);
-  m_actors.clear();
   InstanceCount::decrement(this);
 }
 
 /// Pre-track action callback
 void DigiSynchronize::execute(DigiContext& context)  const   {
   auto start = chrono::high_resolution_clock::now();
-  if ( m_parallel )
-    m_kernel.submit(m_actors, &context);
-  else
-    m_kernel.execute(m_actors, &context);
+  if ( !m_actors.empty() )   {
+    m_kernel.submit(m_actors.get_group(), m_actors.size(), &context, m_parallel);
+  }
   chrono::duration<double> secs = chrono::high_resolution_clock::now() - start;
   debug("+++ Event: %8d (DigiSynchronize) Parallel: %-4s  %3ld actions [%8.3g sec]",
         context.event->eventNumber, yes_no(m_parallel), m_actors.size(),
@@ -60,7 +57,7 @@ void DigiSynchronize::execute(DigiContext& context)  const   {
 /// Add an actor responding to all callbacks. Sequence takes ownership.
 void DigiSynchronize::adopt(DigiEventAction* action) {
   if (action)    {
-    m_actors.emplace_back(new Worker(action, 0));
+    m_actors.insert(new Worker(action, 0));
     return;
   }
   except("DigiSynchronize","++ Attempt to add invalid actor!");
diff --git a/DDDigi/src/noise/DigiSignalProcessorSequence.cpp b/DDDigi/src/noise/DigiSignalProcessorSequence.cpp
index 25aec3071d13c336034e933295c933119c7f833e..f5d54c266ca8045aa204cf585f97ef3bf52a3f56 100644
--- a/DDDigi/src/noise/DigiSignalProcessorSequence.cpp
+++ b/DDDigi/src/noise/DigiSignalProcessorSequence.cpp
@@ -13,6 +13,7 @@
 
 // Framework include files
 #include <DD4hep/InstanceCount.h>
+#include <DDDigi/DigiKernel.h>
 #include <DDDigi/DigiSegmentation.h>
 #include <DDDigi/noise/DigiSignalProcessorSequence.h>
 
@@ -21,6 +22,14 @@
 
 using namespace dd4hep::digi;
 
+template <> void 
+DigiParallelWorker<DigiSignalProcessor, 
+		   DigiSignalProcessorSequence::CallData,
+		   int>::execute(void* data) const  {
+  calldata_t* args = reinterpret_cast<calldata_t*>(data);
+  args->value += (*action)(args->context);
+}
+
 /// Standard constructor
 DigiSignalProcessorSequence::DigiSignalProcessorSequence(const DigiKernel& kernel, const std::string& nam)
   : DigiSignalProcessor(kernel, nam)
@@ -35,9 +44,8 @@ DigiSignalProcessorSequence::~DigiSignalProcessorSequence() {
 
 /// Adopt a new action as part of the sequence. Sequence takes ownership.
 void DigiSignalProcessorSequence::adopt(DigiSignalProcessor* action)    {
-  if ( action )    {
-    action->addRef();
-    m_actors.add(action);
+  if (action)    {
+    m_actors.insert(new Worker(action, 0));
     return;
   }
   except("DigiSignalProcessorSequence","++ Attempt to add invalid actor!");
@@ -45,8 +53,12 @@ void DigiSignalProcessorSequence::adopt(DigiSignalProcessor* action)    {
 
 /// Pre-track action callback
 double DigiSignalProcessorSequence::operator()(DigiCellContext& context)  const   {
+  CallData args { context, 0e0 };
   double result = context.data.signal;
-  for ( const auto* p : m_actors )
-    result += p->operator()(context);
+  for ( const auto* p : m_actors.get() )  {
+    args.value = 0e0;
+    p->execute(&args);
+    result += args.value;
+  }
   return context.data.kill ? 0e0 : result;
 }
diff --git a/examples/DDDigi/scripts/TestMultiInteractions.py b/examples/DDDigi/scripts/TestMultiInteractions.py
index 223e18e673d85fba4f6ff69240d317e5cf9216ba..35f5152e3877bc7212aee1f6e2dada2640653259 100644
--- a/examples/DDDigi/scripts/TestMultiInteractions.py
+++ b/examples/DDDigi/scripts/TestMultiInteractions.py
@@ -42,7 +42,11 @@ def run():
   digi.info('Created input.overlay75')
   # ========================================================================================================
   event = digi.event_action('DigiSequentialActionSequence/EventAction')
-  combine = event.adopt_action('DigiContainerCombine/Combine', input_masks=[0x0, 0x1, 0x2, 0x3], deposit_mask=0xFEED)
+  combine = event.adopt_action('DigiContainerCombine/Combine',
+                               parallel=True,
+                               input_masks=[0x0, 0x1, 0x2, 0x3],
+                               deposit_mask=0xFEED,
+                               erase_combined=False)
   combine.erase_combined = True  # Not thread-safe! only do in SequentialActionSequence
   dump = event.adopt_action('DigiStoreDump/StoreDump')
   digi.check_creation([combine, dump])
diff --git a/examples/DDDigi/scripts/TestSegmentationSplit.py b/examples/DDDigi/scripts/TestSegmentationSplit.py
index bbe09bc19f01eaf78cb009db768f50cca962efda..5283350c200cb38b4f706d11718ade56e55e22d0 100644
--- a/examples/DDDigi/scripts/TestSegmentationSplit.py
+++ b/examples/DDDigi/scripts/TestSegmentationSplit.py
@@ -24,15 +24,17 @@ def run():
   event = digi.event_action('DigiSequentialActionSequence/EventAction')
   combine = event.adopt_action('DigiContainerCombine/Combine', input_masks=[0x0], deposit_mask=0xFEED)
   combine.erase_combined = True  # Not thread-safe! only do in SequentialActionSequence
-  splitter = event.adopt_action('DigiSegmentSplitter/Splitter',
-                                input_segment='deposits',
-                                input_mask=0xFEED,
-                                output_segment='',
-                                output_mask=0xBABE,
-                                detector='SiTrackerBarrel',
+  split_action = event.adopt_action('DigiContainerSequenceAction/SplitSequence',
+                                    parallel=True,
+                                    input_segment='deposits',
+                                    mask=0xFEED)
+  splitter = digi.create_action('DigiSegmentSplitter/Splitter',
+                                parallel=True,
                                 split_by='layer',
+                                detector='SiTrackerBarrel',
                                 processor_type='DigiSegmentDepositPrint')
-  splitter.parallel = True
+  split_action.adopt_container_processor(splitter, 'SiTrackerBarrelHits')
+
   dump = event.adopt_action('DigiStoreDump/StoreDump')
   digi.check_creation([combine, dump, splitter])
   digi.info('Created event.dump')
diff --git a/examples/DDDigi/src/DigiTestAction.cpp b/examples/DDDigi/src/DigiTestAction.cpp
index b798cf74ef7286d991d9587229e42177efee8984..d35cae8d5a6e86189b6d4c97b693345f4af954da 100644
--- a/examples/DDDigi/src/DigiTestAction.cpp
+++ b/examples/DDDigi/src/DigiTestAction.cpp
@@ -85,7 +85,7 @@ static void noop(int) {}
 using namespace std;
 using namespace dd4hep::digi;
 
-DECLARE_DIGIEVENTACTION_NS(dd4hep::digi,DigiTestAction)
+DECLARE_DIGIACTION_NS(dd4hep::digi,DigiTestAction)
 
 /// Standard constructor
 DigiTestAction::DigiTestAction(const DigiKernel& kernel, const string& nam)