diff --git a/Calorimeter/CaloEvent/CaloEvent/CaloCellClusterWeights.h b/Calorimeter/CaloEvent/CaloEvent/CaloCellClusterWeights.h
index b8b0ec8688918b2649a62c32b224a146b1a4b118..956378604ca1d511aa0d93d731faf097deeb7bf6 100644
--- a/Calorimeter/CaloEvent/CaloEvent/CaloCellClusterWeights.h
+++ b/Calorimeter/CaloEvent/CaloEvent/CaloCellClusterWeights.h
@@ -32,8 +32,6 @@ public:
   typedef store_t::iterator                   iterator;        ///< @brief Iterator type 
   /// @}
 
-  /// @name Constructors
-  /// @{
   /// @brief Default constructor
   ///
   /// The constructed data object provides a reserved and sized store appropriate for the total number of cells in the calorimeter.
@@ -45,8 +43,8 @@ public:
   /// 
   /// @param size requested store size
   CaloCellClusterWeights(size_t size);
-  /// @}
-
+  /// @brief Copy constructor
+  CaloCellClusterWeights(const CaloCellClusterWeights& cellClusterWeights);
   /// @brief Destructor 
   virtual ~CaloCellClusterWeights();
 
diff --git a/Calorimeter/CaloEvent/src/CaloCellClusterWeights.cxx b/Calorimeter/CaloEvent/src/CaloCellClusterWeights.cxx
index 4efe29d829c934c75ae0ada0c873adc514750155..8ea3633c3efb8015809b691da3194cb42b1f877e 100644
--- a/Calorimeter/CaloEvent/src/CaloCellClusterWeights.cxx
+++ b/Calorimeter/CaloEvent/src/CaloCellClusterWeights.cxx
@@ -21,6 +21,10 @@ CaloCellClusterWeights::CaloCellClusterWeights()
   : CaloCellClusterWeights( CELLCLUSTERLOOKUP )
 { }
 
+CaloCellClusterWeights::CaloCellClusterWeights(const CaloCellClusterWeights& cellClusterWeights)
+  : _hashTable(cellClusterWeights._hashTable)
+{ }
+
 CaloCellClusterWeights::~CaloCellClusterWeights()
 { }
 
diff --git a/Calorimeter/CaloExample/CaloRecEx/share/CaloRecOutputItemList_jobOptions.py b/Calorimeter/CaloExample/CaloRecEx/share/CaloRecOutputItemList_jobOptions.py
index 6d7041f7d903aa99217a21eb90999f15029e6d73..ac1be5c7246b363a033773abcddadbefeea7ea0a 100644
--- a/Calorimeter/CaloExample/CaloRecEx/share/CaloRecOutputItemList_jobOptions.py
+++ b/Calorimeter/CaloExample/CaloRecEx/share/CaloRecOutputItemList_jobOptions.py
@@ -7,10 +7,6 @@ CaloESDList = []
 
 CaloESDList += [ "CaloCellContainer#AllCalo" ]
 
-# compactify calo cell
-from CaloTools.CaloToolsConf import CaloCompactCellTool
-svcMgr.ToolSvc += CaloCompactCellTool()
-
 # add explicitly E4', MBTS cells and trigger output to ESD
 CaloESDList += [ "TileCellContainer#E4prContainer" ]
 CaloESDList += [ "TileCellContainer#MBTSContainer" ]
@@ -21,6 +17,11 @@ CaloClusterItemList=[]
 CaloClusterKeys=[]
 
 CaloClusterKeys+=["CaloCalTopoClusters"]
+if jobproperties.CaloRecFlags.doCaloTopoTower.get_Value():
+    CaloClusterKeys+=["CaloCalTopoTowers"]
+if jobproperties.CaloRecFlags.doCaloTopoSignal.get_Value():
+    CaloClusterKeys+=["CaloCalTopoSignals"]
+##CaloClusterKeys+=["CaloCalFwdTopoTowers"]
 CaloClusterKeys+=["CombinedCluster"]
 #CaloClusterKeys+=["EMTopoCluster430"]
 CaloClusterKeys+=["EMTopoSW35"]
@@ -84,9 +85,9 @@ if jobproperties.Beam.beamType() == 'cosmics' or jobproperties.Beam.beamType() =
 
 #List of AOD moments: (copied from CaloClusterTopoGetter)
 
-AODMoments=[#"LATERAL"
+AODMoments=["SECOND_R"
+            #,"LATERAL"
             #,"LONGITUDINAL"
-            "SECOND_R" 
             ,"SECOND_LAMBDA"
             ,"CENTER_MAG"
             ,"CENTER_LAMBDA"
@@ -107,36 +108,55 @@ AODMoments=[#"LATERAL"
             ,"EM_PROBABILITY"
             #,"PTD"
             ,"BadChannelList"
-            ,#"LATERAL"
+            #,"MASS"
             ]
+
+if jobproperties.CaloRecFlags.doExtendedClusterMoments.get_Value():
+    AODMoments += ["LATERAL"
+                   ,"ENG_BAD_HV_CELLS"
+                   ,"N_BAD_HV_CELLS"
+                   ,"SIGNIFICANCE"
+                   ,"CELL_SIGNIFICANCE"
+                   ,"CELL_SIG_SAMPLING"
+                   ,"PTD"
+                   ,"MASS"
+                   ]
 try:
     from Digitization.DigitizationFlags import digitizationFlags
     if digitizationFlags.doDigiTruth():
 
-      AODMoments+=["SECOND_R_DigiHSTruth"
-                ,"SECOND_LAMBDA_DigiHSTruth"
-                ,"CENTER_MAG_DigiHSTruth"
-                ,"CENTER_LAMBDA_DigiHSTruth"
-                ,"FIRST_ENG_DENS_DigiHSTruth"
-                ,"ENG_FRAC_MAX_DigiHSTruth"
-                ,"ISOLATION_DigiHSTruth"
-                ,"ENG_BAD_CELLS_DigiHSTruth"
-                ,"N_BAD_CELLS_DigiHSTruth"
-                ,"BADLARQ_FRAC_DigiHSTruth"
-                #,"ENG_BAD_HV_CELLS_Truth"
-                #,"N_BAD_HV_CELLS_Truth"
-                ,"ENG_POS_DigiHSTruth"
-                #,"SIGNIFICANCE_Truth"
-                #,"CELL_SIGNIFICANCE_Truth"
-                #,"CELL_SIG_SAMPLING_Truth"
-                ,"AVG_LAR_Q_DigiHSTruth"
-                ,"AVG_TILE_Q_DigiHSTruth"
-                ,"EM_PROBABILITY_DigiHSTruth"
-                #,"PTD_Truth"
-                ,"ENERGY_DigiHSTruth"
-                ,"ETA_DigiHSTruth"
-                ,"PHI_DigiHSTruth"
-                ]
+        AODMoments+=["SECOND_R_DigiHSTruth"
+                     ,"SECOND_LAMBDA_DigiHSTruth"
+                     ,"CENTER_MAG_DigiHSTruth"
+                     ,"CENTER_LAMBDA_DigiHSTruth"
+                     ,"FIRST_ENG_DENS_DigiHSTruth"
+                     ,"ENG_FRAC_MAX_DigiHSTruth"
+                     ,"ISOLATION_DigiHSTruth"
+                     ,"ENG_BAD_CELLS_DigiHSTruth"
+                     ,"N_BAD_CELLS_DigiHSTruth"
+                     ,"BADLARQ_FRAC_DigiHSTruth"
+                     #,"ENG_BAD_HV_CELLS_Truth"
+                     #,"N_BAD_HV_CELLS_Truth"
+                     ,"ENG_POS_DigiHSTruth"
+                     #,"SIGNIFICANCE_Truth"
+                     #,"CELL_SIGNIFICANCE_Truth"
+                     #,"CELL_SIG_SAMPLING_Truth"
+                     ,"AVG_LAR_Q_DigiHSTruth"
+                     ,"AVG_TILE_Q_DigiHSTruth"
+                     ,"EM_PROBABILITY_DigiHSTruth"
+                     #,"PTD_Truth"
+                     ,"ENERGY_DigiHSTruth"
+                     ,"ETA_DigiHSTruth"
+                     ,"PHI_DigiHSTruth"
+                 ]
+        if jobproperties.CaloRecFlags.doExtendedClusterMoments.get_Value():
+            AODMoments+=["ENG_BAD_HV_CELLS_Truth"
+                         ,"N_BAD_HV_CELLS_Truth"
+                         ,"SIGNIFICANCE_Truth"
+                         ,"CELL_SIGNIFICANCE_Truth"
+                         ,"CELL_SIG_SAMPLING_Truth"
+                         ,"PTD_Truth"
+                 ]
 except:
     log = logging.getLogger('CaloRecOutputItemList')
     log.info('Unable to import DigitizationFlags in CaloRecOutputItemList_jobOptions. Expected in AthenaP1')
@@ -153,6 +173,11 @@ CaloClusterKeys=[]
 
 
 CaloClusterKeys+=["CaloCalTopoClusters"]
+if jobproperties.CaloRecFlags.doCaloTopoTower.get_Value():
+    CaloClusterKeys+=["CaloCalTopoTowers"]
+if jobproperties.CaloRecFlags.doCaloTopoSignal.get_Value():
+    CaloClusterKeys+=["CaloCalTopoSignals"]
+##CaloClusterKeys+=["CaloCalFwdTopoTowers"]
 CaloClusterKeys+=["CombinedCluster"]
 #CaloClusterKeys+=["EMTopoCluster430"]
 CaloClusterKeys+=["EMTopoSW35"]
diff --git a/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerCalibrator.h b/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerCalibrator.h
index 52c5a21cb3ef285d77b32495b26ed130d68d722b..dd6b13bbedfea55d726448bea8c313e4da91f955 100644
--- a/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerCalibrator.h
+++ b/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerCalibrator.h
@@ -3,6 +3,8 @@
 #ifndef CALOREC_CALOTOPOCLUSTERFROMTOWERCALIBRATOR_H
 #define CALOREC_CALOTOPOCLUSTERFROMTOWERCALIBRATOR_H
 
+#include "StoreGate/ReadHandleKey.h"
+
 #include "AthenaBaseComps/AthAlgTool.h" 
 
 #include "CaloRec/CaloClusterCollectionProcessor.h"
@@ -18,6 +20,9 @@ public:
   /// @brief Tool constructor
   CaloTopoClusterFromTowerCalibrator(const std::string& type,const std::string& name,const IInterface* pParent);
 
+  /// @brief Tool initialization
+  StatusCode initialize();
+ 
   /// @brief Tool execution
   StatusCode execute(xAOD::CaloClusterContainer* pClusCont);
 
@@ -25,16 +30,20 @@ private:
 
   /// @name Tool properties
   /// @{
-  std::string m_cellClusterWeightsKey; ///> @brief Key for cell-cluster weight look up object
-  bool        m_orderByPt;             ///> @brief Turn on pT ordering
+  SG::ReadHandleKey<CaloCellClusterWeights> m_cellClusterWeightKey; ///< Key for handle to cell-cluster weight look up object 
+  bool                                      m_orderByPt;            ///< Turn on pT ordering if @c true
   /// @}
-
-  /// @brief Default container key
-  static std::string m_defaultKey;
 };
 /// @class CaloTopoClusterFromTowerCalibrator
 ///
-/// @brief A cluster builder tool calibrated topo-clusters formed from calorimeter towers to the LCW scale. 
+/// @brief A cluster builder tool to calibrate topo-clusters formed from (EM) calorimeter towers to the LCW scale. 
 ///
+/// This module applies LCW weights to cells contributing to towers represented by @c xAOD::CaloCluster objects. 
+/// The overall energy contribution of a given cell contributing to a given tower is then @f$ w_{\rm geo} \times w_{\rm LCW} \times E_{\rm cell} @f$,
+/// where @f$ w_{\rm geo} @f$ is the geometrical weight, @f$ w_{\rm LCW} @f$ is
+/// the calibration weight the cell received from the LCW calibration in the context of the @c xAOD::CaloCluster objects it contributes to (at most two),
+/// and @f$ E_{\rm cell} @f$ is the cell energy on EM scale. More details on the weights are given on 
+/// <a href="https://twiki.cern.ch/twiki/bin/view/AtlasSandboxProtected/CaloTowerPerformance" title="https://twiki.cern.ch/twiki/bin/view/AtlasSandboxProtected/CaloTowerPerformance">this page</a>.
+///  
 /// @author Peter Loch <loch@physics.arizona.edu>      
 #endif
diff --git a/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerHelpers.h b/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerHelpers.h
index bf56e0146012d557a9dacfc7078d4dd5d3f3df8a..797de7a586b8ec3570d9c301d7204e6b3b413193 100644
--- a/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerHelpers.h
+++ b/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerHelpers.h
@@ -8,6 +8,14 @@
 #include "xAODCaloEvent/CaloCluster.h"
 
 #include <cstdarg>
+#include <string>
+#include <map>
+
+#define _SNAMELU( NAME ) { CaloSampling::NAME , std::string( #NAME ) }
+#define _SIDLU( ID ) { std::string( #ID ) , CaloSampling::ID } 
+
+#define _MNAMELU( NAME ) { xAOD::CaloCluster::NAME, std::string( #NAME ) }
+#define _MIDLU( ID ) { std::string( #ID ) , xAOD::CaloCluster::ID }
 
 class CaloCell;
 
@@ -147,5 +155,117 @@ namespace CaloRec {
     bool calculateKine(xAOD::CaloCluster* pClus,bool onlyKine=false);
     /// @}
   } // Helpers
+
+  ///@brief Stores
+  namespace Lookup {
+    ///@name Sampling names and identifier maps
+    ///@{
+    static const std::map<CaloSampling::CaloSample,std::string> samplingNames  = {
+      // EM
+      _SNAMELU( PreSamplerB ), _SNAMELU( EMB1 ),  _SNAMELU( EMB2 ),  _SNAMELU( EMB3 ), 
+      _SNAMELU( PreSamplerE ), _SNAMELU( EME1 ),  _SNAMELU( EME2 ),  _SNAMELU( EME3 ),
+      // HAD 
+      _SNAMELU( HEC0 ),     _SNAMELU( HEC1 ),     _SNAMELU( HEC2 ),     _SNAMELU( HEC3 ), 
+      _SNAMELU( TileBar0 ), _SNAMELU( TileBar1 ), _SNAMELU( TileBar2 ),
+      // Tile
+      _SNAMELU( TileBar0 ), _SNAMELU( TileBar1 ), _SNAMELU( TileBar2 ),
+      _SNAMELU( TileGap1 ), _SNAMELU( TileGap2 ), _SNAMELU( TileGap3 ),
+      _SNAMELU( TileExt0 ), _SNAMELU( TileExt1 ), _SNAMELU( TileExt2 ),
+      // FCal
+      _SNAMELU( FCAL0 ),    _SNAMELU( FCAL1 ),    _SNAMELU( FCAL2 ),
+      // Mini-FCal
+      _SNAMELU( MINIFCAL0 ),    _SNAMELU( MINIFCAL1 ),    _SNAMELU( MINIFCAL2 ), _SNAMELU( MINIFCAL3 ),
+      // unknown
+      _SNAMELU( Unknown ) 
+    };  ///< Name lookup by sampling identifier (data)
+    static const std::map<std::string,CaloSampling::CaloSample> samplingIds = {
+      // EM
+      _SIDLU( PreSamplerB ), _SIDLU( EMB1 ),  _SIDLU( EMB2 ),  _SIDLU( EMB3 ), 
+      _SIDLU( PreSamplerE ), _SIDLU( EME1 ),  _SIDLU( EME2 ),  _SIDLU( EME3 ),
+      // HAD 
+      _SIDLU( HEC0 ),     _SIDLU( HEC1 ),     _SIDLU( HEC2 ),     _SIDLU( HEC3 ), 
+      _SIDLU( TileBar0 ), _SIDLU( TileBar1 ), _SIDLU( TileBar2 ),
+      // Tile
+      _SIDLU( TileBar0 ), _SIDLU( TileBar1 ), _SIDLU( TileBar2 ),
+      _SIDLU( TileGap1 ), _SIDLU( TileGap2 ), _SIDLU( TileGap3 ),
+      _SIDLU( TileExt0 ), _SIDLU( TileExt1 ), _SIDLU( TileExt2 ),
+      // FCal
+      _SIDLU( FCAL0 ),    _SIDLU( FCAL1 ),    _SIDLU( FCAL2 ),
+      // Mini-FCal
+      _SIDLU( MINIFCAL0 ),    _SIDLU( MINIFCAL1 ),    _SIDLU( MINIFCAL2 ), _SIDLU( MINIFCAL3 ),
+      // unknown
+      _SIDLU( Unknown )
+    };  ///< Identifier lookup by sampling name (data)
+    static const std::string&       getSamplingName(CaloSampling::CaloSample sid); ///< Lookup sampling name by identifier (function) 
+    static CaloSampling::CaloSample getSamplingId(const std::string& sname);       ///< Lookup sampling identifier by name (function)
+    ///@}
+    ///@name Cluster moment names and identifier maps
+    ///@{
+    static const std::map<xAOD::CaloCluster::MomentType,std::string> clusterMomentNames = {
+      _MNAMELU( FIRST_PHI ),_MNAMELU( FIRST_ETA ),_MNAMELU( SECOND_R ),_MNAMELU( SECOND_LAMBDA ),_MNAMELU( DELTA_PHI ),_MNAMELU( DELTA_THETA ),_MNAMELU( DELTA_ALPHA ),      
+      _MNAMELU( CENTER_X ),_MNAMELU( CENTER_Y ),_MNAMELU( CENTER_Z ),_MNAMELU( CENTER_MAG ),_MNAMELU( CENTER_LAMBDA ),_MNAMELU( LATERAL ),_MNAMELU( LONGITUDINAL ),     
+      _MNAMELU( ENG_FRAC_EM ),_MNAMELU( ENG_FRAC_MAX ),_MNAMELU( ENG_FRAC_CORE ),_MNAMELU( FIRST_ENG_DENS ),_MNAMELU( SECOND_ENG_DENS ),_MNAMELU( ENG_POS ),  
+      _MNAMELU( ISOLATION ),_MNAMELU( ENG_BAD_CELLS ),_MNAMELU( N_BAD_CELLS ),_MNAMELU( N_BAD_CELLS_CORR ),_MNAMELU( BAD_CELLS_CORR_E ),_MNAMELU( BADLARQ_FRAC ),    
+      _MNAMELU( SIGNIFICANCE ),_MNAMELU( CELL_SIGNIFICANCE ),_MNAMELU( CELL_SIG_SAMPLING ),_MNAMELU( AVG_LAR_Q ),_MNAMELU( AVG_TILE_Q ),_MNAMELU( ENG_BAD_HV_CELLS ),
+      _MNAMELU( N_BAD_HV_CELLS ),_MNAMELU( PTD ),_MNAMELU( MASS ),_MNAMELU( EM_PROBABILITY ),_MNAMELU( HAD_WEIGHT ),_MNAMELU( OOC_WEIGHT ),_MNAMELU( DM_WEIGHT ),                  
+      _MNAMELU( TILE_CONFIDENCE_LEVEL ),_MNAMELU( VERTEX_FRACTION ),_MNAMELU( NVERTEX_FRACTION ),_MNAMELU( ETACALOFRAME ),_MNAMELU( PHICALOFRAME ),_MNAMELU( ETA1CALOFRAME ),    
+      _MNAMELU( PHI1CALOFRAME ),_MNAMELU( ETA2CALOFRAME ),_MNAMELU( PHI2CALOFRAME ),_MNAMELU( ENG_CALIB_TOT ),_MNAMELU( ENG_CALIB_OUT_L ),_MNAMELU( ENG_CALIB_OUT_M ),  
+      _MNAMELU( ENG_CALIB_OUT_T ),_MNAMELU( ENG_CALIB_DEAD_L ),_MNAMELU( ENG_CALIB_DEAD_M ),_MNAMELU( ENG_CALIB_DEAD_T ),_MNAMELU( ENG_CALIB_EMB0 ),_MNAMELU( ENG_CALIB_EME0 ),
+      _MNAMELU( ENG_CALIB_TILEG3 ),_MNAMELU( ENG_CALIB_DEAD_TOT ),_MNAMELU( ENG_CALIB_DEAD_EMB0 ),_MNAMELU( ENG_CALIB_DEAD_TILE0 ),_MNAMELU( ENG_CALIB_DEAD_TILEG3 ),
+      _MNAMELU( ENG_CALIB_DEAD_EME0 ),_MNAMELU( ENG_CALIB_DEAD_HEC0 ),_MNAMELU( ENG_CALIB_DEAD_FCAL ),_MNAMELU( ENG_CALIB_DEAD_LEAKAGE ),_MNAMELU( ENG_CALIB_DEAD_UNCLASS ),
+      _MNAMELU( ENG_CALIB_FRAC_EM ),_MNAMELU( ENG_CALIB_FRAC_HAD ),_MNAMELU( ENG_CALIB_FRAC_REST ),_MNAMELU( ENERGY_DigiHSTruth ),_MNAMELU( ETA_DigiHSTruth ),_MNAMELU( PHI_DigiHSTruth ),
+      _MNAMELU( TIME_DigiHSTruth ),_MNAMELU( ENERGY_CALIB_DigiHSTruth ),_MNAMELU( ETA_CALIB_DigiHSTruth ),_MNAMELU( PHI_CALIB_DigiHSTruth ),_MNAMELU( TIME_CALIB_DigiHSTruth ),
+      _MNAMELU( FIRST_PHI_DigiHSTruth ),_MNAMELU( FIRST_ETA_DigiHSTruth ),_MNAMELU( SECOND_R_DigiHSTruth ),_MNAMELU( SECOND_LAMBDA_DigiHSTruth ),_MNAMELU( DELTA_PHI_DigiHSTruth ),
+      _MNAMELU( DELTA_THETA_DigiHSTruth ),_MNAMELU( DELTA_ALPHA_DigiHSTruth ),_MNAMELU( CENTER_X_DigiHSTruth ),_MNAMELU( CENTER_Y_DigiHSTruth ),_MNAMELU( CENTER_Z_DigiHSTruth ),
+      _MNAMELU( CENTER_MAG_DigiHSTruth ),_MNAMELU( CENTER_LAMBDA_DigiHSTruth ),_MNAMELU( LATERAL_DigiHSTruth ),_MNAMELU( LONGITUDINAL_DigiHSTruth ),_MNAMELU( ENG_FRAC_EM_DigiHSTruth ),
+      _MNAMELU( ENG_FRAC_MAX_DigiHSTruth ),_MNAMELU( ENG_FRAC_CORE_DigiHSTruth ),_MNAMELU( FIRST_ENG_DENS_DigiHSTruth ),_MNAMELU( SECOND_ENG_DENS_DigiHSTruth ),
+      _MNAMELU( ISOLATION_DigiHSTruth ),_MNAMELU( ENG_BAD_CELLS_DigiHSTruth ),_MNAMELU( N_BAD_CELLS_DigiHSTruth ),_MNAMELU( N_BAD_CELLS_CORR_DigiHSTruth ),
+      _MNAMELU( BAD_CELLS_CORR_E_DigiHSTruth ),_MNAMELU( BADLARQ_FRAC_DigiHSTruth ),_MNAMELU( ENG_POS_DigiHSTruth ),_MNAMELU( SIGNIFICANCE_DigiHSTruth ),
+      _MNAMELU( CELL_SIGNIFICANCE_DigiHSTruth ),_MNAMELU( CELL_SIG_SAMPLING_DigiHSTruth ),_MNAMELU( AVG_LAR_Q_DigiHSTruth ),_MNAMELU( AVG_TILE_Q_DigiHSTruth ),
+      _MNAMELU( ENG_BAD_HV_CELLS_DigiHSTruth ),_MNAMELU( N_BAD_HV_CELLS_DigiHSTruth ),_MNAMELU( EM_PROBABILITY_DigiHSTruth ),_MNAMELU( HAD_WEIGHT_DigiHSTruth ),
+      _MNAMELU( OOC_WEIGHT_DigiHSTruth ),_MNAMELU( DM_WEIGHT_DigiHSTruth )
+    }; ///< Moment names by moment types
+    static const std::map<std::string,xAOD::CaloCluster::MomentType> clusterMomentTypes = {
+      _MIDLU( FIRST_PHI ),_MIDLU( FIRST_ETA ),_MIDLU( SECOND_R ),_MIDLU( SECOND_LAMBDA ),_MIDLU( DELTA_PHI ),_MIDLU( DELTA_THETA ),_MIDLU( DELTA_ALPHA ),      
+      _MIDLU( CENTER_X ),_MIDLU( CENTER_Y ),_MIDLU( CENTER_Z ),_MIDLU( CENTER_MAG ),_MIDLU( CENTER_LAMBDA ),_MIDLU( LATERAL ),_MIDLU( LONGITUDINAL ),     
+      _MIDLU( ENG_FRAC_EM ),_MIDLU( ENG_FRAC_MAX ),_MIDLU( ENG_FRAC_CORE ),_MIDLU( FIRST_ENG_DENS ),_MIDLU( SECOND_ENG_DENS ),_MIDLU( ENG_POS ),  
+      _MIDLU( ISOLATION ),_MIDLU( ENG_BAD_CELLS ),_MIDLU( N_BAD_CELLS ),_MIDLU( N_BAD_CELLS_CORR ),_MIDLU( BAD_CELLS_CORR_E ),_MIDLU( BADLARQ_FRAC ),    
+      _MIDLU( SIGNIFICANCE ),_MIDLU( CELL_SIGNIFICANCE ),_MIDLU( CELL_SIG_SAMPLING ),_MIDLU( AVG_LAR_Q ),_MIDLU( AVG_TILE_Q ),_MIDLU( ENG_BAD_HV_CELLS ),
+      _MIDLU( N_BAD_HV_CELLS ),_MIDLU( PTD ),_MIDLU( MASS ),_MIDLU( EM_PROBABILITY ),_MIDLU( HAD_WEIGHT ),_MIDLU( OOC_WEIGHT ),_MIDLU( DM_WEIGHT ),                  
+      _MIDLU( TILE_CONFIDENCE_LEVEL ),_MIDLU( VERTEX_FRACTION ),_MIDLU( NVERTEX_FRACTION ),_MIDLU( ETACALOFRAME ),_MIDLU( PHICALOFRAME ),_MIDLU( ETA1CALOFRAME ),    
+      _MIDLU( PHI1CALOFRAME ),_MIDLU( ETA2CALOFRAME ),_MIDLU( PHI2CALOFRAME ),_MIDLU( ENG_CALIB_TOT ),_MIDLU( ENG_CALIB_OUT_L ),_MIDLU( ENG_CALIB_OUT_M ),  
+      _MIDLU( ENG_CALIB_OUT_T ),_MIDLU( ENG_CALIB_DEAD_L ),_MIDLU( ENG_CALIB_DEAD_M ),_MIDLU( ENG_CALIB_DEAD_T ),_MIDLU( ENG_CALIB_EMB0 ),_MIDLU( ENG_CALIB_EME0 ),
+      _MIDLU( ENG_CALIB_TILEG3 ),_MIDLU( ENG_CALIB_DEAD_TOT ),_MIDLU( ENG_CALIB_DEAD_EMB0 ),_MIDLU( ENG_CALIB_DEAD_TILE0 ),_MIDLU( ENG_CALIB_DEAD_TILEG3 ),
+      _MIDLU( ENG_CALIB_DEAD_EME0 ),_MIDLU( ENG_CALIB_DEAD_HEC0 ),_MIDLU( ENG_CALIB_DEAD_FCAL ),_MIDLU( ENG_CALIB_DEAD_LEAKAGE ),_MIDLU( ENG_CALIB_DEAD_UNCLASS ),
+      _MIDLU( ENG_CALIB_FRAC_EM ),_MIDLU( ENG_CALIB_FRAC_HAD ),_MIDLU( ENG_CALIB_FRAC_REST ),_MIDLU( ENERGY_DigiHSTruth ),_MIDLU( ETA_DigiHSTruth ),_MIDLU( PHI_DigiHSTruth ),
+      _MIDLU( TIME_DigiHSTruth ),_MIDLU( ENERGY_CALIB_DigiHSTruth ),_MIDLU( ETA_CALIB_DigiHSTruth ),_MIDLU( PHI_CALIB_DigiHSTruth ),_MIDLU( TIME_CALIB_DigiHSTruth ),
+      _MIDLU( FIRST_PHI_DigiHSTruth ),_MIDLU( FIRST_ETA_DigiHSTruth ),_MIDLU( SECOND_R_DigiHSTruth ),_MIDLU( SECOND_LAMBDA_DigiHSTruth ),_MIDLU( DELTA_PHI_DigiHSTruth ),
+      _MIDLU( DELTA_THETA_DigiHSTruth ),_MIDLU( DELTA_ALPHA_DigiHSTruth ),_MIDLU( CENTER_X_DigiHSTruth ),_MIDLU( CENTER_Y_DigiHSTruth ),_MIDLU( CENTER_Z_DigiHSTruth ),
+      _MIDLU( CENTER_MAG_DigiHSTruth ),_MIDLU( CENTER_LAMBDA_DigiHSTruth ),_MIDLU( LATERAL_DigiHSTruth ),_MIDLU( LONGITUDINAL_DigiHSTruth ),_MIDLU( ENG_FRAC_EM_DigiHSTruth ),
+      _MIDLU( ENG_FRAC_MAX_DigiHSTruth ),_MIDLU( ENG_FRAC_CORE_DigiHSTruth ),_MIDLU( FIRST_ENG_DENS_DigiHSTruth ),_MIDLU( SECOND_ENG_DENS_DigiHSTruth ),
+      _MIDLU( ISOLATION_DigiHSTruth ),_MIDLU( ENG_BAD_CELLS_DigiHSTruth ),_MIDLU( N_BAD_CELLS_DigiHSTruth ),_MIDLU( N_BAD_CELLS_CORR_DigiHSTruth ),
+      _MIDLU( BAD_CELLS_CORR_E_DigiHSTruth ),_MIDLU( BADLARQ_FRAC_DigiHSTruth ),_MIDLU( ENG_POS_DigiHSTruth ),_MIDLU( SIGNIFICANCE_DigiHSTruth ),
+      _MIDLU( CELL_SIGNIFICANCE_DigiHSTruth ),_MIDLU( CELL_SIG_SAMPLING_DigiHSTruth ),_MIDLU( AVG_LAR_Q_DigiHSTruth ),_MIDLU( AVG_TILE_Q_DigiHSTruth ),
+      _MIDLU( ENG_BAD_HV_CELLS_DigiHSTruth ),_MIDLU( N_BAD_HV_CELLS_DigiHSTruth ),_MIDLU( EM_PROBABILITY_DigiHSTruth ),_MIDLU( HAD_WEIGHT_DigiHSTruth ),
+      _MIDLU( OOC_WEIGHT_DigiHSTruth ),_MIDLU( DM_WEIGHT_DigiHSTruth )
+    }; ///< Moment names buy moment identifiers
+    static const std::string& getMomentName(xAOD::CaloCluster::MomentType momentType);                                ///< Get moment name associated with a moment type 
+    static bool               getMomentType(const std::string& momentName,xAOD::CaloCluster::MomentType& momentType); ///< Get moment type associated with a moment name
+    static bool               haveMomentType(const std::string& momentName);                                          ///< Returns @c true in case moment is known  
+    ///@}
+  } // Lookup
 } // CaloRec
+
+inline const std::string&       CaloRec::Lookup::getSamplingName(CaloSampling::CaloSample sid) { return samplingNames.find(sid)->second; }
+inline CaloSampling::CaloSample CaloRec::Lookup::getSamplingId(const std::string& sname) { 
+  auto fid(samplingIds.find(sname)); return fid != samplingIds.end() ? fid->second : samplingIds.find("Unknown")->second; 
+}
+
+inline bool               CaloRec::Lookup::haveMomentType(const std::string& momentName) { return clusterMomentTypes.find(momentName) != clusterMomentTypes.end(); }
+inline const std::string& CaloRec::Lookup::getMomentName(xAOD::CaloCluster::MomentType momentType) { return clusterMomentNames.at(momentType); }
+inline bool               CaloRec::Lookup::getMomentType(const std::string& momentName,xAOD::CaloCluster::MomentType& momentType) {
+  bool isOk(haveMomentType(momentName)); if ( isOk ) { momentType = clusterMomentTypes.at(momentName); } return isOk;
+}
+
+ 
 #endif
diff --git a/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerMaker.h b/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerMaker.h
index c9f493dc982eecdde5c04799d4b8c76f71f05c1b..858bb1dfbcda02d2b2e01266c6e828fc3aa0fdab 100644
--- a/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerMaker.h
+++ b/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerMaker.h
@@ -1,248 +1,254 @@
 // -*- c++ -*- 
-/* Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration */
+/* Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration */
 #ifndef CALOREC_CALOTOPOCLUSTERFROMTOWERMAKER_H
 #define CALOREC_CALOTOPOCLUSTERFROMTOWERMAKER_H
 
-#include "AthenaBaseComps/AthAlgTool.h" 
+#include "StoreGate/ReadHandleKey.h"
+#include "StoreGate/WriteHandleKey.h"
+
+#include "GaudiKernel/ServiceHandle.h"
 
-#include "CaloEvent/CaloCellTowerLink.h"
+#include "AthenaBaseComps/AthAlgTool.h" 
 
 #include "CaloRec/CaloClusterCollectionProcessor.h"
+#include "CaloRec/CaloTowerGeometrySvc.h"
 #include "CaloRec/CaloProtoCluster.h"
 
+#include "CaloEvent/CaloCell.h"
+#include "CaloEvent/CaloClusterCellLink.h"
+#include "CaloEvent/CaloCellClusterWeights.h"
+#include "CaloEvent/CaloCellContainer.h"
+
+#include "CaloGeoHelpers/CaloSampling.h"
+
 #include "xAODCaloEvent/CaloClusterContainer.h"
-#include "xAODCaloEvent/CaloTowerContainer.h"
 
 #include <string>
-#include <map>
-
 #include <vector>
-#include <boost/tuple/tuple.hpp>
+#include <bitset>
+#include <map>
 
-class CaloClusterCellLink;
-class CaloCell;
-class CaloCellClusterWeights;
+#ifndef _CALOTOPOCLUSTERFROMTOWERMAKER_BITSET_SIZE
+#define _CALOTOPOCLUSTERFROMTOWERMAKER_BITSET_SIZE 28
+#endif
 
 class CaloTopoClusterFromTowerMaker : public AthAlgTool, virtual public CaloClusterCollectionProcessor
 {
 public:
 
-  /// @brief Tool constructor
+  ///@brief Tool constructor
   CaloTopoClusterFromTowerMaker(const std::string& type,const std::string& name,const IInterface* pParent);
 
-  /// @brief Tool initialization
-  StatusCode initialize();
-
-  /// @brief Tool execution
-  StatusCode execute(xAOD::CaloClusterContainer* pClusCont);
-
-  /// @brief Tool finalize
-  StatusCode finalize();
+  ///@name @c AthAlgTool and @c CaloClusterCellProcessor interface implementations
+  ///@{
+  StatusCode initialize();                                   ///< Setting up the operational mode and corresponding parameters
+  StatusCode execute(xAOD::CaloClusterContainer* pClusCont); ///< Execute the tool and fill the @c xAOD::CaloClusterContainer pointed to by @c pClusCont
+  StatusCode finalize();                                     ///< Finalize the tool (no action)
+  ///@}
 
 private:
 
-  /// @name Tool properties
-  /// @{
-  std::string m_towerContainerKey;    ///> @brief Key (name) of input tower container
-  std::string m_cellTowerLinksKey;    ///> @brief Derived property (cannot be set by user)
-  std::string m_cellContainerKey;     ///> @brief Key (name) of input cell container 
-  std::string m_clusterContainerKey;  ///> @brief Key of topo-cluster container to be used as source for cells
-  std::string m_cellClusterWeightKey; ///> @brief Key for cell-cluster weight look up object
-  bool        m_orderByPt;            ///> @brief Orders cluster container by @f$ p_{\text{T}} @f$, default @c true
-  double      m_energyThreshold;      ///> @brief Cell energy threshold
-  bool        m_applyLCW;             ///> @brief Prepare LCW calibration
-  /// @}
+  ///@name Internally used types
+  ///@{
+  typedef std::vector<CaloProtoCluster> protocont_t; ///< Container for @c CaloProtoCluster objects
+  typedef std::size_t                   uint_t;      ///< Unsigned integral type
+  ///@}
 
-  /// @name Processing flags
+  /// @name Tool properties
   /// @{
-  /// @brief Energy threshold flag
-  bool        m_applyEnergyThreshold;
-  /// @brief Use cells from topo-clusters flag
-  bool        m_useCellsFromClusters; 
-  /// @brief Prepare LCW calibration
-  bool        m_prepareLCW;
+  ServiceHandle<CaloTowerGeometrySvc>           m_towerGeometrySvc;         ///< Tower geometry service
+  SG::ReadHandleKey<xAOD::CaloClusterContainer> m_clusterContainerKey;      ///< Topo-cluster container key
+  SG::ReadHandleKey<CaloCellContainer>          m_cellContainerKey;         ///< Calorimeter cell container
+  SG::WriteHandleKey<CaloCellClusterWeights>    m_cellClusterWeightKey;     ///< Cell signal weights in clusters key
+  bool                                          m_orderByPt;                ///< Orders cluster container by @f$ p_{\text{T}} @f$, default @c true
+  bool                                          m_prepareLCW;               ///< Prepare LCW calibration, default is @c false
+  bool                                          m_useCellsFromClusters;     ///< Use cells from topo-clusters if @c true, else use all cells, default is @c true
+  bool                                          m_applyCellEnergyThreshold; ///< Apply cell energy threshold, default is @c false 
+  bool                                          m_doCellIndexCheck;         ///< Check cell hash index consistency if @c true (default @c false)
+  bool                                          m_buildCombinedSignal;      ///< Build topo-clusters within given @f$ y @f$ range, else topo-towers
+  double                                        m_energyThreshold;          ///< Cell energy threshold, default is set in @c m_energyThresholdDef
+  double                                        m_clusterRange;             ///< Range where topo-clusters are used when <tt>m_buildCombinedSignal = true</tt>
   /// @}
 
   /// @name Constants and parameters
   /// @{
-  /// @brief Number of cells
-  int         m_numberOfCells;
-  /// @brief Number of samplings
-  int         m_numberOfSamplings;
-  /// @brief Default energy threshold
-  static double m_energyThresholdDef;
-  /// @brief Default container key
-  static std::string m_defaultKey;
+  uint_t             m_numberOfCells;          ///< Number of cells (highest cell index + 1)
+  uint_t             m_maxCellHash;            ///< Maximum hash index of cell ( number of cells - 1)
+  uint_t             m_numberOfSamplings;      ///< Number of samplings
+  uint_t             m_numberOfTowers;         ///< Number of towers
+  static double      m_energyThresholdDef;     ///< Default energy threshold
+  static double      m_clusterRangeDef;        ///< Default cluster @f$ y @f$ range
+  static std::string m_defaultKey;             ///< Default container key
+  static uint_t      m_errorValueUINT;         ///< Error value for @c uint_t type values
   /// @}
 
-  /// @brief Access to cell-to-tower links
-  const CaloCellTowerLink::Map* m_cellTowerLinksHandle;
-
-  /// @name Cache for cell weights
-  /// @{
-  /// @brief Data structure tagging a cell as a cluster cell and storing its weight
-  typedef boost::tuples::tuple<bool,double> celltag_t;      
-  /// @brief Random access lookup of cell tags
-  typedef std::vector<celltag_t>            celltagstore_t;
-  /// @brief Cell tag store
-  celltagstore_t m_cellTagStore;
-  /// @brief Previously used cell indices
-  std::vector<size_t> m_cellIdxStore;
-  /// @}
+  ///@name Internally used helpers
+  ///@{
+  xAOD::CaloCluster::ClusterSize getClusterSize(uint_t etaBins,uint_t phiBins); ///< Returns a cluster size tag from number of eta and phi bins in tower grid
+  xAOD::CaloCluster::ClusterSize getClusterSize(uint_t towerBins);              ///< Returns a cluster size tag from number of towers (bins) in tower grid
+  int cleanupCells(CaloClusterCellLink* clk,uint_t nclus);                      ///< Checks @c CaloClusterCellLink for consistency
+  ///@}
 
-  /// @brief Pathologies
-  ///
-  /// Internally used object collects pathologies observed for @c CaloCell objects in execution.
-  /// Each pathology is identified by a pre-defined message, and the number of pathologies is produced by
-  /// counting the number of occurancies of a given message.
-  class CellPathology
-  {
-  public:
-    /// @name Internally used data types
-    /// @{
-    typedef std::string                   pathology_t;
-    typedef int                           count_t;
-    typedef std::map<pathology_t,count_t> cache_t;
-    /// @}
-
-    /// @brief Constructor
-    CellPathology();
-    /// @name Set pathologies
-    /// @{
-    /// @brief Invalid @c CaloCell pointer 
-    ///
-    /// An invalid @c NULL  pointer was found at a given valid index in the @c CaloCellContainer. 
-    ///
-    /// @param cellhash hash identifier (index in @c CaloCellContainer) for a given @c CaloCell. 
-    /// @param cptr     cell pointer (should be @c NULL in this case)
-    void setInvalidPointer(int cellhash,const CaloCell* cptr);
-    /// @brief Invalid @c CaloCell hash
-    ///
-    /// A cell identifier is found to be out of range.
-    ///
-    /// @param cellhash hash identifier (index in @c CaloCellContainer - here invalid!) for a given @c CaloCell. 
-    /// @param cptr     cell pointer (should be @c NULL in this case)
-    void setInvalidHash(int cellhash,const CaloCell* cptr);
-    /// @brief Invalid sampling identifier for given @c CaloCell
-    ///
-    /// The sampling identifier associated with a @c CaloCell is not valid (not in the enumerator list).
-    ///
-    /// @param cellhash hash identifier (index in @c CaloCellContainer - here invalid!) for a given @c CaloCell. 
-    /// @param cptr     cell pointer (should be @c NULL in this case)
-    void setInvalidSampling(int cellhash,const CaloCell* cptr);
-    /// @}
-
-    /// @name Accessors to pathologies stats
-    /// @{
-    const cache_t& invalidPointer()  const; ///< @brief Occurances of invalid pointers
-    const cache_t& invalidHash()     const; ///< @brief Occurances of invalid hash identifier
-    const cache_t& invalidSampling() const; ///< @brief Occurances of invalid sampling identifier
-    /// @}
-
-  private:
-
-    /// @name Caches
-    /// @{
-    cache_t _invalidPointer;    ///< @brief Invalid pointer
-    cache_t _invalidHash;       ///< @brief Invalid hash
-    cache_t _invalidSampling;   ///< @brief Invalid sampling
-    /// @}
-
-    /// @name Helpers
-    /// @{
-    /// @brief Add a pathology to the cache
-    ///
-    /// @param msg message describing pathology
-    /// @param map cache
-    void _addMsg(const pathology_t& msg,cache_t& map);
-    /// @}
-  };
-
-  /// @brief Cluster size tag 
-  xAOD::CaloCluster::ClusterSize getClusterSize(const xAOD::CaloTowerContainer& towerCont);
-
-  /// @brief Clean up cell links - remove null pointers
-  int cleanupCells(CaloClusterCellLink* clk);
-
-  //  /// @brief Calculate kinemactics
-  //  bool calculateKine(xAOD::CaloCluster* pClus);
-
-  /// @brief Book keeping
-  void addWarning(const std::string& msg);
-  std::map<std::string,unsigned int> m_warning;
-  void addMessage(const std::string& msg);
-  std::map<std::string,unsigned int> m_message;
-  CellPathology m_cellProblems;
-  void printProblems(const std::map<std::string,int>& map);
-
-  typedef std::vector<CaloProtoCluster*> protocont_t;
-
-  /// @name Helpers
-  /// @{
-  /// @brief Build inclusive tower clusters
-  ///
-  /// Fills all cells into proto-clusters.
-  ///
-  /// @return @c false in case of problems with data access or inconsistent data structures.
-  ///
-  /// @param pCellCont reference to non-modifiable @c CaloCellContainer
-  /// @param pProtoCont reference to @c CaloProtoCluster container filled on output.
-  ///
-  bool buildInclClusters(const CaloCellContainer& pCellCont,protocont_t& pProtoCont);     
-  /// @brief Build exclusive tower clusters
+  ///@name Tower builders
   ///
-  /// Fills cells above energy threshold into proto-clusters.
+  ///@return @c false in case of problems with data access or inconsistent data structures 
   ///
-  /// @return @c false in case of problems with data access or inconsistent data structures.
+  ///@param pCellCont reference to non-modifiable @c CaloCellContainer
+  ///@param pProtoCont reference to @c CaloProtoCluster container filled on output.
+  ///@param clusCont reference to non-modifiable @c xAOD::CaloClusterContainer
+  ///@param protoCont reference to modifiable proto-cluster container
   ///
-  /// @param pCellCont reference to non-modifiable @c CaloCellContainer
-  /// @param pProtoCont reference to @c CaloProtoCluster container filled on output.
-  bool buildExclClusters(const CaloCellContainer& pCellCont,protocont_t& pProtoCont);     
-  //  bool buildFilteredClusters(const CaloCellContainer& pCellCont,protocont_t& pProtoCont); ///< @brief Build tower clusters from topo-cluster cells
-  /// @brief Add cells to proto-clusters
+  ///@return 
+  ///@{
+  uint_t buildInclTowers(const CaloCellContainer& pCellCont,protocont_t& pProtoCont);            ///< Inclusive towers
+  uint_t buildExclTowers(const CaloCellContainer& pCellCont,protocont_t& pProtoCont);            ///< Exclusive towers
+  uint_t buildEMTopoTowers(const xAOD::CaloClusterContainer& clusCont,protocont_t& protoCont);   ///< EM topo-towers
+  uint_t buildLCWTopoTowers(const xAOD::CaloClusterContainer& clusCont,protocont_t& protoCont);  ///< LCW topo-towers
+  ///@}
+  /// @brief Adding cells to proto-clusters
   ///
   /// @return @c true if cell successfully added to one (or more) proto-clusters
   ///
   /// @param cptr       pointer ton non-modifiable @c CaloCell object
   /// @param pProtoCont reference to proto-cluster container
-  bool addCellToProtoCluster(const CaloCell* cptr,protocont_t& pProtoCont);
-  /// @}
-
-  /// @brief Cache
-  CaloCellClusterWeights* m_cellClusterWeights;
+  /// @param weight     additional (global) weight of cell (e.g. for geometrical weight for combined EM-scale signals)  
+  bool addCellToProtoCluster(const CaloCell* cptr,protocont_t& pProtoCont,double weight=1.);
+  
+  ///@name Helpers
+  ///@{
+  bool   filterProtoCluster(const CaloClusterCellLink& clnk)  const; ///< Checks for and removes invalid cell links  
+  bool   checkCellIndices(const CaloCellContainer* pCellCont) const; ///< Checks consistency between cell indices and hash identifiers
+  bool   isValidIndex(uint_t idx)                             const; ///< Checks if argument is a valid index value 
+  uint_t badIndexValue()                                      const; ///< Returns value indicating a bad index
+  ///@}
+
+  ///@name Excluded samplings
+  ///@{
+  std::vector<CaloSampling::CaloSample>                       m_excludedSamplings;         ///< List of excluded samplings (@c CaloSampling::CaloSample enumerators)
+  std::vector<std::string>                                    m_excludedSamplingsName;     ///< List of excluded samplings (human-readable names)
+  std::bitset< _CALOTOPOCLUSTERFROMTOWERMAKER_BITSET_SIZE >   m_excludedSamplingsPattern;  ///< Bit pattern indicates if sampling is excluded
+  ///@}
+
+  ///@name Monitoring
+  ///@{
+  ///@}
 };
 
-inline void CaloTopoClusterFromTowerMaker::CellPathology::_addMsg(const std::string& msg,std::map<std::string,int>& map)
-{ if ( map.find(msg) == map.end() ) { map[msg] = 0; } map[msg] += 1; }
+inline CaloTopoClusterFromTowerMaker::uint_t CaloTopoClusterFromTowerMaker::badIndexValue()          const { return m_errorValueUINT;       } 
+inline bool                                  CaloTopoClusterFromTowerMaker::isValidIndex(uint_t idx) const { return idx != badIndexValue(); }
 
-inline const std::map<std::string,int>& CaloTopoClusterFromTowerMaker::CellPathology::invalidPointer() const
-{ return _invalidPointer; }
-inline const std::map<std::string,int>& CaloTopoClusterFromTowerMaker::CellPathology::invalidHash() const
-{ return _invalidHash; }
-inline const std::map<std::string,int>& CaloTopoClusterFromTowerMaker::CellPathology::invalidSampling() const
-{ return _invalidSampling; }
-
-/// @class CaloTopoClusterFromTowerMaker
-/// @author Peter Loch <loch@physics.arizona.edu>
+///@class CaloTopoClusterFromTowerMaker
 ///
-/// @brief A cluster builder tool forming topo-clusters representing calorimeter tower signals on a regular grid in @f$ (\eta,\phi) @f$ space.
+/// @brief A cluster builder tool forming topo-clusters representing calorimeter tower signals on a regular grid in @f$ (\eta,\phi) @f$ space. By default,
+///        EM-scale <i>topo-towers</i> are created from cells in topo-clusters.
 ///
-/// This tool provides functionality allowing the creation of topo-cluster collections representing towers. The output data objects are 
-/// @c xAOD::CaloCluster , collected into @c xAOD::CaloClusterContainer . Several configuration options are supported:
+/// This tool fills EM-scale towers and stores them as @c xAOD::CaloCluster. It supports several operational modes, which are
+/// controlled by tool properties. It fills a container of type @c xAOD::CaloClusterContainer. The properties controlling its
+/// specific behavior are:  
 ///
+/// <table width="90%" align="center" style="border-width:0px;">
+/// <tr><td align="center" colspan="4" style="background-color:rgb(245,245,220);color:rgb(165,42,42);"><b>Properties defining tool behavior</b></td></tr>
+/// <tr style="color:rgb(131,42,34);">
+/// <td align="left" width="25%">Property name</td>
+/// <td align="center" width="25%">Property type</td>
+/// <td align="center" width="25%">Default value</td>
+/// <td align="left" width="25%">Comment</td>
+/// </tr> 
+/// <tr>
+/// <td align="left" valign="top"><tt>OrderClusterByPt</tt></td>
+/// <td align="center" valign="top"><tt>bool</tt></td>
+/// <td align="center" valign="top"><tt>false</tt></td>
+/// <td align="left" valign="top">if @c true, the @c xAOD::CaloClusterContainer is ordered by @f$ p_{\rm T}^{\rm clus} @f$.  See further comments below.</td>
+/// </tr>
+/// <tr>
+/// <td align="left" valign="top"><tt>PrepareLCW</tt></td>
+/// <td align="center" valign="top"><tt>bool</tt></td>
+/// <td align="center" valign="top"><tt>false</tt></td>
+/// <td align="left" valign="top">if @c true, the tool fills a @c CaloCellClusterWeights object and records it into the event store to be used by @c CaloTopoClusterFromTowerCalibrator</td>
+/// </tr>
+/// <tr>
+/// <td align="left" valign="top"><tt>UseCellsFromClusters</tt></td>
+/// <td align="center" valign="top"><tt>bool</tt></td>
+/// <td align="center" valign="top"><tt>true</tt></td>
+/// <td align="left" valign="top">if @c true, only cells from topo-clusters are used to fill the towers (<i>topo-towers</i>); else, @a inclusive @a towers are filled with all cells.</td>
+/// </tr>
+/// <tr><td align="center" colspan="4" style="background-color:rgb(245,245,220);color:rgb(165,42,42);"><b>Properties setting variables for operational modes</b></td></tr>
+/// <tr style="color:rgb(131,42,34)">
+/// <td align="left" width="25%">Property name</td>
+/// <td align="center" width="25%">Property type</td>
+/// <td align="center" width="25%">Default value</td>
+/// <td align="left" width="25%">Comment</td>
+/// </tr> 
+/// <tr>
+/// <td align="left" valign="top"><tt>CellEnergyThreshold</tt></td>
+/// <td align="center" valign="top"><tt>double</tt></td>
+/// <td align="center" valign="top"><tt>m_energyThresholdDef</tt></td>
+/// <td align="left" valign="top">cell energy threshold used in exclusive mode only. See further comments below.</td>
+/// </tr>
+/// <tr>
+/// <td align="left" valign="top"><tt>CellContainerKey</tt></td>
+/// <td align="center" valign="top"><tt>SG::ReadHandleKey<CaloCellContainer></tt></td>
+/// <td align="center" valign="top"><tt>"AllCalo"</tt></td>
+/// <td align="left" valign="top">cell container key is needed to pick up @c CaloCellContainer for all operational modes.</td> 
+/// </tr>
+/// <tr>
+/// <td align="left" valign="top"><tt>ClusterContainerKey</tt></td>
+/// <td align="center" valign="top"><tt>SG::ReadHandleKey<xAOD::CaloClusterContainer></tt></td>
+/// <td align="center" valign="top"><tt>"CaloTopoCluster"</tt></td>
+/// <td align="left" valign="top">cluster container key is needed to pick up @c xAOD::CaloClusterContainer for filtered mode (<tt>UseCellsFromCluster = true</tt>)
+/// </tr>
+/// <tr>
+/// <td align="left" valign="top"><tt>CellClusterWeightKey</tt></td>
+/// <td align="center" valign="top"><tt>SG::WriteHandleKey<CaloCellClusterWeights></tt></td>
+/// <td align="center" valign="top"><tt>&minus;N/A&minus;</tt></td>
+/// <td align="left" valign="top">key for @c CaloCellClusterWeights object is needed if <tt>PrepareLCW = true</tt>. Default is empty key. 
+/// </tr>
+/// <tr>
+/// <td align="left" valign="top"><tt>BuildCombinedTopoSignal</tt></td>
+/// <td align="center" valign="top"><tt>bool</tt></td>
+/// <td align="center" valign="top"><tt>false</tt></td>
+/// <td align="left" valign="top">turns on combined topo-cluster/topo-tower output, with topo-clusters used within the rapidity range defined by <tt>TopoClusterRange</tt> and topo-towers elsewhere.</td></tr>
+/// </tr>
+/// <tr>
+/// <td align="left" valign="top"><tt>TopoClusterRange</tt></td>
+/// <td align="center" valign="top"><tt>double</tt></td>
+/// <td align="center" valign="top"><tt>5.</tt></td>
+/// <td align="left" valign="top">sets the range @f$ y_{\rm topo-cluster}^{\rm max} @f$ for using topo-clusters when <tt>BuildCombinedTopoSignal = true</tt>; 
+/// topo-clusters with @f$ \left|y_{\rm topo-cluster}\right| < y_{\rm topo-cluster}^{\rm max} @f$ are used. 
+/// </tr>
+/// </table> 
+///                                 
+/// The towers can be classified as:
 /// -# <b>inclusive cell towers</b>
-///    All cells are collected into towers, independent of their signal. This is the default behaviour.
+///    All cells are collected into inclusive towers, independent of their signal. Requires properties <tt>UseCellsFromClusters = false</tt> and <tt>UseCellEnergyThreshold = false</tt>. Only EM
+///    towers are possible, as cells not collected into topo-clustersdo not have a calibration applied.
 /// -# <b>exclusive cell towers</b> 
-///    Cells with @f$ E > E_{\rm min} @f$ are collected into towers. This behaviour is turned on by poviding a property @c CellEnergyThreshold setting @f$ E_{\rm min} @f$.
+///    Cells with @f$ E > E_{\rm min} @f$ are collected into exclusive towers. This behaviour is turned on by  <tt>UseCellsFromClusters = false</tt> and <tt>UseCellEnergyThreshold = true</tt>. A
+///    meaningful <tt>CellEnergyThreshold</tt> value needs to be provided in addition.
 /// -# <b>filtered mode</b>
-///    Cells contributing to standard topo-clusters are collected into towers. This behaviour is triggered by providing the @c CaloTopoClusterContainerKey property with a 
-///    valid key for a topo-cluster collection in the event store.
+///    Cells contributing to standard topo-clusters are collected into topo-towers. This behaviour is triggered by <tt>UseCellsFromClusters = true</tt>. Optionally, LCW calibration can be applied
+///    to these towers by setting <tt>PrepareLCW = true</tt> and scheduling a @c CaloTopoClusterFromTowerCalibrator tool after the cluster moment calculators. The values of the <tt>UseEnergyThreshold</tt>
+///    and <tt>CellEnergyThreshold</tt> properties are ignored in this mode. A valid event store key needs to be provided in the to pick up the topo-cluster container. Note that building EM 
+///    topo-towers requires topo-clusters on EM scale (no LCW applied) to get the correct geometrical cell weights only. LCW topo-towers require LCW scale topo-clusters to get the correct full geometrical 
+///    and calibration weights.
+/// -# <b>mixed mode</b>
+///    Cells contributing to standard topo-clusters are collected into towers if these topo-clusters are outside of a give rapidity range. The rapidity range is defined by the <tt>TopoClusterRange</tt>
+///    property. This mode is turned on by setting the property <tt>BuildCombinedTopoSignal = true</tt>. It is turned off by default (<tt>BuildCombinedTopoSignal = false</tt>). 
+///    EM scale and LCW scale is possible, as in the filtered mode. 
 ///
 ///  Configuration 2 and 3 are exclusive, with 3 overwriting 2. The output topo-clusters represent calorimeter towers on the EM scale. The can be handed to cluster moment
 ///  tools (needs EM scale) and, if desired, to a dedicated cluster calibration tool of type @c xAOD::CaloTowerClusterFromTowerCalibrator .  
 ///
-///  To avoid mulitple retrievals of the same weights by searching for cells in (many) topo-clusters, the overall weight of the cell signal is stored in a random access
+///  To avoid multiple retrievals of the same weights by searching for cells in (many) topo-clusters, the overall weight of the cell signal is stored in a random access
 ///  look-up table stored in a @c CaloCellClusterWeights object in the detector store. This object is created by this tool, if needed. If the tool property 
-///  @c CellWeightLookupKey is set, this object will be generated, filled, and recorded. 
-///      
+///  @c CellWeightLookupKey is set, this object will be generated, filled, and recorded. This is essential for calibrated topo-towers!
+///
+///@note The @c OrderByPt property, which orders the container by descending transverse momentum, is only useful for EM towers. Applyin LCW may lead to a different 
+///      order - if a container with LCW towers should be ordered, the corresponding property @c OrderByPt of the @c CaloTopoClusterFromTowerCalibrator tool should
+///      be set to @c true. 
+///
+///@note Many more details on the towers are available on 
+///      <a href="https://twiki.cern.ch/twiki/bin/view/AtlasSandboxProtected/CaloTowerPerformance" title="https://twiki.cern.ch/twiki/bin/view/AtlasSandboxProtected/CaloTowerPerformance">this page</a>.
+///
+/// @author Peter Loch <loch@physics.arizona.edu>
 #endif
diff --git a/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerMonitor.h b/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerMonitor.h
new file mode 100644
index 0000000000000000000000000000000000000000..52abb14cae72c62adc049bf768c2d524b454455d
--- /dev/null
+++ b/Calorimeter/CaloRec/CaloRec/CaloTopoClusterFromTowerMonitor.h
@@ -0,0 +1,342 @@
+// -*- c++ -*-
+#ifndef CALOREC_CALOTOPOCLUSTERFROMTOWERMONITOR_H
+#define CALOREC_CALOTOPOCLUSTERFROMTOWERMONITOR_H
+
+#include "StoreGate/ReadHandleKey.h"
+
+#include "GaudiKernel/ServiceHandle.h"
+
+#include "AthenaBaseComps/AthHistogramAlgorithm.h"
+
+#include "xAODCaloEvent/CaloClusterContainer.h"
+
+#include "CaloRec/CaloTowerGeometrySvc.h"
+#include "CaloRec/CaloTopoClusterFromTowerHelpers.h"
+
+#include "xAODCaloEvent/CaloCluster.h"
+#include "CaloEvent/CaloCell.h"
+
+#include <vector>
+#include <bitset>
+#include <map>
+#include <cmath>
+
+#include "TH1D.h"
+#include "TH2D.h"
+
+///@brief Algorithm to monitor kinematics and composition of towers (and clusters)
+///
+/// This algorithm fills a few histograms relevant for monitoring tower-related information, As both topo-clusters and towers
+/// use the same representation in data, both signal definitions can be monitored - even though most histograms are designed
+/// for distributions of tower-specific signal features.
+///
+/// The algorithm has several process control properties and configured values:
+///
+/// | Property          |  Default     | Controled feature or value
+/// :------------------ | :----------: |:-------------------------------------------------------------------------------------------
+/// | @c DoGeoAutoBins  |   @c true    | if @c true the binning of @f$ y @f$ and @f$ \phi @f$ is constructed from the tower geometry database;
+/// | ^                 | ^            | else it is user-defined by the corresponding value property
+/// | Hotspot analysis is on if @f$ y_{\rm min}^{\rm hotspot} < y_{\rm max}^{\rm hotspot} @f$ and @f$ \phi_{\rm min}^{\rm hotspot} < \phi_{\rm max}^[\rm hotspot} @f$ (on by defaults) ||
+/// | @c EtaMinHotspot  |   0.         | defines lower limit @f$ y_{\rm min}^{\rm hotspot} @f$ of rapidity for the hotspot analysis
+/// | @c EtaMaxHotspot  |   0.1        | defines upper limit @f$ y_{\rm max}^{\rm hotspot} @f$ of rapidity for the hotspot analysis
+/// | @c PhiMinHotspot  |   0.         | defines lower limit @f$ \phi_{\rm min}^{\rm hotspot} @f$ of azimuth for the hotspot analysis
+/// | @c PhiMaxHotspot  |   0.1        | defines upper limit @f$ \phi_{\rm max}^{\rm hotspot} @f$ of azimuth for the hotspot analysis  
+/// | Histogram binning: (pseudo)-rapidity (@f$ y_{\rm min} \geq y_{\rm max} @f$ returns error) ||
+/// | @c EtaTowersBins  |   100        | number of rapidity bins
+/// | @c EtaTowersMin   |  -5.         | lower limit of rapidity range @f$ y_{\rm min} @f$
+/// | @c EtaTowersMax   |   5.         | upper limit of rapidity range @f$ y_{\rm max} @f$
+/// | Histogram binning: azimuth (@f$ \phi_{\rm min \geq \phi_[\rm max} @f$ returns error) ||
+/// | @c PhiTowersBins  |  64          | number of azimuth bins
+/// | @c PhiTowersMin   | @f$ -\pi @f$ | lower limit of azimuth range @f$ \phi_{\rm min} @f$
+/// | @c PhiTowersMax   | @f$ +\pi @f$ | upper limit of azimuth range @f$ \phi_{\rm max} @f$
+/// | Histogram binning: transverse momentum (@f$ p_{\rm T,min} < p_{\rm T,max} @f$ returns error) ||
+/// | @c PtTowersBins   | 220          | number of transverse momentum bins
+/// | @c PtTowersMin    | -10 GeV      | lower limit of transverse momentum range @f$ p_{\rm T,min} @f$
+/// | @c PtTowersMax    | 100 GeV      | upper limit of transverse momentum range @f$ p_{\rm T,max} @f$
+class CaloTopoClusterFromTowerMonitor : public AthHistogramAlgorithm
+{
+public:
+
+  /// Default algorithm constructor
+  CaloTopoClusterFromTowerMonitor(const std::string& name,ISvcLocator* pSvcLocator);
+  /// Base-class destructor
+  virtual ~CaloTopoClusterFromTowerMonitor();
+
+  ///@brief Initialization
+  ///
+  /// This method configures the algorithm, allocates the tower geometry service and books the histograms by 
+  /// invoking the @c book() method. In addition, histograms are filled with geometry information retrieved
+  /// from the tower geometry service (static information not depending on event variables).   
+  virtual StatusCode initialize();
+  ///@brief Execution
+  ///
+  /// This method allocates the input data container and fills all histograms. If configured, it also provides
+  ///  
+  virtual StatusCode execute();    ///< Execution fills histograms.
+
+
+private:
+
+  ///@name Data access properties
+  ///@{
+  SG::ReadHandleKey<xAOD::CaloClusterContainer> m_towerContainerKey; ///< Allocator for input @c xAOD::CaloClusterContainer 
+  ServiceHandle<CaloTowerGeometrySvc>           m_towerGeometrySvc;  ///< Allocator for tower geometry services
+  ///@}
+
+  ///@name Histogram binning properties
+  ///@{
+  int m_ncBins;    ///< Number of cells in towers - number of bins
+  double m_ncMin;  ///< Number of cells in towers - lower limit of value range
+  double m_ncMax;  ///< Number of cells in towers - upper limit of value range
+  int m_nBins;     ///< Tower multiplicity - number of bins  
+  double m_nMin;   ///< Tower multiplicity - lower limit of value range  
+  double m_nMax;   ///< Tower multiplicity - upper limit of value range
+  int m_ptBins;    ///< Tower @f$ p_{\rm T} @f$ - number of bins 
+  double m_ptMin;  ///< Tower @f$ p_{\rm T} @f$ - lower limit of value range (in GeV)
+  double m_ptMax;  ///< Tower @f$ p_{\rm T} @f$ - upper limit of value range (in GeV)
+  int m_etaBins;   ///< Tower rapidity - number of bins
+  double m_etaMin; ///< Tower rapidity - lower limit of value range
+  double m_etaMax; ///< Tower rapidity - upper limit of value range
+  int m_phiBins;   ///< Tower azimuth - number of bins
+  double m_phiMin; ///< Tower azimuth - lower limit of value range
+  double m_phiMax; ///< Tower azimuth - upper limit of value range
+
+  double m_hsEtaMin; double m_hsEtaMax;
+  double m_hsPhiMin; double m_hsPhiMax;
+
+  bool m_doGeoAutoBins; 
+  bool m_doHotspot;
+  ///@}
+
+  TH1D* h_n;
+  TH1D* h_pt;
+  TH1D* h_eta;
+  TH1D* h_phi;
+  TH1D* h_nc;
+  TH1D* h_samp;
+
+  TH2D* d_n_eta_phi;
+  TH2D* d_nc_eta_phi;
+  TH2D* d_pt_eta;
+  TH2D* d_nc_eta;
+
+  TH2D* d_wgt_samp;
+  TH2D* d_ntt_samp;
+  TH2D* d_geo_samp;
+  TH2D* d_maxtowers_samp;
+  TH2D* d_wgttowers_samp; 
+
+  TH2D* d_maxcells_eta;
+  TH2D* d_allwghts_eta;
+
+  TH2D* d_deta_eta;
+  TH2D* d_dphi_eta;
+  TH2D* d_dphi_deta;
+
+  TH2D* d_detac_eta;
+  TH2D* d_dphic_eta;
+  TH2D* d_dphic_detac;
+
+  TH2D* d_detac_samp;
+  TH2D* d_dphic_samp;
+
+  // hot spot
+  TH1D* h_nc_hs;
+  TH1D* h_n_hs;
+  TH1D* h_pt_hs;
+  TH1D* h_eta_hs;
+  TH1D* h_phi_hs;
+  TH1D* h_samp_hs;
+
+  TH2D* d_n_eta_phi_hs;
+  TH2D* d_nc_eta_phi_hs;
+
+  TH2D* d_deta_eta_hs;
+  TH2D* d_dphi_eta_hs;
+  TH2D* d_dphi_deta_hs;
+
+  TH2D* d_detac_eta_hs;
+  TH2D* d_dphic_eta_hs;
+  TH2D* d_dphic_detac_hs;
+
+  TH2D* d_detac_samp_hs;
+  TH2D* d_dphic_samp_hs;
+
+  std::vector<TH2D*> d_maxcells_phi_eta_slice;
+  std::vector<TH2D*> d_allwghts_phi_eta_slice;
+
+protected:
+
+  bool isInHotspot(const xAOD::CaloCluster& ptow) const;
+  bool fillComposition(const xAOD::CaloCluster& ptow,std::vector<double>& deta,std::vector<double>& dphi,std::vector<CaloSampling::CaloSample>& csam) const;
+  bool setAxisTitle(TH1* h,const std::string& title,const std::string& axis="x"); 
+
+  virtual StatusCode book();
+  std::bitset<200000> m_cellTags;
+
+  ///////////////////////
+  // BookAny Templates //
+  ///////////////////////
+
+  template<class H>
+  H* bookAny(const std::string& hname,const std::string& htitle,const std::string& xtitle,int nxbins,double xmin,double xmax) {
+    H* hptr = (H*)bookGetPointer( H(hname.c_str(),htitle.c_str(),nxbins,xmin,xmax) );
+    if ( hptr == 0 ) { 
+      ATH_MSG_WARNING( CaloRec::Helpers::fmtMsg("Cannot book distribution \042%s\042 with title \042%s\042",hname.c_str(),htitle.c_str()) );
+    } else { 
+      hptr->Sumw2();
+      if ( !xtitle.empty() && xtitle != "" ) { hptr->GetXaxis()->SetTitle(xtitle.c_str()); }
+    }
+    return hptr;
+  }
+
+  template<class H>
+  H* bookAny(const std::string& hname,const std::string& htitle,const std::string& xtitle,int nxbins,double xmin,double xmax,int nybins,double ymin,double ymax) {
+    H* hptr = (H*)bookGetPointer( H(hname.c_str(),htitle.c_str(),nxbins,xmin,xmax,nybins,ymin,ymax) );
+    if ( hptr == 0 ) { 
+      ATH_MSG_WARNING( CaloRec::Helpers::fmtMsg("Cannot book distribution \042%s\042 with title \042%s\042",hname.c_str(),htitle.c_str()) );
+    } else { 
+      hptr->Sumw2();
+      if ( !xtitle.empty() && xtitle != "" ) { hptr->GetXaxis()->SetTitle(xtitle.c_str()); }
+    }
+    return hptr;
+  }
+
+  template<class H>
+  H* bookAny(const std::string& hname,const std::string& htitle,const std::string& xtitle,
+	     int nxbins,double xmin,double xmax,int nybins,double ymin,double ymax,int nzbins,double zmin,double zmax) {
+      H* hptr = (H*)bookGetPointer( H(hname.c_str(),htitle.c_str(),nxbins,xmin,xmax,nybins,ymin,ymax,nzbins,zmin,zmax) );
+    if ( hptr == 0 ) { 
+      ATH_MSG_WARNING( CaloRec::Helpers::fmtMsg("Cannot book distribution \042%s\042 with title \042%s\042",hname.c_str(),htitle.c_str()) );
+    } else { 
+      hptr->Sumw2();
+      if ( !xtitle.empty() && xtitle != "" ) { hptr->GetXaxis()->SetTitle(xtitle.c_str()); }
+    }
+    return hptr;
+  }
+
+  template<class H>
+      H* bookAny(const std::string& hname,const std::string& htitle,int nxbins,double xmin,double xmax) {
+    H* hptr = (H*)bookGetPointer( H(hname.c_str(),htitle.c_str(),nxbins,xmin,xmax) );
+    if ( hptr == 0 ) { 
+      ATH_MSG_WARNING( CaloRec::Helpers::fmtMsg("Cannot book distribution \042%s\042 with title \042%s\042",hname.c_str(),htitle.c_str()) );
+    } else { 
+      hptr->Sumw2();
+      hptr->GetXaxis()->SetTitle(hptr->GetTitle());
+    }
+    return hptr;
+  }
+
+  template<class H>
+    H* bookAny(const std::string& hname,const std::string& htitle,int nxbins,double xmin,double xmax,int nybins,double ymin,double ymax) { 
+      H* hptr = (H*)bookGetPointer( H(hname.c_str(),htitle.c_str(),nxbins,xmin,xmax,nybins,ymin,ymax) );
+    if ( hptr == 0 ) { 
+      ATH_MSG_WARNING( CaloRec::Helpers::fmtMsg("Cannot book distribution \042%s\042 with title \042%s\042",hname.c_str(),htitle.c_str()) );
+    } else { 
+      hptr->Sumw2();
+      hptr->GetXaxis()->SetTitle(hptr->GetTitle());
+    }
+    return hptr;
+  }
+
+  template<class H>
+  H* bookAny(const std::string& hname,const std::string& htitle,int nxbins,double xmin,double xmax,int nybins,double ymin,double ymax,int nzbins,double zmin,double zmax) {
+    H* hptr = (H*)bookGetPointer( H(hname.c_str(),htitle.c_str(),nxbins,xmin,xmax,nybins,ymin,ymax,nzbins,zmin,zmax) );
+    if ( hptr == 0 ) { 
+      ATH_MSG_WARNING( CaloRec::Helpers::fmtMsg("Cannot book distribution \042%s\042 with title \042%s\042",hname.c_str(),htitle.c_str()) );
+    } else { 
+      hptr->Sumw2();
+      hptr->GetXaxis()->SetTitle(hptr->GetTitle());
+    }
+    return hptr;
+  }
+
+  ////////////////////////
+  // Book for Samplings //
+  ////////////////////////
+
+  template<class H>
+    H* bookForSamplings(const std::string& hname,const std::string& htitle) {
+    int nsamp((int)CaloRec::Lookup::getSamplingId("MINIFCAL0")); double xmin(-0.5); double xmax(xmin+1.*(nsamp+1));
+    H* hptr = bookAny<H>(hname,htitle,"",nsamp+1,xmin,xmax);
+    if ( hptr != 0 ) { 
+      for ( int isamp(0); isamp < nsamp; ++isamp ) { 
+	hptr->GetXaxis()->SetBinLabel(isamp+1,CaloRec::Lookup::getSamplingName((CaloSampling::CaloSample)isamp).c_str()); 
+      }
+      hptr->GetXaxis()->SetBinLabel(hptr->GetNbinsX(),CaloRec::Lookup::getSamplingName(CaloSampling::Unknown).c_str());
+    }
+    return hptr;
+  }
+
+  template<class H>
+  H* bookForSamplings(const std::string& hname,const std::string& htitle,int nybins,double ymin,double ymax) {
+    int nsamp((int)CaloRec::Lookup::getSamplingId("MINIFCAL0")); double xmin(-0.5); double xmax(xmin+1.*(nsamp+1));
+    H* hptr = bookAny<H>(hname,htitle,"",nsamp+1,xmin,xmax,nybins,ymin,ymax);
+    if ( hptr != 0 ) { 
+      for ( int isamp(0); isamp < nsamp; ++isamp ) { 
+	hptr->GetXaxis()->SetBinLabel(isamp+1,CaloRec::Lookup::getSamplingName((CaloSampling::CaloSample)isamp).c_str()); 
+      }
+      hptr->GetXaxis()->SetBinLabel(hptr->GetNbinsX(),CaloRec::Lookup::getSamplingName(CaloSampling::Unknown).c_str());
+    }
+    return hptr;
+  }
+
+  ///////////////////////
+  // Book for Rapidity //
+  ///////////////////////
+
+  template<class H>
+  H* bookForEta(const std::string& hname,const std::string& htitle) { return bookAny<H>(hname,htitle,"y_{tower}",m_etaBins,m_etaMin,m_etaMax); }
+
+  template<class H>
+  H* bookForEta(const std::string& hname,const std::string& htitle,int nybins,double ymin,double ymax) { 
+    return bookAny<H>(hname,htitle,"y_{tower}",m_etaBins,m_etaMin,m_etaMax,nybins,ymin,ymax);
+  }
+
+  //////////////////////
+  // Book for Azimuth //
+  //////////////////////
+
+  template<class H>
+  H* bookForPhi(const std::string& hname,const std::string& htitle) { return bookAny<H>(hname,htitle,"#phi_{tower} [rad]",m_phiBins,m_phiMin,m_phiMax); }
+
+  template<class H>
+  H* bookForPhi(const std::string& hname,const std::string& htitle,int nybins,double ymin,double ymax) { 
+    return bookAny<H>(hname,htitle,"#phi_{tower} [rad]",m_phiBins,m_phiMin,m_phiMax,nybins,ymin,ymax);
+  }
+
+  ///////////////////
+  // Fill Sampling //
+  ///////////////////
+
+  template<class H>
+  void fillSampling(H* hptr,CaloSampling::CaloSample csamp) { 
+    int isamp(std::min(static_cast<int>(csamp),static_cast<int>(CaloRec::Lookup::getSamplingId("MINIFCAL0"))));
+    hptr->Fill(1.*isamp);
+  }
+
+  template<class H>
+  void fillSampling(H* hptr,CaloSampling::CaloSample csamp,double yval) { 
+    int isamp(std::min(static_cast<int>(csamp),static_cast<int>(CaloRec::Lookup::getSamplingId("MINIFCAL0"))));
+    hptr->Fill(1.*isamp,yval);
+  }
+
+  template<class H>
+  void fillSampling(H* hptr,CaloSampling::CaloSample csamp,double yval,double zval) { 
+    int isamp(std::min(static_cast<int>(csamp),static_cast<int>(CaloRec::Lookup::getSamplingId("MINIFCAL0"))));
+    hptr->Fill(1.*isamp,yval,zval);
+  }
+}; // CaloTopoClusterFromTowerMonitor
+
+inline bool CaloTopoClusterFromTowerMonitor::isInHotspot(const xAOD::CaloCluster& ptow) const
+{ return m_doHotspot && ( ptow.eta() >= m_hsEtaMin && ptow.eta() < m_hsEtaMax ) && ( ptow.phi() >= m_hsPhiMin && ptow.phi() < m_hsPhiMax ); }
+
+inline bool CaloTopoClusterFromTowerMonitor::setAxisTitle(TH1* hptr,const std::string& title,const std::string& axis) {
+  if ( axis == "x" ) { hptr->GetXaxis()->SetTitle(title.c_str()); return true; }
+  if ( axis == "y" ) { hptr->GetYaxis()->SetTitle(title.c_str()); return true; }
+  if ( axis == "z" ) { hptr->GetZaxis()->SetTitle(title.c_str()); return true; }
+  return false;
+}
+#endif
+
+
diff --git a/Calorimeter/CaloRec/CaloRec/CaloTopoClusterTowerMerger.h b/Calorimeter/CaloRec/CaloRec/CaloTopoClusterTowerMerger.h
new file mode 100644
index 0000000000000000000000000000000000000000..792ae2cf9f7fb14c16004a9d1b33c8f687c24206
--- /dev/null
+++ b/Calorimeter/CaloRec/CaloRec/CaloTopoClusterTowerMerger.h
@@ -0,0 +1,86 @@
+// -*- c++ -*-
+
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+
+#ifndef CALOREC_CALOTOPOCLUSTERTOWERMERGER_H
+#define CALOREC_CALOTOPOCLUSTERTOWERMERGER_H
+
+#include "StoreGate/ReadHandleKey.h"
+#include "StoreGate/WriteHandleKey.h"
+
+#include "AthenaBaseComps/AthAlgorithm.h"
+
+#include "xAODCaloEvent/CaloCluster.h"
+#include "xAODCaloEvent/CaloClusterContainer.h"
+
+#include <vector>
+//#include <tuple>
+#include <string>
+#include <cmath>
+
+class CaloTopoClusterTowerMerger : public AthAlgorithm
+{
+public:
+  ///@brief Algorithm constructor
+  CaloTopoClusterTowerMerger(const std::string& name,ISvcLocator* pSvcLocator);
+  ///@brief Baseclass destructor 
+  virtual ~CaloTopoClusterTowerMerger();
+
+  ///@name Algorithm interface
+  ///@{
+  virtual StatusCode initialize(); ///< Initialization sets up read and write handle keys
+  virtual StatusCode execute();    ///< Execution merges the container contents 
+  ///@}
+
+private:
+
+  ///@name Internally used types
+  ///@{
+  typedef SG::ReadHandleKey<xAOD::CaloClusterContainer>    rhandlekey_t;   ///< Input data handle key type
+  typedef SG::WriteHandleKey<xAOD::CaloClusterContainer>   whandlekey_t;   ///< Output data handle key type
+  typedef SG::ReadHandle<xAOD::CaloClusterContainer>       rhandle_t;      ///< Input data handle type
+  typedef SG::WriteHandle<xAOD::CaloClusterContainer>      whandle_t;      ///< Output data handle type
+  ///@}
+
+  ///@name Algorithm properties
+  ///@{
+  rhandlekey_t  m_clusterContainerKey;    ///< Input topo-cluster container
+  rhandlekey_t  m_towerContainerKey;      ///< Input topo-tower container
+  whandlekey_t  m_topoSignalContainerKey; ///< Output merged (view) container
+  double        m_clusterRange;           ///< Rapidity range for topo-clusters 
+  // bool          m_copyMoments;            ///< Explicitely copy cluster moments
+  ///@}
+
+  ///@name Helpers
+  ///@{
+  bool       makeDeepCopy(const xAOD::CaloCluster& rClus,xAOD::CaloClusterContainer* pClusCont) const; ///< Attaches a deep copy to container, returns @c true if successful. 
+  //  bool       fillMoments(const xAOD::CaloCluster& rClus);                                             ///< Copies list of filled moments into lookup 
+  bool       clusterFilter(const xAOD::CaloCluster& rClus) const;                                      ///< Filter topo-cluster
+  bool       towerFilter(const xAOD::CaloCluster& rTowr) const;                                        ///< Filter topo-tower
+  StatusCode addContainerWriteHandle(whandle_t& signalHandle);                                         ///< Add a write handle for a container (in CaloClusterStoreHelper from r21.9)
+  ///@}
+
+  ///@name Moment lookup
+  ///@{
+  // static std::vector<std::tuple<xAOD::CaloCluster::MomentType,std::string> > m_momentMap;  ///< Map moment types to human readable names
+  // static std::vector<xAOD::CaloCluster::MomentType>                          m_momentList; ///< List of used moments for given tower collection
+  ///@}
+};
+
+
+inline bool CaloTopoClusterTowerMerger::clusterFilter(const xAOD::CaloCluster& rClus)   const { return std::abs(rClus.eta()) <= m_clusterRange; }
+inline bool CaloTopoClusterTowerMerger::towerFilter(const xAOD::CaloCluster& /*rTowr*/) const { return true; }
+
+///@class CaloTopoClusterTowerMerger
+///
+/// This algorithm merges objects from two @c xAOD::CaloClusterContainer. In the context of the
+/// mixed topo-tower/topo-cluster output, the objects in the topo-cluster container are taken
+/// up to a client-defined (symmetric) rapidity range. The rest of the phase space is then filled 
+/// with the objects from the topo-tower container. It is assumed that the overlap resolution
+/// is performed when the topo-tower container is filled. The mixed object container contains deep copies 
+/// of the objects in the input containers.
+/// 
+#endif
diff --git a/Calorimeter/CaloRec/CaloRec/CaloTowerGeometrySvc.h b/Calorimeter/CaloRec/CaloRec/CaloTowerGeometrySvc.h
new file mode 100644
index 0000000000000000000000000000000000000000..6d2f445548ea860896ee62dad476ca57bcd72b70
--- /dev/null
+++ b/Calorimeter/CaloRec/CaloRec/CaloTowerGeometrySvc.h
@@ -0,0 +1,414 @@
+// -*- c++ -*-
+#ifndef CALOREC_CALOTOWERGEOMETRYSVC_H
+#define CALOREC_CALOTOWERGEOMETRYSVC_H
+
+/* Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration */
+
+#include "AthenaBaseComps/AthService.h"
+
+#include "Identifier/IdentifierHash.h"
+
+#include "CaloGeoHelpers/CaloPhiRange.h"
+
+#include "CaloDetDescr/CaloDetDescrElement.h"
+#include "CaloDetDescr/CaloDetDescrManager.h"
+
+#include "CaloEvent/CaloCell.h"
+
+#include <string>
+#include <vector>
+#include <cmath>
+#include <array>
+#include <tuple>
+#include <fstream>
+
+///@name Forward declarations
+///@{
+///template<class T> class SvcFactory;
+///@}
+
+///@brief Interface accessor for @c CaloTowerGeometrySvc
+static const InterfaceID IID_CaloTowerGeometrySvc( "CaloTowerGeometrySvc", 1, 0 );
+
+class CaloTowerGeometrySvc : public AthService
+{
+protected:
+
+  ///@brief To allow interface queries by the service factory (?)
+  friend class SvcFactory<CaloTowerGeometrySvc>;
+
+public:
+
+  ///@name Gaudi interfaces and implementations
+  ///@{
+  static const InterfaceID& interfaceID() { return IID_CaloTowerGeometrySvc; }      ///< Interface indentifier needed by Gaudi
+  virtual StatusCode queryInterface(const InterfaceID& riid, void** ppcInterface);  ///< Interface query with fallbacks
+  ///@}
+
+  ///@{
+  typedef std::size_t                  uint_t;           ///< Type for unsigned integer
+  typedef IdentifierHash::value_type   index_t;          ///< Type for scalar (pseudorapidity,azimuth) index (is an unsigned int type)
+  typedef std::tuple<index_t,index_t>  towerindex_t;     ///< Type for composite (tower) index 
+  typedef std::tuple<index_t,double>   element_t;        ///< Type storing tower index and geometrical weight
+  typedef std::vector<element_t>       elementvector_t;  ///< Type for list of elements holding tower index and list of weights
+  typedef std::vector<elementvector_t> elementmap_t;     ///< Type for lists of lists of elements (lookup table type) 
+  ///@}
+
+  ///@brief Standard constructor
+  ///
+  /// Constructs towergrid as defined by properties.
+  ///
+  ///@param[in] name name of service (implementation of @c Gaudi::Service )
+  ///@param[in] pSvc pointer to service locator (from configuration framework)
+  CaloTowerGeometrySvc(const std::string& name,ISvcLocator* pSvc);
+  ///@brief Base class destructor
+  virtual ~CaloTowerGeometrySvc() { };
+
+  ///@name Implementation of (Ath)Service interfaces
+  ///@{
+  virtual StatusCode initialize() override;                                      ///< Initialize service
+  virtual StatusCode finalize()   override;                                      ///< Finalize service
+  //  virtual StatusCode queryInterface(const InterfaceID& iid,void** ppvInterface); ///< Need to fix compiler issue (FIXME)   
+  ///@}
+
+  // --- Full documentation of this block after end of class definition!
+  ///@name Retrieve towers for cells
+  ///@{
+  StatusCode      access(const CaloCell* pCell,std::vector<index_t>& towerIdx,std::vector<double>& towerWghts)   const;
+  StatusCode      access(IdentifierHash cellHash,std::vector<index_t>& towerIdx,std::vector<double>& towerWghts) const;
+  elementvector_t getTowers(const CaloCell* pCell)                                                               const;
+  elementvector_t getTowers(IdentifierHash cellHash)                                                             const; 
+  ///@}
+
+  ///@name Tower bin descriptors and other size information
+  ///@{
+  uint_t maxCellHash()      const; ///< Maximum cell hash value
+  uint_t totalNumberCells() const; ///< Total number of cells 
+  uint_t etaBins()          const; ///< Number of pseudorapidity bins
+  double etaMin()           const; ///< Lower boundary of pseudorapidity range
+  double etaMax()           const; ///< Upper boundary of pseudorapidity range 
+  double etaWidth()         const; ///< Width of pseudorapidity bin  
+  uint_t phiBins()          const; ///< Number of azimuth bins
+  double phiMin()           const; ///< Lower boundary of azimuth
+  double phiMax()           const; ///< Upper boundary of azimuth
+  double phiWidth()         const; ///< Width of azimuth bin
+  uint_t towerBins()        const; ///< Total number of towers
+  double towerArea()        const; ///< Area of individual tower 
+  ///@}
+
+  ///@name Tower index calculators, translaters, manipulators and converters
+  ///@{
+  index_t etaIndex(const CaloCell* pCell)            const; ///< Get tower @f$ \eta @f$ bin index for a calorimeter cell referenced by a pointer
+  index_t etaIndex(IdentifierHash cellHash)          const; ///< Get tower @f$ \eta @f$ bin index for a calorimeter cell referenced by its hash identifier
+  index_t etaIndex(double eta)                       const; ///< Get tower @f$ \eta @f$ bin index for a given value of @f$ \eta @f$
+  index_t etaIndexFromTowerIndex(index_t towerIdx)   const; ///< Get tower @f$ \eta @f$ bin index for a given global tower index
+  index_t phiIndex(const CaloCell* pCell)            const; ///< Get tower @f$ \phi @f$ bin index for a calorimeter cell referenced by a pointer
+  index_t phiIndex(IdentifierHash cellHash)          const; ///< Get tower @f$ \phi @f$ bin index for a calorimeter cell referenced by its hash identifier
+  index_t phiIndex(double phi)                       const; ///< Get tower @f$ \phi @f$ bin index for a given value of @f$ \phi @f$
+  index_t phiIndexFromTowerIndex(index_t towerIdx)   const; ///< Get tower @f$ \phi @f$ bin index for a given global tower index
+  index_t towerIndex(const CaloCell* pCell)          const; ///< Get global tower index for a calorimeter cell referenced by a pointer
+  index_t towerIndex(IdentifierHash cellHash)        const; ///< Get global tower index for a calorimeter cell referenced by its hash identifier
+  index_t towerIndex(double eta,double phi)          const; ///< Get global tower index for a pair of @f$ (\eta,\phi) @f$ values
+  index_t towerIndex(index_t etaIdx,index_t phiIdx)  const; ///< Get global tower index for a pair of @f$ (\eta,\phi) @f$ indices
+  index_t towerIndex(const element_t& elm)           const; ///< Get global tower index from payload data
+  index_t invalidIndex()                             const; ///< Returns value of invalid index
+  bool    isInvalidIndex(index_t idx)                const; ///< Returns @c true if argument is equal to the value provided by @c invalidIndex() 
+  bool    isInvalidIndex(index_t idx,index_t maxIdx) const; ///< Returns @c true if first argument is equal to the value provided by @c invalidIndex() or if first argument is not smaller than second argument
+  bool    isInvalidEtaIndex(index_t idx)             const; ///< Returns @c true if argument is not a valid pseudorapidity index
+  bool    isInvalidPhiIndex(index_t idx)             const; ///< Returns @c true if argumant is not a valid azimuth index
+  bool    isInvalidTowerIndex(index_t idx)           const; ///< Returns @c true if argument is not a valid tower index
+  ///@}
+
+  ///@name Variable generators using tower indices
+  ///@{
+  double towerEtaLocal(index_t etaIndex)    const; ///< Return pseudorapdity from local index (bin center) 
+  double towerPhiLocal(index_t phiIndex)    const; ///< Return azimuth from local index (bin center)
+  double towerEta(index_t towerIndex)       const; ///< Return pseudorapidity from global tower index (bin center)
+  double towerPhi(index_t towerIndex)       const; ///< Return azimuth from global tower index (bin center)
+  double invalidValue()                     const; ///< Return invalid value
+  bool   isInvalidValue(double val)         const; ///< Return @c true if given value is invalid
+  ///@}
+
+  ///@name Helper functions
+  ///@{
+  double  cellWeight(const element_t& elm)                     const; ///< Retrieve cell signal weight from lookup table entry
+  double  cellWeight(IdentifierHash cellHash,index_t towerIdx) const; ///< Retrieve cell signal weight from cell identifier and tower index
+  double  cellWeight(const CaloCell* pCell, index_t towerIdx)  const; ///< Retrieve cell signal weight from pointer to cell object and tower index
+  ///@}
+
+  ///@name Access to storage
+  ///@{
+  elementmap_t::const_iterator begin() const;  ///< Iterator points to first entry in internal look-up table (only @c const access!)
+  elementmap_t::const_iterator end()   const;  ///< Iterator marks end of internal look-up table (only @c const access)  
+  size_t                       size()  const;  ///< Size of internal look-up table
+  bool                         empty() const;  ///< Internal look-up table is empty if @c true
+  ///@}
+
+private:
+
+  ///@name Helpers
+  ///@{
+  StatusCode f_setupSvc();                                                                    ///< Internally used function setting up other services needed by this service
+  StatusCode f_setupTowerGrid();                                                              ///< Internally used function setting up the lookup store
+  StatusCode f_setupTowerGridFCal(const CaloDetDescrElement* pCaloDDE,std::ofstream& logger); ///< Internally used function mapping an FCal cell onto the tower grid
+  StatusCode f_setupTowerGridProj(const CaloDetDescrElement* pCaloDDE,std::ofstream& logger); ///< Internally used function mapping a projective cell onto the tower grid
+  double     f_assign(IdentifierHash cellHash,index_t towerIdx,double wgt);                   ///< Internally used function assigning tower to cell with update of weight if same tower is already assigned
+  ///@}
+
+  ///@name Access to detector store and other services and stores
+  ///@{
+  const CaloDetDescrManager* m_caloDDM;               ///< Pointer to calorimeter detector description 
+  std::string m_logFileName;                          ///< Name of log file
+  ///@}
+
+protected:
+
+  ///@name Internal stores and derived parameters
+  ///@{
+  elementmap_t m_towerLookup;   ///< Cell-to-tower mapping lookup store
+  double       m_towerEtaWidth; ///< Width of tower bin in pseudorapidity
+  double       m_towerPhiWidth; ///< Width of tower bin in azimuth 
+  double       m_towerArea;     ///< Area of individual tower
+  uint_t       m_towerBins;     ///< Maximum number of towers
+  uint_t       m_maxCellHash;   ///< Maximum cell hash value
+  uint_t       m_numberOfCells; ///< Total number of cells 
+  ///@}
+  
+  ///@name Properties
+  ///@{
+  ///@brief Internally stored tower grid descriptors
+  uint_t m_towerEtaBins; ///< Number of @f$ \eta @f$ bins 
+  double m_towerEtaMin;  ///< Lower boundary @f$ \eta_{\rm min} @f$
+  double m_towerEtaMax;  ///< Upper boundary @f$ \eta_{\rm max} @f$
+  bool   m_adjustEta;    ///< Adjust FCal cells to eta boundary (default @c true ) 
+  uint_t m_towerPhiBins; ///< Number of @f$ \phi @f$ bins 
+  double m_towerPhiMin;  ///< Lower boundary @f$ \phi_{\rm min} @f$
+  double m_towerPhiMax;  ///< Upper boundary @f$ \phi_{\rm max} @f$
+  double m_fcal1Xslice;  ///< Number of x slices for cells in FCal1
+  double m_fcal1Yslice;  ///< Number of y slices for cells in FCal1
+  double m_fcal2Xslice;  ///< Number of x slices for cells in FCal2
+  double m_fcal2Yslice;  ///< Number of y slices for cells in FCal2
+  double m_fcal3Xslice;  ///< Number of x slices for cells in FCal3
+  double m_fcal3Yslice;  ///< Number of y slices for cells in FCal3
+  ///@} 
+
+  ///@name Process flags, helpers and numerical constants
+  ///@{
+  static index_t m_invalidIndex;                                       ///< Invalid index indicator
+  static double  m_invalidValue;                                       ///< Return value for out-of-range indices andother invalid conversions to a physical quantity
+  const CaloDetDescrManager* f_caloDDM()                        const; ///< Pointer to calorimeter detector description manager
+  const CaloDetDescrElement* f_caloDDE(const CaloCell* pCell)   const; ///< Retrieve calorimeter detector description element for a cell object referenced by a pointer
+  const CaloDetDescrElement* f_caloDDE(IdentifierHash cellHash) const; ///< Retrieve calorimeter detector description element for a given cell hash identifier
+  double f_cellEta(const CaloCell* pCell)                       const; ///< Retrieve calorimeter cell pseudorapidity for a cell object referenced by a pointer
+  double f_cellEta(IdentifierHash cellHash)                     const; ///< Retrieve calorimeter cell pseudorapidity for a given cell hash identifier
+  double f_cellPhi(const CaloCell* pCell)                       const; ///< Retrieve calorimeter cell azimuth for a cell object referenced by a pointer
+  double f_cellPhi(IdentifierHash cellHash)                     const; ///< Retrieve calorimeter cell azimuth for a given cell hash identifier
+  ///@}
+
+  ///@name Stores
+  ///@{
+  std::array<double,3> m_ndxFCal; ///< Stores number of fragments along x for each FCal module
+  std::array<double,3> m_ndyFCal; ///< Stores number of fragments along y for each FCal module
+  std::array<double,3> m_wgtFCal; ///< Stores geometrical weights
+  ///@}
+};
+
+//-------------------------------------------------//
+// Documentation for grouped methods and functions //
+// (removed from before/after method for better    //
+// formatting by doxygen in html).                 //
+//-------------------------------------------------//
+
+///@fn StatusCode CaloTowerGeometrySvc::access(const CaloCell* pCell,std::vector<index_t>& towerIdx,std::vector<double>& towerWghts) const
+///
+///@brief Retrieve the list of towers associated with a calorimeter cell referenced by a pointer
+///
+/// The tower indices and weights are returned in two index-parallel vectors. 
+/// Previous content of these two vectors is removed if this method finds towers for the cell. 
+/// 
+///@return Returns @c StatusCode::SUCCESS if list of towers found, else @s StatusCode::FAILURE.
+///
+///@param[in] pCell      pointer to non-modifiable @c CaloCell object.
+///@param[in] towerIdx   reference to modifiable vector of indices (payload type @c index_t ); vector is filled if cell is successfully mapped. 
+///@param[in] towerWghts reference to modifiable vector of weights (payload type @c double ); vector is filled if cell is successfully mapped. 
+
+///@fn StatusCode CaloTowerGeometrySvc::access(IdentifierHash cellHash,std::vector<index_t>& towerIdx,std::vector<double>& towerWghts) const;
+///
+///@brief Retrieve the list of towers associated with a calorimeter cell referenced its hash identifier
+///
+/// The tower indices and weights are returned in two index-parallel vectors. 
+/// Previous content of these two vectors is removed if this method finds towers for the cell. 
+/// 
+///@return Returns @c StatusCode::SUCCESS if list of towers found, else @s StatusCode::FAILURE.
+///
+///@param[in] cellHash   hash identifier referencing a calorimeter cell.
+///@param[in] towerIdx   reference to modifiable vector of indices (payload type @c index_t ); vector is filled if cell is successfully mapped. 
+///@param[in] towerWghts reference to modifiable vector of weights (payload type @c double ); vector is filled if cell is successfully mapped.
+
+///@fn CaloTowerGeometrySvc::elementvector_t CaloTowerGeometrySvc::getTowers(const CaloCell* pCell) const;
+///
+///@brief Retrieve the list of towers associated with a calorimeter cell referenced by a pointer
+/// 
+///@return Returns a vector of (index,weight) pairs as a @c elementvector_t container. The container is empty
+/// if the cell does not have any overlap with a tower.
+///
+///@param[in] pCell      pointer to non-modifiable @c CaloCell object.
+
+///@fn CaloTowerGeometrySvc::elementvector_t CaloTowerGeometrySvc::getTowers(IdentifierHash cellHash) const; 
+///
+///@brief Retrieve the list of towers associated with a calorimeter cell referenced by its hash identifier
+///
+///@return Returns a vector of (index,weight) pairs as a @c elementvector_t container. The container is empty
+/// if the cell does not have any overlap with a tower.
+///
+///@param[in] cellHash   hash identifier referencing a calorimeter cell.
+
+//---------------------//
+// Class documentation //
+//---------------------//
+
+/// @class CaloTowerGeometrySvc
+///
+/// @brief Tower geometry store and description provider
+///
+/// This service sets up a lookup table storing the geometrical area overlap fraction of a calorimeter cell
+/// with towers in a given grid. This lookup table is set up at instantiation of the service. It can only be 
+/// defined at that time. The default setup is a @f$ \Delta\eta\times\Delta\phi = 0.1 \times \pi/32 @f$ grid.
+/// Any regular grid can be constructed. The grid definition can be provided as property.
+///
+/// The cell-to-tower information is stored internally as a (random access) lookup table. For a given cell,
+/// the hash index is used to retrieve a list of towers this cell overlaps with, and the overlap paramater
+/// (area fraction used as a geometrical weight). This indices and geometrical weights are represented by
+/// a list of pairs of @c int and @c double numbers. Each cell can potential overlap with more than one 
+/// tower. A more detailed description of towers and the geometrical overlap is available on the
+/// <a href="https://twiki.cern.ch/twiki/bin/view/AtlasSandboxProtected/CaloTowerPerformance">calorimeter tower project page</a>.  
+///
+/// The lookup table is implemented for random access and using the cell hash identifier to retrieve the 
+/// requested list of twoer indices and weights. Several retrieval mechanisms are supported (see documentation
+/// of the corresponding methods). 
+///
+/// To map the azimuth of a cell to a tower, @f$ -\pi < \phi < \pi @f$ is used (ATLAS standard). For
+/// consistency, all @f$ \phi @f$ values are mapped into this range. 
+///
+/// The service inherits from @c AthService and thus from the @c Service base class in Gaudi. The managed tower grid
+/// is defined by service properties, with the following naming convention:  
+/// - pseudorapidity range
+///     - number of bins <tt>TowerEtaBins</tt> (default 100)
+///     - lower boundary of pseudorapidity range <tt>TowerEtaMin</tt> (default -5.0)
+///     - upper boundary of pseudorapidity range <tt>TowerEtaMax</tt> (default  5.0)
+/// - azimuth range
+///     - number of bins <tt>TowerPhiBins</tt> (default 64)
+///     - lower boundary of azimuthal range <tt>TowerPhiMin</tt> (default -&pi;)
+///     - upper boundary of azimuthal range <tt>TowerPhiMax</tt> (default &pi;) 
+/// 
+/// Addtional properties of this service define the granularity of the cell splitting in the ATLAS FCal. This
+/// is used to map the FCal readout cells (rectangular slabs) onto the tower grid and calculate the geometrical
+/// (area) overlap fraction, which is used to distribute the cell energy to the towers. 
+/// - horizontal FCal cell splitting (along @a x axis)
+///     - number of @a x slices in FCal1 <tt>FCal1NSlicesX</tt> (default 4)
+///     - number of @a x slices in FCal2 <tt>FCal2NSlicesX</tt> (default 4)
+///     - number of @a x slices in FCal3 <tt>FCal3NSlicesX</tt> (default 6)
+/// - vertical FCal cell splitting (along @a y axis)
+///     - number of @a y slices in FCal1 <tt>FCal1NSlicesY</tt> (default 4)
+///     - number of @a y slices in FCal2 <tt>FCal2NSlicesY</tt> (default 6)
+///     - number of @a y slices in FCal3 <tt>FCal3NSlicesY</tt> (default 6)
+///
+/// @warning It is recommended to @b not change the parameters for the FCal cell slicing. This configuration option is provided for expert use for R & D purposes only.   
+///  
+/// @todo Allow regional grids (varying segmentation as function of @f$ \eta @f$ . This requires additional interfaces (or interface changes) and 
+///       and modifications of the index construction.
+///
+/// @author Peter Loch <loch@physics.arizona.edu>
+///
+
+//------------------//
+// Inline Functions //
+//------------------//
+
+//---------------------------//
+// Control and configuration //
+//---------------------------//
+inline CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::invalidIndex() const { return m_invalidIndex; }
+inline StatusCode CaloTowerGeometrySvc::f_setupSvc() { 
+  m_caloDDM = CaloDetDescrManager::instance(); 
+  return f_caloDDM() != 0 ? StatusCode::SUCCESS : StatusCode::FAILURE; 
+}
+
+//------------------------------------//
+// Public access to tower descriptors //
+//------------------------------------//
+inline CaloTowerGeometrySvc::uint_t CaloTowerGeometrySvc::maxCellHash()      const { return m_maxCellHash;   }
+inline CaloTowerGeometrySvc::uint_t CaloTowerGeometrySvc::totalNumberCells() const { return m_numberOfCells; }  
+
+inline CaloTowerGeometrySvc::uint_t CaloTowerGeometrySvc::etaBins()          const { return m_towerEtaBins;  }
+inline double                       CaloTowerGeometrySvc::etaMin()           const { return m_towerEtaMin;   }
+inline double                       CaloTowerGeometrySvc::etaMax()           const { return m_towerEtaMax;   }
+inline double                       CaloTowerGeometrySvc::etaWidth()         const { return m_towerEtaWidth; }
+
+inline CaloTowerGeometrySvc::uint_t CaloTowerGeometrySvc::phiBins()          const { return m_towerPhiBins;  }
+inline double                       CaloTowerGeometrySvc::phiMin()           const { return m_towerPhiMin;   }
+inline double                       CaloTowerGeometrySvc::phiMax()           const { return m_towerPhiMax;   }
+inline double                       CaloTowerGeometrySvc::phiWidth()         const { return m_towerPhiWidth; }
+
+inline CaloTowerGeometrySvc::uint_t CaloTowerGeometrySvc::towerBins()        const { return m_towerBins;     }
+inline double                       CaloTowerGeometrySvc::towerArea()        const { return m_towerArea;     } 
+
+//----------------//
+// Index checking //
+//----------------//
+inline bool CaloTowerGeometrySvc::isInvalidIndex(index_t idx)                const { return idx == invalidIndex(); }
+inline bool CaloTowerGeometrySvc::isInvalidIndex(index_t idx,index_t maxIdx) const { return idx == invalidIndex() || idx >= maxIdx; }
+inline bool CaloTowerGeometrySvc::isInvalidEtaIndex(index_t idx)             const { return isInvalidIndex(idx,m_towerEtaBins); }
+inline bool CaloTowerGeometrySvc::isInvalidPhiIndex(index_t idx)             const { return isInvalidIndex(idx,m_towerPhiBins); }
+inline bool CaloTowerGeometrySvc::isInvalidTowerIndex(index_t idx)           const { return isInvalidIndex(idx,m_towerBins); }
+
+//------------------------------//
+// Index retrieval/construction //
+//------------------------------// 
+inline CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::etaIndex(const CaloCell* pCell)           const { return etaIndex(pCell->eta()); }
+inline CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::phiIndex(const CaloCell* pCell)           const { return phiIndex(pCell->phi()); }
+
+inline CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::towerIndex(IdentifierHash cellHash)       const { return towerIndex(etaIndex(cellHash),phiIndex(cellHash)); }
+inline CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::towerIndex(double eta,double phi)         const { return towerIndex(etaIndex(eta),phiIndex(phi)); }
+inline CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::towerIndex(index_t etaIdx,index_t phiIdx) const { return !isInvalidEtaIndex(etaIdx) && !isInvalidPhiIndex(phiIdx) ? phiIdx+etaIdx*m_towerPhiBins :  invalidIndex(); } 
+inline CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::towerIndex(const CaloCell* pCell)         const { return towerIndex(pCell->eta(),pCell->phi()); }
+inline CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::towerIndex(const element_t& elm)          const { return std::get<0>(elm); }
+
+inline CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::etaIndexFromTowerIndex(index_t towerIdx)  const { return (index_t)(towerIdx/phiBins()); }
+inline CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::phiIndexFromTowerIndex(index_t towerIdx)  const { return (index_t)(towerIdx%phiBins()); } 
+
+//-----------------------------//
+// Access to tower description //
+//-----------------------------//
+
+inline double CaloTowerGeometrySvc::invalidValue()                const { return m_invalidValue; }
+inline bool   CaloTowerGeometrySvc::isInvalidValue(double val)    const { return val == invalidValue(); }
+inline double CaloTowerGeometrySvc::towerEtaLocal(index_t etaIdx) const { return !isInvalidEtaIndex(etaIdx) ? etaMin()+(static_cast<double>(etaIdx)+0.5)*etaWidth() : invalidValue(); }
+inline double CaloTowerGeometrySvc::towerPhiLocal(index_t phiIdx) const { return !isInvalidPhiIndex(phiIdx) ? phiMin()+(static_cast<double>(phiIdx)+0.5)*phiWidth() : invalidValue(); }
+inline double CaloTowerGeometrySvc::towerEta(index_t towerIdx)    const { return towerEtaLocal(etaIndexFromTowerIndex(towerIdx)); }
+inline double CaloTowerGeometrySvc::towerPhi(index_t towerIdx)    const { return towerPhiLocal(phiIndexFromTowerIndex(towerIdx)); }  
+
+inline CaloTowerGeometrySvc::elementmap_t::const_iterator CaloTowerGeometrySvc::begin() const { return m_towerLookup.begin(); }
+inline CaloTowerGeometrySvc::elementmap_t::const_iterator CaloTowerGeometrySvc::end()   const { return m_towerLookup.end(); }
+inline size_t                                             CaloTowerGeometrySvc::size()  const { return m_towerLookup.size(); }
+inline bool                                               CaloTowerGeometrySvc::empty() const { return m_towerLookup.empty(); }
+
+//-------------------//
+// Other data access //
+//-------------------//
+inline double CaloTowerGeometrySvc::cellWeight(const CaloCell* pCell,index_t towerIdx) const { return cellWeight(pCell->caloDDE()->calo_hash(),towerIdx); }
+inline double CaloTowerGeometrySvc::cellWeight(const element_t& elm)                   const { return std::get<1>(elm); }
+
+
+//----------------------------------//
+// Internal functions and accessors //
+//----------------------------------//
+inline const CaloDetDescrManager* CaloTowerGeometrySvc::f_caloDDM()                        const { return m_caloDDM; } 
+inline const CaloDetDescrElement* CaloTowerGeometrySvc::f_caloDDE(const CaloCell* pCell)   const { return pCell->caloDDE(); }
+inline const CaloDetDescrElement* CaloTowerGeometrySvc::f_caloDDE(IdentifierHash cellHash) const { return f_caloDDM()->get_element(cellHash); }
+
+inline double CaloTowerGeometrySvc::f_cellEta(IdentifierHash cellHash) const { return f_caloDDE(cellHash)->eta(); }
+inline double CaloTowerGeometrySvc::f_cellEta(const CaloCell* pCell)   const { return pCell->eta(); } 
+inline double CaloTowerGeometrySvc::f_cellPhi(IdentifierHash cellHash) const { return CaloPhiRange::fix(f_caloDDE(cellHash)->phi()); }
+inline double CaloTowerGeometrySvc::f_cellPhi(const CaloCell* pCell)   const { return pCell->phi(); } 
+#endif
diff --git a/Calorimeter/CaloRec/python/CaloRecFlags.py b/Calorimeter/CaloRec/python/CaloRecFlags.py
index bb6f10ecd1022ec247009a4d03861282596929ee..5f94d15e10d139c3611e4e85cd4d0becd458ec86 100644
--- a/Calorimeter/CaloRec/python/CaloRecFlags.py
+++ b/Calorimeter/CaloRec/python/CaloRecFlags.py
@@ -87,7 +87,6 @@ class doCaloTowerFromCluster(CaloRecFlagsJobProperty):
     allowedTypes=['bool']
     StoredValue=False
 
-
 class doCaloTopoTower(CaloRecFlagsJobProperty):
     """ switch noise suppressed towers based on standard tower + topo clusters
     """
@@ -95,6 +94,20 @@ class doCaloTopoTower(CaloRecFlagsJobProperty):
     allowedTypes=['bool']
     StoredValue=False
 
+class doCaloTopoSignal(CaloRecFlagsJobProperty):
+    """ produce mixed topo-cluster and topo-tower container 
+    """
+    statusOn=True
+    allowedTypes=['bool']
+    storedValue=False
+
+class doExtendedClusterMoments(CaloRecFlagsJobProperty):
+    """ add more cluster moments for R&D
+    """
+    statusOn=True
+    allowedTypes=['bool']
+    storedValue=False
+
 class doTileMuId(CaloRecFlagsJobProperty):
     """ switch for TileMuId - muon finding algorighm
     """
@@ -170,7 +183,7 @@ jobproperties.add_Container(CaloRecFlags)
 
 
 # I want always the following flags in the Rec container  
-_list_Calo=[Enabled,doCaloTopoCluster,doEmCluster,doCaloEMTopoCluster,emTopoClusterThreshold,doCaloCluster,doCaloTopoTower,doTileMuId,doTileCellCorrection,doLArAffectedRegion,doLArAutoConfiguration,doLArNoisyRO,doEMDigits,doFillMBTSBackgroundBit,doLArNoiseBurstVeto,clusterCellGetterName,doCaloTowerFromCells,doCaloTowerFromCluster]
+_list_Calo=[Enabled,doCaloTopoCluster,doEmCluster,doCaloEMTopoCluster,emTopoClusterThreshold,doCaloCluster,doCaloTopoTower,doCaloTopoSignal,doExtendedClusterMoments,doTileMuId,doTileCellCorrection,doLArAffectedRegion,doLArAutoConfiguration,doLArNoisyRO,doEMDigits,doFillMBTSBackgroundBit,doLArNoiseBurstVeto,clusterCellGetterName,doCaloTowerFromCells,doCaloTowerFromCluster]
 for j in _list_Calo: 
     jobproperties.CaloRecFlags.add_JobProperty(j)
 del _list_Calo
diff --git a/Calorimeter/CaloRec/python/MakeClustersFromTowers.py b/Calorimeter/CaloRec/python/MakeClustersFromTowers.py
index 6bc75b051bf3c1f72f4a94ab13a76ad389879347..afa1f7e3cc89f884175c9d573e5b8d8c840c280b 100644
--- a/Calorimeter/CaloRec/python/MakeClustersFromTowers.py
+++ b/Calorimeter/CaloRec/python/MakeClustersFromTowers.py
@@ -1,164 +1,261 @@
 # Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
 
-from CaloRec.CaloRecConf import CaloTowerxAODFromCells
-from CaloRec.CaloRecConf import CaloTopoClusterFromTowerMaker
+from AthenaCommon.AppMgr import ServiceMgr as svcMgr
 
-from AthenaCommon.Logging import logging
+from CaloRec.CaloRecConf import CaloTopoClusterFromTowerMaker
+from CaloRec.CaloRecConf import CaloTowerGeometrySvc
 
+from   AthenaCommon.Logging import logging
 import AthenaCommon.Constants as Lvl
 
-def ClustersFromTowersDict(clusterBuilderName='TowerFromClusterMaker',
-                           towerBuilderAlgo=CaloTowerxAODFromCells('CmbTowerAlgo'),
-                           orderedClusterByPt=False,
-                           doLCW=False,                      #### don't apply LCW by default 
-                           caloTopoClusterKey='NONE',        #### all cells is default
-                           caloCellClusterWeightKey='NONE'): #### no cell weight data object needed
+####################################
+## Tower configuration dictionary ##
+####################################
+
+def ClustersFromTowersDict(clusterBuilderName       = 'TowerFromClusterTool',
+                           towerGeometrySvc         = CaloTowerGeometrySvc('CaloTowerGeometrySvc'),
+                           cellContainerKey         = 'AllCalo',
+                           buildTopoTowers          = True,
+                           topoClusterContainerKey  = 'CaloTopoCluster',
+                           cellClusterWeightKey     = 'CaloCellClusterWeightKey',
+                           orderClusterByPt         = False,
+                           applyCellEnergyThreshold = False,
+                           doCellIndexCheck         = False,
+                           cellEnergyThreshold      = 0.,
+                           applyLCW                 = False,
+                           buildCombinedSignal      = False,
+                           clusterRange             = 5.):
     ''' Configuration dictionary for tower-to-cluster converter 
     '''
-    configDict = { 'ClusterBuilderName'          : clusterBuilderName,
-                   'CaloTowerBuilder'            : towerBuilderAlgo,
-                   'OrderClusterByPt'            : orderedClusterByPt,
-                   'CaloTopoClusterContainerKey' : caloTopoClusterKey,
-                   'CellClusterWeightKey'        : caloCellClusterWeightKey,
-                   'ApplyLCW'                    : doLCW
+    configDict = { 'ClusterBuilderName'          : clusterBuilderName,         ### name of the tower builder tool
+                   'CaloTowerGeometrySvc'        : towerGeometrySvc,           ### tower geometry provider
+                   'CaloCellContainerKey'        : cellContainerKey,           ### (input)   cell container key
+                   'CaloTopoClusterContainerKey' : topoClusterContainerKey,    ### (input)   topo-cluster container key
+                   'CellClusterWeightKey'        : cellClusterWeightKey,       ### (output)  cell weights in topo-clusters
+                   'BuildTopoTowers'             : buildTopoTowers,            ### (control) form topo-towers
+                   'OrderClusterByPt'            : orderClusterByPt,           ### (control) order final clusters by Pt
+                   'ApplyCellEnergyThreshold'    : applyCellEnergyThreshold,   ### (control) apply energy thresholds to cells
+                   'CellEnergyThreshold'         : cellEnergyThreshold,        ### (control) value of energy threshold
+                   'PrepareLCW'                  : applyLCW,                   ### (control) prepare (and apply) LCW
+                   'DoCellIndexCheck'            : doCellIndexCheck,           ### (control) check cell hash indices
+                   'BuildCombinedTopoSignal'     : buildCombinedSignal,        ### (control) build combined topo-cluster/topo-tower container
+                   'TopoClusterRange'            : clusterRange,               ### (control) range for topo-cluster in combined mode 
                    }
     return configDict
 
-def MakeClustersFromTowers(clusterMakerName='CaloClusterMaker',clusterContainerKey='TowerTopoCluster',configDict=ClustersFromTowersDict(),applyEnergyThreshold=False,debugOn=False):
-    ''' This function generates an instance of a cluster algorithm producting clusters trom towers with or without moments 
+###################################
+## Tower algorithm configuration ##
+###################################
+
+def MakeClustersFromTowers(towerMakerName      = 'CaloTowerBuilderAlg',        ### name of tower builder algorithm
+                           towerContainerKey   = 'CaloTowerTopoCluster',       ### output container key
+                           configDict          = ClustersFromTowersDict(),     ### tower builder tool configuration
+                           debugOn             = False):
+    ''' This function generates an instance of a cluster algorithm configuration producting clusters trom towers with or without moments. 
     '''
-    # collect inputs
     mlog = logging.getLogger('MakeClustersFromTowers.py:: ')
-    mlog.info('ClusterMakerName    = "'+clusterMakerName+'"')
-    mlog.info('ClusterContainerKey = <'+clusterContainerKey+'>')
-    mlog.info('Converter parameters: ',configDict)
-
-    # configure cluster builder
-    cnvname  = configDict['ClusterBuilderName']
-    twralgo  = configDict['CaloTowerBuilder']
-    towerkey = twralgo.CaloTowerContainer
-    mlog.info('(input) CaloTowerContainer <'+towerkey+'>')
-    cellkey  = 'AllCalo' ### twralgo.InputCellContainer --> this does not work, why?
-    mlog.info('(input) CaloCellContainer  <'+cellkey+'>')
-    ptorder  = configDict['OrderClusterByPt']
-    tcluskey = configDict['CaloTopoClusterContainerKey']
-    tcwgtkey = configDict['CellClusterWeightKey'] 
-    #### buildtt  = ( tcluskey != 'NONE' and tcwgtkey != 'NONE' )
-    buildtt  = configDict['ApplyLCW']
-    ''' Configuration module for the tower converter
+    mlog.info('TowerMakerName    = "'+towerMakerName+'"')
+    mlog.info('TowerContainerKey = <'+towerContainerKey+'>')
+
+    ########################################
+    ## Configuring the tower builder tool ##
+    ########################################
+
+    ''' collect properties from dictionary and set correct dependencies
+    ''' 
+    mlog.info('Converter properties: ',configDict)
+    excludedKeys = [ 'ClusterBuilderName' ]
+    if configDict['PrepareLCW']: 
+        towerBuilder  = CaloTopoClusterFromTowerMaker(configDict['ClusterBuilderName'],OrderClusterByPt=False) ### order by pt after LCW calibration!
+        excludedKeys += [ 'OrderClusterByPt' ]
+    else:
+        towerBuilder  = CaloTopoClusterFromTowerMaker(configDict['ClusterBuilderName'])
+
+    ''' Copy properties from dictionary
     '''
-    towerConverter = CaloTopoClusterFromTowerMaker(cnvname,CaloTowerContainerKey=towerkey,CaloCellContainerKey=cellkey,OrderClusterByPt=ptorder)
-    ''' Refinement of converter configuration
+    for key,value in configDict.items():
+        if key not in excludedKeys:
+            setattr(towerBuilder,key,value)
+
+    ''' Check basic consistency of configuration
+    '''
+    mlog.info('Consistency check')
+    if towerBuilder.PrepareLCW and not towerBuilder.BuildTopoTowers:
+        raise RuntimeError('{0}[inconsistent configuration] applying LCW requires to build topo-towers'.format(towerBuilder.name()))
+    if towerBuilder.BuildCombinedTopoSignal and not towerBuilder.BuildTopoTowers:
+        raise RuntimeError('{0}[inconsistent configuration] building combined topo-cluster/topo-tower signals requires to build topo-towers'.format(towerBuilder.name()))
+    if towerBuilder.ApplyCellEnergyThreshold and towerBuilder.BuildTopoTowers:
+        raise RuntimeError('{0}[inconsistent configuration] applying cell energy thresholds for topo-towres not yet implemented'.format(towerBuilder.name()))
+
+    ''' Tower converter configuration summary
+    '''
+    if towerBuilder.BuildTopoTowers:
+        if towerBuilder.PrepareLCW:
+            ''' LCW topo-towers
+            '''
+            mlog.info('################################################')
+            mlog.info('## Produce LCW calibrated topo-tower clusters ##')
+            mlog.info('################################################')
+            mlog.info('CaloTopoClusterContainerKey .. {0}'.format(towerBuilder.CaloTopoClusterContainerKey))
+            mlog.info('CellClusterWeightKey ......... {0}'.format(towerBuilder.CellClusterWeightKey))
+        else:
+            ''' EM topo-towers
+            '''
+            mlog.info('###############################################')
+            mlog.info('## Produce EM calibrated topo-tower clusters ##')
+            mlog.info('###############################################')
+            mlog.info('CaloTopoClusterContainerKey .. {0}'.format(towerBuilder.CaloTopoClusterContainerKey))
+
+        if towerBuilder.BuildCombinedTopoSignal:
+            mlog.info(' ')
+            mlog.info('Combined topo-cluster/topo-towermode with y_max = {0}'.format(towerBuilder.TopoClusterRange))
+    else:    
+        ''' EM towers
+        '''
+        mlog.info('########################################')
+        mlog.info('## Produce EM standard tower clusters ##')
+        mlog.info('########################################')
+
+    ''' Set debug flag (not a property in the dictionary)
     '''
-    mlog.info(' ')
-    if buildtt:
-        mlog.info('################################################')
-        mlog.info('## Produce LCW calibrated topo-tower clusters ##')
-        mlog.info('################################################')
-        mlog.info('CaloTopoClusterContainerKey .. {0}'.format(tcluskey))
-        mlog.info('CellClusterWeightKey ......... {0}'.format(tcwgtkey))
-        towerConverter.CaloTopoClusterContainerKey = tcluskey
-        towerConverter.CellClusterWeightKey        = tcwgtkey
-        towerConverter.ApplyLCW                    = True
-    else:
-        mlog.info('####################################################')
-        mlog.info('## Produce EM calibrated inclusive tower clusters ##')
-        mlog.info('####################################################')
-    mlog.info(' ')
-    if applyEnergyThreshold:
-        towerConverter.CellEnergyThreshold = twralgo.CellEnergyThreshold 
     if debugOn:
-        towerConverter.OutputLevel = Lvl.DEBUG
-    # setting up the moments: external tools
+        towerBuilder.OutputLevel  = Lvl.DEBUG
+
+    towerCoreName = towerMakerName
+    if towerCoreName.find('Builder') > 0:
+        (towerCoreName.replace('Builder',' ')).rstrip(' ')
+    elif towerCoreName.find('Maker') > 0:
+        (towerCoreName.replace('Maker',' ')).rstrip(' ')
+
+    ############################
+    ## Setting up the moments ##
+    ############################
+    
+    ''' External tools for moment calculation
+    '''
     from CaloTools.CaloNoiseToolDefault import CaloNoiseToolDefault
-    caloNoiseTool = CaloNoiseToolDefault()
     from AthenaCommon.AppMgr import ToolSvc
-    ToolSvc += caloNoiseTool
+    caloNoiseTool  = CaloNoiseToolDefault()
+    ToolSvc       += caloNoiseTool
 
-    # moment maker
+    ''' Cluster moment maker (all general moments)
+    '''
     from CaloRec.CaloTopoClusterFlags import jobproperties
-    from AthenaCommon.SystemOfUnits import deg, GeV, MeV
-    from CaloRec.CaloRecConf import CaloClusterMomentsMaker
-    clusterMoments = CaloClusterMomentsMaker (clusterMakerName+'MomentMaker')
-    clusterMoments.MaxAxisAngle = 20*deg
-    clusterMoments.CaloNoiseTool = caloNoiseTool
-    clusterMoments.UsePileUpNoise = True
+    from AthenaCommon.SystemOfUnits   import deg, GeV, MeV
+    from CaloRec.CaloRecConf          import CaloClusterMomentsMaker
+    clusterMoments                  = CaloClusterMomentsMaker (towerMakerName+'MomentMaker')
+    clusterMoments.MaxAxisAngle     = 20*deg
+    clusterMoments.CaloNoiseTool    = caloNoiseTool
+    clusterMoments.UsePileUpNoise   = True
     clusterMoments.TwoGaussianNoise = jobproperties.CaloTopoClusterFlags.doTwoGaussianNoise()
     clusterMoments.MinBadLArQuality = 4000
-    clusterMoments.MomentsNames = ["FIRST_PHI" 
-                                   ,"FIRST_ETA"
-                                   ,"SECOND_R" 
-                                   ,"SECOND_LAMBDA"
-                                   ,"DELTA_PHI"
-                                   ,"DELTA_THETA"
-                                   ,"DELTA_ALPHA" 
-                                   ,"CENTER_X"
-                                   ,"CENTER_Y"
-                                   ,"CENTER_Z"
-                                   ,"CENTER_MAG"
-                                   ,"CENTER_LAMBDA"
-                                   ,"LATERAL"
-                                   ,"LONGITUDINAL"
-                                   ,"FIRST_ENG_DENS" 
-                                   ,"ENG_FRAC_EM" 
-                                   ,"ENG_FRAC_MAX" 
-                                   ,"ENG_FRAC_CORE" 
-                                   ,"SECOND_ENG_DENS" 
-                                   ,"ISOLATION"
-                                   ,"ENG_BAD_CELLS"
-                                   ,"N_BAD_CELLS"
-                                   ,"N_BAD_CELLS_CORR"
-                                   ,"BAD_CELLS_CORR_E"
-                                   ,"BADLARQ_FRAC"
-                                   ,"ENG_POS"
-                                   ,"SIGNIFICANCE"
-                                   ,"CELL_SIGNIFICANCE"
-                                   ,"CELL_SIG_SAMPLING"
-                                   ,"AVG_LAR_Q"
-                                   ,"AVG_TILE_Q"
-                                   ,"PTD"
-                                   ,"MASS"
-                                   ]
-
-    # only add HV related moments if it is offline.
+    clusterMoments.MomentsNames     = [
+        "FIRST_PHI" 
+        ,"FIRST_ETA"
+        ,"SECOND_R" 
+        ,"SECOND_LAMBDA"
+        ,"DELTA_PHI"
+        ,"DELTA_THETA"
+        ,"DELTA_ALPHA" 
+        ,"CENTER_X"
+        ,"CENTER_Y"
+        ,"CENTER_Z"
+        ,"CENTER_MAG"
+        ,"CENTER_LAMBDA"
+        ,"LATERAL"
+        ,"LONGITUDINAL"
+        ,"FIRST_ENG_DENS" 
+        ,"ENG_FRAC_EM" 
+        ,"ENG_FRAC_MAX" 
+        ,"ENG_FRAC_CORE" 
+        ,"SECOND_ENG_DENS" 
+        ,"ISOLATION"
+        ,"ENG_BAD_CELLS"
+        ,"N_BAD_CELLS"
+        ,"N_BAD_CELLS_CORR"
+        ,"BAD_CELLS_CORR_E"
+        ,"BADLARQ_FRAC"
+        ,"ENG_POS"
+        ,"SIGNIFICANCE"
+        ,"CELL_SIGNIFICANCE"
+        ,"CELL_SIG_SAMPLING"
+        ,"AVG_LAR_Q"
+        ,"AVG_TILE_Q"
+        ,"PTD"
+        ,"MASS"
+    ]
+
+    ''' HV related moments for offline data
+    '''
     from IOVDbSvc.CondDB import conddb
     if not conddb.isOnline:
         from LArRecUtils.LArHVScaleRetrieverDefault import LArHVScaleRetrieverDefault
-        clusterMoments.LArHVScaleRetriever=LArHVScaleRetrieverDefault()
-        clusterMoments.MomentsNames += ["ENG_BAD_HV_CELLS"
-                                        ,"N_BAD_HV_CELLS"
-                                        ]
+        clusterMoments.LArHVScaleRetriever = LArHVScaleRetrieverDefault()
+        clusterMoments.MomentsNames       += ["ENG_BAD_HV_CELLS","N_BAD_HV_CELLS"]
 
-    # cluster maker
+    ###############################################################
+    ## Set up the tower builder algorithm - as a cluster builder ##
+    ###############################################################
+
+    ''' Basic algorithm properties
+    '''
     from CaloRec.CaloRecConf import CaloClusterMaker
-    clusterMaker = CaloClusterMaker(clusterMakerName)
-    clusterMaker.ClustersOutputName = clusterContainerKey
-    clusterMaker.ClusterMakerTools  = [ towerConverter ]
-    mlog.info('instantiated CaloClusterMaker "{0}"'.format(clusterMaker.name()))
+    towerMaker                    = CaloClusterMaker(towerMakerName)
+    towerMaker.ClustersOutputName = towerContainerKey
+    towerMaker.ClusterMakerTools  = [ towerBuilder ]
+    towerMaker                   += towerBuilder
+    mlog.info('instantiated CaloClusterMaker "{0}" configuration'.format(towerMaker.name()))
 
-    # bad cell corrections          
-##    from CaloClusterCorrection.CaloClusterBadChannelListCorr import CaloClusterBadChannelListCorr
-##    badChannelCorr = CaloClusterBadChannelListCorr()
+    ''' Set up bad cell corrections
+    '''
+    from CaloClusterCorrection.CaloClusterBadChannelListCorr import CaloClusterBadChannelListCorr
+    badChannelCorr = CaloClusterBadChannelListCorr()
 
-    # Correction tools
-##    clusterMaker.ClusterCorrectionTools += [ badChannelCorr ]
-    clusterMaker.ClusterCorrectionTools += [ clusterMoments ]
+    ''' Register correction and moment tools
+    '''
+    towerMaker.ClusterCorrectionTools += [ badChannelCorr ]
+    towerMaker.ClusterCorrectionTools += [ clusterMoments ]
+    towerMaker                        += clusterMoments
 
-    # configuring the algorithm
-    clusterMaker += towerConverter
-##    clusterMaker += badChannelCorr
-    clusterMaker += clusterMoments
+    ####################################
+    ## Configure LCW calibration tool ##
+    ####################################
 
-    if buildtt:
+    if towerBuilder.PrepareLCW:
         from CaloRec.CaloRecConf import CaloTopoClusterFromTowerCalibrator
-        calgname = clusterMakerName+'Calibrator'
-        mlog.info('TopoTowers: add LCW calibration tool <'+calgname+'>')
-        clusterCalibrator = CaloTopoClusterFromTowerCalibrator(calgname)
-        mlog.info('TopoTowers: '+calgname+'.CellClusterWeightKey = "'+tcwgtkey+'"')
-        clusterCalibrator.CellClusterWeightKey     = tcwgtkey
-        clusterCalibrator.OrderClusterByPt         = ptorder
-        clusterMaker.ClusterCorrectionTools       += [ clusterCalibrator ]
-        clusterMaker                              += clusterCalibrator
-
-    # done
-    return clusterMaker
+        ''' Configure name for calibration tool
+        '''
+        towerCalName    = towerCoreName+'Calibrator'
+        towerCalibrator = CaloTopoClusterFromTowerCalibrator(towerCalName)
+        mlog.info('add LCW calibration tool <'+towerCalName+'>')
+        mlog.info('TopoTowers: '+towerCalName+'.CellClusterWeightKey = "'+towerBuilder.CellClusterWeightKey+'"')
+        towerCalibrator.CellClusterWeightKey = towerBuilder.CellClusterWeightKey
+        towerCalibrator.OrderClusterByPt     = configDict['OrderClusterByPt']
+        if debugOn:
+            towerCalibrator.OutputLevel = Lvl.DEBUG
+        ''' Schedule calibration tool
+        '''
+        towerMaker.ClusterCorrectionTools += [ towerCalibrator ]
+        towerMaker                        += towerCalibrator
+
+    #######################
+    # Configuration done ##
+    #######################
+
+    return towerMaker
+
+##
+##    toolname       = configDict['ClusterBuilderName']     ### name of the tower builder tool
+##    cellkey        = configDict['CaloCellContainerKey']   ### cell container key
+##    buildtopotower = configDict['BuildTopoTowers']        ### controls if topo-towers or inclusive towers are built
+##    towergeosvc    = configDict['CaloTowerGeometrySvc']   ### tower geometry provider 
+##    if ( buildtopotower ):
+##        topoclusterkey = configDict['CaloTopoClusterContainerKey']
+##    else:
+##        topoclusterkey = 'N/A'
+##
+##    cellweightkey  = configDict['CellClusterWeightKey']
+##
+##    mlog.info('(input) CaloCellContainer        <'+cellkey+'>')
+##    mlog.info('(input) CaloTopoClusterContainer <'+topoclusterkey+'>')
+##    mlog.info('(input) CellClusterWeightKey     <'+cellweightkey+'>')
diff --git a/Calorimeter/CaloRec/share/CaloRec_jobOptions.py b/Calorimeter/CaloRec/share/CaloRec_jobOptions.py
index de9da525e1346511f3fee547907118882a33ca95..100a5278e0f7c6607d8037b36ee26899478bc003 100644
--- a/Calorimeter/CaloRec/share/CaloRec_jobOptions.py
+++ b/Calorimeter/CaloRec/share/CaloRec_jobOptions.py
@@ -248,14 +248,14 @@ else:
 #
 # functionality : Noise suppressed tower
 #
-if jobproperties.CaloRecFlags.doCaloTopoTower() and DetFlags.haveRIO.Calo_on():
-   try:
-       include ("CaloRec/CaloTopoTower_jobOptions.py")
-   except Exception:
-       treatException("Problem with CaloTopoTower. Switched off.")
-       jobproperties.CaloRecFlags.doCaloTopoTower=False
-else:
-   jobproperties.CaloRecFlags.doCaloTopoTower=False
+#if jobproperties.CaloRecFlags.doCaloTopoTower() and DetFlags.haveRIO.Calo_on():
+#   try:
+#       include ("CaloRec/CaloTopoTower_jobOptions.py")
+#   except Exception:
+#       treatException("Problem with CaloTopoTower. Switched off.")
+#       jobproperties.CaloRecFlags.doCaloTopoTower=False
+#else:
+#   jobproperties.CaloRecFlags.doCaloTopoTower=False
 
 #
 # functionality : muon candidates in Tile
@@ -361,3 +361,10 @@ if rec.doWritexAOD():
 #L1Calo Trigger tower decoration
 if globalflags.DataSource()=='data' and rec.doESD() and rec.doCalo() and rec.doTrigger():
     include("TrigT1CaloCalibTools/DecorateL1CaloTriggerTowers_prodJobOFragment.py")
+
+#new style CaloTopoTowers
+if jobproperties.CaloRecFlags.doCaloTopoTower():
+    include ( "CaloRec/CaloTopoTowerFragment.py" )
+#mixed topo-cluster/topo-tower 
+if jobproperties.CaloRecFlags.doCaloTopoSignal():
+    include ("CaloRec/CaloTopoSignalFragment.py" )
diff --git a/Calorimeter/CaloRec/share/CaloTopoSignalFragment.py b/Calorimeter/CaloRec/share/CaloTopoSignalFragment.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e936582044478363aab955eff6ba93da427ad18
--- /dev/null
+++ b/Calorimeter/CaloRec/share/CaloTopoSignalFragment.py
@@ -0,0 +1,65 @@
+######################################
+## Create standard 0.1 x 0.1 towers ##
+######################################
+
+
+from AthenaCommon.Logging import logging
+mlog = logging.getLogger('CaloTopoSognalFragment.py:: ')
+
+import AthenaCommon.Constants as Lvl
+from AthenaCommon.AppMgr import ServiceMgr as svcMgr
+
+from CaloRec.MakeClustersFromTowers import ClustersFromTowersDict, MakeClustersFromTowers
+from CaloRec.CaloRecConf            import CaloTowerGeometrySvc, CaloTopoClusterTowerMerger
+
+mlog.info(' ')
+mlog.info('##################################')
+mlog.info('## Topological Signal Formation ##')
+mlog.info('##################################')
+mlog.info(' ')
+
+#############################
+## Tower Geometry Provider ##
+#############################
+
+if not hasattr(svcMgr,'CaloTowerGeometryProvider'):
+    mlog.info("setting up tower geometry provider")
+    caloTowerGeoSvc              = CaloTowerGeometrySvc('CaloTowerGeometryProvider')
+    caloTowerGeoSvc.TowerEtaBins = 100
+    caloTowerGeoSvc.TowerEtaMin  = -5.
+    caloTowerGeoSvc.TowerEtaMax  =  5.
+    svcMgr                      += caloTowerGeoSvc
+
+
+#############################
+## CaloTopoTower Formation ##
+#############################
+
+caloTowerDict = ClustersFromTowersDict(clusterBuilderName='CaloFwdTopoTowerBuilder',
+                                       towerGeometrySvc=svcMgr.CaloTowerGeometryProvider,
+                                       cellContainerKey='AllCalo',
+                                       buildTopoTowers=True,
+                                       topoClusterContainerKey='CaloCalTopoClusters',
+                                       cellClusterWeightKey='CaloCalFwdTopoTowerCellWeights',
+                                       orderClusterByPt=False,
+                                       applyCellEnergyThreshold=False,
+                                       doCellIndexCheck=False,
+                                       cellEnergyThreshold=0.,
+                                       applyLCW=True,
+                                       buildCombinedSignal=True,
+                                       clusterRange=2.5)
+
+caloTowerAlgo = MakeClustersFromTowers(towerMakerName      = 'CaloFwdTopoTowerMaker',
+                                       towerContainerKey = 'CaloCalFwdTopoTowers',
+                                       configDict          = caloTowerDict,
+                                       debugOn             = False)
+#merging
+caloTowerMerger                         = CaloTopoClusterTowerMerger("CaloTopoSignalMaker")
+caloTowerMerger.TopoClusterRange        = caloTowerAlgo.CaloFwdTopoTowerBuilder.TopoClusterRange
+caloTowerMerger.TopoClusterContainerKey = caloTowerAlgo.CaloFwdTopoTowerBuilder.CaloTopoClusterContainerKey
+caloTowerMerger.TopoTowerContainerKey   = caloTowerAlgo.ClustersOutputName
+caloTowerMerger.TopoSignalContainerKey  = 'CaloCalTopoSignals'
+caloTowerMerger.OutputLevel             = Lvl.DEBUG
+
+topSequence+=caloTowerAlgo
+topSequence+=caloTowerMerger
diff --git a/Calorimeter/CaloRec/share/CaloTopoTowerFragment.py b/Calorimeter/CaloRec/share/CaloTopoTowerFragment.py
new file mode 100644
index 0000000000000000000000000000000000000000..56abf8c621187e77f2187bc65d939d3c759e8082
--- /dev/null
+++ b/Calorimeter/CaloRec/share/CaloTopoTowerFragment.py
@@ -0,0 +1,56 @@
+######################################
+## Create standard 0.1 x 0.1 towers ##
+######################################
+
+
+from AthenaCommon.Logging import logging
+mlog = logging.getLogger('CaloTopoTowerFragment.py:: ')
+
+import AthenaCommon.Constants as Lvl
+from AthenaCommon.AppMgr import ServiceMgr as svcMgr
+
+from CaloRec.MakeClustersFromTowers import ClustersFromTowersDict, MakeClustersFromTowers
+from CaloRec.CaloRecConf            import CaloTowerGeometrySvc
+
+mlog.info(' ')
+mlog.info('##################################')
+mlog.info('## Standard Tower Configuration ##')
+mlog.info('##################################')
+mlog.info(' ')
+
+#############################
+## Tower Geometry Provider ##
+#############################
+
+if not hasattr(svcMgr,'CaloTowerGeometryProvider'):
+    mlog.info("setting up tower geometry provider")
+    caloTowerGeoSvc              = CaloTowerGeometrySvc('CaloTowerGeometryProvider')
+    caloTowerGeoSvc.TowerEtaBins = 100
+    caloTowerGeoSvc.TowerEtaMin  = -5.
+    caloTowerGeoSvc.TowerEtaMax  =  5.
+    svcMgr                      += caloTowerGeoSvc
+
+#############################
+## CaloTopoTower Formation ##
+#############################
+
+caloTowerDict = ClustersFromTowersDict(clusterBuilderName='CaloTopoTowerBuilder',
+                                       towerGeometrySvc=svcMgr.CaloTowerGeometryProvider,
+                                       cellContainerKey='AllCalo',
+                                       buildTopoTowers=True,
+                                       topoClusterContainerKey='CaloCalTopoClusters',
+                                       cellClusterWeightKey='CaloCalTopoTowerCellWeights',
+                                       orderClusterByPt=False,
+                                       applyCellEnergyThreshold=False,
+                                       doCellIndexCheck=False,
+                                       cellEnergyThreshold=0.,
+                                       applyLCW=True,
+                                       buildCombinedSignal=False,
+                                       clusterRange=5.)
+
+caloTowerAlgo = MakeClustersFromTowers(towerMakerName      = 'CaloTopoTowerMaker',
+                                       towerContainerKey = 'CaloCalTopoTowers',
+                                       configDict          = caloTowerDict,
+                                       debugOn             = False)
+
+topSequence+=caloTowerAlgo
diff --git a/Calorimeter/CaloRec/src/CaloClusterMomentsMaker.cxx b/Calorimeter/CaloRec/src/CaloClusterMomentsMaker.cxx
index 7b80840b591317c65e1903bf01bdcd92a6cfcedb..bdf1d6d8c7fea57daa37ba28b38bf744f80b4ad9 100644
--- a/Calorimeter/CaloRec/src/CaloClusterMomentsMaker.cxx
+++ b/Calorimeter/CaloRec/src/CaloClusterMomentsMaker.cxx
@@ -40,6 +40,7 @@
 #include <cstdint>
 #include <limits>
 #include <math.h>
+//#include <cstdio>
 
 
 using HepGeom::Vector3D;
@@ -375,20 +376,22 @@ StatusCode CaloClusterMomentsMaker::execute(xAOD::CaloClusterContainer *theClusC
       // loop over all cell members and fill cell vector for used cells
       xAOD::CaloCluster::cell_iterator cellIter    = theCluster->cell_begin();
       xAOD::CaloCluster::cell_iterator cellIterEnd = theCluster->cell_end();
+      //      printf("CaloClusterMomentMaker::execute(...) - number of cells cluster %4i is %zu\n",iClus,theCluster->size());
+      //     int iCell(0);
       for(; cellIter != cellIterEnd; cellIter++ ){
-        CxxUtils::prefetchNext(cellIter, cellIterEnd);
+	CxxUtils::prefetchNext(cellIter, cellIterEnd);
         const CaloCell* pCell = *cellIter;
-
-	Identifier myId = pCell->ID();
-	IdentifierHash myHashId = m_calo_id->calo_cell_hash(myId); 
-	if ( clusterIdx[(unsigned int)myHashId].first != noCluster) {
-	  // check weight and assign to current cluster if weight is > 0.5
-	  double weight = cellIter.weight();
-	  if ( weight > 0.5 )
+	//	printf("CaloClusterMomentMaker::execute(...) - cell %4i/%4zu reference %p\n",++iCell,theCluster->size(),(void*)pCell);
+	if ( pCell != 0 ) { 
+	  Identifier myId = pCell->ID();
+	  IdentifierHash myHashId = m_calo_id->calo_cell_hash(myId); 
+	  if ( clusterIdx[(unsigned int)myHashId].first != noCluster) {
+	    // check weight and assign to current cluster if weight is > 0.5
+	    double weight = cellIter.weight();
+	    if ( weight > 0.5 )  clusterIdx[(unsigned int)myHashId].first = iClus;
+	  } else {
 	    clusterIdx[(unsigned int)myHashId].first = iClus;
-	}
-	else {
-	  clusterIdx[(unsigned int)myHashId].first = iClus;
+	  }
 	}
       }
       ++iClus;
diff --git a/Calorimeter/CaloRec/src/CaloTopoClusterFromTowerCalibrator.cxx b/Calorimeter/CaloRec/src/CaloTopoClusterFromTowerCalibrator.cxx
index 4a7c082f7420d3140da8abb728c82cd6522f3213..c75e4053aabb4e92aa9b3dacef043305f183d58c 100644
--- a/Calorimeter/CaloRec/src/CaloTopoClusterFromTowerCalibrator.cxx
+++ b/Calorimeter/CaloRec/src/CaloTopoClusterFromTowerCalibrator.cxx
@@ -1,9 +1,12 @@
 /* Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration */
+
 #include "CaloRec/CaloTopoClusterFromTowerCalibrator.h"
 #include "CaloRec/CaloTopoClusterFromTowerHelpers.h"
 
 #include "CaloEvent/CaloCellClusterWeights.h"
 
+#include "CaloGeoHelpers/CaloSampling.h"
+
 #include <numeric>
 
 namespace {                                                                             /////////////////////////////////////////////////////////////
@@ -16,24 +19,30 @@ namespace {
   }
 }
 
-std::string CaloTopoClusterFromTowerCalibrator::m_defaultKey = "NONE";
-
 CaloTopoClusterFromTowerCalibrator::CaloTopoClusterFromTowerCalibrator(const std::string& type,const std::string& name,const IInterface* pParent)
   : AthAlgTool(type,name,pParent)
-  , m_cellClusterWeightsKey(m_defaultKey)
+  , m_cellClusterWeightKey("CaloTopoClusterCellWeights")
   , m_orderByPt(false)
 { 
   declareInterface<CaloClusterCollectionProcessor>(this);
-  declareProperty("CellClusterWeightKey",m_cellClusterWeightsKey,"SG Key for CellClusterWeights (input)");
-  declareProperty("OrderClusterByPt",           m_orderByPt,            "Order clusters by calibrated Pt (input)");
+  declareProperty("CellClusterWeightKey",m_cellClusterWeightKey,"SG Key for CellClusterWeights (input)");
+  declareProperty("OrderClusterByPt",    m_orderByPt,           "Order clusters by calibrated Pt (input)");
 }
 
+StatusCode CaloTopoClusterFromTowerCalibrator::initialize()
+{ 
+  ATH_CHECK(m_cellClusterWeightKey.initialize()); 
+  return StatusCode::SUCCESS;
+}
 
 StatusCode CaloTopoClusterFromTowerCalibrator::execute(xAOD::CaloClusterContainer* pClusCont)
 {
   // retrieve weights
-  const CaloCellClusterWeights* pCellWeights = 0;
-  CHECK(evtStore()->retrieve(pCellWeights,CaloCellClusterWeights::key(m_cellClusterWeightsKey) )); // PA change
+  SG::ReadHandle<CaloCellClusterWeights> pCellWeights(m_cellClusterWeightKey);
+  if ( !pCellWeights.isValid() ) { 
+    ATH_MSG_ERROR( "Cannot allocate CaloCellClusterWeights with key <" << m_cellClusterWeightKey.key() << ">" );
+    return StatusCode::FAILURE;
+  }
 
   /////////////////////////
   // Calibrated clusters //
@@ -46,7 +55,15 @@ StatusCode CaloTopoClusterFromTowerCalibrator::execute(xAOD::CaloClusterContaine
       const CaloCellClusterWeights::weight_t& wght(pCellWeights->at(*fCell));      // Retrieve list of weights associated with this cluster. //
       double weight(fCell.weight());                                               // Get cell-in-tower weight.                              //
       weight *= accumulateWeight(wght);                                            // Combine with calibration weights.                      //
-      pClus->reweightCell(fCell,weight);                                           // Set new weight.                                        //
+      if ( weight == 0. ) { 
+	ATH_MSG_DEBUG( CaloRec::Helpers::fmtMsg("[NO_LCW_REWEIGHT] Tower (%6.3f,%6.3f) cell [%6zu] weight = %6.3f [# LCW weights %zu geo %6.3f LCW %6.3f] SamplingID %2u Name \042%s\042",
+						pClus->eta(),pClus->phi(),(size_t)fCell->caloDDE()->calo_hash(),weight,wght.size(),fCell.weight(),weight/std::max(fCell.weight(),1e-08),
+						(unsigned int)fCell->caloDDE()->getSampling(),CaloSampling::getSamplingName(fCell->caloDDE()->getSampling()).c_str()) );
+      } else {
+	ATH_MSG_DEBUG(   CaloRec::Helpers::fmtMsg("[DO_LCW_REWEIGHT] Tower (%6.3f,%6.3f) cell [%6zu] weight = %6.3f [# LCW weights %zu geo %6.3f LCW %6.3f]",
+						  pClus->eta(),pClus->phi(),(size_t)fCell->caloDDE()->calo_hash(),weight,wght.size(),fCell.weight(),weight/fCell.weight()) );
+	pClus->reweightCell(fCell,weight);                                         // Set new weight.                                        //
+      }
     }                                                                              ////////////////////////////////////////////////////////////
     // preserve raw (EM) kinematics
     double rawE(pClus->e());                       ////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/Calorimeter/CaloRec/src/CaloTopoClusterFromTowerMaker.cxx b/Calorimeter/CaloRec/src/CaloTopoClusterFromTowerMaker.cxx
index 2ef79a197b00eda28935230512d108ad6ea85212..bb4fa41e25257d1de7b3db657191bcbbb1f17616 100644
--- a/Calorimeter/CaloRec/src/CaloTopoClusterFromTowerMaker.cxx
+++ b/Calorimeter/CaloRec/src/CaloTopoClusterFromTowerMaker.cxx
@@ -1,14 +1,15 @@
-/* Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration */
+/* Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration */
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/SystemOfUnits.h"
+
+#include "AthenaKernel/Units.h"
+
 #include "CaloRec/CaloTopoClusterFromTowerMaker.h"
 #include "CaloRec/CaloTopoClusterFromTowerHelpers.h"
 
-
-#include "xAODCaloEvent/CaloTower.h"
-#include "xAODCaloEvent/CaloTower.h"
 #include "xAODCaloEvent/CaloClusterKineHelper.h"
 
 #include "CaloEvent/CaloClusterCellLink.h"
-#include "CaloEvent/CaloCellTowerLink.h"
 #include "CaloEvent/CaloCellClusterWeights.h"
 
 #include "CaloGeoHelpers/CaloSampling.h"
@@ -16,277 +17,280 @@
 #include "CaloGeoHelpers/proxim.h"
 
 #include "CaloRec/CaloProtoCluster.h"
+#include "CaloRec/CaloTowerGeometrySvc.h"
+
+#include "CaloDetDescr/CaloDetDescrElement.h"
+#include "CaloDetDescr/CaloDetDescrManager.h"
 
 #include <algorithm>
 
 #include <cstdio>
+#include <cstdarg>
 #include <string>
 #include <cmath>
-#include <array>
 #include <memory>
 #include <vector>
+#include <atomic>
+#include <tuple>
 
-//////////////////////////////////////////////////
-// CaloTopoClusterFromTowerMaker::CellPathology //
-//////////////////////////////////////////////////
-
-CaloTopoClusterFromTowerMaker::CellPathology::CellPathology()
-{ }
-
-void CaloTopoClusterFromTowerMaker::CellPathology::setInvalidPointer(int cellhash,const CaloCell* cptr)
-{
-  std::string msg(CaloRec::Helpers::fmtMsg("CaloCell [%06i] has invalid pointer %p",cellhash,(void*)cptr));
-  _addMsg(msg,_invalidPointer);
+namespace {
+  MsgStream& operator<<(MsgStream& mstr,const SG::ReadHandleKey<CaloCellContainer>&          ckey) { mstr << ckey.key(); return mstr; }
+  MsgStream& operator<<(MsgStream& mstr,const SG::ReadHandleKey<xAOD::CaloClusterContainer>& ckey) { mstr << ckey.key(); return mstr; }
+  MsgStream& operator<<(MsgStream& mstr,const SG::WriteHandleKey<CaloCellClusterWeights>&    ckey) { mstr << ckey.key(); return mstr; }
 }
 
-void CaloTopoClusterFromTowerMaker::CellPathology::setInvalidHash(int cellhash,const CaloCell* cptr)
-{
-  std::string msg(CaloRec::Helpers::fmtMsg("CaloCell [%06i] at %p has invalid hash %i",cellhash,(void*)cptr,cellhash));
-  _addMsg(msg,_invalidHash);
-}
-
-void CaloTopoClusterFromTowerMaker::CellPathology::setInvalidSampling(int cellhash,const CaloCell* cptr)
-{
-  int hash(CaloSampling::Unknown);
-  if ( cptr != 0 ) { hash = (int)cptr->caloDDE()->getSampling(); } 
-  std::string msg(CaloRec::Helpers::fmtMsg("CaloCell [%06i] at %p has invalid sampling %2i (not in [0,%2i]",cellhash,(void*)cptr,hash,(int)CaloSampling::Unknown));
-  _addMsg(msg,_invalidSampling);
-}
+std::atomic<bool> CaloTopoClusterFromTowerMaker_checkCellIndices(false);
 
 ///////////////////////////////////
 // CaloTopoClusterFromTowerMaker //
 ///////////////////////////////////
 
-double CaloTopoClusterFromTowerMaker::m_energyThresholdDef = -100000000.; // in MeV
-std::string CaloTopoClusterFromTowerMaker::m_defaultKey    = "NONE";
+double                                CaloTopoClusterFromTowerMaker::m_energyThresholdDef = -100000000.; // in MeV
+double                                CaloTopoClusterFromTowerMaker::m_clusterRangeDef    = 5.;
+std::string                           CaloTopoClusterFromTowerMaker::m_defaultKey         = "NONE";
+CaloTopoClusterFromTowerMaker::uint_t CaloTopoClusterFromTowerMaker::m_errorValueUINT     = uint_t(-1); 
 
 CaloTopoClusterFromTowerMaker::CaloTopoClusterFromTowerMaker(const std::string& type,
 							     const std::string& name,
 							     const IInterface* pParent)
   : AthAlgTool(type,name,pParent)
-  , m_towerContainerKey(m_defaultKey)
-  , m_cellTowerLinksKey(m_defaultKey)
+  , m_towerGeometrySvc("CaloTowerGeometrySvc",name)
+  , m_clusterContainerKey("CaloTopoCluster")
   , m_cellContainerKey("AllCalo")
-  , m_clusterContainerKey(m_defaultKey)
-  , m_cellClusterWeightKey(m_defaultKey)
+  , m_cellClusterWeightKey("CaloTopoClusterCellWeights")
   , m_orderByPt(false)
-  , m_energyThreshold(m_energyThresholdDef-1.)
-  , m_applyLCW(false)
-  , m_applyEnergyThreshold(false)
-  , m_useCellsFromClusters(false)
   , m_prepareLCW(false)
+  , m_useCellsFromClusters(true)
+  , m_applyCellEnergyThreshold(false)
+  , m_doCellIndexCheck(false)
+  , m_buildCombinedSignal(false)
+  , m_energyThreshold(m_energyThresholdDef-1.)
+  , m_clusterRange(m_clusterRangeDef)
   , m_numberOfCells(0)
-  , m_numberOfSamplings(CaloSampling::Unknown)
-  , m_cellTowerLinksHandle(0)
-  , m_cellClusterWeights(0)
+  , m_maxCellHash(0)
+  , m_numberOfSamplings(static_cast<uint_t>(CaloSampling::Unknown))
+  , m_numberOfTowers(0)
 {
   declareInterface<CaloClusterCollectionProcessor>(this);
-  declareProperty("CaloTowerContainerKey",       m_towerContainerKey,   "SG Key for CaloTowerContainer (input)");
-  declareProperty("CaloCellContainerKey",        m_cellContainerKey,    "SG Key for CaloCellContainer (input)");
-  declareProperty("CaloTopoClusterContainerKey", m_clusterContainerKey, "SG Key for CaloClusterContainer (input)");
-  declareProperty("CellClusterWeightKey",        m_cellClusterWeightKey,"SG Key for CellClusterWeights (output)");
-  declareProperty("OrderClusterByPt",            m_orderByPt,           "Turn on/off pT-ordering of CaloClusterContainer (output)");
-  declareProperty("CellEnergyThreshold",         m_energyThreshold,     "Energy threshold for cells filled in clusters");
-  declareProperty("ApplyLCW",                    m_applyLCW,            "Prepare data structure to apply LCW");
+  declareProperty("CaloTowerGeometrySvc",        m_towerGeometrySvc=ServiceHandle<CaloTowerGeometrySvc>("CaloTowerGeometrySvc",name), "Service providing tower geometry");
+  declareProperty("CaloCellContainerKey",        m_cellContainerKey,                                                                  "SG Key for CaloCellContainer (input)");
+  declareProperty("BuildTopoTowers",             m_useCellsFromClusters,                                                              "Turn on/off topo-tower formation");
+  declareProperty("CaloTopoClusterContainerKey", m_clusterContainerKey,                                                               "SG Key for CaloClusterContainer (input)");
+  declareProperty("CellClusterWeightKey",        m_cellClusterWeightKey,                                                              "SG Key for CellClusterWeights (output)");
+  declareProperty("OrderClusterByPt",            m_orderByPt,                                                                         "Turn on/off pT-ordering of CaloClusterContainer (output)");
+  declareProperty("ApplyCellEnergyThreshold",    m_applyCellEnergyThreshold,                                                          "Turn on/off cell energy thresholds");
+  declareProperty("CellEnergyThreshold",         m_energyThreshold,                                                                   "Energy threshold for cells filled in clusters");
+  declareProperty("PrepareLCW",                  m_prepareLCW,                                                                        "Prepare data structure to apply LCW");
+  declareProperty("ExcludedSamplings",           m_excludedSamplingsName,                                                             "Excluded samplings by name");
+  declareProperty("DoCellIndexCheck",            m_doCellIndexCheck,                                                                  "Check cell hash indices for consistency");
+  declareProperty("BuildCombinedTopoSignal",     m_buildCombinedSignal,                                                               "Build topo-clusters and topo-towers");
+  declareProperty("TopoClusterRange",            m_clusterRange,                                                                      "Rapidity range for using topo-clusters in combined signal mode");
 }
 
 StatusCode CaloTopoClusterFromTowerMaker::initialize()
 {
-  // build SG key for cell-tower link map object
-  m_cellTowerLinksKey = m_towerContainerKey + CaloCellTowerLink::extTag;
-  ATH_MSG_INFO("Input retrieved from CaloTowerxAODContainer <" << m_towerContainerKey << "> and CaloCellTowerLink::Map <" << m_cellTowerLinksKey << ">");
-  // check if energy threshold is to be applied
-  m_applyEnergyThreshold = m_energyThreshold > m_energyThresholdDef;
-  // check if filtered cells to be used
-  m_useCellsFromClusters = m_clusterContainerKey != m_defaultKey;
-  // analyze configuration
+  //--------------------//
+  // Set up handle keys //
+  //--------------------//
+
+  ATH_CHECK(m_cellContainerKey.initialize());
+  
+  //---------------------//
+  // Check configuration //
+  //---------------------//
+
+  // tower geometry service
+  ATH_MSG_INFO("Allocate tower geometry service:");
+  if ( !m_towerGeometrySvc.isValid() ) { 
+    ATH_MSG_ERROR("[reject] cannot allocate tower geometry service - fatal");
+    return StatusCode::FAILURE; 
+  } else {
+    ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("Tower geometry service is allocated, describes %6zu towers in grid:", m_towerGeometrySvc->towerBins()) );
+    ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("[accept] %3zu eta bins in [%5.2f,%5.2f]",m_towerGeometrySvc->etaBins(),m_towerGeometrySvc->etaMin(),m_towerGeometrySvc->etaMax()) );
+    ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("[accept] %3zu phi bins in [%5.2f,%5.2f]",m_towerGeometrySvc->phiBins(),m_towerGeometrySvc->phiMin(),m_towerGeometrySvc->phiMax()) );
+  }
+
+  // tower builder configurations
   if ( m_useCellsFromClusters ) {
-    if ( m_applyEnergyThreshold ) { 
-      ATH_MSG_WARNING("Misconfiguration - cannot apply energy threshold to cells from TopoClusters, will accept all clustered cells"); 
-      m_applyEnergyThreshold = false;
-    } else { 
-      ATH_MSG_INFO("Filtered mode, cluster from towers filled with cells from TopoClusters");
+    // topo-tower
+    ATH_MSG_INFO("Configure for building topo-towers (filtered mode):");
+    // energy threshold not (yet) implemented for topo-towers
+    if ( m_applyCellEnergyThreshold ) {
+      ATH_MSG_WARNING("[ignore] cannot apply energy thresholds to topo-towers!");
+      m_applyCellEnergyThreshold = false;
     }
+    ATH_CHECK(m_clusterContainerKey.initialize());
+    // check on request for LCW
+    if ( m_prepareLCW ) { 
+      ATH_CHECK(m_cellClusterWeightKey.initialize());
+      ATH_MSG_INFO("[accept] prepare for LCW calibration - initialize CaloCellClusterWeights key object <" << m_cellClusterWeightKey << ">");
+    } else {
+      ATH_MSG_INFO("[accept] use EM scale");
+    }
+  } else { 
+    // inclusive/exclusive towers
+    ATH_MSG_INFO("Configure for building cell towers:");
+    if ( m_applyCellEnergyThreshold ) { 
+      ATH_MSG_INFO("[accept] configure exclusive towers: use cell energy threshold");
+      if ( m_energyThreshold < m_energyThresholdDef ) { 
+	ATH_MSG_ERROR("######## [reject] invalid cell energy threshold " << m_energyThreshold/Athena::Units::GeV 
+		      << " GeV is smaller than default (no-op) " << m_energyThresholdDef/Athena::Units::GeV << " GeV - fatal");
+	return StatusCode::FAILURE;
+      }
+      ATH_MSG_INFO("######## [accept] energy threshold for cells to contribute to towers is " << m_energyThreshold/Athena::Units::GeV << " GeV");
+    } else {
+      ATH_MSG_INFO("[accept] configure inclusive towers");
+    } // end inclusive/exclusive tower configuration
+  } // end tower builder configuration
+
+  // local data (constant parameters)
+  m_numberOfCells  = m_towerGeometrySvc->totalNumberCells();
+  m_maxCellHash    = m_towerGeometrySvc->maxCellHash();
+  m_numberOfTowers = m_towerGeometrySvc->towerBins();
+  ATH_MSG_INFO("Additional tool parameters:");
+  if ( m_numberOfCells > 0 ) { 
+    ATH_MSG_INFO("[accept] maximum cell hash index is " << m_maxCellHash);
+    ATH_MSG_INFO("[accept] maximum number of cells is " << m_numberOfCells);
   } else { 
-    if ( m_applyEnergyThreshold ) { 
-      ATH_MSG_INFO(CaloRec::Helpers::fmtMsg("Exclusive mode, cluster from towers filled with cells with energy > %.3f MeV",m_energyThreshold)); 
-    } else { 
-      ATH_MSG_INFO("Inclusive mode, all cells are used");
+    ATH_MSG_ERROR("[reject] invalid maximum cell hash index/total number of cells " << m_maxCellHash << "/" << m_numberOfCells << " - fatal");
+    return StatusCode::FAILURE;
+  }
+  if ( m_numberOfTowers > 0 ) { 
+    ATH_MSG_INFO("[accept] maximum number of towers is " << m_numberOfTowers);
+  } else {
+    ATH_MSG_ERROR("[reject] invalid maximum number of towers " << m_numberOfTowers << " - fatal");
+    return StatusCode::FAILURE;
+  }
+
+  if ( m_excludedSamplingsName.empty() ) { 
+    m_excludedSamplings.clear();
+    m_excludedSamplingsPattern.reset();
+    ATH_MSG_INFO("Cells from all samplings used for topo-cluster included"); 
+  } else {
+    size_t nex(std::min(m_excludedSamplingsName.size(), m_excludedSamplingsPattern.size()));
+    if ( m_excludedSamplingsName.size() > m_excludedSamplingsPattern.size() ) { 
+      ATH_MSG_WARNING( CaloRec::Helpers::fmtMsg("Configuration problem: number of excluded sampling names %zu exceeds expected maximum %zu - ignore last %zu name(s)",
+						m_excludedSamplingsName.size(), m_excludedSamplingsPattern.size(),m_excludedSamplingsName.size()-m_excludedSamplingsPattern.size()) );
+    }
+    m_excludedSamplings.resize(nex);
+    m_excludedSamplingsPattern.reset();
+    for ( size_t i(0); i<nex; ++i ) {
+      m_excludedSamplings[i] = CaloRec::Lookup::getSamplingId(m_excludedSamplingsName.at(i));
+      m_excludedSamplingsPattern.set(i);
+      ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("CaloSampling \042%10.10s\042 has id %2zu (name in lookup table \042%10.10s\042)",
+					      m_excludedSamplingsName.at(i).c_str(),(size_t)m_excludedSamplings.at(i),CaloRec::Lookup::getSamplingName(m_excludedSamplings.at(i)).c_str()) );
     }
   }
-  // build SG key for cell weight lookup
-  m_prepareLCW = m_applyLCW && m_useCellsFromClusters && ( m_cellClusterWeightKey != m_defaultKey );  
-  if ( m_prepareLCW ) { ATH_MSG_INFO("Prepare LCW calibration"); }
-  else { ATH_MSG_INFO("No preparation for LCW calibration"); }
+
+  ATH_MSG_INFO("Other properties:");
+  std::map<bool,std::string> blu { { true, "true" }, { false, "false" } };
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("PrepareLCW ................. %s",             blu[m_prepareLCW].c_str())               ); 
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("BuildTopoTowers ............ %s",             blu[m_useCellsFromClusters].c_str())     );
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("ApplyCellEnergyThreshold ... %s",             blu[m_applyCellEnergyThreshold].c_str()) );
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("OrderClusterByPt ........... %s",             blu[m_orderByPt].c_str())                );
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("DoCellIndexCheck ........... %s",             blu[m_doCellIndexCheck].c_str())         );
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("BuildCombinedTopoSignal .... %s",             blu[m_buildCombinedSignal].c_str())      );
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("TopoClusterRange ........... %.2f",           m_clusterRange)                          );
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("ExcludedSamplings .......... %zu (number of)",m_excludedSamplingsName.size())          );
 
   return StatusCode::SUCCESS;
 }
 
 StatusCode CaloTopoClusterFromTowerMaker::finalize()
+{ return StatusCode::SUCCESS; }
+
+StatusCode CaloTopoClusterFromTowerMaker::execute(xAOD::CaloClusterContainer* pClusCont)
 {
-  ATH_MSG_INFO(" ");
-  ATH_MSG_INFO("Summary of warnings in execute():");
-  ATH_MSG_INFO("Count:  Warning:");
-  for ( auto fiter(m_warning.begin()); fiter!=m_warning.end(); ++fiter ) {
-    ATH_MSG_INFO(CaloRec::Helpers::fmtMsg("%6i  \042%s\042",(int)fiter->second,fiter->first.c_str()));
+  /////////////////
+  // Check input //
+  /////////////////
+
+  // CaloCellContainer is needed to construct CaloProtoCluster
+  SG::ReadHandle<CaloCellContainer> pCellCont(m_cellContainerKey);
+  if ( !pCellCont.isValid() ) { 
+    ATH_MSG_ERROR("Cannot allocate CaloCellContainer with key <" << m_cellContainerKey << ">");
+    return StatusCode::FAILURE;
   }
-  ATH_MSG_INFO(" ");
 
-  ATH_MSG_INFO("Summary of messages in execute():");
-  ATH_MSG_INFO("Count:  Message:");
-  for ( auto fiter(m_message.begin()); fiter!=m_message.end(); ++fiter ) {
-    ATH_MSG_INFO(CaloRec::Helpers::fmtMsg("%6i  \042%s\042",(int)fiter->second,fiter->first.c_str()));
+  if ( msgLvl(MSG::DEBUG) && m_numberOfCells != pCellCont->size() ) { 
+    ATH_MSG_DEBUG( CaloRec::Helpers::fmtMsg("[mismatch] number of cells in CaloCellContainer %6zu, total number of cell descriptors %6zu",
+					      pCellCont->size(),m_towerGeometrySvc->totalNumberCells()) );
   }
-  if ( !m_cellProblems.invalidPointer().empty() ) {
-    ATH_MSG_INFO(" ");
-    ATH_MSG_INFO("Observed cell pathologies [invalid pointer]:");
-    printProblems(m_cellProblems.invalidPointer());
-  }
-  if ( !m_cellProblems.invalidHash().empty() ) { 
-    ATH_MSG_INFO(" ");
-    ATH_MSG_INFO("Observed cell pathologies [invalid hash]:");
-    printProblems(m_cellProblems.invalidHash());
-  }
-  if ( !m_cellProblems.invalidSampling().empty() ) { 
-    ATH_MSG_INFO(" ");
-    ATH_MSG_INFO("Observed cell pathologies [invalid sampling]:");
-    printProblems(m_cellProblems.invalidSampling());
-  }
-  ATH_MSG_INFO(" ");
-
-  return StatusCode::SUCCESS;
-}
 
-StatusCode CaloTopoClusterFromTowerMaker::execute(xAOD::CaloClusterContainer* pClusCont)
-{
-  // find cell-tower links
-  CHECK(detStore()->retrieve(m_cellTowerLinksHandle,m_cellTowerLinksKey));
-
-  // find input: CaloCell container
-  const CaloCellContainer* pCellCont = 0;
-  CHECK(evtStore()->retrieve(pCellCont,m_cellContainerKey));
-  m_numberOfCells = (int)pCellCont->size();
-
-  // cross check
-  this->addMessage(CaloRec::Helpers::fmtMsg("CaloCellContainer::%s size %i, CaloCellTowerlink::Map size %i",
-			     m_cellContainerKey.c_str(),(int)pCellCont->size(),(int)m_cellTowerLinksHandle->size()));
-  for ( size_t itl(0); itl<m_cellTowerLinksHandle->size(); ++itl ) { 
-    if ( itl >= pCellCont->size() ) {
-      this->addWarning(CaloRec::Helpers::fmtMsg("CaloCell [%06i] has hash out of reach (max hash is %i)",(int)itl,(int)pCellCont->size()-1));
-    } else { 
-      if ( pCellCont->at(itl) == 0 ) { this->addWarning(CaloRec::Helpers::fmtMsg("CaloCell [%06i] has invalid pointer",(int)itl)); }
-    }
-  }
-  //  int icell(0);
-  //  int ncell(0);
-  //  for ( auto cptr : *pCellCont ) { printf("CaloCell [%06i/%06i] ptr = %p\n",icell++,m_numberOfCells,(void*)cptr); if ( cptr == 0 ) { ++ncell; } }
-  //  this->addMessage(CaloRec::Helpers::fmtMsg("Allocated CaloCellContainer with key <%s> at %p",m_cellContainerKey.c_str(),(void*)pCellCont));
-
-  // find input: tower container 
-  const xAOD::CaloTowerContainer* pTowCont = 0;
-  CHECK(evtStore()->retrieve(pTowCont,m_towerContainerKey));
-  //  this->addMessage(CaloRec::Helpers::fmtMsg("Allocated CaloTowerContainer with key <%s> at %p",m_towerContainerKey.c_str(),(void*)pTowCont));
-
-  //////////////////////////////////////////////////////////////////////////////////////////
-  // Tricky bit:                                                                          //
-  // Especially for fine tower grids some towers may have 0 energy - in particular if the //
-  // grid spans -5.0 < eta < 5.0. The proto-cluster container has the same size as the    //
-  // tower container, so it will have empty proto-clusters. These are removed when the    //
-  // proto-clusters are converted to full CaloCluster objects. The tower center in        //
-  // (eta,phi) is preserved in eta0(), phi0() of the CaloCluster. The cluster container   //
-  // index has no relation to the tower position anymore.                                 //
-  ////////////////////////////////////////////////////////////////////////////////////////// 
+  if ( m_doCellIndexCheck ) { this->checkCellIndices(pCellCont.cptr()); }
 
   /////////////////////////
   // Set up ProtoCluster //
   /////////////////////////
 
-  protocont_t pProtoCont(pTowCont->size(),(CaloProtoCluster*)0);
-  for ( size_t itow(0); itow<pTowCont->size(); ++itow) { pProtoCont[itow] = new CaloProtoCluster(pCellCont); }
+  // index of CaloProtoCluster in container relates to tower position! DO NOT sort, shuffle, or remove elements!  
+  protocont_t pProtoCont; pProtoCont.reserve(m_numberOfTowers);
+  for ( uint_t i(0); i<m_numberOfTowers; ++i ) { pProtoCont.push_back(CaloProtoCluster(pCellCont.cptr())); }
 
   //////////////////////////////////////////////////////
   // Apply overall cell filter and fill protoclusters //
   //////////////////////////////////////////////////////
 
-  if ( m_useCellsFromClusters ) {                                         ///////////////////////////////////////////////////////
-    // retrieve TopoCluster container                                     // The weights extracted here are the cell signal    //
-    const xAOD::CaloClusterContainer* pTopoClusCont = 0;                  // weights after LCW (typically)! These are products //
-    CHECK(evtStore()->retrieve(pTopoClusCont,m_clusterContainerKey));     // of the geometrical weight in cell splitting       //
-    // instantiate cell-cluster link object                               // between clusters and the calibration weights.     //
-    m_cellClusterWeights = new CaloCellClusterWeights(pCellCont->size()); // The geometrical weight of a cell in the towers is //
-    // fill the weights: cluster loop                                     // used in the addCellToProtoCluster method.         //
-    for ( auto pClus : *pTopoClusCont ) {                                 ///////////////////////////////////////////////////////
-      // cell loop
-      for ( auto fCell(pClus->cell_begin()); fCell != pClus->cell_end(); ++fCell ) {
-	if ( !m_cellClusterWeights->check(*fCell) ) { this->addCellToProtoCluster(*fCell,pProtoCont);  }  // fill protocluster only once/cell 
-	m_cellClusterWeights->set(*fCell,fCell.weight());                                                 // prep for calibration tool
-      } // cell in cluster loop
-    } // cluster loop
-    // store LCW weights for calibrator
-    if ( m_prepareLCW ) { 
-      CHECK(evtStore()->record(m_cellClusterWeights,CaloCellClusterWeights::key(m_cellClusterWeightKey))); 
-    } else {
-      delete m_cellClusterWeights; m_cellClusterWeights = 0; 
-    }
+  // The weights extracted for cells from clusters are LCW weights (typically). The total
+  // contribution of a LCW-weighted cell to towers is Ecell*Weight_LCW*Weight_tower.
 
-  /////////////////////////////////////////////////////////////
-  // Generate and fill inclusive or exclusive proto-clusters //
-  /////////////////////////////////////////////////////////////
-	
-  } else { 
-    if ( !m_applyEnergyThreshold ) { buildInclClusters(*pCellCont,pProtoCont); } else { buildExclClusters(*pCellCont,pProtoCont); }
-  }
+  // If EM clusters are used, the weights of a clustered cell are completely defined
+  // by the tower grid. As cells can shared between clusters, each cell can only be
+  // projected onto the towergrid once, with Ecell*Weight_tower
 
-  ////////////////////////////
-  // Convert proto-clusters //
-  ////////////////////////////
+  // The CaloCellClusterWeights object is used to store the combined LCW weight.
+  // for each clustered cell. In case of EM, the LCW weights are ignored and this
+  // object is not used - a simple vector<bool> tags cells already put into towers.
 
-  pClusCont->reserve(pProtoCont.size());
-
-  xAOD::CaloCluster::ClusterSize csize = this->getClusterSize(*pTowCont);
-  int ictr(0);
-  for ( size_t ipc(0); ipc<pProtoCont.size(); ++ipc ) {
-    // auto fproto(pProtoCont.begin());
-    // while ( fproto != pProtoCont.end() ) { 
-    //   CaloProtoCluster* pProto = *fproto;
-    CaloProtoCluster* pProto = pProtoCont.at(ipc);
-    if ( pProto != 0 ) { 
-      CaloClusterCellLink* lptr = pProto->releaseCellLinks(); // take over CaloClusterCellLink object
-      // convert proto-cluster to cluster
-      if ( lptr->size() > 0 ) {                                            // remove empty proto-clusters
-	xAOD::CaloCluster* clptr = new xAOD::CaloCluster();                // new empty cluster
-	pClusCont->push_back(clptr);                                       // put into container
-	clptr->addCellLink(lptr);                                          // transfer cell links to CaloCluster
-	clptr->setClusterSize(csize);                                      // set the cluster size spec
-	CaloRec::Helpers::calculateKine(clptr,false);                      // calculate kinematics and other signals from cells
-	clptr->setEta0(pTowCont->eta(ipc));                                // save the tower center eta
-	clptr->setPhi0(pTowCont->phi(ipc));                                // save the tower center phi
-	++ictr;                                                            // counts the number of CaloCluster produced in the end
-      } // tower has cells
-      delete pProto;                                                       // delete the proto-cluster object
-      pProtoCont[ipc] = (CaloProtoCluster*)0;                              // FIXME (needed?)
-    } // valid proto-cluster
+  uint_t cCtr(0);
+  if ( m_useCellsFromClusters ) {
+    // retrieve topo-cluster container for topo-towers
+    SG::ReadHandle<xAOD::CaloClusterContainer> pTopoClusCont(m_clusterContainerKey);
+    if ( !pTopoClusCont.isValid() ) {
+      ATH_MSG_ERROR("Cannot allocate xAOD::CaloClusterContainer with key <" << m_clusterContainerKey << ">");
+      return StatusCode::FAILURE;
+    } // check on ReadHandle validity
+    cCtr = m_prepareLCW ? this->buildLCWTopoTowers(*pTopoClusCont,pProtoCont) : this->buildEMTopoTowers(*pTopoClusCont,pProtoCont);
+    if ( !isValidIndex(cCtr) ) { ATH_MSG_WARNING("problems building EM or LCW topo-towers"); return StatusCode::SUCCESS; }
+  } else {
+    // fill inclusive/exclusive towers
+    cCtr = m_applyCellEnergyThreshold ?  this->buildExclTowers(*pCellCont,pProtoCont) : this->buildInclTowers(*pCellCont,pProtoCont);
+    if ( !isValidIndex(cCtr) ) { ATH_MSG_WARNING("problems building EM inclusive or exclusive towers"); return StatusCode::SUCCESS; }
+  } // end topo-towers/inclusive-exclusive towers
+
+  // allocate sufficient space in vector
+  pClusCont->reserve(cCtr);
+  // pick up cluster size tag and set up counter
+  xAOD::CaloCluster::ClusterSize csize = this->getClusterSize(m_numberOfTowers);
+  // loop proto-clusters
+  for ( uint_t ipc(0); ipc<pProtoCont.size(); ++ipc ) {
+    CaloProtoCluster& pProto  = pProtoCont.at(ipc);                     // pick up proto-cluster
+    CaloClusterCellLink* lptr = pProto.releaseCellLinks();              // take over CaloClusterCellLink object
+    this->cleanupCells(lptr,ipc);                                       // clean up cell links 
+    if ( this->filterProtoCluster(*lptr) ) {                            // ignore empty proto-clusters (no cells assigned)
+      xAOD::CaloCluster* clptr = new xAOD::CaloCluster();               // new empty cluster
+      pClusCont->push_back(clptr);                                      // put into container
+      clptr->addCellLink(lptr);                                         // transfer cell links to CaloCluster
+      clptr->setClusterSize(csize);                                     // set the cluster size spec
+      CaloRec::Helpers::calculateKine(clptr,false);                     // calculate kinematics and other signals from cells
+      clptr->setEta0(m_towerGeometrySvc->towerEta(ipc));                // save the tower center eta
+      clptr->setPhi0(m_towerGeometrySvc->towerPhi(ipc));                // save the tower center phi
+    } else {
+      delete lptr;
+    }
   } // proto-cluster loop
 
-  pProtoCont.clear();  // clean up (objects in this container have already been destroyed)
-
-  ////////////////////
-  // Check clusters //
-  ////////////////////
-
-  size_t idc(0);
-  for ( auto pClus : *pClusCont ) { 
-    if ( pClus->getCellLinks() == 0 || pClus->getCellLinks()->size() == 0 ) { 
-      this->addWarning(CaloRec::Helpers::fmtMsg("Cluster #%05i has invalid pointer (%p) or no cells",idc,(void*)pClus));
-    }
-    ++idc;
-  } 
+  // clean up proto-cluster container
+  pProtoCont.clear();
 
-  ////////////////////////
-  // Sort protoclusters //
-  ////////////////////////
+  /////////////
+  // Sorting //
+  /////////////
 
-  // definitively removes the index-to-tower center relation in all cases!
+  // All towers/clusters at this point are on EM scale. Sorting LCW towers by pT should be done in the 
+  // CaloTopoClusterFromTowerCalibrator tool to assure desired ordering on the final scale.
+  // The link between tower location and index of tower representation (CaloCluster) in its
+  // container is definitively broken after sorting (was never ok in mixed cluster/tower mode).
   if ( m_orderByPt ) { 
     std::sort(pClusCont->begin(),pClusCont->end(),[](xAOD::CaloCluster* pc1,xAOD::CaloCluster* pc2) {
    	volatile double pt1(pc1->pt()); // FIXME needed? (this was just copied)
@@ -294,226 +298,317 @@ StatusCode CaloTopoClusterFromTowerMaker::execute(xAOD::CaloClusterContainer* pC
    	return ( pt1 > pt2 );  
       }
       );
+  } // end ordered by pT
+
+  return StatusCode::SUCCESS;
+} // end execute
+
+//////////////////////
+// Fill topo-towers //
+//////////////////////
+
+// EM 
+CaloTopoClusterFromTowerMaker::uint_t CaloTopoClusterFromTowerMaker::buildEMTopoTowers(const xAOD::CaloClusterContainer& pClusCont,protocont_t& pProtoCont)
+{
+  // presets
+  uint_t cCtr(0);
+  std::vector<bool> cellTags(m_numberOfCells,false);
+
+  // -- EM scale clusters
+  if ( !m_buildCombinedSignal ) { 
+    // topo-towers
+    for ( auto pClus : pClusCont ) { 
+      for ( auto fCell(pClus->cell_begin()); fCell != pClus->cell_end(); ++fCell ) { 
+	uint_t cidx(static_cast<uint_t>((*fCell)->caloDDE()->calo_hash()));
+	if ( cidx < cellTags.size() ) {
+	  if ( !cellTags.at(cidx) ) { cellTags[cidx] = this->addCellToProtoCluster(*fCell,pProtoCont); }  
+	} else {
+	  ATH_MSG_ERROR( CaloRec::Helpers::fmtMsg("Invalid cell hash index %6zu >= maximum index %6zu for cell in %s at (eta,phi) = (%6.3,%f6.3)",
+						  cidx,cellTags.size(),CaloSampling::getSamplingName((*fCell)->caloDDE()->getSampling()).c_str(),(*fCell)->eta(),(*fCell)->phi()) );
+	  return m_errorValueUINT;
+	}
+      } // end cells-in-cluster loop
+    } // end cluster loop
+  } else { 
+    // selected topo-towers for combined signal
+    std::vector<std::tuple<const CaloCell*,double> > cellList(m_numberOfCells,std::tuple<const CaloCell*,double>(0,0.));
+    for ( auto pClus : pClusCont ) { 
+      if ( std::abs(pClus->eta()) > m_clusterRange ) { 
+	for ( auto fCell(pClus->cell_begin()); fCell != pClus->cell_end(); ++fCell ) { 
+	  uint_t cidx(static_cast<uint_t>((*fCell)->caloDDE()->calo_hash()));
+	  if ( cellTags.at(cidx) ) { 
+	    std::get<1>(cellList[cidx]) += fCell.weight();
+	  } else { 
+	    cellList[cidx] = std::tuple<const CaloCell*,double>(*fCell,fCell.weight());
+	    cellTags[cidx] = true; 
+	  } 
+	} // cell in cluster loop
+      } else { 
+	++cCtr; 
+      } // cluster range check
+    } // cluster loop
+    // fill proto-cluster
+    for ( auto tpl : cellList ) { this->addCellToProtoCluster(std::get<0>(tpl),pProtoCont,std::get<1>(tpl)); }
+  } // end of fill mode
+
+  //
+  return cCtr+pProtoCont.size();
+}
+
+// LCW
+CaloTopoClusterFromTowerMaker::uint_t CaloTopoClusterFromTowerMaker::buildLCWTopoTowers(const xAOD::CaloClusterContainer& pClusCont,protocont_t& pProtoCont)
+{ 
+  // Need to keep track of LCW weights (up to two per cell) available from the topo-cluster(s) the cell is assigned to.
+  // Each cell in a topo-cluster is, at first occurance, added to the CaloProtoCluster(s) representing the tower(s) and its
+  // LCW calibration weight is stored in a lookup table indexed by the calorimeter hash id of the cell. The second 
+  // time the same cell is found in another topo-cluster, only its LCW weight is added to the lookup table (stored in
+  // CaloCellClusterWeights for use in the downstream tower calibration tool) - the assignment to tower(s) has already
+  // happened.
+
+  uint_t cCtr(0);
+  // write handle object on the stack
+  SG::WriteHandle<CaloCellClusterWeights> cellClusterWeightHandle(m_cellClusterWeightKey);
+  // record output container
+  if ( cellClusterWeightHandle.record(std::make_unique<CaloCellClusterWeights>(m_towerGeometrySvc->maxCellHash())).isFailure() ) {
+    ATH_MSG_WARNING( "Cannot record cluster cell weights with key <" << m_cellClusterWeightKey << ">" );
+    return m_errorValueUINT;
   }
+  // project cells on tower grid
+  if ( !m_buildCombinedSignal ) { 
+    for ( auto pClus : pClusCont ) { 
+      for ( auto fCell(pClus->cell_begin()); fCell != pClus->cell_end(); ++fCell ) {
+	// map to towers only once
+	if ( !cellClusterWeightHandle.ptr()->check(*fCell) ) { this->addCellToProtoCluster(*fCell,pProtoCont); }
+	// store all associated LCW weights
+	cellClusterWeightHandle.ptr()->set(*fCell,fCell.weight());
+      } // end cells-in-cluster loop
+    } // end cluster loop
+  } else { 
+    for ( auto pClus : pClusCont ) {
+      if ( std::abs(pClus->eta()) > m_clusterRange ) {  
+	for ( auto fCell(pClus->cell_begin()); fCell != pClus->cell_end(); ++fCell ) {
+	  // map to towers only once
+	  if ( !cellClusterWeightHandle.ptr()->check(*fCell) ) { this->addCellToProtoCluster(*fCell,pProtoCont); }
+	  // store all associated LCW weights
+	  cellClusterWeightHandle.ptr()->set(*fCell,fCell.weight());
+	} // end cells-in-cluster loop
+      } else { 
+	++cCtr; 
+      } // end range check
+    } // end cluster loop
+  } // end combined signal check
+
+  //
+  return cCtr+pProtoCont.size();
+}
+
+/////////////////
+// Fill towers //
+/////////////////
 
+// inclusive
+CaloTopoClusterFromTowerMaker::uint_t CaloTopoClusterFromTowerMaker::buildInclTowers(const CaloCellContainer& pCellCont,protocont_t& pProtoCont)
+{
+  // loop cell container - counter icl replaces cell hash index for NULL pointers in cell container
+  uint_t icl(0);
+  for ( auto cptr : pCellCont ) { 
+    if ( cptr == 0 ) { 
+      ATH_MSG_ERROR( CaloRec::Helpers::fmtMsg("CaloCellContainer[%6zu] contains invalid cell object pointer %p",icl,(void*)cptr) ); 
+      return m_errorValueUINT;
+    } else {
+      // existing cell with non-zero energy (negative or positive)
+      if ( std::fabs(cptr->e()) > 0. ) { this->addCellToProtoCluster(cptr,pProtoCont); }
+    } // end pointer check 
+    ++icl;
+  } // end cell loop
+  return pProtoCont.size();
+}
+
+// exclusive
+CaloTopoClusterFromTowerMaker::uint_t CaloTopoClusterFromTowerMaker::buildExclTowers(const CaloCellContainer& pCellCont,protocont_t& pProtoCont)
+{
+  // loop cell container
+  uint_t icl(0);
+  for ( auto cptr : pCellCont ) {
+    if ( cptr == 0 ) { 
+      ATH_MSG_ERROR( CaloRec::Helpers::fmtMsg("CaloCellContainer[%6zu] contains invalid cell object pointer %p",icl,(void*)cptr) ); 
+      return m_errorValueUINT;
+    } else {
+      // existing cell with energy above threshold
+      if ( cptr->e() > m_energyThreshold ) { this->addCellToProtoCluster(cptr,pProtoCont); }
+    } // end pointer check
+    ++icl;
+  } // end cell loop
   return StatusCode::SUCCESS;
 }
 
-xAOD::CaloCluster::ClusterSize CaloTopoClusterFromTowerMaker::getClusterSize(const xAOD::CaloTowerContainer& towerCont)
+bool CaloTopoClusterFromTowerMaker::addCellToProtoCluster(const CaloCell* cptr,protocont_t& pProtoCont,double weight) 
 {
-  return towerCont.nTowers() == 6400 
-    ? xAOD::CaloCluster::Tower_01_01   : towerCont.nTowers() == 25600 
-    ? xAOD::CaloCluster::Tower_005_005 : xAOD::CaloCluster::CSize_Unknown;  
+  // invalid input
+  if ( cptr == 0 ) { return false; }
+
+  // get towers for cell from geometry service
+  uint_t nctr(0);
+  for ( auto elm : m_towerGeometrySvc->getTowers(cptr) ) { 
+    auto towerIdx(m_towerGeometrySvc->towerIndex(elm));
+    if ( !m_towerGeometrySvc->isInvalidIndex(towerIdx) ) {
+      if ( !m_excludedSamplingsPattern[(size_t)cptr->caloDDE()->getSampling()] ) {
+	uint_t cellIdx(pProtoCont.at(towerIdx).getCellLinks()->getCellContainer()->findIndex(cptr->caloDDE()->calo_hash()));
+	pProtoCont[towerIdx].addCell(cellIdx,m_towerGeometrySvc->cellWeight(elm)*weight); ++nctr; 
+      }
+    }
+  }
+  return nctr > 0;
+}
+
+/////////////
+// Helpers //
+/////////////
+
+xAOD::CaloCluster::ClusterSize CaloTopoClusterFromTowerMaker::getClusterSize(uint_t etaBins,uint_t phiBins)
+{ return this->getClusterSize(etaBins*phiBins); }
+
+xAOD::CaloCluster::ClusterSize CaloTopoClusterFromTowerMaker::getClusterSize(uint_t nTowers)
+{
+  // check for tower sizes
+  return nTowers == 6400                    // known "standard" towers 0,1 x 0.1
+    ? xAOD::CaloCluster::Tower_01_01 
+    : nTowers == 25600                      // known "fine" towers 0.05 x 0.05
+    ? xAOD::CaloCluster::Tower_005_005 
+    : xAOD::CaloCluster::Tower_fixed_area;  // unspecified towers 
 }
 
-int CaloTopoClusterFromTowerMaker::cleanupCells(CaloClusterCellLink* clk)
+int CaloTopoClusterFromTowerMaker::cleanupCells(CaloClusterCellLink* clk,uint_t nclus)
 {
+  // Any pathology here probably indicates a configuration problem with the conditions (geometry)
+  // database (wrong tag for data?)
+
   // check on null pointers in cell links
   int nrc(0); int hid(0);
   auto fcell(clk->begin());
   while ( fcell != clk->end() ) {
     const CaloCell* pCell = *fcell;
-    this->addMessage(CaloRec::Helpers::fmtMsg("CaloCell* = %p in CaloClusterCellLink* = %p"));
-    bool removeCell(false);
-    if ( pCell == 0  ) {
-      m_cellProblems.setInvalidPointer(hid,pCell);
-      removeCell = true;
+    auto nc(clk->getCellContainer()->size());
+    const CaloCell* aCell = fcell.index() < nc ? clk->getCellContainer()->at(fcell.index()) : (const CaloCell*)0;
+    if ( pCell == 0 ) {
+      ATH_MSG_WARNING( CaloRec::Helpers::fmtMsg("CaloCellContainer[%6zu/%6zu] - tower %5zu at (%6.3f,%6.3f) - cell pointer invalid (%p/%p) [removed %3i of %3zu cells]",
+						fcell.index(),nc-1,nclus,m_towerGeometrySvc->towerEta(nclus),m_towerGeometrySvc->towerPhi(nclus),
+						(void*)pCell,(void*)aCell,++nrc,clk->size()) );
+      fcell = clk->removeCell(fcell);
     } else {
-      int hash((int)pCell->caloDDE()->calo_hash());
-      if ( hash >= m_numberOfCells ) { m_cellProblems.setInvalidHash(hash,pCell); removeCell = true; }
-      int samp((int)pCell->caloDDE()->getSampling());
-      if ( samp >= m_numberOfSamplings ) { m_cellProblems.setInvalidSampling(hash,pCell); removeCell = true; } 
-    }
-    if ( removeCell ) { fcell = clk->removeCell(fcell); ++nrc; } else { ++fcell; }
+      uint_t chash(static_cast<uint_t>(pCell->caloDDE()->calo_hash()));
+      uint_t csamp(static_cast<uint_t>(pCell->caloDDE()->getSampling()));
+      if (chash > m_maxCellHash ) {
+	// check cell hash
+	ATH_MSG_WARNING( CaloRec::Helpers::fmtMsg("Tower %5zu at (%6.3f,%6.3f) linked cell %3i - cell hash index (%6zu/%6zu) invalid",
+						  nclus,m_towerGeometrySvc->towerEta(nclus),m_towerGeometrySvc->towerPhi(nclus),hid,chash,m_maxCellHash) );
+	fcell = clk->removeCell(fcell);	++nrc;
+      } else if ( csamp >= m_numberOfSamplings ) {
+	// check sampling id
+	ATH_MSG_WARNING( CaloRec::Helpers::fmtMsg("Tower %5zu at (%6.3f,%6.3f) linked cell %3i -cell sampling id (%3zu/%3zu) invalid",
+						  nclus,m_towerGeometrySvc->towerEta(nclus),m_towerGeometrySvc->towerPhi(nclus),hid,csamp,m_numberOfSamplings) );
+	fcell = clk->removeCell(fcell); ++nrc;
+      } else if ( fcell.weight() <= 0.0000001 ) { 
+	// remove cells with 0 weight 
+	fcell = clk->removeCell(fcell); ++nrc;
+      } else { 
+	// next cell 
+	++fcell;
+      }
+    } // end remove cell due to pointer invalid
     ++hid;
-  }
+  } // end loop on cells in cell link object
   return nrc;
 }
 
-// bool CaloTopoClusterFromTowerMaker::calculateKine(xAOD::CaloCluster* pClus)
-// { return CaloRec::Helpers::updateKine
-//   // input
-//   if ( pClus == 0 ) { 
-//     ATH_MSG_WARNING("Invalid (NULL) pointer to xAOD::CaloCluster object");
-//     return false;
-//   }
-//   // copy from CaloKineHelper::calculateKine(...) - but without the prefetch!
-//   const CaloClusterCellLink* clk = pClus->getCellLinks();
-//   if ( clk == 0 ) { 
-//     this->addWarning(CaloRec::Helpers::fmtMsg("Invalid reference (pointer %p) to CaloClusterCellLink object",(void*)clk));
-//     return false;
-//   }
-//   if ( clk->size() == 0 ) { 
-//     this->addWarning(CaloRec::Helpers::fmtMsg("Number of linked cells in cluster is %i",(int)clk->size()));
-//     return false;
-//   }
-
-//   // accumulator object
-//   CaloRec::Helpers::CaloClusterSignalAccumulator accum;
-//   // accumulate cells
-//   for ( auto cIter(clk->begin()); cIter!=clk->end(); ++cIter) { 
-//     const CaloCell* pCell = *cIter;
-//     if ( pCell != 0 ) {
-//       if ( !CaloRec::Helpers::caloCellAccumulator(*pCell,accum,cIter.weight(),false) ) { 
-// 	this->addWarning(CaloRec::Helpers::fmtMsg("CaloCell [%06i] has invalid sampling id %2i (not in [0,%2i])",(int)(*cIter)->caloDDE()->calo_hash(),(int)(*cIter)->caloDDE()->getSampling(),(int)CaloSampling::Unknown));
-//       }
-//     } else {
-//       this->addWarning(CaloRec::Helpers::fmtMsg("Unexpected CaloCell pointer %p detected",(void*)pCell)); 
-//     }
-//   }
-
-//   // set cluster kinematics: energy & mass
-//   pClus->setE(accum.cluster.accumE);
-//   pClus->setM(0.);
-
-//   // set cluster kinematics: directions
-//   if ( accum.cluster.accumAbsE != 0. ) {
-//     double invPosNorm(1./accum.cluster.accumAbsE);
-//     pClus->setEta(accum.cluster.accumEta*invPosNorm);
-//     pClus->setPhi(CaloPhiRange::fix(accum.cluster.accumPhi*invPosNorm));
-//   } else {
-//     pClus->setEta(0.);
-//     pClus->setPhi(0.);
-//   }
-
-//   // set cluster kinematics: time
-//   if ( accum.cluster.accumTimeNorm != 0. ) {
-//     pClus->setTime(accum.cluster.accumTime/accum.cluster.accumTimeNorm);
-//   } else {
-//     pClus->setTime(0.);
-//   }
-
-//   // set sampling pattern
-//   uint32_t samplingPattern(0);
-//   for ( int i(0); i<(int)CaloSampling::Unknown; ++i ) {
-//     if ( accum.sampling.presenceInSample[i] ) { samplingPattern |= (0x1U<<i); }
-//     if ( samplingPattern != pClus->samplingPattern() ) { 
-//       pClus->clearSamplingData();
-//       pClus->setSamplingPattern(samplingPattern,true);
-//     }
-//   }
-
-//   // set sampling variables
-//   for ( int i(0); i<(int)CaloSampling::Unknown; ++i ) { 
-//     if ( accum.presenceInSample[i] ) { 
-//       CaloSampling::CaloSample sam = (CaloSampling::CaloSample)i;
-//       pClus->setEnergy(sam,accum.energyInSample[i]);
-//       if ( accum.posNormInSample[i] != 0. ) { 
-// 	pClus->setEta(sam,accum.etaInSample[i]/accum.posNormInSample[i]);
-// 	pClus->setPhi(sam,accum.phiInSample[i]/accum.posNormInSample[i]);
-//       } else { 
-// 	pClus->setEta(sam,accum.etaInSample[i]);
-// 	pClus->setPhi(sam,accum.phiInSample[i]);
-//       }
-//       pClus->setEmax(sam,accum.maxEnergyInSample[i]);
-//       pClus->setEtamax(sam,accum.etaMaxEnergyInSample[i]);
-//       pClus->setPhimax(sam,accum.phiMaxEnergyInSample[i]);
-//     } // check if sampling is in cluster
-//   } // loop on samplings
-
-//   return true;
-// }
-
-void CaloTopoClusterFromTowerMaker::addWarning(const std::string& msg)
-{
-  ATH_MSG_WARNING(msg);
-  auto fiter(m_warning.find(msg)); 
-  if ( fiter == m_warning.end() ) { m_warning[msg] = 0; }
-  ++m_warning[msg];
-}
+bool CaloTopoClusterFromTowerMaker::filterProtoCluster(const CaloClusterCellLink& clnk) const
+{ return clnk.size() > 0; }
 
-void CaloTopoClusterFromTowerMaker::addMessage(const std::string& msg)
+bool CaloTopoClusterFromTowerMaker::checkCellIndices(const CaloCellContainer* pCellCont) const
 {
-  ATH_MSG_DEBUG(msg);
-  auto fiter(m_message.find(msg)); 
-  if ( fiter == m_message.end() ) { m_message[msg] = 0; }
-  ++m_message[msg];
-}
+  ////////////////////////////
+  // input and setup checks //
+  ////////////////////////////
 
-void CaloTopoClusterFromTowerMaker::printProblems(const std::map<std::string,int>& map)
-{
-  static std::string _dstr = "....................................................................";
-  static size_t      _lds = _dstr.length();
-  static size_t      _occ = _lds - 3;
-  //
-  for ( auto fmap(map.begin()); fmap!=map.end(); ++fmap ) { 
-    std::string msg = fmap->first.length() > _occ ? fmap->first.substr(0,_occ) : fmap->first;
-    std::string prn(std::string(_dstr).replace(0,msg.length(),msg));
-    ATH_MSG_INFO(CaloRec::Helpers::fmtMsg("%s %i",prn.c_str(),fmap->second));
+  // check argument
+  if ( pCellCont == 0 ) { 
+    ATH_MSG_WARNING( CaloRec::Helpers::fmtMsg("Invalid pointer to CaloCellContainer (%p)",(void*)pCellCont) ); return false;
+  } else if ( pCellCont->empty() ) { 
+    ATH_MSG_WARNING( CaloRec::Helpers::fmtMsg("CaloCellContainer at %p is empty (size %zu)",(void*)pCellCont,pCellCont->size()) ); return false; 
   }
-}
+  // check the atomic state
+  if ( CaloTopoClusterFromTowerMaker_checkCellIndices ) { return true; }
+  // set the atomic flag
+  ATH_MSG_INFO( "Cell hash index check requested" ); 
+  CaloTopoClusterFromTowerMaker_checkCellIndices = true;
+  // assign output file
+  std::string algname(this->name());
+  if ( algname.find_last_of('.') != std::string::npos ) { algname = algname.substr(algname.find_last_of('.')+1); }
+  std::string logname(CaloRec::Helpers::fmtMsg("%s.cellhash_index_check.dat",this->name().c_str())); 
+  std::ofstream logstream; logstream.open(logname);
+  if ( !logstream.is_open() ) { 
+    ATH_MSG_WARNING( CaloRec::Helpers::fmtMsg("Cannot open log file \042%s\042 - no hash index checking",logname.c_str()) ); 
+    return false;
+  }
+  logstream << "##########################################################################" << std::endl;
+  logstream << "### This file contains a list of CaloCell indices in CaloCellContainer ###" << std::endl;
+  logstream << "### for which this index is not the same as the calorimeter cell hash  ###" << std::endl;
+  logstream << "### identifier. An empty list indicates full consistency between this  ###" << std::endl;
+  logstream << "### index and the hash identifier for all cells.                       ###" << std::endl;
+  logstream << "##########################################################################" << std::endl;
+  logstream << "<begin list>--------------------------------------------------------------" << std::endl;
 
-bool CaloTopoClusterFromTowerMaker::buildInclClusters(const CaloCellContainer& pCellCont,protocont_t& pProtoCont)
-{
-  // inclusive mode
-  this->addMessage(CaloRec::Helpers::fmtMsg("Found %6i cell links in CaloCellTowerLink::Map::%s",(int)m_cellTowerLinksHandle->size(),m_cellTowerLinksKey.c_str()));
+  /////////////////////////
+  // loop cell container //
+  /////////////////////////
 
-  // loop cell container
-  size_t icl(0);
-  for ( auto cptr : pCellCont ) { 
-    // FIXME check pointer value
-    if ( cptr == 0 ) { m_cellProblems.setInvalidPointer(icl,cptr); }
-    // existing cell
-    else {
-      if ( std::fabs(cptr->e()) > 0. ) { this->addCellToProtoCluster(cptr,pProtoCont); }
+  // prepare tag store
+  size_t ifc(0); std::bitset<200000> chkflg; chkflg.reset();
+  for ( size_t i(0); i<pCellCont->size(); ++i ) { 
+    if ( pCellCont->at(i) != 0 ) { 
+      size_t chash((size_t)pCellCont->at(i)->caloDDE()->calo_hash());
+      if ( chash != i ) {
+	std::string cni("UKNOWN");
+	double etai(0.); double phii(0.);
+	const CaloDetDescrElement* iel = i < CaloDetDescrManager::instance()->element_size() ? CaloDetDescrManager::instance()->get_element(i) : 0;
+	if ( iel != 0 ) {
+	  cni  = CaloRec::Lookup::getSamplingName(iel->getSampling());
+	  etai = iel->eta_raw();
+	  phii = iel->phi_raw();
+	}
+	std::string cnc("UNKNOWN");
+	double etac(0.); double phic(0.);
+	const CaloDetDescrElement* cel = chash < CaloDetDescrManager::instance()->element_size() ? CaloDetDescrManager::instance()->get_element(chash) : 0;
+	if ( cel != 0 ) { 
+	  cnc  = CaloRec::Lookup::getSamplingName(cel->getSampling());
+	  etac = cel->eta_raw();
+	  phic = cel->phi_raw();
+	}
+	size_t cidx(pCellCont->findIndex(chash));
+	logstream << CaloRec::Helpers::fmtMsg("[%06zu] Cell %6zu [%12.12s %5.3f %5.3f] non-matching id %6zu [%12.12s %5.3f %5.3f] findCell() index %6zu",
+					      ++ifc,i,cni.c_str(),etai,phii,chash,cnc.c_str(),etac,phic,cidx) << std::endl; 
+      }
+      chkflg.set(chash);
     }
-    ++icl;
-  } // cell loop
-  return true;
-}
-
-bool CaloTopoClusterFromTowerMaker::buildExclClusters(const CaloCellContainer& pCellCont,protocont_t& pProtoCont)
-{
-  // inclusive mode
-  this->addMessage(CaloRec::Helpers::fmtMsg("Found %6i cell links in CaloCellTowerLink::Map::%s",(int)m_cellTowerLinksHandle->size(),m_cellTowerLinksKey.c_str()));
+  }
+  logstream << "<end list>----------------------------------------------------------------" << std::endl;
+  logstream.close();
 
-  // loop cell container
-  for ( auto cptr : pCellCont ) { 
-    // FIXME check pointer value
-    if ( cptr == 0 ) { m_cellProblems.setInvalidPointer(999999,cptr); }
-    // existing cell
-    else {
-      if ( cptr->e() > m_energyThreshold ) { this->addCellToProtoCluster(cptr,pProtoCont); }
-    }
-  } // cell loop
-  return true;
-}
+  /////////////////////////
+  // check missed hashes //
+  /////////////////////////
 
-// bool CaloTopoClusterFromTowerMaker::buildFilteredClusters(const CaloCellContainer& pCellCont,protocont_t& pProtoCont)
-// {
-//   // inclusive mode
-//   this->addMessage(CaloRec::Helpers::fmtMsg("Found %6i cell links in CaloCellTowerLink::Map::%s",(int)m_cellTowerLinksHandle->size(),m_cellTowerLinksKey.c_str()));
-
-//   // loop cell container
-//   size_t icl(0);
-//   for ( auto cptr : pCellCont ) { 
-//     // FIXME check pointer value
-//     if ( cptr == 0 ) { m_cellProblems.setInvalidPointer(icl,cptr); }
-//     // existing cell
-//     else {
-//       size_t idx(static_cast<size_t>(cptr->caloDDE()->calo_hash_id()));
-//       if ( m_cellTagStore[idx].get<0>() ) { if ( std::fabs(cptr->e()) > 0. ) { this->addCellToProtoCluster(cptr,pProtoCont); } }
-//     }
-//     ++icl;
-//   } // cell loop
-//   return true;
-// }
-
-bool CaloTopoClusterFromTowerMaker::addCellToProtoCluster(const CaloCell* cptr,protocont_t& pProtoCont) 
-{
-  if ( cptr == 0 ) { return false; }
-  // cell hash
-  int cellidx(cptr->caloDDE()->calo_hash());
-  if ( cellidx >= (int)m_cellTowerLinksHandle->size() ) { 
-    this->addWarning(CaloRec::Helpers::fmtMsg("CaloCell [%06i] has hash outside of link lookup (max hash is %i)",cellidx,(int)m_cellTowerLinksHandle->size())); 
-    return false; 
+  // number of non-matched hashes
+  if ( ifc > 0 ) { 
+    ATH_MSG_DEBUG( CaloRec::Helpers::fmtMsg("Found %zu non-matching cell hashes",ifc) );
+  }
+  // list of non-matched hashes
+  std::vector<size_t> chl; chl.reserve(m_towerGeometrySvc->totalNumberCells());
+  for ( size_t i(0); i<chl.size(); ++i ) { if ( !chkflg.test(i) ) { chl.push_back(i); } }
+  if ( !chl.empty() ) { 
+    for ( auto h : chl ) { ATH_MSG_DEBUG( CaloRec::Helpers::fmtMsg("Cell hash %6zu not in CaloCellContainer",h) ); }
   }
-  if ( cellidx < m_numberOfCells ) {
-    for ( auto link : m_cellTowerLinksHandle->elementList(cellidx) ) {
-      int towidx(CaloCellTowerLink::towerIndex(link));
-      if ( towidx < (int)pProtoCont.size() ) { pProtoCont[towidx]->addCell(cellidx,CaloCellTowerLink::cellWeight(link)); }
-      else { this->addWarning(CaloRec::Helpers::fmtMsg("Invalid tower index %i (not in [0,%i])",(int)towidx,(int)pProtoCont.size()-1)); }
-    } // loop all cell->tower links
-  } // valid hash index
+
   return true;
 }
+
diff --git a/Calorimeter/CaloRec/src/CaloTopoClusterFromTowerMonitor.cxx b/Calorimeter/CaloRec/src/CaloTopoClusterFromTowerMonitor.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..ad12d4b149b8279ef12c6b55620e99888c6a3c4b
--- /dev/null
+++ b/Calorimeter/CaloRec/src/CaloTopoClusterFromTowerMonitor.cxx
@@ -0,0 +1,421 @@
+
+#include "GaudiKernel/MsgStream.h"
+#include "GaudiKernel/SystemOfUnits.h"
+#include "GaudiKernel/PhysicalConstants.h"
+
+#include "CaloRec/CaloTopoClusterFromTowerMonitor.h"
+
+#include "CaloGeoHelpers/CaloPhiRange.h"
+
+#include "CaloDetDescr/CaloDetDescrElement.h"
+#include "CaloDetDescr/CaloDetDescrManager.h"
+
+#include "TH1D.h"
+#include "TH2D.h"
+
+#include <vector>
+
+namespace { 
+  MsgStream& operator<<(MsgStream& mstr,const SG::ReadHandleKey<xAOD::CaloClusterContainer>& ckey) { mstr << ckey.key();   return mstr; }
+  MsgStream& operator<<(MsgStream& mstr,const ServiceHandle<CaloTowerGeometrySvc>&           shdl) { mstr << shdl->name(); return mstr; }
+}
+
+CaloTopoClusterFromTowerMonitor::CaloTopoClusterFromTowerMonitor(const std::string& name,ISvcLocator* pSvcLocator)
+  : AthHistogramAlgorithm(name,pSvcLocator)
+  , m_towerContainerKey("LCWTowerTopoClusterStd")
+  , m_towerGeometrySvc("CaloTowerGeometrySvc",name)
+  , m_ncBins(100)
+  , m_ncMin(-0.5)
+  , m_ncMax(m_ncMin+m_ncBins)
+  , m_nBins(320)
+  , m_nMin(-0.05)
+  , m_nMax(m_nMin+m_nBins*20.)
+  , m_ptBins(220)
+  , m_ptMin(-10.)
+  , m_ptMax(100.)
+  , m_etaBins(100)
+  , m_etaMin(-5.)
+  , m_etaMax(5.)
+  , m_phiBins(64)
+  , m_phiMin(-Gaudi::Units::pi)
+  , m_phiMax(Gaudi::Units::pi)
+  , m_hsEtaMin(0.)
+  , m_hsEtaMax(0.1)
+  , m_hsPhiMin(0.)
+  , m_hsPhiMax(0.1)
+  , m_doGeoAutoBins(true)
+  , m_doHotspot(false)
+  , h_n((TH1D*)0), h_pt((TH1D*)0), h_eta((TH1D*)0), h_phi((TH1D*)0), h_nc((TH1D*)0), h_samp((TH1D*)0)
+  , d_n_eta_phi((TH2D*)0), d_nc_eta_phi((TH2D*)0)
+  , d_pt_eta((TH2D*)0), d_nc_eta((TH2D*)0)
+  , d_wgt_samp((TH2D*)0), d_ntt_samp((TH2D*)0), d_geo_samp((TH2D*)0)
+  , d_maxtowers_samp((TH2D*)0), d_wgttowers_samp((TH2D*)0) 
+  , d_maxcells_eta((TH2D*)0), d_allwghts_eta((TH2D*)0)
+  , d_deta_eta((TH2D*)0),  d_dphi_eta((TH2D*)0),  d_dphi_deta((TH2D*)0)
+  , d_detac_eta((TH2D*)0), d_dphic_eta((TH2D*)0), d_dphic_detac((TH2D*)0), d_detac_samp((TH2D*)0), d_dphic_samp((TH2D*)0)
+  , h_nc_hs((TH1D*)0), h_n_hs((TH1D*)0), h_pt_hs((TH1D*)0), h_eta_hs((TH1D*)0), h_phi_hs((TH1D*)0), h_samp_hs((TH1D*)0)
+  , d_n_eta_phi_hs((TH2D*)0), d_nc_eta_phi_hs((TH2D*)0)
+  , d_deta_eta_hs((TH2D*)0), d_dphi_eta_hs((TH2D*)0), d_dphi_deta_hs((TH2D*)0)
+  , d_detac_eta_hs((TH2D*)0), d_dphic_eta_hs((TH2D*)0), d_dphic_detac_hs((TH2D*)0), d_detac_samp_hs((TH2D*)0), d_dphic_samp_hs((TH2D*)0) 
+{
+  declareProperty("CaloTowerContainerKey", m_towerContainerKey, "Input container key"     );
+  declareProperty("CaloTowerGeometrySvc",  m_towerGeometrySvc,  "Tower geometry provider" );
+  declareProperty("NTowerCellsBins",       m_ncBins,            "Number of bins in cell-in-tower multiplicity");
+  declareProperty("NTowerCellsMin",        m_ncMin,             "Lower limit in cell-in-tower multiplicity");
+  declareProperty("NTowerCellsMax",        m_ncMax,             "Upper limit in cell-in-tower multiplicity");
+  declareProperty("NTowersBins",           m_nBins,             "Number of bins in tower multiplicity binning");
+  declareProperty("NTowersMin",            m_nMin,              "Lower limit in tower multiplicity binning");
+  declareProperty("NTowersMax",            m_nMax,              "Upper limit in tower multiplicity binning");          
+  declareProperty("PtTowersBins",          m_ptBins,            "Number of bins in tower pT binning");
+  declareProperty("PtTowersMin",           m_ptMin,             "Lower limit in tower pT binning");
+  declareProperty("PtTowersMax",           m_ptMax,             "Upper limit in tower pT binning");          
+  declareProperty("EtaTowersBins",         m_etaBins,           "Number of bins in tower rapidity binning");
+  declareProperty("EtaTowersMin",          m_etaMin,            "Lower limit in tower rapidity binning");
+  declareProperty("EtaTowersMax",          m_etaMax,            "Upper limit in tower rapidity binning");          
+  declareProperty("PhiTowersBins",         m_phiBins,           "Number of bins in tower azimuth binning");
+  declareProperty("PhiTowersMin",          m_phiMin,            "Lower limit in tower azimuth binning");
+  declareProperty("PhiTowersMax",          m_phiMax,            "Upper limit in tower azimuth binning");
+  declareProperty("EtaMinHotspot",         m_hsEtaMin,          "lower limit in tower rapidity for hotspot");
+  declareProperty("EtaMaxHotspot",         m_hsEtaMax,          "Upper limit in tower rapidity for hotspot");
+  declareProperty("PhiMinHotspot",         m_hsPhiMin,          "lower limit in tower azimuth for hotspot");
+  declareProperty("PhiMaxHotspot",         m_hsPhiMax,          "Upper limit in tower azimuth for hotspot");
+  declareProperty("DoGeoAutoBins",         m_doGeoAutoBins,     "Flag controls automatic binning for rapidity and azimuth");          
+}
+
+CaloTopoClusterFromTowerMonitor::~CaloTopoClusterFromTowerMonitor()
+{ }
+
+StatusCode CaloTopoClusterFromTowerMonitor::initialize()
+{
+  // initialize read handle key
+  ATH_CHECK(m_towerContainerKey.initialize());
+
+  // tower geometry service
+  ATH_MSG_INFO( "Allocate tower geometry service:" );
+  if ( !m_towerGeometrySvc.isValid() ) { 
+    ATH_MSG_ERROR("[reject] - cannot allocate tower geometry service - fatal");
+    return StatusCode::FAILURE;
+  } else {
+    ATH_MSG_INFO( "[accept] - allocated tower geometry provider \042" << m_towerGeometrySvc << "\042");
+  }
+
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("Tower geometry service is allocated, describes %6zu towers in grid:", m_towerGeometrySvc->towerBins()) );
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("[accept] %3zu eta bins in [%5.2f,%5.2f]",m_towerGeometrySvc->etaBins(),m_towerGeometrySvc->etaMin(),m_towerGeometrySvc->etaMax()) );
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("[accept] %3zu phi bins in [%5.2f,%5.2f]",m_towerGeometrySvc->phiBins(),m_towerGeometrySvc->phiMin(),m_towerGeometrySvc->phiMax()) );
+
+  std::string cfgStr;  
+  if ( m_doGeoAutoBins ) { 
+    ATH_MSG_INFO("Eta and phi binning taken from tower geometry provider"); 
+    m_etaBins  = m_towerGeometrySvc->etaBins();
+    m_etaMin   = m_towerGeometrySvc->etaMin();
+    m_etaMax   = m_towerGeometrySvc->etaMax();
+    m_phiBins  = m_towerGeometrySvc->phiBins();
+    m_phiMin   = m_towerGeometrySvc->phiMin();
+    m_phiMax   = m_towerGeometrySvc->phiMax();
+    m_hsPhiMax = m_hsPhiMin+m_towerGeometrySvc->phiWidth(); 
+    cfgStr = "autoconfig";
+  } else {
+    ATH_MSG_INFO("Eta and phi binning taken from specific configuration");
+    cfgStr = "userconfig"; 
+  }
+
+  m_doHotspot = m_hsEtaMin < m_hsEtaMax &&  m_hsPhiMin < m_hsPhiMax;
+
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("Eta and phi binning for histograms (total %zu towers)",m_etaBins*m_phiBins) );
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("[%s] %3zu eta bins in [%5.2f,%5.2f]",cfgStr.c_str(),m_etaBins,m_etaMin,m_etaMax) );
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("[%s] %3zu phi bins in [%5.2f,%5.2f]",cfgStr.c_str(),m_phiBins,m_phiMin,m_phiMax) );
+
+  ATH_CHECK( book() ); 
+
+  // database plots
+  std::vector<std::vector<double> > cWghts(m_towerGeometrySvc->towerBins(),std::vector<double>());
+  size_t chash(0);
+  for ( auto fLnk(m_towerGeometrySvc->begin()); fLnk != m_towerGeometrySvc->end(); ++fLnk, ++chash ) { 
+    CaloSampling::CaloSample samp = CaloDetDescrManager::instance()->get_element(chash)->getSampling();
+    double cs(static_cast<double>(samp));
+    double nt(static_cast<double>(fLnk->size()));
+    d_maxtowers_samp->Fill(cs,nt);
+    for ( auto elm : *fLnk ) {
+      // collect for towers
+      size_t tidx(static_cast<size_t>(m_towerGeometrySvc->towerIndex(elm)));
+      double weight(m_towerGeometrySvc->cellWeight(elm));
+      cWghts[tidx].push_back(weight);
+      // plot for smaplings
+      d_wgttowers_samp->Fill(cs,weight);
+    }
+  }
+
+  for ( size_t tidx(0); tidx<cWghts.size(); ++tidx ) { 
+    double eta(m_towerGeometrySvc->towerEta(tidx));
+    double phi(m_towerGeometrySvc->towerPhi(tidx));
+    double nc(1.0*cWghts.at(tidx).size());
+    d_maxcells_eta->Fill(eta,nc);
+    size_t etaIdx(m_towerGeometrySvc->etaIndexFromTowerIndex(tidx));
+    d_maxcells_phi_eta_slice[etaIdx]->Fill(phi,nc);
+    for ( auto w : cWghts.at(tidx) ) { d_allwghts_eta->Fill(eta,w); d_allwghts_phi_eta_slice[etaIdx]->Fill(phi,w); }
+  }
+
+  if ( msgLvl(MSG::DEBUG) ) { 
+    ATH_MSG_DEBUG( CaloRec::Helpers::fmtMsg("+------------+--------------+") );
+    ATH_MSG_DEBUG( CaloRec::Helpers::fmtMsg("| SamplingId | SamplingName |") );
+    ATH_MSG_DEBUG( CaloRec::Helpers::fmtMsg("+------------+--------------+") );
+    const auto& smap = CaloRec::Lookup::samplingNames;
+    for ( auto fsamp(smap.begin()); fsamp != smap.end(); ++fsamp ) { 
+      ATH_MSG_DEBUG( CaloRec::Helpers::fmtMsg("|     %02i     |  %11.11s |",fsamp->first,fsamp->second.c_str()) );
+    }
+    ATH_MSG_DEBUG( CaloRec::Helpers::fmtMsg("+------------+--------------+") );
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode CaloTopoClusterFromTowerMonitor::book()
+{
+  ////////////////////////////////////////////////////////////
+  // composition, multiplicity, kinematics (event by event) //
+  ////////////////////////////////////////////////////////////
+
+  int    detaBins(105);  int    dphiBins(128);
+  double detaMin(-1.05); double dphiMin(-Gaudi::Units::pi/2.);
+  double detaMax(1.05);  double dphiMax(Gaudi::Units::pi/2.);
+
+  // re-center delta phi
+  double dphi((dphiMax-dphiMin)/(1.*dphiBins)); double dphiMinOld(dphiMin); double dphiMaxOld(dphiMax); //int dphiBinsOld(dphiBins);
+  int dphiBinsOld = (dphiMax-dphiMin)/dphi;
+  dphiMin  -= (dphi/2.); dphiMax  += (dphi/2.);
+  double dphim(dphi);
+  dphiBins  = (dphiMax-dphiMin)/dphim;
+
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("re-center delta_phi distributions, old/new binning [%6.3f,%6.3f]/[%6.3f,%6.3f] with %3i/%3i bins %5.3f/%5.3f rad wide",
+					 dphiMinOld,dphiMaxOld,dphiMin,dphiMax,dphiBinsOld,dphiBins,dphi,dphim) );
+  
+
+  h_n    = bookAny<TH1D>("TowerMultiplicity",    "N_{tower}",          m_nBins, m_nMin, m_nMax);
+  h_pt   = bookAny<TH1D>("TowerPt",              "p_{T}^{tower} [GeV]",m_ptBins,m_ptMin,m_ptMax);
+  h_nc   = bookAny<TH1D>("TowerCellMultiplicity","N_{cell}^{tower}",   m_ncBins,m_ncMin,m_ncMax);
+  h_eta  = bookForEta<TH1D>("TowerRapidity",     "y_{tower}");
+  h_phi  = bookForPhi<TH1D>("TowerAzimuth",      "#phi_{tower} [rad]");
+
+  h_samp = bookForSamplings<TH1D>("TowerCellSamplings","TowerCellSamplings"); setAxisTitle(h_samp,"N_{cells} in sampling","y");
+  // relations (event by event)
+  d_n_eta_phi  = bookAny<TH2D>("TowerMultiplicityVsEtaVsPhi",    "N_{tower}(y_{tower},#phi_{tower})",       "y_{tower}",m_etaBins,m_etaMin,m_etaMax,m_phiBins,m_phiMin,m_phiMax);
+  setAxisTitle(d_n_eta_phi,"#phi_{tower} [rad]","y"); setAxisTitle(d_n_eta_phi,"N_{tower}","z");
+  d_nc_eta_phi = bookAny<TH2D>("TowerCellMultiplicityVsEtaBsPhi","N_{cell}^{tower}(y_{tower},#phi_{tower})","y_{tower}",m_etaBins,m_etaMin,m_etaMax,m_phiBins,m_phiMin,m_phiMax); 
+  setAxisTitle(d_nc_eta_phi,"#phi_{tower} [rad]","y"); setAxisTitle(d_nc_eta_phi,"N_{cell}^{tower}","z");
+  d_pt_eta     = bookAny<TH2D>("TowerPtVsEta",                   "p_{T}^{tower}(y_{tower})",                "y_{tower}",m_etaBins,m_etaMin,m_etaMax,m_ptBins, m_ptMin, m_ptMax );
+  setAxisTitle(d_pt_eta,"p_{T}^{tower} [GeV]","y");
+  d_nc_eta     = bookAny<TH2D>("TowerCellMultiplicityVsEta",     "N_{cell}^{tower}(y_{tower})",             "y_{tower}",m_etaBins,m_etaMin,m_etaMax,m_ncBins, m_ncMin, m_ncMax );
+  setAxisTitle(d_pt_eta,"N_{cell}^{tower} [GeV]","y");
+  // distance to nominal
+  d_deta_eta  = bookAny<TH2D>("TowerDeltaEtaVsEta",         "(y_{tower}-y_{tower,0})(y_{tower})",      "y_{tower}",      m_etaBins,m_etaMin,m_etaMax,detaBins,detaMin,detaMax);
+  setAxisTitle(d_deta_eta,"#Deltay_{tower}","y");
+  d_dphi_eta  = bookAny<TH2D>("TowerDeltaPhiVsEta",         "(#phi_{tower}-#phi_{tower,0})(y_{tower})","y_{tower}",      m_etaBins,m_etaMin,m_etaMax,dphiBins,dphiMin,dphiMax);
+  setAxisTitle(d_dphi_eta,"#Delta#phi_{tower} [rad]","y");
+  d_dphi_deta = bookAny<TH2D>("TowerDeltaPhiVsDeltaEta",    "#Delta#phi_{tower}(#Deltay_{tower})",     "#Deltay_{tower}",detaBins, detaMin, detaMax, dphiBins,dphiMin,dphiMax);
+  setAxisTitle(d_dphi_deta,"#Delta#phi_{tower} [rad]","y");
+  // cell distance to nominal
+  d_detac_eta   = bookAny<TH2D>("CellDeltaEtaVsEta",         "(y_{cell #in tower}#minusy_{tower,0})(y_{tower})",             "y_{tower}",             
+				m_etaBins,m_etaMin,m_etaMax,detaBins,detaMin,detaMax);
+  setAxisTitle(d_detac_eta,"#Deltay_{cell #in tower}","y");
+  d_dphic_eta   = bookAny<TH2D>("CellDeltaPhiVsEta",         "(#phi_{cell #in tower}^{cell}#minus#phi_{tower,0})(y_{tower})","y_{tower}",             
+				m_etaBins,m_etaMin,m_etaMax,dphiBins,dphiMin,dphiMax);
+  setAxisTitle(d_dphic_eta,"#Delta#phi_{cell #in tower}","y");
+  d_dphic_detac = bookAny<TH2D>("CellDeltaPhiVsDeltaEta",    "#Delta#phi_{cell #in tower}^{cell}(#Deltay_{tower}^{cell})",   "#Deltay_{cell #in tower}",
+				detaBins,detaMin,detaMax,dphiBins,dphiMin,dphiMax);
+  setAxisTitle(d_dphic_detac,"#Delta#phi_{cell #in tower}","y");
+
+  ///////////////////////////
+  // features in samplings //
+  ///////////////////////////
+
+  int    fwbins(250);
+  double fwmin(-0.1);
+  double fwmax(4.9);
+
+  int    gwbins(60);
+  double gwmin(-0.1);
+  double gwmax(1.1);
+
+  int    ctbins(20);
+  double ctmin(-0.5);
+  double ctmax(19.5);
+
+  // per event
+  d_wgt_samp   = bookForSamplings<TH2D>("EvtFullWeightvsSampling",    "[Event] Full weight vs sampling",                  fwbins,fwmin,fwmax);
+  setAxisTitle(d_wgt_samp,"w_{geo#plusLCW}","y");
+  d_ntt_samp   = bookForSamplings<TH2D>("EvtNTowersPerCellvsSampling","[Event] Number of towers/cell vs sampling",        ctbins,ctmin,ctmax);
+  setAxisTitle(d_ntt_samp,"N_{towers}/cell","y");
+  d_geo_samp   = bookForSamplings<TH2D>("EvtGeoWeightvsSampling",     "[Event] Geometrical weight vs sampling",           gwbins,gwmin,gwmax);
+  setAxisTitle(d_geo_samp,"w_{geo}","y");
+  d_detac_samp = bookForSamplings<TH2D>("EvtDetaCellvsSampling",      "[Event] y_{cell #in tower}#minusy_{0}^{tower}",     detaBins,detaMin,detaMax);  
+  setAxisTitle(d_detac_samp,"#Deltay_{cell #in tower}","y");
+  d_dphic_samp = bookForSamplings<TH2D>("EvtDphiCellvsSampling",      "[Event] #phi_{cell #in tower}#minus#phi_{0}^{tower}",dphiBins,dphiMin,dphiMax);  
+  setAxisTitle(d_dphic_samp,"#Delta#phi_{cell #in tower}","y");
+
+  // from database
+  d_maxtowers_samp = bookForSamplings<TH2D>("DBNTowersPerCellvsSampling", "[DB] Number of towers/cell vs sampling",ctbins,ctmin,ctmax);
+  setAxisTitle(d_maxtowers_samp,"N_{towers}/cell (maximum sharing)","y");
+  d_wgttowers_samp = bookForSamplings<TH2D>("DBGeoWeightvsSampling",      "[DB] Geometrical weight vs sampling",   gwbins,gwmin,gwmax);
+  setAxisTitle(d_wgttowers_samp,"w_{geo} (from database)","y");
+
+  /////////////////////
+  // features in eta //
+  /////////////////////
+
+  int    tcbins(100);
+  double tcmin(-0.5);
+  double tcmax(99.5);
+
+  d_maxcells_eta = bookForEta<TH2D>("DBNTowerCellsvsEta","[DB] Cells in towers vs y_{tower}",   tcbins,tcmin,tcmax); setAxisTitle(d_maxcells_eta,"N_{cells #in tower} (from database)","y");
+  d_allwghts_eta = bookForEta<TH2D>("DBGeoWeightvsEta",  "[DB] Geometrical weight vs y_{tower}",gwbins,gwmin,gwmax); setAxisTitle(d_allwghts_eta,"w_{geo} (from database)","y");
+
+  // analyzing the database (in tower eta slices)
+  double deta((m_etaMax-m_etaMin)/(1.*m_etaBins)); 
+  double eta0(m_etaMin);
+
+  d_maxcells_phi_eta_slice.resize(m_etaBins,(TH2D*)0);
+  d_allwghts_phi_eta_slice.resize(m_etaBins,(TH2D*)0);
+
+  std::string hname; std::string htitle;
+  for ( int i(0); i<m_etaBins; ++i, eta0+=deta ) { 
+    hname  = CaloRec::Helpers::fmtMsg("DBTowerCellsvsPhiEta%02i",i);
+    htitle = CaloRec::Helpers::fmtMsg("[DB] Cells in towers vs #phi_{tower} for y #in [%6.3f,%6.3f]",eta0,eta0+deta); 
+    d_maxcells_phi_eta_slice[i] = bookForPhi<TH2D>(hname,htitle,tcbins,tcmin,tcmax); setAxisTitle(d_maxcells_phi_eta_slice.at(i),"N_{cells #in tower} (from database)","y");
+    hname  = CaloRec::Helpers::fmtMsg("DBGeoWeightvsPhiEta%02i",i);
+    htitle = CaloRec::Helpers::fmtMsg("[DB] Geometrical weight vs #phi_{tower} for y #in [%6.3f,%6.3f]",eta0,eta0+deta); 
+    d_allwghts_phi_eta_slice[i] = bookForPhi<TH2D>(hname,htitle,gwbins,gwmin,gwmax); setAxisTitle(d_allwghts_phi_eta_slice.at(i),"w_{geo} (from database)","y");
+  }
+
+  /////////////////////////
+  // debugging y=0,phi=0 //
+  /////////////////////////
+
+  if ( m_doHotspot ) { 
+    h_n_hs    = bookAny<TH1D>("HSTowerMultiplicity",    "[Hotspot] N_{tower}",       "N_{tower}",          m_nBins, m_nMin, m_nMax);
+    h_pt_hs   = bookAny<TH1D>("HSTowerPt",              "[Hotspot] p_{T}^{tower}",   "p_{T}^{tower} [GeV]",m_ptBins,m_ptMin,m_ptMax);
+    h_nc_hs   = bookAny<TH1D>("HSTowerCellMultiplicity","[Hotspot] N_{cell}^{tower}","N_{cell}^{tower}",   m_ncBins,m_ncMin,m_ncMax);
+    h_eta_hs  = bookForEta<TH1D>("HSTowerEta",          "[Hotspot] y_{tower}");    
+    h_phi_hs  = bookForPhi<TH1D>("HSTowerPhi",          "[Hotspot] #phi_{tower}"); 
+    
+    h_samp_hs = bookForSamplings<TH1D>("HSTowerCellSampling","HSTowerCellSampling");
+    
+    d_n_eta_phi_hs  = bookAny<TH2D>("HSTowerMultiplicityVsEtaVsPhi",    "[Hotspot] N_{tower}(y_{tower},#phi_{tower})"        "y_{tower}",m_etaBins,m_etaMin,m_etaMax,m_phiBins,m_phiMin,m_phiMax);
+    setAxisTitle(d_n_eta_phi_hs,"#phi_{tower} [rad]","y"); setAxisTitle(d_n_eta_phi_hs,"N_{tower}","z");
+    d_nc_eta_phi_hs = bookAny<TH2D>("HSTowerCellMultiplicityVsEtaVsPhi","[Hotspot] N_{cell}^{tower}(y_{tower},#phi_{tower})","y_{tower}",m_etaBins,m_etaMin,m_etaMax,m_phiBins,m_phiMin,m_phiMax);
+    setAxisTitle(d_nc_eta_phi_hs,"#phi_{tower} [rad]","y"); setAxisTitle(d_nc_eta_phi_hs,"N_{cell}^{tower}","z");
+    d_deta_eta_hs   = bookForEta<TH2D>("HSTowerDeltaEtaVsEta",          "[Hotspot] #Deltay(y_{tower})",detaBins,detaMin,detaMax); setAxisTitle(d_deta_eta_hs,"#Deltay_{tower}","y");
+    setAxisTitle(d_deta_eta_hs,"#Deltay_{tower}","y");
+    d_dphi_eta_hs   = bookForEta<TH2D>("HSTowerDeltaPhiVsEta",          "[Hotspot] #Delta#phi(y_{tower})",dphiBins,dphiMin,dphiMax);
+    setAxisTitle(d_dphi_eta_hs,"#Delta#phi_{tower} [rad]","y");
+    d_dphi_deta_hs  = bookAny<TH2D>("HSTowerDeltaPhiVsDeltaEta",        "[Hotspot] #Delta#phi_{tower}(#Deltay_{tower})","#Deltay_{tower}",detaBins,detaMin,detaMax,dphiBins,dphiMin,dphiMax);
+    setAxisTitle(d_dphi_deta_hs,"#Delta#phi_{tower} [rad]","y");
+    d_detac_eta_hs   = bookForEta<TH2D>("HSCellDeltaEtaVsEta",          "[Hotspot] #Deltay_{cell #in tower}(y_{tower})",detaBins,detaMin,detaMax);
+    setAxisTitle(d_detac_eta_hs,"#Deltay_{cell #in tower}","y");
+    d_dphic_eta_hs   = bookForEta<TH2D>("HSCellDeltaPhiVsEta",          "[Hotspot] #Delta#phi_{cell #in tower}(y_{tower})",dphiBins,dphiMin,dphiMax);
+    setAxisTitle(d_dphic_eta_hs,"#Delta#phi_{cell #in tower}","y");
+    d_dphic_detac_hs = bookAny<TH2D>("HSCellDeltaPhiVsCellDeltaEta",    "[Hotspot] #Delta#phi_{cell #in tower}(#Deltay_{cell #in tower})","#Deltay_{cell #in tower}",
+				      detaBins,detaMin,detaMax,dphiBins,dphiMin,dphiMax);
+    setAxisTitle(d_dphic_detac_hs,"#Delta#phi_{cell #in tower}","y");
+    d_detac_samp_hs = bookForSamplings<TH2D>("HSCellDeltaEtavsSampling", "[Hotspot] #Deltay_{cell #in tower} in samplings",   detaBins,detaMin,detaMax);
+    setAxisTitle(d_detac_samp_hs,"#Deltay_{cell #in tower}","y");
+    d_dphic_samp_hs = bookForSamplings<TH2D>("HSCelLDeltaPhivsSampling", "[Hotspot] #Delta#phi_{cell #in tower} in samplings",dphiBins,dphiMin,dphiMax);
+    setAxisTitle(d_dphic_samp_hs,"#Delta#phi_{cell #in tower}","y");
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode CaloTopoClusterFromTowerMonitor::execute()
+{
+  SG::ReadHandle<xAOD::CaloClusterContainer> towerHandle(m_towerContainerKey);
+  if ( !towerHandle.isValid() ) { 
+    ATH_MSG_ERROR( "cannot allocate cluster container with key <" << m_towerContainerKey << ">" );
+    return StatusCode::FAILURE;
+  }
+
+  // fill plots
+  size_t nhs(0);
+  m_cellTags.reset();
+  std::vector<double> deta; deta.reserve(1000);
+  std::vector<double> dphi; dphi.reserve(deta.capacity()); 
+  std::vector<CaloSampling::CaloSample> csam; csam.reserve(deta.capacity());
+  h_n->Fill(1.0*towerHandle->size());
+  for ( auto ftow(towerHandle->begin()); ftow!=towerHandle->end(); ++ftow ) { 
+    const xAOD::CaloCluster* ptow = *ftow;
+    // tower kinematics
+    double pt(ptow->pt()/Gaudi::Units::GeV);
+    double eta(ptow->eta());
+    double phi(CaloPhiRange::fix(ptow->phi()));
+    double nc(1.0*ptow->size());
+    // tower composition and get distances
+    this->fillComposition(*ptow,deta,dphi,csam);
+    double deltaEta(ptow->eta()-ptow->eta0());
+    double deltaPhi(CaloPhiRange::fix(ptow->phi()-ptow->phi0()));
+    // inclusive plots
+    h_pt->Fill(pt); h_eta->Fill(eta); h_phi->Fill(phi); h_nc->Fill(nc);
+    d_n_eta_phi->Fill(eta,phi); d_nc_eta_phi->Fill(eta,phi,nc);
+    d_pt_eta->Fill(eta,pt);
+    d_nc_eta->Fill(eta,nc);
+    d_deta_eta->Fill(eta,deltaEta);
+    d_dphi_eta->Fill(eta,deltaPhi);
+    d_dphi_deta->Fill(deltaEta,deltaPhi);
+    for ( size_t ic(0); ic<deta.size(); ++ic ) { 
+      d_detac_eta->Fill(eta,deta.at(ic));
+      d_dphic_eta->Fill(eta,dphi.at(ic));
+      d_dphic_detac->Fill(deta.at(ic),dphi.at(ic));
+      fillSampling<TH2D>(d_detac_samp,csam.at(ic),deta.at(ic));
+      fillSampling<TH2D>(d_dphic_samp,csam.at(ic),dphi.at(ic));
+      fillSampling<TH1D>(h_samp,csam.at(ic));
+    }
+    // hot spot
+    if ( this->isInHotspot(*ptow) ) {
+      ++nhs;
+      h_pt_hs->Fill(pt); 
+      h_eta_hs->Fill(eta); 
+      h_phi_hs->Fill(phi); 
+      h_nc_hs->Fill(nc);
+      d_n_eta_phi_hs->Fill(eta,phi);    
+      d_nc_eta_phi_hs->Fill(eta,phi,nc);
+      d_deta_eta_hs->Fill(eta,deltaEta);
+      d_dphi_eta_hs->Fill(eta,deltaPhi);
+      d_dphi_deta_hs->Fill(deltaEta,deltaPhi);
+      for ( size_t ic(0); ic<deta.size(); ++ic ) { 
+	d_detac_eta_hs->Fill(eta,deta.at(ic));
+	d_dphic_eta_hs->Fill(eta,dphi.at(ic));
+	d_dphic_detac_hs->Fill(deta.at(ic),dphi.at(ic));
+	fillSampling<TH2D>(d_detac_samp_hs,csam.at(ic),deta.at(ic));
+	fillSampling<TH2D>(d_dphic_samp_hs,csam.at(ic),dphi.at(ic));
+	fillSampling<TH1D>(h_samp_hs,csam.at(ic));
+      }
+    }
+    // fill in samplings
+    for ( auto fCell(ptow->getCellLinks()->begin()); fCell!=ptow->getCellLinks()->end(); ++fCell ) { 
+      CaloSampling::CaloSample csamp = (*fCell)->caloDDE()->getSampling();
+      fillSampling<TH2D>(d_wgt_samp,csamp,fCell.weight());
+      // check for cell which other tower it contributes to and plot geometrical weights (only 1/cell)
+      size_t chash(static_cast<size_t>((*fCell)->caloDDE()->calo_hash()));
+      if ( !m_cellTags.test(chash) ) { 
+	CaloTowerGeometrySvc::elementvector_t lOfTowers = m_towerGeometrySvc->getTowers(chash);
+	double ntowers(1.0*lOfTowers.size());
+	fillSampling<TH2D>(d_ntt_samp,csamp,ntowers);
+	for ( auto elm : lOfTowers ) { fillSampling<TH2D>(d_geo_samp,csamp,m_towerGeometrySvc->cellWeight(elm)); }
+	m_cellTags.set(chash);
+      }
+    }
+  }
+  // number of towers in hot spot
+  h_n_hs->Fill(1.0*nhs);
+
+  return StatusCode::SUCCESS;
+}
+
+bool CaloTopoClusterFromTowerMonitor::fillComposition(const xAOD::CaloCluster& ptow,std::vector<double>& deta,std::vector<double>& dphi,std::vector<CaloSampling::CaloSample>& csam) const
+{
+  deta.clear(); dphi.clear(); csam.clear();
+  for ( auto fCell(ptow.getCellLinks()->begin()); fCell != ptow.getCellLinks()->end(); ++fCell ) { 
+    deta.push_back((*fCell)->eta()-ptow.eta0()); 
+    dphi.push_back(CaloPhiRange::fix((*fCell)->phi()-ptow.phi0()));
+    csam.push_back((*fCell)->caloDDE()->getSampling());
+  }
+  return !deta.empty();
+}
diff --git a/Calorimeter/CaloRec/src/CaloTopoClusterTowerMerger.cxx b/Calorimeter/CaloRec/src/CaloTopoClusterTowerMerger.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..19e7b6085afa6613fa2c444f19d95b6c718beba4
--- /dev/null
+++ b/Calorimeter/CaloRec/src/CaloTopoClusterTowerMerger.cxx
@@ -0,0 +1,214 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "StoreGate/ReadHandle.h"
+#include "StoreGate/WriteHandle.h"
+
+#include "xAODCaloEvent/CaloClusterAuxContainer.h"
+
+#include "CaloRec/CaloTopoClusterTowerMerger.h"
+#include "CaloRec/CaloTopoClusterFromTowerHelpers.h"
+
+#include "CaloUtils/CaloClusterStoreHelper.h"
+
+// #include <atomic>
+
+// std::atomic<bool> CaloTopoClusterTowerMerger_checkMoments(true);
+
+// #define CL_MNAME( NAME ) xAOD::CaloCluster::NAME
+// #define CL_ENTRY( NAME ) std::tuple< CL_MNAME( MomentType ),std::string>( CL_MNAME( NAME ), #NAME )
+
+#define CL_RHMSG( NAME ) MsgStream& operator<<(MsgStream& mstr,const SG::ReadHandleKey< NAME >& ckey )  { mstr << ckey.key(); return mstr; }
+#define CL_WHMSG( NAME ) MsgStream& operator<<(MsgStream& mstr,const SG::WriteHandleKey< NAME >& ckey ) { mstr << ckey.key(); return mstr; }
+
+namespace {
+  CL_RHMSG( xAOD::CaloClusterContainer )
+}
+
+// std::vector<xAOD::CaloCluster::MomentType>                           CaloTopoClusterTowerMerger::m_momentList = std::vector< CL_MNAME( MomentType ) >();
+//  std::vector<std::tuple<xAOD::CaloCluster::MomentType,std::string> > CaloTopoClusterTowerMerger::m_momentMap {
+//   CL_ENTRY( FIRST_PHI ),
+//   CL_ENTRY( FIRST_ETA ),
+//   CL_ENTRY( SECOND_R ),
+//   CL_ENTRY( SECOND_LAMBDA ),
+//   CL_ENTRY( DELTA_PHI ),
+//   CL_ENTRY( DELTA_THETA ),
+//   CL_ENTRY( DELTA_ALPHA ),
+//   CL_ENTRY( CENTER_X ),
+//   CL_ENTRY( CENTER_Y ),
+//   CL_ENTRY( CENTER_Z ),
+//   CL_ENTRY( CENTER_MAG ),
+//   CL_ENTRY( CENTER_LAMBDA ),
+//   CL_ENTRY( LATERAL ),
+//   CL_ENTRY( LONGITUDINAL ),
+//   CL_ENTRY( ENG_FRAC_EM ),
+//   CL_ENTRY( ENG_FRAC_MAX ),
+//   CL_ENTRY( ENG_FRAC_CORE ),
+//   CL_ENTRY( FIRST_ENG_DENS ),
+//   CL_ENTRY( SECOND_ENG_DENS ),
+//   CL_ENTRY( ISOLATION ),
+//   CL_ENTRY( ENG_BAD_CELLS ),    
+//   CL_ENTRY( N_BAD_CELLS ),
+//   CL_ENTRY( N_BAD_CELLS_CORR ), 
+//   CL_ENTRY( BAD_CELLS_CORR_E ), 
+//   CL_ENTRY( BADLARQ_FRAC ),     
+//   CL_ENTRY( ENG_POS ),
+//   CL_ENTRY( SIGNIFICANCE ),
+//   CL_ENTRY( CELL_SIGNIFICANCE ),
+//   CL_ENTRY( CELL_SIG_SAMPLING ),
+//   CL_ENTRY( AVG_LAR_Q ),
+//   CL_ENTRY( AVG_TILE_Q ),       
+//   CL_ENTRY( ENG_BAD_HV_CELLS ), 
+//   CL_ENTRY( N_BAD_HV_CELLS ),
+//   CL_ENTRY( PTD ),
+//   CL_ENTRY( EM_PROBABILITY ),
+//   CL_ENTRY( HAD_WEIGHT ),
+//   CL_ENTRY( OOC_WEIGHT ),
+//   CL_ENTRY( DM_WEIGHT ),        
+//   CL_ENTRY( TILE_CONFIDENCE_LEVEL ),
+//   CL_ENTRY( VERTEX_FRACTION ),
+//   CL_ENTRY( NVERTEX_FRACTION ),
+//   CL_ENTRY( ETACALOFRAME ), 
+//   CL_ENTRY( PHICALOFRAME ),
+//   CL_ENTRY( ETA1CALOFRAME ),
+//   CL_ENTRY( PHI1CALOFRAME ),
+//   CL_ENTRY( ETA2CALOFRAME ),
+//   CL_ENTRY( PHI2CALOFRAME ),
+//   CL_ENTRY( ENG_CALIB_TOT ),
+//   CL_ENTRY( ENG_CALIB_OUT_L ),
+//   CL_ENTRY( ENG_CALIB_OUT_M ),
+//   CL_ENTRY( ENG_CALIB_OUT_T ),
+//   CL_ENTRY( ENG_CALIB_DEAD_L ),
+//   CL_ENTRY( ENG_CALIB_DEAD_M ),
+//   CL_ENTRY( ENG_CALIB_DEAD_T ),
+//   CL_ENTRY( ENG_CALIB_EMB0 ),       
+//   CL_ENTRY( ENG_CALIB_EME0 ),       
+//   CL_ENTRY( ENG_CALIB_TILEG3 ),    
+//   CL_ENTRY( ENG_CALIB_DEAD_TOT ),   
+//   CL_ENTRY( ENG_CALIB_DEAD_EMB0 ),  
+//   CL_ENTRY( ENG_CALIB_DEAD_TILE0 ),
+//   CL_ENTRY( ENG_CALIB_DEAD_TILEG3 ),
+//   CL_ENTRY( ENG_CALIB_DEAD_EME0 ),  
+//   CL_ENTRY( ENG_CALIB_DEAD_HEC0 ),  
+//   CL_ENTRY( ENG_CALIB_DEAD_FCAL ),  
+//   //  CL_ENTRY( ENG_CALIB_DEAD_LEAKAG ), // not in r21
+//   //  CL_ENTRY( ENG_CALIB_DEAD_UNCLAS ), // not in r21
+//   CL_ENTRY( ENG_CALIB_FRAC_EM ),    
+//   CL_ENTRY( ENG_CALIB_FRAC_HAD ),   
+//   CL_ENTRY( ENG_CALIB_FRAC_REST )  
+// }; 
+
+CaloTopoClusterTowerMerger::CaloTopoClusterTowerMerger(const std::string& name,ISvcLocator* pSvcLocator)
+  : AthAlgorithm(name,pSvcLocator)
+  , m_clusterContainerKey("CaloCalTopoCluster")
+  , m_towerContainerKey("CaloCalFwdTopoTower")
+  , m_topoSignalContainerKey("CaloCalTopoSignal")
+    // ** not in r21 **, m_cellLinkContainerKey("")
+  , m_clusterRange(3.2)
+{
+  declareProperty("TopoClusterContainerKey",m_clusterContainerKey,   "Topo-cluster container key"                                    );
+  declareProperty("TopoTowerContainerKey",  m_towerContainerKey,     "Topo-tower container key"                                      );
+  declareProperty("TopoSignalContainerKey", m_topoSignalContainerKey,"Topo-signal container key"                                     ); 
+  declareProperty("TopoClusterRange",       m_clusterRange,          "Rapidity range for using topo-clusters in combined signal mode");
+}
+
+CaloTopoClusterTowerMerger::~CaloTopoClusterTowerMerger()
+{ }
+
+StatusCode CaloTopoClusterTowerMerger::initialize()
+{
+  if ( m_clusterRange <= 0. ) {
+    ATH_MSG_ERROR( CaloRec::Helpers::fmtMsg("Invalid topo-cluster range |y| < %6.3f - algorithm non-functional",m_clusterRange) );
+    return StatusCode::FAILURE;
+  }
+
+  // ** not in r21 ** if ( m_cellLinkContainerKey.key().empty() ) { m_cellLinkKey = m_topoSignalContainerKey.key() + std::string("_links"); }
+
+  ATH_CHECK( m_clusterContainerKey.initialize()    );
+  ATH_CHECK( m_towerContainerKey.initialize()      );
+  ATH_CHECK( m_topoSignalContainerKey.initialize() );
+  // ** not in r21 **  ATH_CHECK( m_cellLinkContainerKey.initialize()            );
+
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("Topo_cluster with |y| < %.2f will be merged with topo-towers with |y| > %.2f",m_clusterRange,m_clusterRange) );
+  //  if ( m_copyMoments ) { ATH_MSG_INFO( "Cluster moments are explicitly copied." ); }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode CaloTopoClusterTowerMerger::execute()
+{
+
+  // collect input
+  rhandle_t clusterHandle(m_clusterContainerKey);
+  if ( !clusterHandle.isValid() ) { 
+    ATH_MSG_WARNING( "Topo-cluster container with key <" << m_clusterContainerKey << "> not found" );
+    return StatusCode::SUCCESS;
+  }
+  rhandle_t towerHandle(m_towerContainerKey);
+  if ( !towerHandle.isValid() ) {
+    ATH_MSG_WARNING( "Topo-tower container with key <" << m_towerContainerKey << "> not found" ); 
+    return StatusCode::SUCCESS;
+  }
+
+  // prepare output
+  whandle_t signalHandle(m_topoSignalContainerKey);
+  ATH_CHECK(this->addContainerWriteHandle(signalHandle));
+
+  // check cluster moments 
+  // if ( m_copyMoments ) {  if ( CaloTopoClusterTowerMerger_checkMoments ) { this->fillMoments(*(towerHandle.ptr()->front())); CaloTopoClusterTowerMerger_checkMoments = false; } }
+
+  // fill output from topo-clusters
+  for ( auto pClus : *clusterHandle ) { if ( clusterFilter(*pClus) ) { this->makeDeepCopy(*pClus,signalHandle.ptr()); } }
+  // fill output from topo-towers
+  for ( auto pTowr : *towerHandle )   { if ( towerFilter(*pTowr) )   { this->makeDeepCopy(*pTowr,signalHandle.ptr()); } }
+
+  // take care of cell links
+  ATH_CHECK(CaloClusterStoreHelper::finalizeClusters(&(*evtStore()),signalHandle.ptr(),m_topoSignalContainerKey.key(),msg()));
+
+  return StatusCode::SUCCESS;
+}
+
+// bool CaloTopoClusterTowerMerger::fillMoments(const xAOD::CaloCluster& rClus) 
+// {
+//   m_momentList.reserve(m_momentMap.size());
+//   double val(0);
+//   if ( !msgLvl(MSG::DEBUG) ) { 
+//     for ( auto fmom(m_momentMap.begin()); fmom != m_momentMap.end(); ++fmom ) { 
+//       if ( rClus.retrieveMoment(std::get<0>(*fmom),val) ) { m_momentList.push_back(std::get<0>(*fmom)); }
+//     }
+//   } else { 
+//     ATH_MSG_DEBUG("###############################");
+//     ATH_MSG_DEBUG("## Moments (topo-towers)     ##");
+//     ATH_MSG_DEBUG("##---------------------------##");
+//     for ( auto fmom(m_momentMap.begin()); fmom != m_momentMap.end(); ++fmom ) { 
+//       if ( rClus.retrieveMoment( std::get<0>(*fmom), val ) ) { 
+// 	m_momentList.push_back(std::get<0>(*fmom)); 
+// 	ATH_MSG_DEBUG( CaloRec::Helpers::fmtMsg("## %5.5x | \042%16.16s\042 ##", std::get<0>(*fmom), std::get<1>(*fmom).c_str()) );
+//       }
+//     }
+//     ATH_MSG_DEBUG("###############################");
+//   } // debug mode
+//   return true;
+// }
+
+bool CaloTopoClusterTowerMerger::makeDeepCopy(const xAOD::CaloCluster& rClus,xAOD::CaloClusterContainer* pClusCont) const
+{ pClusCont->push_back(new xAOD::CaloCluster(rClus)); return true; }
+
+StatusCode CaloTopoClusterTowerMerger::addContainerWriteHandle(whandle_t& signalHandle)
+{
+  // get a new signal handle
+  signalHandle = std::unique_ptr<xAOD::CaloClusterContainer>(new xAOD::CaloClusterContainer());
+  if ( !signalHandle.isValid() ) { return StatusCode::FAILURE; }
+  // get AUX container
+  xAOD::CaloClusterAuxContainer* auxData = new xAOD::CaloClusterAuxContainer();
+  std::string auxName(m_topoSignalContainerKey.key()+"Aux.");
+  if ( evtStore()->overwrite(auxData,auxName).isFailure() ) {
+    ATH_MSG_ERROR("Failed to record xAOD::CaloClusterAuxContainer with key <" << auxName << ">");
+    delete auxData;
+    return StatusCode::FAILURE;
+  } 
+  // connect store with object container
+  signalHandle.ptr()->setStore(auxData);
+  return StatusCode::SUCCESS;
+}
diff --git a/Calorimeter/CaloRec/src/CaloTowerGeometrySvc.cxx b/Calorimeter/CaloRec/src/CaloTowerGeometrySvc.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..e1dfdcabc7b382581853a7a7dc9fd908d4864310
--- /dev/null
+++ b/Calorimeter/CaloRec/src/CaloTowerGeometrySvc.cxx
@@ -0,0 +1,410 @@
+/* Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration */
+#include "CaloRec/CaloTowerGeometrySvc.h"
+#include "CaloRec/CaloTopoClusterFromTowerHelpers.h"
+
+#include <cmath>
+
+namespace { constexpr auto _pi = 3.14159265358979323846; }
+
+CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::m_invalidIndex = index_t(-1); 
+double                        CaloTowerGeometrySvc::m_invalidValue = -999.;
+
+CaloTowerGeometrySvc::CaloTowerGeometrySvc(const std::string& name,ISvcLocator* pSvcLocator)
+  : AthService(name,pSvcLocator)
+  , m_caloDDM((const CaloDetDescrManager*)0)
+  , m_logFileName("logfile")
+  , m_towerEtaWidth(0.)
+  , m_towerPhiWidth(0.)
+  , m_towerArea(0.)
+  , m_towerBins(0)                              
+  , m_maxCellHash(0)                            //----------------------------------------//
+  , m_towerEtaBins(100)                         // Default tower definition is "hadronic" //
+  , m_towerEtaMin(-5.0)                         // towers of size 0.1 x pi/32.            // 
+  , m_towerEtaMax(5.0)                          //----------------------------------------//
+  , m_adjustEta(true)
+  , m_towerPhiBins(64)                          
+  , m_towerPhiMin(-_pi)                         //----------------------------------------//
+  , m_towerPhiMax(_pi)                          // FCal vertical and horizontal cell      //
+  , m_fcal1Xslice(8.)                           // slicing creates "mini-cells" which are //
+  , m_fcal1Yslice(8.)                           // then projected onto towers. The mini-  //
+  , m_fcal2Xslice(8.)                           // cell signal is 1/(Nx x Ny) x Ecell,    //
+  , m_fcal2Yslice(12.)                          // where Nx(y) are the number of x(y)     //
+  , m_fcal3Xslice(12.)                          // slices.                                //
+  , m_fcal3Yslice(12.)                          //----------------------------------------//
+{
+  // Properties                                               
+  declareProperty("TowerEtaBins",               m_towerEtaBins, "Number of pseudorapidity bins in tower grid");
+  declareProperty("TowerEtaMin",                m_towerEtaMin,  "Lower boundary of pseudorapidity range");
+  declareProperty("TowerEtaMax",                m_towerEtaMax,  "Upper boundary of pseudorapidity range");
+  declareProperty("AdjustFCalToTowerEtaBounds", m_adjustEta,    "Adjust FCal cells to eta boundaries");
+  declareProperty("TowerPhiBins",               m_towerPhiBins, "Number of azimuthal bins in tower grid");
+  declareProperty("TowerPhiMin",                m_towerPhiMin,  "Lower boundary of azimuthal range");
+  declareProperty("TowerPhiMax",                m_towerPhiMax,  "Upper boundary of azimuthal range");
+  // change only for R&D
+  declareProperty("FCal1NSlicesX", m_fcal1Xslice,   "Number of X slices for FCal1 cells");
+  declareProperty("FCal1NSlicesY", m_fcal1Yslice,   "Number of Y slices for FCal1 cells");
+  declareProperty("FCal2NSlicesX", m_fcal2Xslice,   "Number of X slices for FCal2 cells");
+  declareProperty("FCal2NSlicesY", m_fcal2Yslice,   "Number of Y slices for FCal2 cells");
+  declareProperty("FCal3NSlicesX", m_fcal3Xslice,   "Number of X slices for FCal3 cells");
+  declareProperty("FCal3NSlicesY", m_fcal3Yslice,   "Number of Y slices for FCal3 cells");
+}
+
+//-------------//
+// Gaudi stuff //
+//-------------//
+
+StatusCode CaloTowerGeometrySvc::queryInterface(const InterfaceID& riid,void** ppvInterface)
+{
+  if ( CaloTowerGeometrySvc::interfaceID().versionMatch(riid) ) { 
+    *ppvInterface = this;
+    return StatusCode::SUCCESS;
+  } else {
+    return AthService::queryInterface(riid,ppvInterface); 
+  }
+}
+
+//-------------------------//
+// Initialize and Finalize //
+//-------------------------//
+
+StatusCode CaloTowerGeometrySvc::initialize()
+{
+  // set up services and managers
+  if ( f_setupSvc().isFailure() ) {
+    ATH_MSG_ERROR("Cannot allocate the calorimeter detector description manager");
+    return StatusCode::FAILURE;
+  }
+
+  // prepare FCal segmentation 
+  m_ndxFCal[0] = m_fcal1Xslice; m_ndxFCal[1] = m_fcal2Xslice; m_ndxFCal[2] = m_fcal3Xslice;
+  m_ndyFCal[0] = m_fcal1Yslice; m_ndyFCal[1] = m_fcal2Yslice; m_ndyFCal[2] = m_fcal3Yslice;
+  for ( uint_t i(0); i<m_ndxFCal.size(); ++i ) { m_wgtFCal[i] = 1./(m_ndxFCal.at(i)*m_ndyFCal.at(i)); }
+
+  // other derived quantities
+  if ( m_towerEtaBins > 0 ) { 
+    m_towerEtaWidth = (m_towerEtaMax-m_towerEtaMin)/((double)m_towerEtaBins); 
+  } else {
+    ATH_MSG_ERROR("Number of tower eta bins is invalid (" << m_towerEtaBins << " bins)");
+    return StatusCode::FAILURE;
+  }
+  if ( m_towerPhiBins > 0 ) { 
+    m_towerPhiWidth = (m_towerPhiMax-m_towerPhiMin)/((double)m_towerPhiBins);
+  } else {
+    ATH_MSG_ERROR("Number of tower phi bins is invalid (" << m_towerPhiBins << " bins)");
+    return StatusCode::FAILURE;
+  }
+
+  m_towerBins     = m_towerEtaBins*m_towerPhiBins;
+  m_towerArea     = m_towerEtaWidth*m_towerPhiWidth;
+  m_maxCellHash   = f_caloDDM()->element_size()-1;
+  m_numberOfCells = f_caloDDM()->element_size(); 
+
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("Tower description - Eta(bins,min,max,width) = (%3zu,%6.3f,%6.3f,%6.4f)",m_towerEtaBins,m_towerEtaMin,m_towerEtaMax,m_towerEtaWidth) );
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("Tower description - Phi(bins,min,max,width) = (%3zu,%6.3f,%6.3f,%6.4f)",m_towerPhiBins,m_towerPhiMin,m_towerPhiMax,m_towerPhiWidth) );
+  ATH_MSG_INFO( CaloRec::Helpers::fmtMsg("Tower description - maximum number of towers %5zu; (eta,phi)-space area %6.4f; maximum cell hash index %6zu",m_towerBins,m_towerArea,m_maxCellHash) );
+
+  if ( this->msgLvl(MSG::VERBOSE) ) {
+    std::vector<std::string> logger; logger.reserve(m_towerBins+10);
+    logger.push_back(CaloRec::Helpers::fmtMsg("[CaloTowerGeometrySvc::%s] +-------- Tower Index Mapping ---------+------------+------------+",this->name().c_str()));
+    logger.push_back(CaloRec::Helpers::fmtMsg("[CaloTowerGeometrySvc::%s] | %10.10s | %10.10s | %10.10s | %10.10s | %10.10s |",this->name().c_str(),"TowerIndex", " EtaIndex ", " PhiIndex ","     Eta     ","     Phi    "));
+    logger.push_back(CaloRec::Helpers::fmtMsg("[CaloTowerGeometrySvc::%s] +------------+------------+------------+------------+------------+",this->name().c_str()));
+    for ( size_t i(0); i<m_towerBins; ++i ) { 
+      size_t etaIndex(this->etaIndexFromTowerIndex(i));
+      size_t phiIndex(this->phiIndexFromTowerIndex(i));
+      double eta(this->towerEta(i)); double phi(this->towerPhi(i));
+      logger.push_back(CaloRec::Helpers::fmtMsg("[CaloTowerGeometrySvc::%s] |    %5zu   |    %5zu   |    %5zu   |   %6.3f   |   %6.3f   |",this->name().c_str(),i,etaIndex,phiIndex,eta,phi));
+    }
+    logger.push_back(CaloRec::Helpers::fmtMsg("[CaloTowerGeometrySvc::%s] +------------+------------+------------+------------+------------+",this->name().c_str()));
+    // 
+    std::string logFileName = m_logFileName+"."+this->name()+".dat";
+    std::ofstream logfile;
+    logfile.open(logFileName); for ( auto mlog : logger ) { logfile << mlog << std::endl; } logfile.close();
+  }
+  return f_setupTowerGrid(); 
+}
+
+StatusCode CaloTowerGeometrySvc::finalize()
+{ return StatusCode::SUCCESS; }
+
+//-------//
+// Setup //
+//-------//
+
+StatusCode CaloTowerGeometrySvc::f_setupTowerGrid()
+{
+  // initialized
+  if ( m_maxCellHash == 0 ) { 
+    ATH_MSG_ERROR("Service not initialized? Maximum cell hash is " << m_maxCellHash ); 
+    return StatusCode::FAILURE;
+  }
+
+  // payload template
+  elementvector_t ev;
+
+  // set up lookup table
+  ATH_MSG_INFO( "Setting up cell-to-tower lookup for " << m_numberOfCells << " calorimeter cells" );
+  m_towerLookup.resize(m_numberOfCells,ev);
+
+  // dump projections 
+  std::ofstream logger;
+  if ( msgLvl(MSG::DEBUG) ) { 
+    logger.open(CaloRec::Helpers::fmtMsg("%s.calocellprojections_dump.dat",this->name().c_str()));
+    logger << CaloRec::Helpers::fmtMsg("############################################################################") << std::endl;
+    logger << CaloRec::Helpers::fmtMsg("## CaloCell projections onto %3.1f x %3.1f tower grid (projective cells only) ##",m_towerEtaWidth,m_towerPhiWidth) << std::endl;
+    logger << CaloRec::Helpers::fmtMsg("############################################################################") << std::endl; 
+    logger << std::endl;
+  }
+
+  // loop cells
+  for ( auto fcell(m_caloDDM->element_begin()); fcell != m_caloDDM->element_end(); ++fcell ) {
+    // reference cell descriptor
+    const CaloDetDescrElement* pCaloDDE = *fcell; 
+    // check hash id validity
+    index_t cidx(pCaloDDE->calo_hash());
+    if ( cidx >= m_towerLookup.size() ) { 
+      ATH_MSG_WARNING( "Cell hash identifier out of range " << cidx << "/" << m_towerLookup.size() << ", ignore cell" );
+    } else {
+      if ( pCaloDDE->is_lar_fcal() ) { 
+	if ( this->f_setupTowerGridFCal(pCaloDDE,logger).isFailure() ) { return StatusCode::FAILURE; }
+      } else {
+	if ( this->f_setupTowerGridProj(pCaloDDE,logger).isFailure() ) { return StatusCode::FAILURE; }
+      } 
+    } // cell hash in range?    
+  } // loop cell descriptors
+  if ( logger.is_open() ) { logger.close(); }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode CaloTowerGeometrySvc::f_setupTowerGridFCal(const CaloDetDescrElement* pCaloDDE,std::ofstream& /*logger*/)
+{
+  //-----------------------------------------------------------------------------------------//
+  // FCal special - the rectangular (in linear space) calorimeter cells are sub-divided into //
+  // small cells and then shared across as many towers as the small cells cover.             //
+  //-----------------------------------------------------------------------------------------//
+
+  // collect geometrical variables
+  int    cLayer(pCaloDDE->getLayer()-1);   // FCal layer number 1..3 -> array indices 0..2
+
+  double cXpos(pCaloDDE->x());             // FCal cell x position (cell center)             
+  double cYpos(pCaloDDE->y());             // FCal cell y position (cell center)
+  double cZpos(pCaloDDE->z());             // FCal cell z position (cell center)
+  double cZpos2(cZpos*cZpos); 
+
+  double cXwid(pCaloDDE->dx());            // FCal cell x full width 
+  double cYwid(pCaloDDE->dy());            // FCal cell y full width 
+
+  // double xSlice(cXwid/m_ndxFCal[cLayer]);  // FCal cell x slize width
+  // double ySlice(cYwid/m_ndyFCal[cLayer]);  // FCal cell y slice width
+  double cWght(m_wgtFCal[cLayer]);         // FCal cell geometrical (signal) weight
+
+  int nXslice((int)m_ndxFCal[cLayer]);     // FCal number of x slices
+  int nYslice((int)m_ndyFCal[cLayer]);     // FCal number of y slices
+  double cXstp(cXwid/((double)nXslice));   // FCal slice x width
+  double cYstp(cYwid/((double)nYslice));   // FCal slice y width 
+
+  // fill cell fragments
+  //  double xoff(cXpos-cXwid/2.+cXstp/2.); double yoff(cYpos-cYwid/2.+cYstp/2.);
+  double x(cXpos-(cXwid-cXstp)/2.); 
+  double xlim(cXpos+cXwid/2.); double ylim(cYpos+cYwid/2.);
+  double etaOrig(0.); 
+  //  for ( int ix(0); ix < nXslice; ++ix ) {
+  //   double x(xoff+ix*cXstp);
+  while ( x < xlim ) { 
+    //    for ( int iy(0); iy < nYslice; ++iy ) { 
+    //      double y(yoff+iy*cYstp);
+    double y(cYpos-(cYwid-cYstp)/2.);
+    while ( y < ylim ) { 
+      double r(std::sqrt(x*x+y*y+cZpos2));
+      double eta(-0.5*std::log((r-cZpos)/(r+cZpos)));
+      bool etaAdjusted(false);
+      if ( m_adjustEta ) { 
+	if ( eta < m_towerEtaMin ) {
+	  etaAdjusted = true;
+	  etaOrig     = eta;
+	  eta = m_towerEtaMin+m_towerEtaWidth/2.; 
+	} else if ( eta > m_towerEtaMax ) {
+	  etaAdjusted = true;
+	  etaOrig     = eta;
+	  eta = m_towerEtaMax-m_towerEtaWidth/2.;
+	}
+      }
+      double phi(CaloPhiRange::fix(std::atan2(y,x)));
+      index_t towerIdx(this->towerIndex(eta,phi));
+      // tower index not valid
+      if ( isInvalidIndex(towerIdx) ) { 
+	ATH_MSG_WARNING("Found invalid tower index for FCal cell (eta,phi) = (" << eta << "," << phi << ") at (x,y,z) = (" << x << "," << y << "," << cZpos << ") [cell ignored]");
+      } else { 
+	// add tower to lookup
+	if ( etaAdjusted ) { 
+	  ATH_MSG_WARNING("FCal cell direction (eta,phi) = (" << etaOrig << "," << phi << ") for cell at (x,y,z) = (" 
+			  << x << "," << y << "," << cZpos << ") adjusted to (eta,phi) = (" << eta << "," << phi << ") [cell adjusted]");
+	}
+	f_assign(pCaloDDE->calo_hash(),towerIdx,cWght);
+      } // tower index ok
+      y += cYstp;
+    } // loop on y fragments
+    x += cXstp;
+  } // loop on x fragments
+  return StatusCode::SUCCESS;
+}
+
+StatusCode CaloTowerGeometrySvc::f_setupTowerGridProj(const CaloDetDescrElement* pCaloDDE,std::ofstream& logger)
+{
+  // projective readout calos - collect geometrical variables
+  double cEtaPos(pCaloDDE->eta_raw());            // projective cell center in pseudorapidity
+  double cEtaWid(pCaloDDE->deta());               // projective cell width in pseudorapidity
+  double cPhiPos(pCaloDDE->phi_raw());            // projective cell center in azimuth
+  double cPhiWid(pCaloDDE->dphi());               // projective cell width in azimuth
+
+  // check cell-tower overlap area fractions
+  uint_t kEta(static_cast<uint_t>(cEtaWid/m_towerEtaWidth+0.5)); kEta = kEta == 0 ? 1 : kEta; // fully contained cell may have 0 fragments (protection)
+  uint_t kPhi(static_cast<uint_t>(cPhiWid/m_towerPhiWidth+0.5)); kPhi = kPhi == 0 ? 1 : kPhi; 
+
+  // print out
+  if ( kEta > 1 || kPhi > 1 ) {
+    ATH_MSG_VERBOSE("Found cell [" << pCaloDDE->calo_hash() << "/0x" << std::hex << pCaloDDE->identify().get_compact() << std::dec << "] spawning several towers."
+		    << " Neta = " << kEta << ", Nphi = " << kPhi );
+  }
+  
+  // share cells
+  double cWght(1./((double)kEta*kPhi));          // area weight
+  double sEta(cEtaWid/((double)kEta));           // step size (pseudorapidity)
+  double sPhi(cPhiWid/((double)kPhi));           // step size (azimuth)
+  double oEta(cEtaPos-sEta/2.);                  // offset (pseudorapidity)
+  double oPhi(cPhiPos-sPhi/2.);                  // offset (azimuth)
+
+  // loop over cell fragments
+  for ( uint_t ie(1); ie<=kEta; ++ie ) {
+    double ceta(oEta+((double)ie-0.5)*sEta);     // eta of fragment
+    for ( uint_t ip(1); ip<=kPhi; ++ip ) {
+      double cphi(oPhi+((double)ip-0.5)*sPhi);   // phi fragment
+      // tower index
+      index_t towerIdx(this->towerIndex(ceta,cphi));
+      if ( isInvalidIndex(towerIdx) ) { 
+	ATH_MSG_ERROR("Found invalid tower index for non-FCal cell (id,eta,phi) = (" << pCaloDDE->calo_hash() << "," << ceta << "," << cphi << ")");
+	return StatusCode::FAILURE;
+      } // invalid tower index
+      //      m_towerLookup[pCaloDDE->calo_hash()].emplace_back(towerIdx,cWght); // add to tower lookup
+      f_assign(pCaloDDE->calo_hash(),towerIdx,cWght);
+      if ( logger.is_open() ) { 
+	double el(this->towerEta(towerIdx)-this->etaWidth()/2.); double eh(this->towerEta(towerIdx)+this->etaWidth()/2.);
+	double pl(this->towerPhi(towerIdx)-this->phiWidth()/2.); double ph(this->towerPhi(towerIdx)+this->phiWidth()/2.);
+	if ( cEtaPos >= el && cEtaPos <= eh && cPhiPos >= pl && cPhiPos <= ph ) { 
+	  logger << CaloRec::Helpers::fmtMsg("Cell [%6zu] at (%6.3f,%6.3f) in Sampling %10.10s contributes to tower [%5zu] at ([%6.3f,%6.3f],[%6.3f,%6.3f]) with weight %6.4f [IN_BOUNDS]",
+					     (size_t)pCaloDDE->calo_hash(),cEtaPos,cPhiPos,CaloRec::Lookup::getSamplingName(pCaloDDE->getSampling()).c_str(),towerIdx,el,eh,pl,ph,cWght) << std::endl;
+	} else { 
+	  logger << CaloRec::Helpers::fmtMsg("Cell [%6zu] at (%6.3f,%6.3f) in Sampling %10.10s contributes to tower [%5zu] at ([%6.3f,%6.3f],[%6.3f,%6.3f]) with weight %6.4f [OUT_OF_BOUNDS]",
+					     (size_t)pCaloDDE->calo_hash(),cEtaPos,cPhiPos,CaloRec::Lookup::getSamplingName(pCaloDDE->getSampling()).c_str(),towerIdx,el,eh,pl,ph,cWght) << std::endl;
+	}
+      } // debugging central region
+    } // phi fragment loop
+  } // eta fragment loop
+  return StatusCode::SUCCESS;
+} 
+
+//------//
+// Fill //
+//------//
+
+double CaloTowerGeometrySvc::f_assign(IdentifierHash cellHash,index_t towerIdx,double wght) 
+{
+  // check if cell-tower already related
+  uint_t cidx(static_cast<uint_t>(cellHash));
+  for ( element_t& elm : m_towerLookup.at(cidx) ) { 
+    if ( towerIndex(elm) == towerIdx ) { std::get<1>(elm) += wght; return cellWeight(elm); }  
+  }
+  // not yet related
+  m_towerLookup[cidx].emplace_back(towerIdx,wght);
+  return cellWeight(m_towerLookup.at(cidx).back()); 
+}
+
+//--------//
+// Access //
+//--------//
+
+StatusCode CaloTowerGeometrySvc::access(const CaloCell* pCell,std::vector<index_t>& towerIdx,std::vector<double>& towerWghts) const
+{ return this->access(f_caloDDE(pCell)->calo_hash(),towerIdx,towerWghts); }
+
+StatusCode CaloTowerGeometrySvc::access(IdentifierHash cellHash,std::vector<index_t>& towerIdx,std::vector<double>& towerWghts) const 
+{
+  towerIdx.clear();
+  towerWghts.clear();
+
+  uint_t cidx(static_cast<uint_t>(cellHash));
+
+  if ( cidx >= m_towerLookup.size() ) { 
+    ATH_MSG_WARNING("Invalid cell hash index " << cellHash << ", corresponding index " << cidx << " not found in tower lookup");
+    return StatusCode::SUCCESS;
+  }
+
+  if ( towerIdx.capacity()   < m_towerLookup.at(cidx).size() ) { towerIdx.reserve(m_towerLookup.at(cidx).size());   }
+  if ( towerWghts.capacity() < m_towerLookup.at(cidx).size() ) { towerWghts.reserve(m_towerLookup.at(cidx).size()); }
+
+  for ( const auto& elm : m_towerLookup.at(cidx) ) { towerIdx.push_back(towerIndex(elm)); towerWghts.push_back(cellWeight(elm)); }
+
+  return StatusCode::SUCCESS;
+}
+
+CaloTowerGeometrySvc::elementvector_t CaloTowerGeometrySvc::getTowers(const CaloCell* pCell) const
+{ return pCell != 0 ? this->getTowers(f_caloDDE(pCell)->calo_hash()) : elementvector_t(); }
+
+CaloTowerGeometrySvc::elementvector_t CaloTowerGeometrySvc::getTowers(IdentifierHash cellHash) const
+{
+  // check input
+  uint_t cidx(static_cast<uint_t>(cellHash)); 
+  if ( cidx >= m_towerLookup.size() ) { 
+    ATH_MSG_WARNING( CaloRec::Helpers::fmtMsg("invalid cell hash %6zu beyond range (max hash is %6zu)",cidx,m_maxCellHash) ); 
+    return elementvector_t();
+  } else {
+    return m_towerLookup.at(cidx);
+  }
+} 
+
+//-----------------------//
+// Tower Geometry Helper //
+//-----------------------//
+
+double CaloTowerGeometrySvc::cellWeight(IdentifierHash cellHash,index_t towerIdx) const
+{
+  index_t cidx(static_cast<uint_t>(cellHash));
+  double cwght(0.);
+
+  if ( cidx < m_towerLookup.size() ) {
+    for ( auto elm : m_towerLookup.at(cidx) ) { 
+      if ( towerIndex(elm) == towerIdx ) { cwght = cellWeight(elm); break; } 
+    } 
+  }
+  return cwght;
+}
+
+//---------------//
+// Index Helpers //
+//---------------//
+
+CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::etaIndex(IdentifierHash cellHash) const
+{
+  const auto cdde = f_caloDDE(cellHash); 
+  return cdde != 0 ? etaIndex(cdde->eta()) : invalidIndex(); 
+}
+
+CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::etaIndex(double eta) const
+{ 
+  return eta >= m_towerEtaMin && eta <= m_towerEtaMax 
+    ? index_t(std::min(static_cast<uint_t>((eta-m_towerEtaMin)/m_towerEtaWidth),m_towerEtaBins-1))
+    : invalidIndex(); 
+}
+
+CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::phiIndex(IdentifierHash cellHash) const
+{
+  const auto cdde = f_caloDDE(cellHash);
+  return cdde != 0 ? phiIndex(cdde->phi()) : invalidIndex(); 
+}
+
+CaloTowerGeometrySvc::index_t CaloTowerGeometrySvc::phiIndex(double phi) const
+{
+  double dphi(CaloPhiRange::diff(phi,m_towerPhiMin));
+  return dphi >= m_towerPhiMin && dphi <= m_towerPhiMax 
+    ? index_t(std::min(static_cast<uint_t>((phi-m_towerPhiMin)/m_towerPhiWidth),m_towerPhiBins-1))
+    : invalidIndex();
+}
diff --git a/Calorimeter/CaloRec/src/components/CaloRec_entries.cxx b/Calorimeter/CaloRec/src/components/CaloRec_entries.cxx
index 4ca13346c76443b425ba96669d377841c2776dcb..90657db39b7455bd8fc9202fc757f3ac8dbfa3f1 100644
--- a/Calorimeter/CaloRec/src/components/CaloRec_entries.cxx
+++ b/Calorimeter/CaloRec/src/components/CaloRec_entries.cxx
@@ -33,7 +33,9 @@
 #include "CaloRec/CaloTowerxAODAlgoBase.h"
 #include "CaloRec/CaloTopoClusterFromTowerMaker.h"
 #include "CaloRec/CaloTopoClusterFromTowerCalibrator.h"
- 
+#include "CaloRec/CaloTopoClusterFromTowerMonitor.h"
+#include "CaloRec/CaloTowerGeometrySvc.h"
+#include "CaloRec/CaloTopoClusterTowerMerger.h"
 
 
 #include "GaudiKernel/DeclareFactoryEntries.h"
@@ -57,6 +59,8 @@ DECLARE_ALGORITHM_FACTORY( CaloClusterCellLinksUpdater )
 DECLARE_ALGORITHM_FACTORY( CaloTowerxAODFromCells )
 DECLARE_ALGORITHM_FACTORY( CaloTowerxAODFromClusters )
 DECLARE_ALGORITHM_FACTORY( CaloTowerxAODAlgoBase )
+DECLARE_ALGORITHM_FACTORY( CaloTopoClusterFromTowerMonitor )
+
 DECLARE_TOOL_FACTORY ( CaloTopoClusterFromTowerMaker )
 DECLARE_TOOL_FACTORY ( CaloTopoClusterFromTowerCalibrator )
  
@@ -96,6 +100,7 @@ DECLARE_FACTORY_ENTRIES(CaloRec) {
     DECLARE_ALGORITHM( CaloTowerxAODAlgoBase )
     DECLARE_ALGORITHM( CaloTowerxAODFromCells )
     DECLARE_ALGORITHM( CaloTowerxAODFromClusters )
+    DECLARE_ALGORITHM( CaloTopoClusterFromTowerMonitor )
     DECLARE_TOOL( CaloTopoClusterFromTowerMaker )
     DECLARE_TOOL( CaloTopoClusterFromTowerCalibrator )
 
@@ -116,3 +121,7 @@ DECLARE_FACTORY_ENTRIES(CaloRec) {
     DECLARE_TOOL( CaloCellFastCopyTool ) 
     DECLARE_TOOL( CaloClusterSnapshot )
 }
+
+DECLARE_COMPONENT( CaloTowerGeometrySvc )
+DECLARE_COMPONENT( CaloTopoClusterTowerMerger )
+
diff --git a/Control/AthenaMonitoring/python/DQMonFlags.py b/Control/AthenaMonitoring/python/DQMonFlags.py
index e6a15b4c3a7b9fe0778cc7b6d2eef934fee0c6be..e624702ee23d8125eb9ba4c5659a2bc8a2aa8d00 100644
--- a/Control/AthenaMonitoring/python/DQMonFlags.py
+++ b/Control/AthenaMonitoring/python/DQMonFlags.py
@@ -398,12 +398,14 @@ class enableLumiAccess(JobProperty):
     StoredValue=True
 list+=[enableLumiAccess]
 
-class excludeFromCleaning(JobProperty):
-    """ Tools matching regexes in this list will not have event cleaning tool set up """
+class includeInCleaning(JobProperty):
+    """ Tools matching regexes in this list will have event cleaning tool set up """
     statusOn=True
     allowedTypes=['list']
-    StoredValue=['.*LAr.*', '.*Tile.*', '.*SCT.*', 'DQTDataFlowMon']
-list+=[excludeFromCleaning]
+    StoredValue=[ '.*JetMonitoring.*', '.*METMonTool.*', '.*tauMonTool.*',
+                  '.*DQTGlobalWZFinder.*', '.*jetTagMon.*', '.*phMonTool.*',
+                  '.*elMonTool.*', '.*fwdMonTool.*' ]
+list+=[includeInCleaning]
 
 class specialCleaningConfiguration(JobProperty):
     """ Special event cleaning configurations (no regexes) """
diff --git a/Control/AthenaMonitoring/share/DQMonFlagsConfig_jobOptions.py b/Control/AthenaMonitoring/share/DQMonFlagsConfig_jobOptions.py
index 5902d1127ad6b8c7b15005413f60041169975e42..47dfe8eb7566cd989a145aeb0de3e5e46f53391d 100644
--- a/Control/AthenaMonitoring/share/DQMonFlagsConfig_jobOptions.py
+++ b/Control/AthenaMonitoring/share/DQMonFlagsConfig_jobOptions.py
@@ -452,7 +452,7 @@ else:
    local_logger.info("Stream-Aware monitoring is turned OFF")
 
 # If data type is '*comm' disable ATLAS Ready filter by default
-if (rec.projectName.get_Value().endswith('_comm') and 
+if (rec.projectName.get_Value().endswith('comm') and 
     not DQMonFlags.disableAtlasReadyFilter()
     ):
    local_logger.info("This is a commissioning project tag, will attempt to disable ATLAS Ready filter for monitoring tools. To really enable it, use DQMonFlags.disableAtlasReadyFilter.set_Value_and_Lock(False).")
diff --git a/Control/AthenaMonitoring/share/DataQualitySteering_jobOptions.py b/Control/AthenaMonitoring/share/DataQualitySteering_jobOptions.py
index aef73735a8d4cf4548e30035f3bc50fb8a282ccf..1ce2256cbbfd3a0b4524bac6e47c50c27c9e3ddb 100644
--- a/Control/AthenaMonitoring/share/DataQualitySteering_jobOptions.py
+++ b/Control/AthenaMonitoring/share/DataQualitySteering_jobOptions.py
@@ -273,8 +273,8 @@ if DQMonFlags.doMonitoring():
          if rec.triggerStream()=='express':
             local_logger.info('Stream is express and we will add ready tool for %s', tool)
             tool.FilterTools += [monAtlasReadyFilterTool]
-         # unless prevented: configure a generic event cleaning tool
-         if not athenaCommonFlags.isOnline() and not any(re.match(_, tool.name()) for _ in DQMonFlags.excludeFromCleaning()):
+         # if requested: configure a generic event cleaning tool
+         if not athenaCommonFlags.isOnline() and any(re.match(_, tool.name()) for _ in DQMonFlags.includeInCleaning()):
             if tool.name() in DQMonFlags.specialCleaningConfiguration():
                config_ = DQMonFlags.specialCleaningConfiguration()[tool.name()].copy()
                for _ in config_:
diff --git a/Control/PileUpComps/src/PileUpEventLoopMgr.cxx b/Control/PileUpComps/src/PileUpEventLoopMgr.cxx
index 8dc595019377db7e853d0f07b28456648f847628..dfc6788a6327b55927933b5e6d14ac460c54a088 100644
--- a/Control/PileUpComps/src/PileUpEventLoopMgr.cxx
+++ b/Control/PileUpComps/src/PileUpEventLoopMgr.cxx
@@ -498,7 +498,7 @@ StatusCode PileUpEventLoopMgr::nextEvent(int maxevt)
       std::unique_ptr< xAOD::EventAuxInfo > pxAODEventAuxInfo(new xAOD::EventAuxInfo());
       std::unique_ptr< xAOD::EventInfo > pxAODEventInfo( new xAOD::EventInfo());
       pxAODEventInfo->setStore( pxAODEventAuxInfo.get() );
-      CHECK(m_xAODCnvTool->convert( pOverEvent, pxAODEventInfo.get(), false, false ));
+      CHECK(m_xAODCnvTool->convert( pOverEvent, pxAODEventInfo.get(), false, false, m_isEventOverlayJob));
        // Before the record, keep a pointer to the EventInfo object, to be able
       // to add ElementLinks to it afterwards.
       xAOD::EventInfo* pLocalXAODEventInfo = pxAODEventInfo.get();
diff --git a/DataQuality/DataQualityTools/DataQualityTools/DQTGlobalWZFinderTool.h b/DataQuality/DataQualityTools/DataQualityTools/DQTGlobalWZFinderTool.h
index bfee2fe59dbc366f1bab9f922ea2423cadf3a659..35ee98923d0535f3c344d86a4fd5dbdf246f1d14 100644
--- a/DataQuality/DataQualityTools/DataQualityTools/DQTGlobalWZFinderTool.h
+++ b/DataQuality/DataQualityTools/DataQualityTools/DQTGlobalWZFinderTool.h
@@ -22,7 +22,10 @@
 
 #include "TrigDecisionTool/TrigDecisionTool.h"
 #include "TrigEgammaMatchingTool/ITrigEgammaMatchingTool.h"
-#include "xAODEventInfo/EventInfo.h"
+
+#include "PATInterfaces/ISystematicsTool.h"
+#include <PATInterfaces/SystematicRegistry.h>
+#include <PATInterfaces/SystematicVariation.h>
 
 #include "TMath.h"
 #include <string>
@@ -33,6 +36,11 @@ class TProfile;
 class TH1F_LW;
 class TH2F_LW;
 
+namespace xAOD{
+  class EventInfo_v1; typedef EventInfo_v1 EventInfo; 
+  class TruthParticle_v1; typedef TruthParticle_v1 TruthParticle;
+}
+
 namespace CP {
   class IMuonSelectionTool;
   class IIsolationSelectionTool;
@@ -66,20 +74,41 @@ private:
   void doMuonTriggerTP(const xAOD::Muon* , const xAOD::Muon*);
   void doMuonTruthEff(std::vector<const xAOD::Muon*>&);
   void doMuonLooseTP(std::vector<const xAOD::Muon*>& goodmuonsZ, const xAOD::Vertex* pVtx);
+  void doMuonInDetTP(std::vector<const xAOD::Muon*>& goodmuonsZ, const xAOD::Vertex* pVtx);
 
-//----- Electron START -----//
-  void doEleTriggerTP(const xAOD::Electron*,const xAOD::Electron*);
+  //----- Electron START -----//
+  void doEleTriggerTP(const xAOD::Electron* el1, const xAOD::Electron* el2, bool os, bool ss);
   void doEleTP(const xAOD::Electron*, const xAOD::Electron*, const xAOD::Vertex*, const xAOD::EventInfo*, bool);
+  void doEleContainerTP(std::vector<const xAOD::Electron*>, std::vector<const xAOD::Electron*>);
+
   bool goodElectrons(const xAOD::EventInfo*, const xAOD::Electron*, const xAOD::Vertex*, bool); 
+  bool antiGoodElectrons(const xAOD::EventInfo* thisEventInfo, const xAOD::Electron* electron_itr,
+                         const xAOD::Vertex* pVtx, bool isBad);
+  bool kinematicCuts(const xAOD::Electron*);
 
   ToolHandle<Trig::ITrigEgammaMatchingTool> m_elTrigMatchTool;
 
-  TH1F_LW *m_eltrigtp_matches;
+  TH1F *m_ZBosonCounter_El_os;
+  TH1F *m_ZBosonCounter_El_ss;
+
+  TH1F_LW *m_eltrigtp_matches_os;
+  TH1F_LW *m_eltrigtp_matches_ss;
+
 
   TH1F_LW *m_ele_tight_bad_os;
   TH1F_LW *m_ele_tight_bad_ss;
   TH1F_LW *m_ele_tight_good_os;
   TH1F_LW *m_ele_tight_good_ss;
+  TH1F_LW *m_ele_template_os;
+  TH1F_LW *m_ele_template_ss;
+
+  //TH1F_LW *m_elContainertp_match;
+  TH1F_LW *m_elContainertp_nomatch;
+  TH1F_LW *m_ele_tight_passkine;
+
+  TH1F_LW *m_totalSumWeights;
+  TH1F_LW *m_fiducialSumWeights_el;
+  TH1F_LW *m_fiducialSumWeights_mu;
 
 //----- Electron END ------//
 
@@ -118,15 +147,11 @@ private:
 
       TH1F_LW *m_JPsiCounter_Mu;
       TH1F_LW *m_UpsilonCounter_Mu;
-      TH1F *m_ZBosonCounter_El;
       TH1F *m_ZBosonCounter_Mu;
-      TH1F *m_ZBosonCounter_Mu_CMS;
 
 
       //Trigger T&P
       TH1F_LW *m_mutrigtp_matches;
-      TH1F_LW *m_mutrigtp_matches_CMS;
-
 
       //Reco T&P
       TH1F_LW *m_muloosetp_match_os;
@@ -134,11 +159,11 @@ private:
       TH1F_LW *m_muloosetp_nomatch_os;
       TH1F_LW *m_muloosetp_nomatch_ss;
 
-      TH1F_LW *m_muloosetp_match_os_CMS;
-      TH1F_LW *m_muloosetp_match_ss_CMS;
-      TH1F_LW *m_muloosetp_nomatch_os_CMS;
-      TH1F_LW *m_muloosetp_nomatch_ss_CMS;
-
+      // Inner detector T&P
+      TH1F_LW *m_mu_InDet_tp_match_os;
+      TH1F_LW *m_mu_InDet_tp_match_ss;
+      TH1F_LW *m_mu_InDet_tp_nomatch_os;
+      TH1F_LW *m_mu_InDet_tp_nomatch_ss;
 
       // MC truth
       TH1F_LW *m_mcmatch;
@@ -174,7 +199,6 @@ private:
       std::string m_tracksName;
       Float_t m_electronEtCut;
       Float_t m_muonPtCut;
-      Float_t m_muonPtCut_CMS;
 
       Float_t m_metCut;
       Float_t m_zCutLow;
diff --git a/DataQuality/DataQualityTools/src/DQTGlobalWZFinderTool.cxx b/DataQuality/DataQualityTools/src/DQTGlobalWZFinderTool.cxx
index fd9479f11090f5a86c4c5d9311a6c596659da9d9..740e1dee82af254ebf30d80d607b2287553b6164 100644
--- a/DataQuality/DataQualityTools/src/DQTGlobalWZFinderTool.cxx
+++ b/DataQuality/DataQualityTools/src/DQTGlobalWZFinderTool.cxx
@@ -72,7 +72,6 @@ DQTGlobalWZFinderTool::DQTGlobalWZFinderTool(const std::string & type,
      m_tracksName("InDetTrackParticles"), //Kshort
      m_electronEtCut(25),
      m_muonPtCut(25),
-     m_muonPtCut_CMS(30),
      m_metCut(25),
      m_zCutLow(66.0),
      m_zCutHigh(116.0),
@@ -180,9 +179,9 @@ StatusCode DQTGlobalWZFinderTool::bookHistogramsRecurrent()
   failure |= registerHist(fullPathDQTGlobalWZFinder, m_mu_lb = new TProfile("m_mu_lb", "#mu", 1, m_this_lb-0.5, m_this_lb+0.5), lumiBlock, ATTRIB_UNMANAGED, "merge").isFailure();
   failure |= registerHist(fullPathDQTGlobalWZFinder, m_Z_ee_trig_ps = new TProfile("m_Z_ee_trig_ps", "Z->ee trigger PS", 1, m_this_lb-0.5, m_this_lb+0.5), lumiBlock, ATTRIB_UNMANAGED, "merge").isFailure();
   failure |= registerHist(fullPathDQTGlobalWZFinder, m_Z_mm_trig_ps = new TProfile("m_Z_mm_trig_ps", "Z->#mu#mu trigger PS", 1, m_this_lb-0.5, m_this_lb+0.5), lumiBlock, ATTRIB_UNMANAGED, "merge").isFailure();
-  failure |= registerHist(fullPathDQTGlobalWZFinder, m_ZBosonCounter_El = new TH1F("m_Z_Counter_el","Z #rightarrow ee Count per Lumi Block", 1, m_this_lb-0.5, m_this_lb+0.5), lumiBlock, ATTRIB_UNMANAGED, "merge").isFailure();
+  failure |= registerHist(fullPathDQTGlobalWZFinder, m_ZBosonCounter_El_os = new TH1F("m_Z_Counter_el_os","Z #rightarrow ee Count per Lumi Block", 1, m_this_lb-0.5, m_this_lb+0.5), lumiBlock, ATTRIB_UNMANAGED, "merge").isFailure();
+  failure |= registerHist(fullPathDQTGlobalWZFinder, m_ZBosonCounter_El_ss = new TH1F("m_Z_Counter_el_ss","Z #rightarrow ee Count per Lumi Block", 1, m_this_lb-0.5, m_this_lb+0.5), lumiBlock, ATTRIB_UNMANAGED, "merge").isFailure();  
   failure |= registerHist(fullPathDQTGlobalWZFinder, m_ZBosonCounter_Mu = new TH1F("m_Z_Counter_mu","Z #rightarrow #mu#mu Count per Lumi Block", 1, m_this_lb-0.5, m_this_lb+0.5), lumiBlock, ATTRIB_UNMANAGED, "merge").isFailure();
-  failure |= registerHist(fullPathDQTGlobalWZFinder, m_ZBosonCounter_Mu_CMS = new TH1F("m_Z_Counter_mu_CMS","Z #rightarrow #mu#mu Count per Lumi Block (CMS cuts)", 1, m_this_lb-0.5, m_this_lb+0.5), lumiBlock, ATTRIB_UNMANAGED, "merge").isFailure();
 
   if (!failure) {
     m_livetime_lb->Fill(m_this_lb, lbAverageLivefraction());
@@ -278,7 +277,6 @@ bool DQTGlobalWZFinderTool::bookDQTGlobalWZFinderTool()
 
      // T&P trigger rate
      failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_mutrigtp_matches = TH1F_LW::create("m_mutrigtp_matches", "Muon trigger TP stats", 3, -0.5, 2.5), lumiBlock).isFailure();
-     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_mutrigtp_matches_CMS = TH1F_LW::create("m_mutrigtp_matches_CMS", "Muon trigger TP stats (CMS cuts)", 3, -0.5, 2.5), lumiBlock).isFailure();
 
      // T&P muon eff
      failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_muloosetp_match_os = TH1F_LW::create("m_muloosetp_match_os", "Muon loose TP match OS", nzbins, m_zCutLow*GeV, m_zCutHigh*GeV), lumiBlock).isFailure();
@@ -286,20 +284,36 @@ bool DQTGlobalWZFinderTool::bookDQTGlobalWZFinderTool()
      failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_muloosetp_nomatch_os = TH1F_LW::create("m_muloosetp_nomatch_os", "Muon loose TP nomatch OS", nzbins, m_zCutLow*GeV, m_zCutHigh*GeV), lumiBlock).isFailure();
      failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_muloosetp_nomatch_ss = TH1F_LW::create("m_muloosetp_nomatch_ss", "Muon loose TP nomatch SS", nzbins, m_zCutLow*GeV, m_zCutHigh*GeV), lumiBlock).isFailure();
 
-     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_muloosetp_match_os_CMS = TH1F_LW::create("m_muloosetp_match_os_CMS", "Muon loose TP match OS", nzbins, m_zCutLow*GeV, m_zCutHigh*GeV), lumiBlock).isFailure();
-     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_muloosetp_match_ss_CMS = TH1F_LW::create("m_muloosetp_match_ss_CMS", "Muon loose TP match SS", nzbins, m_zCutLow*GeV, m_zCutHigh*GeV), lumiBlock).isFailure();
-     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_muloosetp_nomatch_os_CMS = TH1F_LW::create("m_muloosetp_nomatch_os_CMS", "Muon loose TP nomatch OS", nzbins, m_zCutLow*GeV, m_zCutHigh*GeV), lumiBlock).isFailure();
-     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_muloosetp_nomatch_ss_CMS = TH1F_LW::create("m_muloosetp_nomatch_ss_CMS", "Muon loose TP nomatch SS", nzbins, m_zCutLow*GeV, m_zCutHigh*GeV), lumiBlock).isFailure();
-
+     // T&P muon inner detector
+       failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_mu_InDet_tp_match_os = TH1F_LW::create("m_mu_InDet_tp_match_os", "Muon inner detector TP match OS", nzbins, m_zCutLow*GeV, m_zCutHigh*GeV), lumiBlock).isFailure();
+       failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_mu_InDet_tp_match_ss = TH1F_LW::create("m_mu_InDet_tp_match_ss", "Muon inner detector TP match SS", nzbins, m_zCutLow*GeV, m_zCutHigh*GeV), lumiBlock).isFailure();
+       failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_mu_InDet_tp_nomatch_os = TH1F_LW::create("m_mu_InDet_tp_nomatch_os", "Muon inner detector TP nomatch OS", nzbins, m_zCutLow*GeV, m_zCutHigh*GeV), lumiBlock).isFailure();
+       failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_mu_InDet_tp_nomatch_ss = TH1F_LW::create("m_mu_InDet_tp_nomatch_ss", "Muon inner detector TP nomatch SS", nzbins, m_zCutLow*GeV, m_zCutHigh*GeV), lumiBlock).isFailure();
 
      // Clone of above but for electrons
-     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_eltrigtp_matches = TH1F_LW::create("m_eltrigtp_matches", "Electron trigger TP stats", 3, -0.5, 2.5), lumiBlock).isFailure();
+     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_eltrigtp_matches_os = TH1F_LW::create("m_eltrigtp_matches_os", "Electron trigger TP stats", 3, -0.5, 2.5), lumiBlock).isFailure();
+     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_eltrigtp_matches_ss = TH1F_LW::create("m_eltrigtp_matches_ss", "Electron trigger TP stats", 3, -0.5, 2.5), lumiBlock).isFailure();
      // T&P electron eff
 
-     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_ele_tight_bad_os = TH1F_LW::create("m_ele_tight_bad_os", "1tight 1bad os",nzbins, m_zCutLow*GeV, m_zCutHigh*GeV), lumiBlock).isFailure();
-     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_ele_tight_bad_ss = TH1F_LW::create("m_ele_tight_bad_ss", "1tight 1bad ss",nzbins, m_zCutLow*GeV, m_zCutHigh*GeV), lumiBlock).isFailure();
-     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_ele_tight_good_os = TH1F_LW::create("m_ele_tight_good_os","1tight 1good os",nzbins,m_zCutLow*GeV, m_zCutHigh*GeV),lumiBlock).isFailure();
-     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_ele_tight_good_ss = TH1F_LW::create("m_ele_tight_good_ss","1tight 1good ss",nzbins,m_zCutLow*GeV, m_zCutHigh*GeV),lumiBlock).isFailure();
+     double m_zCutHigh_elTP = 250; 
+     double m_zCutLow_elTP  = 66;
+     int nzbins_elTP = int(ceilf(m_zCutHigh_elTP - m_zCutLow_elTP));
+     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_ele_tight_bad_os  = TH1F_LW::create("m_ele_tight_bad_os"  , "1tight 1bad os" ,nzbins_elTP, m_zCutLow_elTP*GeV, m_zCutHigh_elTP*GeV), 
+				     lumiBlock).isFailure();
+     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_ele_tight_bad_ss  = TH1F_LW::create("m_ele_tight_bad_ss"  , "1tight 1bad ss" ,nzbins_elTP, m_zCutLow_elTP*GeV, m_zCutHigh_elTP*GeV), 
+				     lumiBlock).isFailure();
+     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_ele_tight_good_os = TH1F_LW::create("m_ele_tight_good_os" , "1tight 1good os",nzbins_elTP, m_zCutLow_elTP*GeV, m_zCutHigh_elTP*GeV),
+				     lumiBlock).isFailure();
+     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_ele_tight_good_ss = TH1F_LW::create("m_ele_tight_good_ss" , "1tight 1good ss",nzbins_elTP, m_zCutLow_elTP*GeV, m_zCutHigh_elTP*GeV),
+				     lumiBlock).isFailure();
+     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_ele_template_os   = TH1F_LW::create("m_ele_template_os"   , "template os"	,nzbins_elTP, m_zCutLow_elTP*GeV, m_zCutHigh_elTP*GeV),
+				     lumiBlock).isFailure();
+     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_ele_template_ss   = TH1F_LW::create("m_ele_template_ss"   , "template ss"	,nzbins_elTP, m_zCutLow_elTP*GeV, m_zCutHigh_elTP*GeV),
+				     lumiBlock).isFailure();
+     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_elContainertp_nomatch = TH1F_LW::create("m_elContainertp_nomatch" , "m_elContainertp_nomatch" ,nzbins_elTP, m_zCutLow_elTP*GeV, 
+                 m_zCutHigh_elTP*GeV), lumiBlock).isFailure();
+     failure = failure | registerHist(fullPathDQTGlobalWZFinder, m_ele_tight_passkine   = TH1F_LW::create("m_ele_tight_passkine" , "m_ele_tight_passkine" ,nzbins_elTP, m_zCutLow_elTP*GeV,
+                 m_zCutHigh_elTP*GeV), lumiBlock).isFailure();
 
 
      if (thisEventInfo->eventType(xAOD::EventInfo::IS_SIMULATION)) {
@@ -352,9 +366,8 @@ StatusCode DQTGlobalWZFinderTool::fillHistograms()
        m_evtWeight = thisEventInfo->mcEventWeight();
        ATH_MSG_DEBUG("Event Weight: " << m_evtWeight);
      }
- 
-     //Get MET
-     
+
+     //Get MET     
      Double_t phiMet = 0, metMet = 0;
      //     const MissingET *missET;
      const xAOD::MissingETContainer *missETcont(0);
@@ -381,10 +394,7 @@ StatusCode DQTGlobalWZFinderTool::fillHistograms()
 
      ATH_MSG_DEBUG(" MET = " << metMet << " and met phi = " << phiMet);
 
-
      //Get Electrons
-     
-     //     const ElectronContainer* elecTES = 0;
      const xAOD::ElectronContainer* elecTES = 0;
      if ( evtStore()->contains<xAOD::ElectronContainer>(m_electronContainerName) ) {
         sc=evtStore()->retrieve( elecTES, m_electronContainerName);
@@ -403,7 +413,6 @@ StatusCode DQTGlobalWZFinderTool::fillHistograms()
      ATH_MSG_DEBUG("ElectronContainer successfully retrieved");
 
      //Get Muons        
-     //     const Analysis::MuonContainer* muons  = 0;
      const xAOD::MuonContainer* muons  = 0;
      if(evtStore()->contains<xAOD::MuonContainer>(m_muonContainerName)){
         sc = evtStore()->retrieve(muons,m_muonContainerName);
@@ -426,12 +435,7 @@ StatusCode DQTGlobalWZFinderTool::fillHistograms()
      std::vector<Int_t> goodmuonJPsicharge;
 
      //get primary vertex info
-     //const VxContainer *m_vertices;
      const xAOD::VertexContainer* vertices(0);
-     //bool vertexCut = false;
-     
-     //EP CHECK( m_xaodConverter.retrieve() );
-
      const xAOD::Vertex* pVtx(0);
      if ( evtStore()->contains<xAOD::VertexContainer>(m_VxPrimContainerName)) {
        sc = evtStore()->retrieve(vertices,m_VxPrimContainerName);
@@ -492,7 +496,7 @@ StatusCode DQTGlobalWZFinderTool::fillHistograms()
           m_ele_Et->Fill((*itr)->pt()/GeV, m_evtWeight);
           m_ele_Eta->Fill((*itr)->eta(), m_evtWeight);
           goodelectrons.push_back(*itr);
-        }	
+       }
      }
 
 
@@ -501,26 +505,25 @@ StatusCode DQTGlobalWZFinderTool::fillHistograms()
      Int_t MuJPsi_N = 0;
      ATH_MSG_DEBUG("Start muon selection");
 
-     xAOD::MuonContainer::const_iterator muonItr;
-     for (muonItr=muons->begin(); muonItr != muons->end(); ++muonItr) {
-         Float_t minptCutJPsi(1.0*CLHEP::GeV);
-	 auto muTrk = (*muonItr)->primaryTrackParticle();
-	 float d0sig;
-	 if (!muTrk) {
-	   ATH_MSG_WARNING("No muon track! " << thisEventInfo->runNumber() << " " << thisEventInfo->eventNumber());
-	   continue;
-	 }
-	 try {
-	   d0sig = xAOD::TrackingHelpers::d0significance(muTrk, thisEventInfo->beamPosSigmaX(), thisEventInfo->beamPosSigmaY(), thisEventInfo->beamPosSigmaXY());
-	 } catch (...) {
+     for (xAOD::MuonContainer::const_iterator muonItr=muons->begin(); muonItr != muons->end(); ++muonItr){
+       Float_t minptCutJPsi(1.0*CLHEP::GeV);
+       auto muTrk = (*muonItr)->primaryTrackParticle();
+       float d0sig;
+       if (!muTrk) {
+         ATH_MSG_WARNING("No muon track! " << thisEventInfo->runNumber() << " " << thisEventInfo->eventNumber());
+         continue;
+       }
+       try {
+         d0sig = xAOD::TrackingHelpers::d0significance(muTrk, thisEventInfo->beamPosSigmaX(), thisEventInfo->beamPosSigmaY(), thisEventInfo->beamPosSigmaXY());
+       } catch (...) {
 	   ATH_MSG_DEBUG("Invalid beamspot - muon");
-	   try {
-	     d0sig = xAOD::TrackingHelpers::d0significance(muTrk);
-	   } catch (...) {
-	     ATH_MSG_WARNING("Ridiculous exception thrown - muon");
-	     continue;
-	   }
-	 }
+         try {
+           d0sig = xAOD::TrackingHelpers::d0significance(muTrk);
+         } catch (...) {
+           ATH_MSG_WARNING("Ridiculous exception thrown - muon");
+           continue;
+         }
+       }
 	 
 	 
 	 ATH_MSG_DEBUG("Muon accept: " << m_muonSelectionTool->accept(**muonItr));
@@ -589,13 +592,15 @@ StatusCode DQTGlobalWZFinderTool::fillHistograms()
      }
 
      doMuonLooseTP(goodmuonsZ, pVtx);
+     doMuonInDetTP(goodmuonsZ, pVtx);
      doEleTP(leadingAllEle, subleadingAllEle, pVtx, thisEventInfo, isBad);
+     doEleContainerTP(allElectrons, goodelectrons);
+     
      // Sort Candidates by Pt
-
      const xAOD::Electron* leadingEle(0);
-     const xAOD::Electron*  subleadingEle(0);
-     const xAOD::Muon* leadingMuZ(0);
-     const xAOD::Muon* subleadingMuZ(0);
+     const xAOD::Electron* subleadingEle(0);
+     const xAOD::Muon*      leadingMuZ(0);
+     const xAOD::Muon*     subleadingMuZ(0);
      CLHEP::HepLorentzVector leadingMuJPsi;
      CLHEP::HepLorentzVector subleadingMuJPsi;
      
@@ -608,16 +613,18 @@ StatusCode DQTGlobalWZFinderTool::fillHistograms()
 
      ATH_MSG_DEBUG("Beginning ele loop");
      for (UInt_t iEle = 0; iEle < goodelectrons.size(); iEle++) {
-        Float_t pt = goodelectrons[iEle]->pt();
-	ATH_MSG_DEBUG("Ele pt " << pt);
-        if (! leadingEle || pt > leadingEle->pt()) {
-           subleadingEle = leadingEle;
-           leadingEle = goodelectrons[iEle];
-        }
-        else if (! subleadingEle || pt > subleadingEle->pt()) {
-           subleadingEle = goodelectrons[iEle];
-        }
+       Float_t pt = goodelectrons[iEle]->pt();
+	   ATH_MSG_DEBUG("Ele pt " << pt);
+       if (! leadingEle || pt > leadingEle->pt()) {
+         subleadingEle = leadingEle;
+         leadingEle = goodelectrons[iEle];
+       }
+       else if (! subleadingEle || pt > subleadingEle->pt()) {
+         subleadingEle = goodelectrons[iEle];
+       }
      }
+
+      
      ATH_MSG_DEBUG("Done ele loop");
      ATH_MSG_DEBUG("Start mu Z loop");
      for (UInt_t iMu = 0; iMu < goodmuonsZ.size(); iMu++) {
@@ -665,57 +672,62 @@ StatusCode DQTGlobalWZFinderTool::fillHistograms()
        ATH_MSG_DEBUG("Zee found");
        TLorentzVector Zee = (leadingEle->p4() + subleadingEle->p4());
        Float_t mass = Zee.M();
-       Int_t Zeecharge = leadingEle->charge() + subleadingEle->charge();       
+       Int_t Zeecharge = leadingEle->charge() + subleadingEle->charge();      
        bool ignoreTrig = trigChainsArePassed(m_Z_ee_trigger) || !m_doTrigger;
-       if (mass > m_zCutLow*GeV && mass < m_zCutHigh*GeV ) {
-	 m_Z_Q_ele->Fill(Zeecharge, m_evtWeight);
-	 ATH_MSG_DEBUG( "Found a Z to ee candidate!  Mass = " << mass << ", and charge = " << Zeecharge );
-	 if (Zeecharge == 0){
- 	   if(ignoreTrig){
-	     m_Z_mass_opsele->Fill(mass, m_evtWeight);
-	     m_ZBosonCounter_El->Fill(m_this_lb, m_evtWeight);
-	     ++m_ZBosonCounterSBG_El[0];
-	   }
-	   if(m_doTrigger){
-             doEleTriggerTP(leadingEle, subleadingEle);
-           }
-	 } else{
-	   if(ignoreTrig){
+       bool inMassWindow = (mass > m_zCutLow*GeV && mass < m_zCutHigh*GeV);
+       bool os = false;
+       bool ss = false;
+       (Zeecharge == 0) ? (os = true) : (ss = true);       
+
+       if (inMassWindow){
+	     m_Z_Q_ele->Fill(Zeecharge, m_evtWeight);
+	     ATH_MSG_DEBUG( "Found a Z to ee candidate!  Mass = " << mass << ", and charge = " << Zeecharge );
+       }
+       if(inMassWindow && os && ignoreTrig){
+         m_Z_mass_opsele->Fill(mass, m_evtWeight);
+         m_ZBosonCounter_El_os->Fill(m_this_lb, m_evtWeight);
+         ++m_ZBosonCounterSBG_El[0];
+       }         
+       if(inMassWindow && ss && ignoreTrig){
 	     m_Z_mass_ssele->Fill(mass, m_evtWeight);
+         m_ZBosonCounter_El_ss->Fill(m_this_lb, m_evtWeight);
 	   }
-	 }
+       if(inMassWindow && m_doTrigger){
+         doEleTriggerTP(leadingEle, subleadingEle, os, ss);
        }
      }
 
-     if (isZmumu) {
+
+     if (isZmumu){
        ATH_MSG_DEBUG("Zmumu found");
        TLorentzVector Zmumu = leadingMuZ->p4() + subleadingMuZ->p4();
        Float_t mass = Zmumu.M();
        Int_t Zmumucharge = leadingMuZ->charge() + subleadingMuZ->charge();
        // potentially ignore trigger...
        bool oktrig = trigChainsArePassed(m_Z_mm_trigger) || !m_doTrigger;
-	if (mass > m_zCutLow*GeV && mass < m_zCutHigh*GeV){
-	  ATH_MSG_DEBUG("Found a Z to mumu candidate!  Mass = " << mass << ", and charge = " << Zmumucharge);
-	  if (Zmumucharge == 0) {
-	    if (oktrig) {
-              m_Z_Q_mu->Fill(Zmumucharge, m_evtWeight);
-	      m_Z_mass_opsmu->Fill(mass, m_evtWeight);
-	      m_ZBosonCounter_Mu->Fill(m_this_lb, m_evtWeight);
-	      if(leadingMuZ->pt() > m_muonPtCut_CMS && subleadingMuZ->pt() > m_muonPtCut_CMS){ 
-		m_ZBosonCounter_Mu_CMS->Fill(m_this_lb, m_evtWeight); 
-	      }
-	      ++m_ZBosonCounterSBG_Mu[0];
-	    }
-	    else { ATH_MSG_DEBUG("Trigger failure!"); }
-	    if (m_doTrigger) {
-	      doMuonTriggerTP(leadingMuZ, subleadingMuZ);
-	    }
-	  } else {
-	    if (oktrig) {
-	      m_Z_mass_ssmu->Fill(mass, m_evtWeight);
-	    }
-	  }
-	}
+	   bool inMassWindow = (mass > m_zCutLow*GeV && mass < m_zCutHigh*GeV);
+       bool os = false;
+       bool ss = false;
+       (Zmumucharge == 0) ? (os = true) : (ss = true);
+
+       if(inMassWindow){
+         m_Z_Q_mu->Fill(Zmumucharge, m_evtWeight);
+         ATH_MSG_DEBUG("Found a Z to mumu candidate!  Mass = " << mass << ", and charge = " << Zmumucharge);        
+       }
+       if(inMassWindow && os && oktrig){
+         m_Z_mass_opsmu->Fill(mass, m_evtWeight);
+         m_ZBosonCounter_Mu->Fill(m_this_lb, m_evtWeight);
+         ++m_ZBosonCounterSBG_Mu[0];
+       }
+       if(inMassWindow && os && !oktrig){
+         ATH_MSG_DEBUG("Trigger failure!");      
+       }
+       if(inMassWindow && os && m_doTrigger){ 
+         doMuonTriggerTP(leadingMuZ, subleadingMuZ);      
+       }
+       if(inMassWindow && ss && oktrig){
+         m_Z_mass_ssmu->Fill(mass, m_evtWeight);
+       }
      }   
 
      //JPsi and Upsilon counter
@@ -792,16 +804,15 @@ StatusCode DQTGlobalWZFinderTool::fillHistograms()
        Float_t px = leadingEle->p4().Px();
        Float_t py = leadingEle->p4().Py();
        Float_t pz = leadingEle->p4().Pz();
-        Float_t p = sqrt(px*px+py*py+pz*pz);
-        Float_t et = leadingEle->e()*leadingEle->pt()/p;
-        Float_t mt = sqrt(2*(et*metMet-px*metx-py*mety));     
-
-	if (mt > mtCut)
-	{
-	  m_W_pt_v_met_ele->Fill(leadingEle->pt(), metMet, m_evtWeight);
-	  m_W_mt_ele->Fill(mt, m_evtWeight);
-	  ATH_MSG_DEBUG("Found a W to enu candidate!  M_T = " << mt << ", and MET = " << metMet << ", and ele_pt = " << leadingEle->pt());
-	}
+       Float_t p = sqrt(px*px+py*py+pz*pz);
+       Float_t et = leadingEle->e()*leadingEle->pt()/p;
+       Float_t mt = sqrt(2*(et*metMet-px*metx-py*mety));     
+
+       if (mt > mtCut){
+	 m_W_pt_v_met_ele->Fill(leadingEle->pt(), metMet, m_evtWeight);
+	 m_W_mt_ele->Fill(mt, m_evtWeight);
+	 ATH_MSG_DEBUG("Found a W to enu candidate!  M_T = " << mt << ", and MET = " << metMet << ", and ele_pt = " << leadingEle->pt());
+       }
      }
 
      if (isWmunu) {
@@ -821,6 +832,24 @@ StatusCode DQTGlobalWZFinderTool::fillHistograms()
 	 }
      }
 
+
+     //for(int i=0; i<goodelectrons.size(); i++) delete goodelectrons[i];
+     //for(int i=0; i<goodmuonsZ.size(); i++)    delete goodmuonsZ[i];
+     //for(int i=0; i<allElectrons.size(); i++)  delete allElectrons[i];
+     goodelectrons.clear();
+     goodmuonsZ.clear();
+     allElectrons.clear();
+
+
+/*
+     delete leadingEle;
+     delete subleadingEle;
+     delete leadingMuZ;
+     delete subleadingMuZ;
+     delete leadingAllEle;
+     delete subleadingAllEle;
+*/
+
      return sc;
  
   } //end ifBeam  
@@ -910,8 +939,7 @@ StatusCode DQTGlobalWZFinderTool::procHistograms( )
 }
 
 //------ Electron START ------//
-void DQTGlobalWZFinderTool::doEleTriggerTP(const xAOD::Electron* el1, 
-					   const xAOD::Electron* el2) {
+void DQTGlobalWZFinderTool::doEleTriggerTP(const xAOD::Electron* el1, const xAOD::Electron* el2, bool os, bool ss) {
   int matched = 0;
   std::vector<const xAOD::Electron*> electrons{el1, el2}; 
   for (const auto el: electrons) {
@@ -922,7 +950,10 @@ void DQTGlobalWZFinderTool::doEleTriggerTP(const xAOD::Electron* el1,
       }
     }
   }
-  m_eltrigtp_matches->Fill(matched,m_evtWeight);
+
+  if(os) m_eltrigtp_matches_os->Fill(matched, m_evtWeight);
+  if(ss) m_eltrigtp_matches_ss->Fill(matched, m_evtWeight);
+
 }
 
 void 
@@ -940,17 +971,16 @@ DQTGlobalWZFinderTool::doEleTP(const xAOD::Electron* leadingAllEle,
       auto p2(subleadingAllEle->p4());  
       Float_t mass = (p1+p2).M();
 
-      bool leading_good = goodElectrons(thisEventInfo, leadingAllEle, pVtx, isBad);
-      bool subleading_good = goodElectrons(thisEventInfo, subleadingAllEle, pVtx, isBad);
+      bool leadingPassKinematics = kinematicCuts(leadingAllEle);
+      bool subleadPassKinematics = kinematicCuts(subleadingAllEle);
 
+      if(!leadingPassKinematics || !subleadPassKinematics) return;
 
-      // even if the probe is a bad electron, we still require it to not be in the gap
-      // the no gap is built into the good criterion anyway so don't need it for good electrons
-      bool leading_probe_nogap = fabs((leadingAllEle)->caloCluster()->etaBE(2)) > 1.37 &&
-         	    		 fabs((leadingAllEle)->caloCluster()->etaBE(2)) < 1.52; 
+      bool leading_good    = goodElectrons(thisEventInfo, leadingAllEle, pVtx, isBad);
+      bool subleading_good = goodElectrons(thisEventInfo, subleadingAllEle, pVtx, isBad);
 
-      bool subleading_probe_nogap = fabs((subleadingAllEle)->caloCluster()->etaBE(2)) > 1.37 &&
-                                    fabs((subleadingAllEle)->caloCluster()->etaBE(2)) < 1.52;
+      bool leading_antigood    = antiGoodElectrons(thisEventInfo, leadingAllEle, pVtx, isBad);
+      bool subleading_antigood = antiGoodElectrons(thisEventInfo, subleadingAllEle, pVtx, isBad);
 
       // do trigger matching
       bool leading_trig = false;
@@ -970,49 +1000,114 @@ DQTGlobalWZFinderTool::doEleTP(const xAOD::Electron* leadingAllEle,
       }
 
       // now start to do the selection
-      if( leadingAllEle->passSelection("LHTight") && leading_trig && leading_good ){
+      if(leadingAllEle->passSelection("LHTight") && leading_trig && leading_good){
         if(Zeecharge==0){
-          if(subleading_good){
-             m_ele_tight_good_os->Fill(mass); // leading tight, subleading good, opposite sign
-           }else{
-	      if(!subleading_probe_nogap) {
-                m_ele_tight_bad_os->Fill(mass); // leading tight, subleading fail, opposite sign
-	      }
-            }
-        } else{
-          if(subleading_good){
-            m_ele_tight_good_ss->Fill(mass); // leading tight, subleading good, same sign
-          }else{
-	     if(!subleading_probe_nogap){
-               m_ele_tight_bad_ss->Fill(mass); // leading tight, subleading fail, same sign
-	     }
-           }
-        }
-      }
-
-      if( subleadingAllEle->passSelection("LHTight") && subleading_trig && subleading_good ){
+          (subleading_good) ? m_ele_tight_good_os->Fill(mass) : m_ele_tight_bad_os->Fill(mass); 
+          if(subleading_antigood) m_ele_template_os->Fill(mass);
+        }else{
+          (subleading_good) ? m_ele_tight_good_ss->Fill(mass) : m_ele_tight_bad_ss->Fill(mass);  
+          if(subleading_antigood) m_ele_template_ss->Fill(mass);
+         }  
+      }  
+
+      if(subleadingAllEle->passSelection("LHTight") && subleading_trig && subleading_good){
         if(Zeecharge==0){
-          if(leading_good){
-             m_ele_tight_good_os->Fill(mass); // subleading tight, leading good, opposite sign
-           }else{
-	      if(!leading_probe_nogap){
-                m_ele_tight_bad_os->Fill(mass); // subleading tight, leading fail, opposite sign
-	      }
-            }
-        } else{
-          if(leading_good){
-            m_ele_tight_good_ss->Fill(mass); // subleading tight, leading good, same sign
-          }else{
-	     if(!leading_probe_nogap){
-               m_ele_tight_bad_ss->Fill(mass); // subleading tight, leading fail, same sign
-	     }
-          }
+          (leading_good) ? m_ele_tight_good_os->Fill(mass) : m_ele_tight_bad_os->Fill(mass);   
+          if(leading_antigood) m_ele_template_os->Fill(mass);
+        }else{
+          (leading_good) ? m_ele_tight_good_ss->Fill(mass) : m_ele_tight_bad_ss->Fill(mass); 
+          if(leading_antigood) m_ele_template_ss->Fill(mass);
         }
       } 
+
     }// subleading pointer exixts
   }// leading pointer exists
 }// end doEleTP
 
+
+void DQTGlobalWZFinderTool::doEleContainerTP(std::vector<const xAOD::Electron*> allElectrons, std::vector<const xAOD::Electron*> goodelectrons) {
+  const xAOD::PhotonContainer* Photons(0);
+  evtStore()->retrieve(Photons, "Photons");
+  if (!Photons) {
+    ATH_MSG_FATAL("Unable to retrieve photons to do electron T&P");
+    return;
+  }
+
+  for (const auto& tagEl : goodelectrons) {
+    bool matched = false;
+    for (const auto chain: m_Z_ee_trigger) {
+      if (m_elTrigMatchTool->match(tagEl, chain) || ! m_doTrigger) {
+        matched=true;
+        break;
+      }
+    }
+
+    if (!matched) continue;
+    auto tagElp4(tagEl->p4());
+    
+    if (tagEl->passSelection("LHTight")){
+      for (const auto& el2 : allElectrons){
+        if (el2 != tagEl && kinematicCuts(el2)){
+          auto probeElp4(el2->p4());
+          Float_t mass = (tagElp4+probeElp4).M();
+          m_ele_tight_passkine->Fill(mass);
+          break;
+        }
+      }
+    }
+
+
+    for (const auto& photon : *Photons) {
+      auto photonp4(photon->p4());
+      Float_t mass = (tagElp4+photonp4).M();
+
+      bool passPT  = (photon->pt() >  m_muonPtCut*GeV);      
+      bool passEta = true;
+      if (fabs(photon->eta()) > 2.4) 
+        passEta = false;
+      if (fabs(photon->eta()) > 1.37 && fabs(photon->eta()) < 1.52) 
+        passEta = false;
+      //bool inMassWindow = (mass > m_zCutLow*GeV && mass < m_zCutHigh*GeV);
+
+      if (!passPT || !passEta) // || !inMassWindow) 
+        continue;
+
+      for (const auto& el2 : allElectrons){
+        // slightly relax pT cut for probe electron
+        bool passKinematics = true;
+        if (el2->pt() < (m_muonPtCut-2)*GeV)
+          passKinematics = false; 
+        if (fabs((el2)->caloCluster()->etaBE(2)) > 2.4)
+          passKinematics = false;
+        if (fabs((el2)->caloCluster()->etaBE(2)) > 1.37 && fabs((el2)->caloCluster()->etaBE(2)) < 1.52)
+          passKinematics = false;
+
+        double deltaR = (el2->p4()).DeltaR(photon->p4());        
+        if (!passKinematics || tagEl == el2 || deltaR < 0.1) 
+          continue;       
+
+        m_elContainertp_nomatch->Fill(mass);
+        break;
+        
+      }
+    }
+  }
+}
+
+
+bool DQTGlobalWZFinderTool::kinematicCuts(const xAOD::Electron* electron){
+
+  bool isGood = true;
+  if(electron->pt() < m_muonPtCut*GeV) isGood = false;
+
+  if(fabs((electron)->caloCluster()->etaBE(2)) > 2.4) isGood = false;
+  
+  if(fabs((electron)->caloCluster()->etaBE(2)) > 1.37 && 
+     fabs((electron)->caloCluster()->etaBE(2)) < 1.52) isGood = false;
+
+  return isGood;
+}
+
 bool DQTGlobalWZFinderTool::goodElectrons(const xAOD::EventInfo* thisEventInfo, 
     		   		          const xAOD::Electron* electron_itr, 
 		  			  const xAOD::Vertex* pVtx, 
@@ -1049,8 +1144,7 @@ bool DQTGlobalWZFinderTool::goodElectrons(const xAOD::EventInfo* thisEventInfo,
        fabs((elTrk->z0()+elTrk->vz()-pVtx->z())*std::sin(elTrk->theta())) < 0.5*mm &&
        !isBad
      ){    // electron dead zone
-    if ( fabs((electron_itr)->caloCluster()->etaBE(2)) > 1.37 &&
-         fabs((electron_itr)->caloCluster()->etaBE(2)) < 1.52 ){
+    if (fabs((electron_itr)->caloCluster()->etaBE(2)) > 1.37 && fabs((electron_itr)->caloCluster()->etaBE(2)) < 1.52 ){
       isGood = false;
     } else{
       isGood = true;
@@ -1059,7 +1153,51 @@ bool DQTGlobalWZFinderTool::goodElectrons(const xAOD::EventInfo* thisEventInfo,
   return isGood;
 }
 
-//------ Electron END ------//
+
+bool DQTGlobalWZFinderTool::antiGoodElectrons(const xAOD::EventInfo* thisEventInfo,
+                                              const xAOD::Electron* electron_itr,
+                                              const xAOD::Vertex* pVtx,
+                                              bool isBad){
+  bool antiGood = false;
+  Float_t m_electronEtCut = m_muonPtCut;
+
+  bool passID  = electron_itr->passSelection("LHLoose"); 
+  bool passIso = m_isolationSelectionTool->accept(*electron_itr);
+  auto elTrk = (electron_itr)->trackParticle();
+
+  if (!elTrk) {
+    return false;
+  }
+  float d0sig;
+  try {
+    d0sig = xAOD::TrackingHelpers::d0significance(elTrk, thisEventInfo->beamPosSigmaX(), thisEventInfo->beamPosSigmaY(), thisEventInfo->beamPosSigmaXY());
+  } catch (...) {
+     ATH_MSG_DEBUG("Invalid beamspot - electron");
+    try {
+      d0sig = xAOD::TrackingHelpers::d0significance(elTrk);
+    } catch (...) {
+       ATH_MSG_WARNING("Ridiculous exception thrown - electron");
+      return false;
+    }
+  }
+
+  // pass basic selection, except ID+Isolation
+  if (((electron_itr)->pt() > m_electronEtCut*GeV) &&
+     fabs((electron_itr)->caloCluster()->etaBE(2)) < 2.4 &&
+     fabs(d0sig) < 5 &&
+     pVtx &&
+     fabs((elTrk->z0()+elTrk->vz()-pVtx->z())*std::sin(elTrk->theta())) < 0.5*mm &&
+     !isBad)
+  { 
+    if(fabs((electron_itr)->caloCluster()->etaBE(2)) > 1.37 && fabs((electron_itr)->caloCluster()->etaBE(2)) < 1.52){
+      antiGood = false;
+    }else{
+      if(!passID && !passIso) antiGood = true;
+    }
+  }
+
+  return antiGood;
+}
 
 
 // Compute trigger efficiencies
@@ -1077,9 +1215,6 @@ void DQTGlobalWZFinderTool::doMuonTriggerTP(const xAOD::Muon* mu1, const xAOD::M
     }
   }
   m_mutrigtp_matches->Fill(matched,m_evtWeight); 
-  if(mu1->pt() > m_muonPtCut_CMS && mu2->pt() > m_muonPtCut_CMS){
-    m_mutrigtp_matches_CMS->Fill(matched,m_evtWeight); 
-  } 
 }
 
 void DQTGlobalWZFinderTool::doMuonTruthEff(std::vector<const xAOD::Muon*>& goodmuonsZ) {
@@ -1142,14 +1277,8 @@ void DQTGlobalWZFinderTool::doMuonLooseTP(std::vector<const xAOD::Muon*>& goodmu
 	    ATH_MSG_DEBUG("MATCH WOOO, authors " << mu2->allAuthors());
 	    if (trk->charge() != tagmu->charge()) {
 	      m_muloosetp_match_os->Fill(mass); 
-	      if(tagmu->pt() > m_muonPtCut_CMS && trk->pt() > m_muonPtCut_CMS){ 
-		m_muloosetp_match_os_CMS->Fill(mass); 
-	      } 
 	    } else { 
 	      m_muloosetp_match_ss->Fill(mass); 
-              if(tagmu->pt() > m_muonPtCut_CMS && trk->pt() > m_muonPtCut_CMS){
-                m_muloosetp_match_ss_CMS->Fill(mass);  
-              } 
 	    }
 	    matched = true;
 	    break;
@@ -1162,21 +1291,65 @@ void DQTGlobalWZFinderTool::doMuonLooseTP(std::vector<const xAOD::Muon*>& goodmu
 	  ATH_MSG_DEBUG("idtrk pt " << trk->pt()/GeV);
 	  if (trk->charge() != tagmu->charge()) {
               m_muloosetp_nomatch_os->Fill(mass); 
-              if(tagmu->pt() > m_muonPtCut_CMS && trk->pt() > m_muonPtCut_CMS){
-                m_muloosetp_nomatch_os_CMS->Fill(mass);
-              }
 	  } else { 
               m_muloosetp_nomatch_ss->Fill(mass); 
-              if(tagmu->pt() > m_muonPtCut_CMS && trk->pt() > m_muonPtCut_CMS){
-                m_muloosetp_nomatch_ss_CMS->Fill(mass);
-	    }  
 	  }
 	}
       }
     }
   }
 }
-  
+ 
+
+
+void DQTGlobalWZFinderTool::doMuonInDetTP(std::vector<const xAOD::Muon*>& goodmuonsZ, const xAOD::Vertex* pVtx) {
+  const xAOD::EventInfo* thisEventInfo;
+  StatusCode sc = evtStore()->retrieve(thisEventInfo);
+  const xAOD::TrackParticleContainer* msTracks(0);                                                                                                                                                             evtStore()->retrieve(msTracks, "ExtrapolatedMuonTrackParticles");
+  if (!msTracks) {
+    ATH_MSG_FATAL("Unable to retrieve MS tracks to do muon T&P");
+    return;
+  }
+
+  for (const auto& tagmu : goodmuonsZ) {
+    bool matched = false;
+    for (const auto chain: m_Z_mm_trigger) {
+      if (m_muTrigMatchTool->match(tagmu, chain) || ! m_doTrigger) {
+        matched=true;
+        break;
+      }
+    }
+
+    if (!matched) continue;
+    auto tagmup4(tagmu->p4());
+    for (const auto& trk : *msTracks) {
+      if (trk->pt() <  m_muonPtCut*GeV || fabs(trk->eta()) > m_muonMaxEta) continue;
+      
+      auto trkp4(trk->p4());
+      Float_t mass = (tagmup4+trkp4).M();
+      bool matched = false;
+
+      if (mass < m_zCutLow*GeV || mass > m_zCutHigh*GeV) continue;
+      for (const auto& mu2 : goodmuonsZ) {
+        auto mslink = mu2->extrapolatedMuonSpectrometerTrackParticleLink();
+        if (*(mslink.cptr()) == trk) {
+          (trk->charge() != tagmu->charge()) ? static_cast<void>(m_mu_InDet_tp_match_os->Fill(mass)) : static_cast<void>(m_mu_InDet_tp_match_ss->Fill(mass));
+          matched = true;
+          break;
+        }
+      }
+
+      if (!matched)  
+        (trk->charge() != tagmu->charge()) ? static_cast<void>(m_mu_InDet_tp_nomatch_os->Fill(mass)) : static_cast<void>(m_mu_InDet_tp_nomatch_ss->Fill(mass));      
+
+    }
+  }
+}
+
+
+
+
+
 //----------------------------------------------------------------------------------
 StatusCode DQTGlobalWZFinderTool::checkHists(bool /* fromFinalize */)
 //----------------------------------------------------------------------------------
diff --git a/DataQuality/GoodRunsLists/Root/RegularFormula.cxx b/DataQuality/GoodRunsLists/Root/RegularFormula.cxx
index 3e895e244679fda9138876c3419695ceea5bd3b3..f1ea555ca6179d1e7c6857fb9ab1f95052b385a4 100644
--- a/DataQuality/GoodRunsLists/Root/RegularFormula.cxx
+++ b/DataQuality/GoodRunsLists/Root/RegularFormula.cxx
@@ -75,6 +75,7 @@ Root::RegularFormula::parseExpression(const char* expression, TString& expr)
 
   TMsgLogger mylogger( "RegularFormula" );
   mylogger << kINFO << "Now parsing regular expression : " << expression << GEndl;
+  mylogger << kINFO << "Please be aware that Cling errors are expected and aren't inherently a problem" << GEndl;
 
   TFormula analyzer("analyzer","1");
   TObjArray* parArr = parStr.Tokenize(" ");
diff --git a/DataQuality/ZLumiScripts/scripts/dqt_zlumi_alleff_HIST.py b/DataQuality/ZLumiScripts/scripts/dqt_zlumi_alleff_HIST.py
index ecb4f90b5e0d2951ee40585e766dd2da7ca9b252..169c7671d6a75ac2b7fa0d3f01072d6deba6a3f7 100755
--- a/DataQuality/ZLumiScripts/scripts/dqt_zlumi_alleff_HIST.py
+++ b/DataQuality/ZLumiScripts/scripts/dqt_zlumi_alleff_HIST.py
@@ -2,29 +2,262 @@
 # Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration                   
 import sys, os, glob
 import ROOT
-
+import argparse
+import math
+from array import array
+from DQUtils import fetch_iovs
+ROOT.gROOT.SetBatch(ROOT.kTRUE)
 ROOT.gStyle.SetOptStat(0)
 
-#ACCEPTANCE = 3.173927e-01
-ACCEPTANCE = 3.323224e-01
-
-import argparse
 parser = argparse.ArgumentParser()
 parser.add_argument('infile', type=str, help='input HIST file')
 parser.add_argument('--out', type=str, help='output ROOT file')
-parser.add_argument('--plotdir', type=str, help='Directory to dump plots',
-                    default='plots')
+parser.add_argument('--plotdir', type=str, help='Directory to dump plots', default='plots')
 parser.add_argument('--debug', action='store_true', help='Be verbose in output')
 parser.add_argument('--mode', type=str, help='Zee or Zmumu')
-
-
 args = parser.parse_args()
 
 infilename = args.infile
-infile = ROOT.TFile.Open(infilename, 'READ')
+runmode    = args.mode
+infile     = ROOT.TFile.Open(infilename, 'READ')
+
+
+def trigTP(h, effcyt):
+    yld    = (h[2], h[3])
+    ylderr = (h.GetBinError(2), h.GetBinError(3))
+    A, B   = yld
+    o_z_one[0], o_z_two[0] = yld
+
+    if B == 0:
+        return
+
+    eff = 1./(float(A)/B/2.+1.)
+    inverrsq = ((1/2./B)*ylderr[0])**2+((A/2./B**2)*ylderr[1])**2
+    o_trigeff[0] = eff
+    o_trigeffstat[0] = (inverrsq**.5)*(eff**2)
+    o_run[0], o_lb[0] = int(runname[4:]), lbnum
+    try:
+        iov = lblb[int(runname[4:])][lbnum-1]
+        o_lbwhen[0], o_lbwhen[1] = iov.StartTime/1e9, iov.EndTime/1e9
+    except Exception, e:
+        o_lbwhen[0], o_lbwhen[1] = 0, 0
+
+    effcyt.SetBinContent(lbnum-lbnums[0]+1, eff)
+    effcyt.SetBinError(lbnum-lbnums[0]+1, o_trigeffstat[0])
+
+
+def recoTP(hmo, hms, hno, hns, effcyr):
+    bin1 = hmo.GetXaxis().FindBin(86000)
+    bin2 = hmo.GetXaxis().FindBin(95000)
+    matchos, matchoserr = extract(hmo, bin1, bin2)
+    matchss, matchsserr = extract(hms, bin1, bin2)
+    nomatchos, nomatchoserr = extract(hno, bin1, bin2)
+    nomatchss, nomatchsserr = extract(hns, bin1, bin2)
+
+    A    = float(matchos-matchss)
+    Aerr = (matchoserr**2+matchsserr**2)**.5
+    B    = float(nomatchos-nomatchss)
+    Berr = (nomatchoserr**2+nomatchsserr**2)**.5
+
+    if Berr == 0:
+        Berr = 1.
+    if A == 0 or B/A == -1:
+        eff = 1.
+        inverrsq = 1.
+    else:
+        eff = 1./(1.+B/A)
+        inverrsq = ((-B/A**2)*Aerr)**2+((1./A)*Berr)**2
+
+    o_recoeff[0]     = eff
+    o_recoeffstat[0] = (inverrsq**.5)*(eff**2)
+    effcyr.SetBinContent(lbnum-lbnums[0]+1, eff)
+    effcyr.SetBinError(lbnum-lbnums[0]+1, o_recoeffstat[0])
+
+
+def templateMethod(hmo, hms, hno, hns, hto, hts, effcyr):
+    noSign  = False
+    doScale = False    
+    
+    tbin1 = hmo.GetXaxis().FindBin(75000)
+    tbin2 = hmo.GetXaxis().FindBin(104000)
+    tbin3 = hmo.GetXaxis().FindBin(120000)
+    tbin4 = hmo.GetXaxis().FindBin(250000)
+
+    scale_os = 1 if hto.Integral(tbin3, tbin4) == 0 else hno.Integral(tbin3, tbin4)/hto.Integral(tbin3, tbin4)
+    scale_ss = 1 if hts.Integral(tbin3, tbin4) == 0 else hns.Integral(tbin3, tbin4)/hts.Integral(tbin3, tbin4)
+
+    if doScale == True:
+        hto.Scale(scale_os)
+        hts.Scale(scale_ss)
+
+    if noSign == True:
+        hmo.Add(hms)
+        hno.Add(hns)
+        hto.Add(hts)
+
+    matchos_peak, matchos_peakerr = extract(hmo, tbin1, tbin2)
+    matchos_tail, matchos_tailerr = extract(hmo, tbin3, tbin4)
+    matchss_peak, matchss_peakerr = extract(hms, tbin1, tbin2)
+    matchss_tail, matchss_tailerr = extract(hms, tbin3, tbin4)
+
+    nomatchos_peak, nomatchos_peakerr = extract(hno, tbin1, tbin2)
+    nomatchos_tail, nomatchos_tailerr = extract(hno, tbin3, tbin4)
+    nomatchss_peak, nomatchss_peakerr = extract(hns, tbin1, tbin2)
+    nomatchss_tail, nomatchss_tailerr = extract(hns, tbin3, tbin4)
+
+    templateos_peak, templateos_peakerr = extract(hto, tbin1, tbin2)
+    templateos_tail, templateos_tailerr = extract(hto, tbin3, tbin4)
+    templatess_peak, templatess_peakerr = extract(hts, tbin1, tbin2)
+    templatess_tail, templatess_tailerr = extract(hts, tbin3, tbin4)
+
+    totalos_peak = matchos_peak + nomatchos_peak
+    totalos_tail = matchos_tail + nomatchos_tail
+    totalss_peak = matchss_peak + nomatchss_peak
+    totalss_tail = matchss_tail + nomatchss_tail
+
+    totalos_peakerr = pow(totalos_peak, 0.5)
+    totalss_peakerr = pow(totalss_peak, 0.5)
+    totalos_tailerr = pow(totalos_tail, 0.5)
+    totalss_tailerr = pow(totalss_tail, 0.5)
+
+    if templatess_tail == 0:    
+        eff = 1
+        err = 1
+    elif templateos_tail == 0:
+        eff = 1
+        err = 1
+
+    if templatess_tail != 0 and templateos_tail != 0:
+        n1 = matchos_peak
+        n2 = (templateos_peak*matchss_tail)/templatess_tail
+        d1 = totalos_peak
+        d2 = (nomatchos_tail*templateos_peak)/templateos_tail
+
+        eff = (n1 - n2)/(d1- d2)
+        err = templateTPErr(hmo, hms, hno, hns, hto, hts)   
+ 
+
+    o_recoeff[0]     = eff
+    o_recoeffstat[0] = err
+    effcyr.SetBinContent(lbnum-lbnums[0]+1, eff)
+    effcyr.SetBinError(lbnum-lbnums[0]+1, err)
+
+
+def templateTPErr(hmo, hms, hno, hns, hto, hts):
+    bin1 = hmo.GetXaxis().FindBin(75000)
+    bin2 = hmo.GetXaxis().FindBin(104000)
+    bin3 = hmo.GetXaxis().FindBin(120000)
+    bin4 = hmo.GetXaxis().FindBin(250000)
+
+    a, da = extract(hmo, bin1, bin2)
+    b, db = extract(hms, bin3, bin4)
+    c, dc = extract(hto, bin1, bin2)
+    d, dd = extract(hts, bin3, bin4)
+    e, de = extract(hno, bin1, bin2)
+    f, df = extract(hno, bin3, bin4)
+    g, dg = extract(hto, bin3, bin4)
+
+    try:
+        dva = 1/(a-(c*f)/g+e)-(a-(b*c)/d)/(a-(c*f)/g+e)**2
+    except ZeroDivisionError:
+        return 1
+    try:
+        dvb = -c/(d*(-(c*f)/g+a+e))
+    except ZeroDivisionError:
+        return 1    
+    try:
+        dvc = -(g*((a+e)*b*g-a*d*f))/(d*(f*c+(-a-e)*g)**2)
+    except ZeroDivisionError:
+        return 1
+    try:
+        dvd = (b*c)/((-(c*f)/g+a+e)*d**2)
+    except ZeroDivisionError:
+        return 1
+    try:
+        dve = -(a-(b*c)/d)/(e-(c*f)/g+a)**2
+    except ZeroDivisionError:
+        return 1
+    try:
+        dvf = (c*(a-(b*c)/d))/(g*(-(c*f)/g+a+e)**2)
+    except ZeroDivisionError:
+        return 1
+    try:
+        dvg = -(c*(a*d-b*c)*f)/(d*((a+e)*g-c*f)**2)
+    except ZeroDivisionError:
+        return 1
+
+    verbose = False
+    if verbose == True:
+        print a, da, dva, da*dva
+        print b, db, dvb, db*dvb
+        print c, dc, dvc, dc*dvc
+        print d, dd, dvd, dd*dvd
+        print e, de, dve, de*dve
+        print f, df, dvf, df*dvf
+        print g, dg, dvg, dg*dvg
+
+    err = math.sqrt((da*dva)**2 + (db*dvb)**2 + (dc*dvc)**2 + (dd*dvd)**2 + (de*dve)**2 + (df*dvf)**2 + (dg*dvg)**2)
+    return err
+
+def containerEff(h_photon, h_pass, h_tos, h_tss):
+    h_temp = h_tos.Clone()
+    h_temp.Add(h_tss)
+
+    bin1 = h_pass.FindBin(120000)
+    bin2 = h_pass.FindBin(250000)
+    scale = h_pass.Integral(bin1, bin2)/h_temp.Integral(bin1, bin2)
+    h_temp.Scale(scale)
+
+    scale_err = scale*pow((1/math.sqrt(h_pass.Integral(bin1, bin2)))+(1/math.sqrt(h_temp.Integral(bin1, bin2))), 0.5)
+
+    bin66  = h_photon.FindBin(66000)
+    bin75  = h_photon.FindBin(75000)
+    bin100 = h_photon.FindBin(100000)
+    bin116 = h_photon.FindBin(116000)
+
+    N6675   = h_photon.Integral(bin66, bin75)
+    N100116 = h_photon.Integral(bin100, bin116)
+    sigW    = 96 - 86
+    bkgW    = (75 - 66) + (116 - 100)
+
+    bkg = (N6675+N100116)*sigW/bkgW
+    bkgerr = pow(N6675 + N100116, 0.5)*sigW/bkgW
+
+    bin86 = h_pass.FindBin(86000)
+    bin96 = h_pass.FindBin(96000)
+    
+    passval = h_pass.Integral(bin86, bin96) - h_temp.Integral(bin86, bin96)
+    passerr = pow(h_pass.Integral(bin86, bin96) + math.sqrt(scale)*h_temp.Integral(bin86, bin96) + scale_err**2, 0.5)
+   
+    photonval = h_photon.Integral(bin86, bin96)
+    photonerr = pow(h_photon.Integral(bin86, bin96), 0.5)
+
+    a = passval
+    b = photonval
+    c = bkg
+    da = passerr
+    db = photonerr
+    dc = bkgerr
+
+    dda = (b-c)/(b-c+a)**2
+    ddb = -a/(b-c+a)**2
+    ddc = a/(b-c+a)**2
+
+    o_conteff[0] = passval/(photonval - bkg + passval)
+    o_conteffstat[0] = math.sqrt((da*dda)**2 + (db*ddb)**2 + (dc*ddc)**2)
+
+
+def extract(histogram, bin1, bin2):
+    dbl     = ROOT.Double()
+    rv1     = histogram.IntegralAndError(bin1, bin2, dbl)
+    return (rv1, float(dbl))
+
 
-runmode = args.mode
 print 'Running in', runmode, 'mode'
+if runmode == "Zmumu":
+    ACCEPTANCE = 0.3323224
+if runmode == "Zee":
+    ACCEPTANCE = 0.2996
 
 
 runname = None
@@ -46,30 +279,28 @@ for k in infile.Get(runname).GetListOfKeys():
 print 'Now to dump'
 lbnums = sorted([int(_[3:]) for _ in lbdirs])
 
-effcyt = ROOT.TH1F('effcyt', 'Trigger efficiency', lbnums[-1]-lbnums[0]+1, lbnums[0]-0.5, 
-               lbnums[-1]+0.5)
-effcyr = ROOT.TH1F('effcyr', 'Loose muon reco efficiency', lbnums[-1]-lbnums[0]+1, lbnums[0]-0.5, 
-               lbnums[-1]+0.5)
-effcya = ROOT.TH1F('effcya', 'Combined acc x efficiency', lbnums[-1]-lbnums[0]+1, lbnums[0]-0.5, 
-               lbnums[-1]+0.5)
-effcydir = ROOT.TH1F('effcydir', 'Direct acc x efficiency', lbnums[-1]-lbnums[0]+1, lbnums[0]-0.5, 
-               lbnums[-1]+0.5)
+effcyt   = ROOT.TH1F('effcyt', 'Trigger efficiency', lbnums[-1]-lbnums[0]+1, lbnums[0]-0.5, lbnums[-1]+0.5)
+effcyr   = ROOT.TH1F('effcyr', 'Loose electron reco efficiency', lbnums[-1]-lbnums[0]+1, lbnums[0]-0.5, lbnums[-1]+0.5)
+effcya   = ROOT.TH1F('effcya', 'Combined acc x efficiency', lbnums[-1]-lbnums[0]+1, lbnums[0]-0.5, lbnums[-1]+0.5)
+effcydir = ROOT.TH1F('effcydir', 'Direct acc x efficiency', lbnums[-1]-lbnums[0]+1, lbnums[0]-0.5, lbnums[-1]+0.5)
 
-from array import array
 fout = ROOT.TFile(args.out if args.out else '%s_all.root' % runname[4:], 'RECREATE')
-o_run = array('I', [0])
-o_lb = array('I', [0])
-o_lbwhen = array('d', [0., 0.])
-o_z_one = array('f', [0.])
-o_z_two = array('f', [0.])
-o_trigeff = array('f', [0.])
-o_trigeffstat = array('f', [0.])
-o_recoeff = array('f', [0.])
-o_recoeffstat = array('f', [0.])
-o_alleff = array('f', [0.])
-o_alleffstat = array('f', [0.])
-o_ae = array('f', [0.])
-o_aestat = array('f', [0.])
+o_run           = array('I', [0])
+o_lb            = array('I', [0])
+o_lbwhen        = array('d', [0., 0.])
+o_z_one         = array('f', [0.])
+o_z_two         = array('f', [0.])
+o_trigeff       = array('f', [0.])
+o_trigeffstat   = array('f', [0.])
+o_recoeff       = array('f', [0.])
+o_recoeffstat   = array('f', [0.])
+o_conteff       = array('f', [0.])
+o_conteffstat   = array('f', [0.])
+o_alleff        = array('f', [0.])
+o_alleffstat    = array('f', [0.])
+o_ae            = array('f', [0.])
+o_aestat        = array('f', [0.])
+
 tl = ROOT.TTree( 'lumitree', 'Luminosity tree' )
 tl.Branch('run', o_run, 'run/i')
 tl.Branch('lb', o_lb, 'lb/i')
@@ -80,83 +311,47 @@ tl.Branch('trigeff', o_trigeff, 'trigeff/F')
 tl.Branch('trigeffstat', o_trigeffstat, 'trigeffstat/F')
 tl.Branch('recoeff', o_recoeff, 'recoeff/F')
 tl.Branch('recoeffstat', o_recoeffstat, 'recoeffstat/F')
+tl.Branch('conteff', o_conteff, 'conteff/F')
+tl.Branch('conteffstat', o_conteffstat, 'conteffstat/F')
 tl.Branch('alleff', o_alleff, 'alleff/F')
 tl.Branch('alleffstat', o_alleffstat, 'alleffstat/F')
 tl.Branch('ae', o_ae, 'ae/F')
 tl.Branch('aestat', o_aestat, 'aestat/F')
 
 
-from DQUtils import fetch_iovs
-#rset=set(_[0] for _ in rlb)
-#print rset
 lblb = fetch_iovs("LBLB", runs=int(runname[4:])).by_run
 for lb in sorted(lbdirs):
     if runmode == "Zee":
-        h = infile.Get('%s/%s/GLOBAL/DQTGlobalWZFinder/m_eltrigtp_matches' % (runname, lb))
+        h = infile.Get('%s/%s/GLOBAL/DQTGlobalWZFinder/m_eltrigtp_matches_os' % (runname, lb))
         hmo = infile.Get('%s/%s/GLOBAL/DQTGlobalWZFinder/m_ele_tight_good_os' % (runname, lb))
         hms = infile.Get('%s/%s/GLOBAL/DQTGlobalWZFinder/m_ele_tight_good_ss' % (runname, lb))
         hno = infile.Get('%s/%s/GLOBAL/DQTGlobalWZFinder/m_ele_tight_bad_os' % (runname, lb))
         hns = infile.Get('%s/%s/GLOBAL/DQTGlobalWZFinder/m_ele_tight_bad_ss' % (runname, lb))
+        hto = infile.Get('%s/%s/GLOBAL/DQTGlobalWZFinder/m_ele_template_os' % (runname, lb))
+        hts = infile.Get('%s/%s/GLOBAL/DQTGlobalWZFinder/m_ele_template_ss' % (runname, lb))
+        hphoton = infile.Get("%s/%s/GLOBAL/DQTGlobalWZFinder/m_elContainertp_nomatch" % (runname, lb))
+        hpass   = infile.Get("%s/%s/GLOBAL/DQTGlobalWZFinder/m_ele_tight_passkine" % (runname, lb))
+
     if runmode == "Zmumu":
         h = infile.Get('%s/%s/GLOBAL/DQTGlobalWZFinder/m_mutrigtp_matches' % (runname, lb))
         hmo = infile.Get('%s/%s/GLOBAL/DQTGlobalWZFinder/m_muloosetp_match_os' % (runname, lb))
         hms = infile.Get('%s/%s/GLOBAL/DQTGlobalWZFinder/m_muloosetp_match_ss' % (runname, lb))
         hno = infile.Get('%s/%s/GLOBAL/DQTGlobalWZFinder/m_muloosetp_nomatch_os' % (runname, lb))
         hns = infile.Get('%s/%s/GLOBAL/DQTGlobalWZFinder/m_muloosetp_nomatch_ss' % (runname, lb))
-    lbnum = int(lb[3:])
-    yld = (h[2], h[3])
-    ylderr = (h.GetBinError(2), h.GetBinError(3))
-    #print yld, ylderr
-    A, B = yld
-    o_z_one[0], o_z_two[0] = yld
-    if B == 0: continue
-    eff = 1./(float(A)/B/2.+1.)
-    inverrsq = ((1/2./B)*ylderr[0])**2+((A/2./B**2)*ylderr[1])**2
-    o_trigeff[0] = eff
-    o_trigeffstat[0] = (inverrsq**.5)*(eff**2)
-    o_run[0], o_lb[0] = int(runname[4:]), lbnum
-    try:
-        iov = lblb[int(runname[4:])][lbnum-1]
-        o_lbwhen[0], o_lbwhen[1] = iov.StartTime/1e9, iov.EndTime/1e9
-    except Exception, e:
-        o_lbwhen[0], o_lbwhen[1] = 0, 0
-    effcyt.SetBinContent(lbnum-lbnums[0]+1, eff)
-    effcyt.SetBinError(lbnum-lbnums[0]+1, o_trigeffstat[0])
 
-    def extract(histogram):
-        dbl = ROOT.Double()
-        rv1 = histogram.IntegralAndError(21, 30, dbl)
-        return (rv1, float(dbl))
-    matchos, matchoserr = extract(hmo)
-    matchss, matchsserr = extract(hms)
-    nomatchos, nomatchoserr = extract(hno)
-    nomatchss, nomatchsserr = extract(hns)
-    if args.debug:
-        print lb
-        print ' ->', matchos, matchoserr
-        print ' ->', matchss, matchsserr
-        print ' ->', nomatchos, nomatchoserr
-        print ' ->', nomatchss, nomatchsserr
-    A = float(matchos-matchss)
-    Aerr = (matchoserr**2+matchsserr**2)**.5
-    B = float(nomatchos-nomatchss)
-    Berr = (nomatchoserr**2+nomatchsserr**2)**.5
-    if Berr == 0: Berr = 1.
-    if A == 0 or B/A == -1: 
-        eff = 1.
-        inverrsq = 1.
-    else:
-        eff = 1./(1.+B/A)
-        inverrsq = ((-B/A**2)*Aerr)**2+((1./A)*Berr)**2
-    o_recoeff[0] = eff
-    o_recoeffstat[0] = (inverrsq**.5)*(eff**2)
-    effcyr.SetBinContent(lbnum-lbnums[0]+1, eff)
-    effcyr.SetBinError(lbnum-lbnums[0]+1, o_recoeffstat[0])
+    lbnum  = int(lb[3:])
+    trigTP(h, effcyt)
+    if runmode == "Zmumu":
+        recoTP(hmo, hms, hno, hns, effcyr)   
+    elif runmode == "Zee": 
+        templateMethod(hmo, hms, hno, hns, hto, hts, effcyr)
+        containerEff(hphoton, hpass, hto, hts)
 
-    o_ae[0] = ACCEPTANCE*(1-(1-o_trigeff[0])**2)*(o_recoeff[0])**2
-    o_aestat[0] = ACCEPTANCE*((o_recoeff[0]**2*2*(1-o_trigeff[0])*o_trigeffstat[0])**2+(2*o_recoeff[0]*(1-(1-o_trigeff[0])**2)*o_recoeffstat[0])**2)**.5
-    o_alleff[0] = (1-(1-o_trigeff[0])**2)*(o_recoeff[0])**2
+    o_ae[0]         = ACCEPTANCE*(1-(1-o_trigeff[0])**2)*(o_recoeff[0])**2
+    o_aestat[0]     = ACCEPTANCE*((o_recoeff[0]**2*2*(1-o_trigeff[0])*o_trigeffstat[0])**2+(2*o_recoeff[0]*(1-(1-o_trigeff[0])**2)*o_recoeffstat[0])**2)**.5
+    o_alleff[0]     = (1-(1-o_trigeff[0])**2)*(o_recoeff[0])**2
     o_alleffstat[0] = ((o_recoeff[0]**2*2*(1-o_trigeff[0])*o_trigeffstat[0])**2+(2*o_recoeff[0]*(1-(1-o_trigeff[0])**2)*o_recoeffstat[0])**2)**.5
+    
     effcya.SetBinContent(lbnum-lbnums[0]+1, o_ae[0])
     effcya.SetBinError(lbnum-lbnums[0]+1, o_aestat[0])
 
@@ -168,22 +363,24 @@ print 'Done'
 c1 = ROOT.TCanvas()
 effcya.SetMarkerStyle(21)
 effcya.SetMarkerColor(ROOT.kBlue)
-effcya.GetYaxis().SetRangeUser(0.25,0.31)
+effcya.GetYaxis().SetRangeUser(0, 0.4)
 effcya.Draw('PE')
 c1.Print(os.path.join(args.plotdir, '%s_combined_efficiency.eps' % runname[4:]))
 fout.WriteTObject(effcya)
 c1.Clear()
 effcyt.SetMarkerStyle(21)
 effcyt.SetMarkerColor(ROOT.kBlue)
-effcyt.GetYaxis().SetRangeUser(0.66,0.86)
+effcyt.GetYaxis().SetRangeUser(0.6,1.0)
 effcyt.Draw('PE')
+effcyt.Fit('pol1')
 c1.Print(os.path.join(args.plotdir, '%s_trigger_efficiency.eps' % runname[4:]))
 fout.WriteTObject(effcyt)
 c1.Clear()
 effcyr.SetMarkerStyle(21)
 effcyr.SetMarkerColor(ROOT.kBlue)
-effcyr.GetYaxis().SetRangeUser(0.9,1.0)
+effcyr.GetYaxis().SetRangeUser(0.7,1.0)
 effcyr.Draw('PE')
+effcyr.Fit('pol1')
 c1.Print(os.path.join(args.plotdir, '%s_reco_efficiency.eps' % runname[4:]))
 fout.WriteTObject(effcyr)
 fout.Close()
@@ -198,7 +395,6 @@ if sumweights:
     for ibin in xrange(1,sumweights.GetNbinsX()+1):
         o_lb[0] = int(sumweights.GetBinCenter(ibin))
         ctrbin = ctr.FindBin(o_lb[0])
-        print ibin, o_lb[0], sumweights[ibin], ctr[ctrbin]
         if sumweights[ibin] == 0: continue
         p = ctr[ctrbin]/sumweights[ibin]
         o_alleff[0]=p
@@ -209,7 +405,7 @@ if sumweights:
         effcydir.SetBinContent(effcydir.FindBin(o_lb[0]), p)
         effcydir.SetBinError(effcydir.FindBin(o_lb[0]), o_alleffstat[0])
 
-    effcya.GetYaxis().SetRangeUser(0.27,0.31)
+    effcya.GetYaxis().SetRangeUser(0.00,0.50)
     effcya.Draw('PE')
     effcydir.SetMarkerStyle(20)
     effcydir.SetMarkerColor(ROOT.kRed)
@@ -227,3 +423,8 @@ if sumweights:
     effcyrat.Draw('PE')
     effcyrat.Fit('pol1')
     c1.Print(os.path.join(args.plotdir, '%s_tp_correction.eps' % runname[4:]))
+
+
+
+
+
diff --git a/DataQuality/ZLumiScripts/scripts/dqt_zlumi_combine_lumi.py b/DataQuality/ZLumiScripts/scripts/dqt_zlumi_combine_lumi.py
index 9cefac39d98184e18c11aab1d3bb55dac8afad58..e9c800383eaf11c67b58520ef6e013cd545e6bd7 100755
--- a/DataQuality/ZLumiScripts/scripts/dqt_zlumi_combine_lumi.py
+++ b/DataQuality/ZLumiScripts/scripts/dqt_zlumi_combine_lumi.py
@@ -2,40 +2,64 @@
 # Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration                    
 import ROOT
 import sys
-
 import argparse
+from array import array
+
+ROOT.gROOT.SetBatch(ROOT.kTRUE)
+
+
 parser = argparse.ArgumentParser()
 parser.add_argument('recofile', type=str, help='File with per-LB yields')
 parser.add_argument('efffile', type=str, help='File with efficiencies')
 parser.add_argument('outfile', type=str, help='Output file')
-parser.add_argument('--nlb', type=int, help='# of LBs to combine',
-                    default=20)
-args = parser.parse_args()
+parser.add_argument('--nlb', type=int, help='# of LBs to combine', default=20)
+parser.add_argument('--mode', type=str, help='Zee or Zmumu')
+parser.add_argument('--campaign', type=str, help='MC campaign corresponding to year of run')
+
+args          = parser.parse_args()
+runmode       = args.mode
+campaign      = args.campaign
+recozfname    = args.recofile
+effzfname     = args.efffile
+outfname      = args.outfile
+LUMIBLOCKS    = args.nlb
 
-recozfname = args.recofile
-effzfname = args.efffile
-outfname = args.outfile
+ZXSEC         = 1.929
+ZPURITYFACTOR = 0.9935
+
+if runmode == "Zmumu":
+    ACCEPTANCE = 0.3323224
+if runmode == "Zee":
+    ACCEPTANCE = 0.2996
 
-LUMIBLOCKS = args.nlb
-#ACCEPTANCE = 3.173927e-01
-ACCEPTANCE = 3.323224e-01
-ZXSEC=1.929
-ZPURITYFACTOR=0.9935
 
 def correction(mu):
-    # R20.7
-    # return 1.04524-0.000108956*mu
-    # R21
-    #return 1.04701-0.000206159*mu
-    return 0.998758-0.000157214*mu
-    #return 1.
+    if runmode == "Zee":
+        if campaign == "mc16a":
+            return 8.83702e-01 - 5.04610e-04*mu - 3.96025e-06*mu*mu
+        if campaign == "mc16d":
+            return 8.74735e-01 - 1.64286e-04*mu - 7.32040e-06*mu*mu
+        if campaign == "mc16e":
+            # old method
+            #return 8.74217e-01 - 1.66188e-04*mu - 7.75133e-06*mu*mu
+            # template method - OS = OS+SS
+            #return 9.32014e-01 + 1.49928e-06*mu - 5.28293e-06*mu*mu
+            # template method - OS = OS
+            return 9.02495e-01 + 2.15661e-05*mu - 7.04916e-06*mu*mu 
 
-recozfile = ROOT.TFile.Open(recozfname)
-effzfile = ROOT.TFile.Open(effzfname)
+    if runmode == "Zmumu":
+        if campaign == "mc16a":
+            return 9.90074e-01 - 5.34716e-06*mu - 3.23366e-06*mu*mu
+        if campaign == "mc16d":
+            return 9.91619e-01 - 1.21674e-04*mu - 1.58362e-06*mu*mu
+        if campaign == "mc16e":
+            return 9.90808e-01 - 9.99749e-05*mu - 1.40241e-06*mu*mu
 
-recoztree = recozfile.lumitree
-effztree = effzfile.lumitree
 
+recozfile = ROOT.TFile.Open(recozfname)
+effzfile  = ROOT.TFile.Open(effzfname)
+recoztree = recozfile.lumitree
+effztree  = effzfile.lumitree
 entrydict = {}
 
 for i in xrange(recoztree.GetEntries()):
@@ -50,7 +74,8 @@ for i in xrange(recoztree.GetEntries()):
         continue
     lbzero = (recoztree.lb // LUMIBLOCKS)*LUMIBLOCKS
     run = recoztree.run
-    if (run, lbzero) not in entrydict: entrydict[(run, lbzero)] = {'zcount': 0., 'zcounterrsq': 0., 'livetime': 0., 'lbwhen': [-1, -1], 'mu': 0., 'offlumi': 0., 'rolleff': 0., 'rollefferrsq': 0., 'lhcfill': recoztree.lhcfill}
+    if (run, lbzero) not in entrydict: 
+        entrydict[(run, lbzero)] = {'zcount': 0., 'zcounterrsq': 0., 'livetime': 0., 'lbwhen': [-1, -1], 'mu': 0., 'offlumi': 0., 'rolleff': 0., 'rollefferrsq': 0., 'lhcfill': recoztree.lhcfill}
     thisdict = entrydict[(run, lbzero)]
     effcy = (effztree.GetV1()[0]*correction(recoztree.mu))
     #thisdict['zcount'] += recoztree.zraw/effcy
@@ -70,22 +95,22 @@ for i in xrange(recoztree.GetEntries()):
     if thisdict['lbwhen'][1] < recoztree.lbwhen[1] or thisdict['lbwhen'][1] == -1:
         thisdict['lbwhen'][1] = recoztree.lbwhen[1]
 
-from array import array
  
 fout = ROOT.TFile.Open(outfname, 'RECREATE')
-o_run = array('I', [0])
-o_lb = array('I', [0])
-o_lbwhen = array('d', [0., 0.])
-o_zrate = array('f', [0.])
-o_zratestat = array('f', [0.])
-o_zlumi = array('f', [0.])
-o_zlumistat = array('f', [0.])
-o_mu = array('f', [0.])
-o_alleffcorr = array('f', [0.])
+o_run            = array('I', [0])
+o_lb             = array('I', [0])
+o_lbwhen         = array('d', [0., 0.])
+o_zrate          = array('f', [0.])
+o_zratestat      = array('f', [0.])
+o_zlumi          = array('f', [0.])
+o_zlumistat      = array('f', [0.])
+o_mu             = array('f', [0.])
+o_alleffcorr     = array('f', [0.])
 o_alleffcorrstat = array('f', [0.])
-o_offlumi = array('f', [0.])
-o_lblive = array('f', [0.])
-o_lhcfill = array('I', [0])
+o_offlumi        = array('f', [0.])
+o_lblive         = array('f', [0.])
+o_lhcfill        = array('I', [0])
+
 t = ROOT.TTree( 'lumitree', 'Luminosity tree' )
 t.Branch('run', o_run, 'run/i')
 t.Branch('lb', o_lb, 'lb/i')
@@ -107,9 +132,8 @@ for entry, entryval in sorted(entrydict.items()):
         entryval['offlumi'] /= entryval['livetime']
         eff = entryval['rolleff']/entryval['rollefferrsq']
         efferr = 1/entryval['rollefferrsq']**.5
-        #print 'LIVETIME2', entryval['livetime']
         entryval['zrate'] = entryval['zcount']/eff/entryval['livetime']
-        entryval['zratestat'] = (entryval['zcounterrsq']/eff/eff + (entryval['zcount']/eff**2*efferr)**2)**.5/entryval['livetime']
+        entryval['zratestat'] = (entryval['zcounterrsq']/eff + (entryval['zcount']/eff**2*efferr)**2)**.5/entryval['livetime']
         o_run[0], o_lb[0] = entry
         o_lbwhen[0], o_lbwhen[1] = entryval['lbwhen']
         o_zrate[0] = entryval['zrate']
@@ -122,9 +146,9 @@ for entry, entryval in sorted(entrydict.items()):
         o_offlumi[0] = entryval['offlumi']
         o_lblive[0] = entryval['livetime']
         o_lhcfill[0] = entryval['lhcfill']
-        if o_zlumi[0] < 4 or o_zlumi[0] > 15:
-            print o_lb[0], o_zlumi[0], entryval['zcount'], eff, entryval['livetime']
+        
         t.Fill()
+
 #t.Write()
 newrzt = recoztree.CloneTree()
 newrzt.SetName("recolumitree")
diff --git a/DataQuality/ZLumiScripts/scripts/dqt_zlumi_compute_lumi.py b/DataQuality/ZLumiScripts/scripts/dqt_zlumi_compute_lumi.py
index 44a2e9f770cd55b203052f4fd1ba02776233db5a..82be430b92bf4c7ba63d387747b6717a813e442f 100755
--- a/DataQuality/ZLumiScripts/scripts/dqt_zlumi_compute_lumi.py
+++ b/DataQuality/ZLumiScripts/scripts/dqt_zlumi_compute_lumi.py
@@ -2,6 +2,7 @@
 # Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
 
 import ROOT
+ROOT.gROOT.SetBatch(ROOT.kTRUE)
 import sys, os
 import logging
 logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.INFO)
@@ -26,10 +27,8 @@ parser.add_argument('--mode', type=str, help='Zee or Zmumu')
 args = parser.parse_args()
 
 BINWIDTH=10
-
 ZPURITYFACTOR=0.9935
 ZXSEC=1.929
-#ZATIMESC=0.2578
 ZATIMESC=0.29632
 
 def mu_dep_eff(mu):
@@ -51,8 +50,9 @@ for key in fin.GetListOfKeys():
         break
 
 if args.grl:
-    import DQUtils
-    grl = DQUtils.grl.load_grl(args.grl)
+    grlReader = ROOT.Root.TGoodRunsListReader(args.grl)
+    grlReader.Interpret()
+    grl = grlReader.GetMergedGRLCollection()
 else:
     grl = None
 
@@ -60,18 +60,12 @@ if not runname:
     logging.critical("Can't find run_* directory in input file %s", args.infile)
     sys.exit(1)
 
-z_m = fin.Get('%s/GLOBAL/DQTGlobalWZFinder/m_Z_Counter_mu' % runname)
-if args.out:
-    outfname = args.out
-else:
-    outfname = '%s_data.root' % runname[4:]
-
 runmode = args.mode
 print 'Running in', runmode, 'mode'
 if runmode == 'Zee':
-    z_m = fin.Get('%s/GLOBAL/DQTGlobalWZFinder/m_Z_Counter_el' % runname)
+    z_m = fin.Get('%s/GLOBAL/DQTGlobalWZFinder/m_Z_Counter_el_os' % runname)
     if not z_m:
-        logging.critical("Can't retrieve m_Z_Counter_el")
+        logging.critical("Can't retrieve m_Z_Counter_el_os")
         sys.exit(1)
 
 if runmode == 'Zmumu':
@@ -80,6 +74,10 @@ if runmode == 'Zmumu':
         logging.critical("Can't retrieve m_Z_Counter_mu")
         sys.exit(1)
 
+if args.out:
+    outfname = args.out
+else:
+    outfname = '%s_data.root' % runname[4:]
 
 fout = None
 t = None
@@ -198,20 +196,13 @@ if runmode == 'Zmumu':
     efftitle = 'eff #sigma, Z->#mu#mu'
     lumirawtitle = 'Lumi, Z->#mu#mu per LB'
 
-
-lumiplot_m = ROOT.TH1F('lumiplot_m', lumititle % runname[4:], 
-                       int(nrebinned_bins),
-                       lbmin, lbmin+BINWIDTH*nrebinned_bins)
-lumiplot_m_ratio = ROOT.TH1F('lumiplot_m_ratio', 'Z/official lumi ratio (Run %s)' % runname[4:], 
-                       int(nrebinned_bins),
-                       lbmin, lbmin+BINWIDTH*nrebinned_bins)
+lumiplot_m = ROOT.TH1F('lumiplot_m', lumititle, int(nrebinned_bins), lbmin, lbmin+BINWIDTH*nrebinned_bins)
+lumiplot_m_ratio = ROOT.TH1F('lumiplot_m_ratio', 'Z/official lumi ratio (Run %s)' % runname[4:], int(nrebinned_bins), lbmin, lbmin+BINWIDTH*nrebinned_bins)
 lumiplot_m.SetXTitle('LB')
 lumiplot_m.SetYTitle('Luminosity (x 10^{33} cm^{-2} s^{-1})')
-xsec_m = ROOT.TH1F('xsec_m', efftitle, int(nrebinned_bins),
-                       lbmin, lbmin+BINWIDTH*nrebinned_bins)
-lumiplot_raw_m =  ROOT.TH1F('lumiplot_raw_m', lumirawtitle, 
-                           int(lbmax-lbmin),
-                           lbmin, lbmax)
+
+xsec_m = ROOT.TH1F('xsec_m', efftitle, int(nrebinned_bins), lbmin, lbmin+BINWIDTH*nrebinned_bins)
+lumiplot_raw_m =  ROOT.TH1F('lumiplot_raw_m', lumirawtitle, int(lbmax-lbmin), lbmin, lbmax)
 
 num_m, lum, denom, weighted_mu = 0, 0, 0, 0
 tot_num_m, tot_denom, tot_lum = 0, 0, 0
@@ -226,7 +217,7 @@ for ibin in xrange(1, int(lbmax-lbmin)+1):
         l_zatimesc = mu_dep_eff(official_mu[ibin])
     else:
         l_zatimesc = ZATIMESC
-    if grl and not DQUtils.grl.grl_contains_run_lb(grl, (int(runname[4:]), int(lumiplot_raw_m.GetBinCenter(ibin)))):
+    if grl and not grl.HasRunLumiBlock(int(runname[4:]), int(lumiplot_raw_m.GetBinCenter(ibin))):
         o_passgrl[0]=0
     else:
         o_passgrl[0]=1
diff --git a/DataQuality/ZLumiScripts/scripts/dqt_zlumi_display_z_rate.py b/DataQuality/ZLumiScripts/scripts/dqt_zlumi_display_z_rate.py
index ca5211c06ebd044fa9a31fc440533a053d4355ef..b0b2c4dc0245cf8eb78f08ca7d85fde3a29a2d18 100755
--- a/DataQuality/ZLumiScripts/scripts/dqt_zlumi_display_z_rate.py
+++ b/DataQuality/ZLumiScripts/scripts/dqt_zlumi_display_z_rate.py
@@ -6,6 +6,7 @@ import sys, os
 import array
 import argparse
 import time
+ROOT.gROOT.SetBatch(ROOT.kTRUE)
 
 parser = argparse.ArgumentParser()
 parser.add_argument('infile', type=str, help='input HIST file')
diff --git a/Event/xAOD/xAODCaloEvent/xAODCaloEvent/versions/CaloCluster_v1.h b/Event/xAOD/xAODCaloEvent/xAODCaloEvent/versions/CaloCluster_v1.h
index fd20d4e8a9ecf2f0f4fb44ea5ca0a6f5b136ef63..5d1bf70ea4636ce1b28529a8fb82e49b7a6bd33b 100644
--- a/Event/xAOD/xAODCaloEvent/xAODCaloEvent/versions/CaloCluster_v1.h
+++ b/Event/xAOD/xAODCaloEvent/xAODCaloEvent/versions/CaloCluster_v1.h
@@ -80,14 +80,12 @@ namespace xAOD {
          Topo_633   = 12,
          // transient cluster for AODCellContainer
          SW_7_11    = 13,
-         // cluster representation of towers
-         Tower_01_01 = 14,
-         Tower_005_005 = 15,
-
-
-
          //New (2016) egamma cluster
          SuperCluster=14,
+         //New (2019) cluster representation of towers
+         Tower_01_01 = 15,
+         Tower_005_005 = 16,
+	 Tower_fixed_area = 17,
          CSize_Unknown = 99
       };
 
diff --git a/Event/xAOD/xAODCnvInterfaces/xAODCnvInterfaces/IEventInfoCnvTool.h b/Event/xAOD/xAODCnvInterfaces/xAODCnvInterfaces/IEventInfoCnvTool.h
index c0a33d117f28b3fd7581b840ac5e569661852119..919522a7dcef4fc0df55a4dcf572bd7755915887 100644
--- a/Event/xAOD/xAODCnvInterfaces/xAODCnvInterfaces/IEventInfoCnvTool.h
+++ b/Event/xAOD/xAODCnvInterfaces/xAODCnvInterfaces/IEventInfoCnvTool.h
@@ -43,7 +43,8 @@ namespace xAODMaker {
       virtual StatusCode convert( const EventInfo* aod,
                                   xAOD::EventInfo* xaod,
                                   bool pileUpInfo = false,
-                                  bool copyPileUpLinks = true ) = 0;
+                                  bool copyPileUpLinks = true,
+				  bool forceMCInfoCopy = false) = 0;
 
       /// Gaudi interface definition
       static const InterfaceID& interfaceID() {
diff --git a/Event/xAOD/xAODEventInfoCnv/src/EventInfoCnvTool.cxx b/Event/xAOD/xAODEventInfoCnv/src/EventInfoCnvTool.cxx
index 400c3c8c58985b2b249278c779049165ee4a8ff5..1dc82341a959b2d73a6fe9ed19fc3df83c9534d6 100644
--- a/Event/xAOD/xAODEventInfoCnv/src/EventInfoCnvTool.cxx
+++ b/Event/xAOD/xAODEventInfoCnv/src/EventInfoCnvTool.cxx
@@ -113,7 +113,8 @@ namespace xAODMaker {
    StatusCode EventInfoCnvTool::convert( const EventInfo* aod,
                                          xAOD::EventInfo* xaod,
                                          bool pileUpInfo,
-                                         bool copyPileUpLinks ) {
+                                         bool copyPileUpLinks,
+					 bool forceMCInfoCopy) {
 
       if( ! aod ) {
          ATH_MSG_WARNING( "Null pointer received for input!" );
@@ -150,8 +151,9 @@ namespace xAODMaker {
             eventTypeBitmask |= xAOD::EventInfo::IS_CALIBRATION;
          }
          xaod->setEventTypeBitmask( eventTypeBitmask );
-         // Only add MC information for simulation files:
-         if( xaod->eventType( xAOD::EventInfo::IS_SIMULATION ) ) {
+         // Only add MC information for simulation files
+	 //or for Data+MC overlay
+         if( xaod->eventType( xAOD::EventInfo::IS_SIMULATION ) || forceMCInfoCopy ) {
             xaod->setMCChannelNumber( aod->event_type()->mc_channel_number() );
             xaod->setMCEventNumber( aod->event_type()->mc_event_number() );
             std::vector< float >
diff --git a/Event/xAOD/xAODEventInfoCnv/src/EventInfoCnvTool.h b/Event/xAOD/xAODEventInfoCnv/src/EventInfoCnvTool.h
index 5d048af20b334d80e2bf8cc3fccebb8d2ff0c979..d0f07160191aacad8b622a63b71ebfa290cae8e8 100644
--- a/Event/xAOD/xAODEventInfoCnv/src/EventInfoCnvTool.h
+++ b/Event/xAOD/xAODEventInfoCnv/src/EventInfoCnvTool.h
@@ -52,7 +52,8 @@ namespace xAODMaker {
       virtual StatusCode convert( const EventInfo* aod,
                                   xAOD::EventInfo* xaod,
                                   bool pileUpInfo = false,
-                                  bool copyPileUpLinks = true );
+                                  bool copyPileUpLinks = true,
+				  bool forceMCInfoCopy = false);
 
    private:
 #ifndef XAOD_ANALYSIS
diff --git a/InnerDetector/InDetAlignAlgs/InDetAlignGenAlgs/share/CreateMisalignment.py b/InnerDetector/InDetAlignAlgs/InDetAlignGenAlgs/share/CreateMisalignment.py
index 7139fd1ac105b3dbd328efb5de7fad84984e8587..ed12bb4688f1fbfdf41cba4e832231e12e19cc99 100644
--- a/InnerDetector/InDetAlignAlgs/InDetAlignGenAlgs/share/CreateMisalignment.py
+++ b/InnerDetector/InDetAlignAlgs/InDetAlignGenAlgs/share/CreateMisalignment.py
@@ -26,16 +26,15 @@ ROOToutput = True
 createFreshDB = not(ReadDBPoolFile or MisalignmentOnTopOfExistingSet)
 
 if not 'MisalignmentMode' in dir():
-    MisalignmentMode = 3
+    MisalignmentMode = 11 # Radial
 
-MaximumShift = 200*micrometer
+MaximumShift = 100*micrometer
 if MisalignmentMode in [11, 12,31]:
     MaximumShift = 500*micrometer
 
-InFile = 'alignment_nominal'
-#InFile = 'NominalAlignment'
-#InFile = 'MisalignmentSet11'
-OutFiles = 'MisalignmentSet%s' % MisalignmentMode
+InFile = 'NominalAlignment'
+userSuffix = "_p01"
+OutFiles = 'MisalignmentSet%s%s' % (MisalignmentMode, userSuffix)
 
 #####################################################################
 
diff --git a/InnerDetector/InDetAlignAlgs/InDetAlignGenAlgs/src/CreateMisalignAlg.cxx b/InnerDetector/InDetAlignAlgs/InDetAlignGenAlgs/src/CreateMisalignAlg.cxx
index b137992804ee1f7b199691a15a205ed335d9b51d..bf6a095c0c9b0860f679e635db0c4a853c8a57a2 100644
--- a/InnerDetector/InDetAlignAlgs/InDetAlignGenAlgs/src/CreateMisalignAlg.cxx
+++ b/InnerDetector/InDetAlignAlgs/InDetAlignGenAlgs/src/CreateMisalignAlg.cxx
@@ -336,12 +336,12 @@ namespace InDetAlignment
 					m_HumanReadableID = m_sctIdHelper->barrel_ec(SCT_ModuleID)*(m_HumanReadableID + 10000000);
 				}
 				
-				msg(MSG::INFO) << "Human Readable ID: " << m_HumanReadableID << endreq;
+				msg(MSG::DEBUG) << "-- Human Readable ID: " << m_HumanReadableID << endreq;
 				
 				m_VisualizationLookupTree->Fill();
 				
 				// Syntax is (ID, Level) where Level is from 1 to 3 (3 is single module level)
-				if (msgLvl(MSG::INFO)) {
+				if (msgLvl(MSG::DEBUG)) {
           HepGeom::Transform3D InitialAlignment = Amg::EigenTransformToCLHEP(m_IDAlignDBTool->getTrans(SCT_ModuleID,3));
 					msg() << "Initial Alignment of module " << m_idHelper->show_to_string(SCT_ModuleID,0,'/') << endreq;
 					msg() << "Alignment x = ("  << InitialAlignment.getTranslation().x() / CLHEP::micrometer << ") micron" << endreq;
@@ -389,11 +389,11 @@ namespace InDetAlignment
 						m_HumanReadableID = m_pixelIdHelper->barrel_ec(Pixel_ModuleID)*(m_HumanReadableID + 10000000);
 					}
 					
-					msg(MSG::INFO) << "Human Readable ID: " << m_HumanReadableID << endreq;
+					msg(MSG::DEBUG) << "-- Human Readable ID: " << m_HumanReadableID << endreq;
 					
 					m_VisualizationLookupTree->Fill();
 					
-					if (msgLvl(MSG::INFO)) {
+					if (msgLvl(MSG::DEBUG)) {
             HepGeom::Transform3D InitialAlignment = Amg::EigenTransformToCLHEP(m_IDAlignDBTool->getTrans(Pixel_ModuleID,3));
 						msg() << "Initial Alignment of module " << m_idHelper->show_to_string(Pixel_ModuleID,0,'/') << endreq;
 						msg() << "Alignment x = ("  << InitialAlignment.getTranslation().x() / CLHEP::micrometer << ") micron" << endreq;
@@ -583,7 +583,13 @@ namespace InDetAlignment
 			double r = center.rho(); //distance from beampipe
 			double phi = center.phi();
 			double z = center.z();
-			
+
+			msg(MSG::DEBUG) << " Center of the module: radius:"<< r
+                                       << "   phi: " << phi
+                                       << "   x: " << r * cos(phi)
+                                       << "   y: " << r * sin(phi)
+                                       << "   z: " << z << endreq;
+
 			HepGeom::Transform3D parameterizedTrafo;
 			HepGeom::Transform3D alignmentTrafo;
 			
@@ -591,7 +597,7 @@ namespace InDetAlignment
 			// prepare scale factor for different subsystems:
                         double ScaleFactor = 1.;
 
-                        if (m_idHelper->is_pixel(ModuleID))
+                        if (m_idHelper->is_pixel(ModuleID)) // pixel modules
                           {
                             if (m_pixelIdHelper->is_barrel(ModuleID))   {
                               ScaleFactor=m_ScalePixelBarrel;
@@ -605,8 +611,8 @@ namespace InDetAlignment
                             if (m_pixelIdHelper->is_dbm(ModuleID))   {    // DBM
                               ScaleFactor=m_ScalePixelDBM;
                             }
-
-                          } else if (m_idHelper->is_sct(ModuleID))
+                          } 
+			else if (m_idHelper->is_sct(ModuleID)) // sct modules
                           {
                             if (m_sctIdHelper->is_barrel(ModuleID)) {
                               ScaleFactor=m_ScaleSCTBarrel;
@@ -614,8 +620,9 @@ namespace InDetAlignment
                             else {
                               ScaleFactor=m_ScaleSCTEndcap;
                             }
-
-                          } else if (m_idHelper->is_trt(ModuleID))
+			    
+                          } 
+			else if (m_idHelper->is_trt(ModuleID)) // trt modules
                           {
                             if (m_trtIdHelper->is_barrel(ModuleID)) {
                               ScaleFactor=m_ScaleTRTBarrel;
@@ -738,19 +745,33 @@ namespace InDetAlignment
 			
 			else { // systematic misalignments
 				if (m_MisalignmentMode/10==1) {
-					//radial misalignments
-					double deltaR;
-					if (m_MisalignmentMode==11) {
-						//R deltaR = radial expansion
-						if (m_idHelper->is_trt(ModuleID) && abs(m_trtIdHelper->barrel_ec(ModuleID))==2) {
-							//radial mode cannot handle TRT endcap, sorry
-							deltaR = 0.;
-							if (msgLvl(MSG::DEBUG)) msg() << "will not move TRT endcap for radial distortion " << endreq;
-						} else {
-							//deltaR = 0.5 * cos ( 2*phi ) * r/maxRadius * maxDeltaR;
-							deltaR = r/maxRadius * maxDeltaR; //scale linearly in r
-						}
-					} else if (m_MisalignmentMode==12) {
+				  //R deltaR = radial expansion                                                                                                                 
+				  double deltaR;
+				  // 11: radial misalignments
+				  // 10/ May /2019 Salva --> allow radial distortion only of the barrel elements   
+				  if (m_MisalignmentMode==11) {
+				    //R deltaR = radial expansion
+				    if (m_idHelper->is_trt(ModuleID) && abs(m_trtIdHelper->barrel_ec(ModuleID))==2) {
+				      //radial mode cannot handle TRT endcap, sorry
+				      deltaR = 0.;
+				      if (msgLvl(MSG::DEBUG)) msg() << " -> will not move TRT endcap for radial distortion " << endreq;
+				    } 
+				    else if (m_idHelper->is_pixel(ModuleID) && !m_pixelIdHelper->is_barrel(ModuleID)) {
+				      deltaR = 0.;
+				      if (msgLvl(MSG::DEBUG)) msg() << " -> will not move PIX endcap for radial distortion (mode "
+								    << m_MisalignmentMode << ")" << endreq;
+				    }
+				    else if (m_idHelper->is_sct(ModuleID) && !m_sctIdHelper->is_barrel(ModuleID)) {
+				      deltaR = 0.;
+				      if (msgLvl(MSG::DEBUG)) msg() << " -> will not move SCT endcap for radial distortion (mode "
+								    << m_MisalignmentMode << ")" << endreq;
+				    }
+				    else {
+				      //deltaR = 0.5 * cos ( 2*phi ) * r/maxRadius * maxDeltaR;
+				      deltaR = r/maxRadius * maxDeltaR; //scale linearly in r
+				    }
+				    // end of radial (mode 11) 
+				  } else if (m_MisalignmentMode==12) {
 						//Phi deltaR = elliptical (egg-shape)
 						if (m_idHelper->is_trt(ModuleID) && abs(m_trtIdHelper->barrel_ec(ModuleID))==2) {
 							//elliptical mode cannot handle TRT endcap, sorry
diff --git a/InnerDetector/InDetDigitization/PixelRadDamDigitization/src/RadDamageUtil.cxx b/InnerDetector/InDetDigitization/PixelRadDamDigitization/src/RadDamageUtil.cxx
index 5bbfff4583c4370a005029a3cf1c44c2b5f6ea40..7905cf85df7bab71b8ec4c29d11dc6a85a3e8c27 100644
--- a/InnerDetector/InDetDigitization/PixelRadDamDigitization/src/RadDamageUtil.cxx
+++ b/InnerDetector/InDetDigitization/PixelRadDamDigitization/src/RadDamageUtil.cxx
@@ -46,13 +46,13 @@ RadDam::RadDamageUtil::RadDamageUtil(const std::string& type, const std::string&
   AthAlgTool(type,name,parent),
   m_defaultRamo( 1 ),
   m_defaultEField( 1 ),
+  m_EfieldInterpolator(""),
   m_betaElectrons(4.5e-16),
   m_betaHoles(6.0e-16),
-  m_EfieldInterpolator(nullptr),
   m_saveDebugMaps(false),
   m_rndmSvc("AtDSFMTGenSvc",name),
   m_rndmEngineName("PixelDigitization"),
-  m_rndmEngine(0)
+  m_rndmEngine(nullptr)
 { 
   declareProperty("RndmSvc", m_rndmSvc, "Random Number Service used in RadDamageUtil");
   declareProperty("RndmEngine", m_rndmEngineName, "Random engine name");
@@ -239,12 +239,10 @@ double RadDam::RadDamageUtil::weighting2D(double x, double z, double Lx, double
 //=========================================
 // G E N E R A T E   E - F I E L D   M A P
 //=========================================
-const StatusCode RadDam::RadDamageUtil::generateEfieldMap( TH1F* eFieldMap, InDetDD::PixelModuleDesign* module  ){
+const StatusCode RadDam::RadDamageUtil::generateEfieldMap( TH1F*& eFieldMap, InDetDD::PixelModuleDesign* module  ){
 
     //TODO: from DB
     double biasVoltage          = 600.;
-    double depletionVoltage     = 80.;
-    double depletionLength      = 0.2;
     double sensorThickness      = module->thickness(); //default should be 0.2?
     double fluence              = 8.;//*e14 neq/cm^2 
     eFieldMap = new TH1F("hefieldz","hefieldz",200,0,sensorThickness*1e3);
@@ -291,7 +289,7 @@ const StatusCode RadDam::RadDamageUtil::generateEfieldMap( TH1F* eFieldMap, InDe
   return StatusCode::SUCCESS;
 }
 
-StatusCode RadDam::RadDamageUtil::generateEfieldMap( TH1F* &eFieldMap, InDetDD::PixelModuleDesign* module, double fluence,  double biasVoltage, int layer, std::string TCAD_list, bool interpolate ){
+StatusCode RadDam::RadDamageUtil::generateEfieldMap( TH1F* &eFieldMap, InDetDD::PixelModuleDesign* /*module*/, double fluence,  double biasVoltage, int layer, std::string TCAD_list, bool interpolate ){
 
     TString id;
     //TODO adapt saving location for documentation of E field interpolation
@@ -379,7 +377,6 @@ const StatusCode RadDam::RadDamageUtil::generateDistanceTimeMap( TH2F* &distance
     ATH_MSG_DEBUG ("Did not find time and/or distance maps.  Will compute them from the E-field map..");
 
     for (int i=1; i<= distanceMap_e->GetNbinsX(); i++){ //Loop over initial position of charge carrier (in z)
-	    double z_i = distanceMap_e->GetXaxis()->GetBinCenter(i);
 	    double time_e = 0.; //ns
 	    double time_h = 0.; //ns
 	    double distanceTravelled_e=0; //mm 
diff --git a/InnerDetector/InDetDigitization/PixelRadDamDigitization/src/RadDamageUtil.h b/InnerDetector/InDetDigitization/PixelRadDamDigitization/src/RadDamageUtil.h
index fcfb0597a3815a812475014618ba49f718d75bfd..b4bac6410ecec4a40a364e08e9c7d345b48785cd 100644
--- a/InnerDetector/InDetDigitization/PixelRadDamDigitization/src/RadDamageUtil.h
+++ b/InnerDetector/InDetDigitization/PixelRadDamDigitization/src/RadDamageUtil.h
@@ -51,7 +51,7 @@ public:
   virtual ~RadDamageUtil();
   StatusCode initTools();
   const StatusCode generateRamoMap(TH3F* ramPotentialMap, InDetDD::PixelModuleDesign* module);
-  const StatusCode generateEfieldMap(TH1F* eFieldMap, InDetDD::PixelModuleDesign* module);
+  const StatusCode generateEfieldMap(TH1F*& eFieldMap, InDetDD::PixelModuleDesign* module);
   StatusCode generateEfieldMap(TH1F* &eFieldMap, InDetDD::PixelModuleDesign* module, double fluence, double biasVoltage, int layer, std::string TCAD_list, bool interpolate);
   const StatusCode generateDistanceTimeMap( TH2F* &distanceMap_e, TH2F* &distanceMap_h, TH1F* &timeMap_e, TH1F* &timeMap_h, TH2F* &lorentzMap_e, TH2F* &lorentzMap_h, TH1F* &eFieldMap, InDetDD::PixelModuleDesign* module);
   
diff --git a/InnerDetector/InDetDigitization/PixelRadDamDigitization/src/SensorSim3DTool.cxx b/InnerDetector/InDetDigitization/PixelRadDamDigitization/src/SensorSim3DTool.cxx
index 33ea2736b868acbefd84211b6e98e008af722788..7b0a3ee66afe8ac343732cd57a3b099c267aba8a 100644
--- a/InnerDetector/InDetDigitization/PixelRadDamDigitization/src/SensorSim3DTool.cxx
+++ b/InnerDetector/InDetDigitization/PixelRadDamDigitization/src/SensorSim3DTool.cxx
@@ -96,49 +96,54 @@
 
     } else if (m_fluence == 1) {
 
-        mapsPath_list.push_back(PathResolverFindCalibFile("/afs/cern.ch/user/v/vewallan/public/TCADmaps/outputfiles/phi_0_20V.root")); 
+    //Beginning of Run 2 (measured fluence)
+        mapsPath_list.push_back(PathResolverFindCalibFile("PixelDigitization/TCAD_IBL_3Dsensors_efields/phi_0.0e+00_20V_270K.root")); 
 
         fluence_layers.push_back(1e-10);
 
     } else if (m_fluence == 2) {
 
-        mapsPath_list.push_back(PathResolverFindCalibFile("/afs/cern.ch/user/v/vewallan/public/TCADmaps/outputfiles/phi_1e14_20V.root"));
+    //Half-way through 2016 (measured fluence)
+        mapsPath_list.push_back(PathResolverFindCalibFile("PixelDigitization/TCAD_IBL_3Dsensors_efields/phi_5.0e+13_20V_283K.root"));
 
-        fluence_layers.push_back(1e14);
+        fluence_layers.push_back(5e13);
 
     } else if (m_fluence == 3) {
 
-        mapsPath_list.push_back(PathResolverFindCalibFile("/afs/cern.ch/user/v/vewallan/public/TCADmaps/outputfiles/phi_2e14_30V.root"));
+    //End of 2016 (measured fluence)
+        mapsPath_list.push_back(PathResolverFindCalibFile("PixelDigitization/TCAD_IBL_3Dsensors_efields/phi_1.0e+14_20V_283K.root"));
 
-        fluence_layers.push_back(2e14);
+        fluence_layers.push_back(1e14);
 
     } else if (m_fluence == 4) {
 
-        mapsPath_list.push_back(PathResolverFindCalibFile("/afs/cern.ch/user/v/vewallan/public/TCADmaps/outputfiles/phi_5e14_40V.root")); 
+    //End of 2017 (measured fluence)
+        mapsPath_list.push_back(PathResolverFindCalibFile("PixelDigitization/TCAD_IBL_3Dsensors_efields/phi_2.5e+14_40V_261K.root")); 
 
-        fluence_layers.push_back(5e14);
+        fluence_layers.push_back(2.5e14);
 
     } else if (m_fluence == 5) {
 
-        mapsPath_list.push_back(PathResolverFindCalibFile("/afs/cern.ch/user/v/vewallan/public/TCADmaps/outputfiles/phi_1e15_50V.root"));
+    //End of Run 2 (end of 2018) (measured fluence)
+        mapsPath_list.push_back(PathResolverFindCalibFile("PixelDigitization/TCAD_IBL_3Dsensors_efields/phi_5.0e+14_40V_261K.root"));
 
-        fluence_layers.push_back(1e15); 
+        fluence_layers.push_back(5e14); 
 
     } else if (m_fluence == 6) {
 
-        mapsPath_list.push_back(PathResolverFindCalibFile("/afs/cern.ch/user/v/vewallan/public/TCADmaps/outputfiles/phi_5e15_160V.root"));
+        mapsPath_list.push_back(PathResolverFindCalibFile("PixelDigitization/TCAD_IBL_3Dsensors_efields/phi_5e15_160V.root"));
 
         fluence_layers.push_back(5e15);
 
     } else if (m_fluence == 7) {
 
-        mapsPath_list.push_back(PathResolverFindCalibFile("/afs/cern.ch/user/v/vewallan/public/TCADmaps/outputfiles/phi_6e15_190V_new.root"));       
+        mapsPath_list.push_back(PathResolverFindCalibFile("PixelDigitization/TCAD_IBL_3Dsensors_efields/phi_6e15_190V_new.root"));       
 
         fluence_layers.push_back(6e15);
 
     } else if (m_fluence == 8) {
 
-        mapsPath_list.push_back(PathResolverFindCalibFile("/afs/cern.ch/user/v/vewallan/public/TCADmaps/outputfiles/phi_1e16_260V_new.root"));
+        mapsPath_list.push_back(PathResolverFindCalibFile("PixelDigitization/TCAD_IBL_3Dsensors_efields/phi_1e16_260V_new.root"));
 
         fluence_layers.push_back(1e16);
     }
diff --git a/InnerDetector/InDetMonitoring/InDetPerformanceMonitoring/AutomatedCheck/acZmumu.py b/InnerDetector/InDetMonitoring/InDetPerformanceMonitoring/AutomatedCheck/acZmumu.py
index e8e9ddbb119bd154f4bea17ba69f0b387e55a534..fc23753caa263902aedb000e509255ec816df8af 100644
--- a/InnerDetector/InDetMonitoring/InDetPerformanceMonitoring/AutomatedCheck/acZmumu.py
+++ b/InnerDetector/InDetMonitoring/InDetPerformanceMonitoring/AutomatedCheck/acZmumu.py
@@ -4,10 +4,11 @@ m_storingFolder = ""
 m_recordsFileName = ""
 m_athenaVersion = ""
 m_testArea = ""
+m_packagePath = ""
 m_theUser = ""
-m_scriptName = "runzmumu_UserConstants.py"
 m_savingFile = "acZmumu_history.txt"
 m_reconmerge = "merge" #"%"
+m_workDirPlatform = ""
 
 # options
 m_minEvents = 10000
@@ -21,23 +22,21 @@ m_dataProject = "data17_13TeV"
 m_userFiles = 0 # this means all the files
 m_amitag = "%"
 m_physicsType = "physics_Main"
-m_usingMC = False
 m_mcDataSetName = "mc16_13TeV.361107.PowhegPythia8EvtGen_AZNLOCTEQ6L1_Zmumu.recon.ESD.e3601_s3126_r10201"
+m_scriptName = "runzmumu_UserConstants.py"
+m_userDataSet = "NONE"
 
 ###################################################################################################
 def findListOfDataSets():
     import os
+    import sys
 
     listOfDataSets = []
-    #ami list datasets data18_13TeV.%.physics_Main.merge.DESDM_ZMUMU%
-    #theAMIsearchCommand = "ami list datasets %s.%%.physics_Main.%s.%s.%s --order run_number --fields events,nfiles"  %(m_dataProject, m_reconmerge, m_dataType, m_amitag)
-    #theAMIsearchCommand = "ami list datasets %s.%%.%s.%s.%s.%s --order run_number --fields events,nfiles"  %(m_dataProject, m_physicsType, m_reconmerge, m_dataType, m_amitag)
     theAMIsearchCommand = "ami list datasets %s.%%.%s.%s.%s.%s --order run_number --fields events,nfiles"  %(m_dataProject, m_physicsType, m_reconmerge, m_dataType, m_amitag)
-    #theAMIsearchCommand = "ami list datasets %s.%%.physics_HardProbes.%s.%s.%s --order run_number --fields events,nfiles"  %(m_dataProject, m_reconmerge, m_dataType, m_amitag)
     
-    # case of using MC 
-    if (m_usingMC):
-        theAMIsearchCommand = "ami list datasets %s --order run_number --fields events,nfiles"  %(m_mcDataSetName)
+    # case of using data set provided by the user
+    if ("NONE" not in m_userDataSet):
+        theAMIsearchCommand = "ami list datasets %s --fields events,nfiles"  %(m_userDataSet)
 
     print (" <acZmumu> AMI data set search command: \n  -->  %s" %(theAMIsearchCommand))
     amiReturn = os.popen(theAMIsearchCommand).readlines()
@@ -50,10 +49,20 @@ def findListOfDataSets():
         if ("events" in theLine):
             lineWithContent = False # remove the header
 
-        if (lineWithContent):
+        if (lineWithContent and "NONE" in m_userDataSet):
             theLine.rstrip() # remove trailing blank spaces
             listOfDataSets.append(theLine) # add this data set
+        
+        if ("NONE" not in m_userDataSet):
+            tempstring = str(m_userDataSet[0:30])
+            if (tempstring in theLine):
+                listOfDataSets.append(m_userDataSet)
     
+    # if user provides the data set name, it may happen (if errors) the data set is not found or does not exist
+    if (len(listOfDataSets) == 0 and "NONE" not in m_userDataSet):
+        print " <acZmumu> ** WARNING ** user data set: %s not found " %m_userDataSet
+        sys.exit(" >> STOP excution")
+
     return listOfDataSets
 
 ###################################################################################################
@@ -98,8 +107,10 @@ def preliminaries ():
     global m_athenaVersion
     global m_testArea
     global m_theUser
+    global m_packagePath
+    global m_workDirPlatform
 
-    m_athenaVersion, m_testArea, m_theUser = getAthenaBasics () 
+    m_athenaVersion, m_testArea, m_theUser, m_packagePath, m_workDirPlatform = getAthenaBasics () 
     m_year = getYear ()
 
     # reports folder
@@ -133,7 +144,7 @@ def extractRunsAndProperties (listOfDataSets):
     # the list of data sets can contain dummy lines
     infoFromAMI = {}
 
-    if (len(listOfDataSets)>0):
+    if (len(listOfDataSets)>0 and ("NONE" in m_userDataSet)):
         print (" <acZmumu> #data sets= %d" %(len(listOfDataSets)))
         # extract data set name
         # first is the data project
@@ -173,8 +184,9 @@ def extractRunsAndProperties (listOfDataSets):
                 infoFromAMI[theRunNumber]["dataset"] = "%s%s" %(infoFromAMI[theRunNumber]["dataset"],theDataSet)
                 continue
     else:
-        print (" <acZmumu> ERROR ** list of data sets is empty. Stop Execution")
-        exit ()
+        if ("NONE" in m_userDataSet):
+            print (" <acZmumu> ERROR ** list of data sets is empty. Stop Execution")
+            exit ()
 
     return infoFromAMI
 
@@ -253,6 +265,35 @@ def crossCheckInfo(infoFromAMI, infoFromRecordsFile):
 
 ###################################################################################################                                                                                
 def submitGridJobs (infoFromAMI, listOfNewRuns, listOfPendingRuns):
+    
+    # submitting jobs with real data -> listOfNewRuns must be filled
+    if (len(listOfNewRuns) > 0):
+        submitGridJobsListOfRuns (infoFromAMI, listOfNewRuns, listOfPendingRuns)
+    
+    # submitting job when the user provides the data set
+    if ("NONE" not in m_userDataSet):
+        submitGridJobsUserDataSet () 
+
+    return
+
+###################################################################################################                                                                                
+def submitGridJobsUserDataSet ():
+    import os
+
+    print " <acZmumu> submitting grid job when user provides the data set name "
+    theCommand = getGridSubmissionCommand(0, infoFromAMI)
+    if (m_submitExec): 
+        print (" <acZmumu> m_submitExec = True --> job to be submmited");
+        # move to the submission folder
+        submissionPath = "%s/run" %(m_testArea) 
+        os.chdir(submissionPath)
+        print (" <acZmumu> path: %s" %(submissionPath))
+        os.system(theCommand)
+
+    return
+
+###################################################################################################                                                                                
+def submitGridJobsListOfRuns (infoFromAMI, listOfNewRuns, listOfPendingRuns):
     import os
 
     listOfSubmittedRuns = []
@@ -349,6 +390,26 @@ def getAthenaBasics ():
         print (" <acZmumu> ERROR ** no Athena TestArea defined --> job submission is not possible. STOP execution")
         exit()
 
+    workdirplatform = ""
+    try:
+        workdirplatform = os.getenv("WorkDir_PLATFORM","")
+    except:
+        print (" <acZmumu> ERROR ** no WorkDir_PLATFORM defined --> job submission is not possible. STOP execution")
+        exit()
+
+    print " == athenabasics == workdirplatform = %s" %workdirplatform
+
+    packagePath = ""
+    try:
+        thepwd = os.getcwd()
+        thisfoldername = os.path.basename(thepwd)
+        tempList = thepwd.split("athena")
+        tempword = tempList[-1]
+        tempList = tempword.split(thisfoldername)
+        packagePath = tempList[0]
+    except:
+        packagePath = ""
+
     # voms proxy must be initiated
     goodVoms = True
     vomsInfoReturn = os.popen("voms-proxy-info").readlines()
@@ -365,7 +426,7 @@ def getAthenaBasics ():
         print (" <acZmumu> ERROR ** no voms initiated --> It is not possible to consult AMI. Stop execution")
         exit()
 
-    return (athenaVersion, testArea, theUser)
+    return (athenaVersion, testArea, theUser, packagePath, workdirplatform)
 
 ###################################################################################################                                                                                
 def getGridSubmissionCommand(runNumber, infoFromAMI):
@@ -373,13 +434,41 @@ def getGridSubmissionCommand(runNumber, infoFromAMI):
     # build the command for submission
 
     #theScript = "%s/InnerDetector/InDetMonitoring/InDetPerformanceMonitoring/share/runzmumu_run2paper.py" %(m_testArea)
-    theScript = "%s/InnerDetector/InDetMonitoring/InDetPerformanceMonitoring/share/%s" %(m_testArea, m_scriptName)
-    theInput = "--inDS=%s" %(infoFromAMI[runNumber]["dataset"])
-    theOutput = "--outDS=user.%s.%s_%s_%d_Zmumu_%s_%d " %(m_theUser, m_athenaVersion, m_dataProject, runNumber, m_userLabel, infoFromAMI[runNumber]["attempt"])
-    #theOptions = "--nfiles %d --useShortLivedReplicas  --forceStaged --nFilesPerJob %d" %(infoFromAMI[runNumber]["nfiles"], 20)
-    theOptions = "--nfiles %d --useShortLivedReplicas  --forceStaged  --excludedSite=ANALY_HPC2N" %(infoFromAMI[runNumber]["nfiles"])
-    
-    theCommand = "pathena %s %s %s %s" %(theScript, theInput, theOutput, theOptions)
+    #theScript = "%s/InnerDetector/InDetMonitoring/InDetPerformanceMonitoring/share/%s" %(m_testArea, m_scriptName)
+    theScript = "%s%sshare/%s" %(m_testArea, m_packagePath, m_scriptName)
+
+    theInput = "NONE"
+    if (runNumber>0):
+        theInput = "--inDS=%s" %(infoFromAMI[runNumber]["dataset"])
+    else:
+        if ("NONE" not in m_userDataSet):
+            theInput = "--inDS=%s" %m_userDataSet
+    if ("NONE" in theInput):
+        sys.exit(" <acZmumu> ** ERROR ** no input available for the grid submission command. ** STOP execution **")
+
+    theOuput = "NONE"
+    if (runNumber>0):
+        theOutput = "--outDS=user.%s.%s_%s_%d_Zmumu_%s_%d " %(m_theUser, m_athenaVersion, m_dataProject, runNumber, m_userLabel, infoFromAMI[runNumber]["attempt"])
+    else: 
+        if ("NONE" not in m_userDataSet):
+            theOutput = "--outDS=user.%s.%s_Zmumu_%s" %(m_theUser, m_athenaVersion, m_userLabel)
+    if ("NONE" in theOutput):
+        sys.exit(" <acZmumu> ** ERROR ** no output available for the grid submission command. ** STOP execution **")
+
+    # warning: if one wants to limit the file per job just add to the options: --nFilesPerJob Nfiles
+    #theOptions = "--nfiles %d --useShortLivedReplicas  --forceStaged  --site=ANALY_ECDF_SL7" %(infoFromAMI[runNumber]["nfiles"])
+
+    theOptions = "NONE"
+    if (runNumber>0):
+        theOptions = "--nfiles %d --useShortLivedReplicas  --forceStaged" %(infoFromAMI[runNumber]["nfiles"])
+    else: 
+        if ("NONE" not in m_userDataSet):
+            theOptions = "--useShortLivedReplicas  --forceStaged"
+
+    theExtraOptions = "" 
+    theExtraOptions = "--cmtConfig %s --excludedSite=ANALY_HPC2N,ANALY_RHUL_SL6,ANALY_JINR_MIG,ANALY_IHEP,ANALY_JINR,ANALY_CSCS-HPC" %m_workDirPlatform 
+
+    theCommand = "pathena %s %s %s %s %s" %(theScript, theInput, theOutput, theOptions, theExtraOptions)
     print "%s " %theCommand
 
     return theCommand
@@ -387,13 +476,14 @@ def getGridSubmissionCommand(runNumber, infoFromAMI):
 ###################################################################################################                                                                                
 def updateRecordsFile(listOfSubmittedRuns, infoFromAMI):
 
-    print (" <acZmumu> updateRecordsFile --> %s " %(m_recordsFileName))
-    fileToUpdate = open(m_recordsFileName, "a");
-    fileToUpdate.write("\n")
-    for runNumber in listOfSubmittedRuns:
-        fileToUpdate.write("%d:%s\n" %(runNumber, infoFromAMI[runNumber]))
+    if ("NONE" in m_userDataSet):
+        print (" <acZmumu> updateRecordsFile --> %s " %(m_recordsFileName))
+        fileToUpdate = open(m_recordsFileName, "a");
+        fileToUpdate.write("\n")
+        for runNumber in listOfSubmittedRuns:
+            fileToUpdate.write("%d:%s\n" %(runNumber, infoFromAMI[runNumber]))
     
-    fileToUpdate.close()
+        fileToUpdate.close()
 
     return
 
@@ -407,9 +497,6 @@ def welcomeBanner ():
     print ("\n")
     print ("  config:")
     print ("  ** Exec: %r" %m_submitExec)
-    print ("  ** using MC? %r" %m_usingMC)
-    if (m_usingMC):
-        print ("  ** mc data set %s" %m_mcDataSetName)
     print ("  ** data project: %s " %m_dataProject)
     print ("  ** min events: %d"  %m_minEvents)
     print ("  ** min Run: %d"  %m_firstRun)
@@ -421,6 +508,9 @@ def welcomeBanner ():
     if (m_userFiles > 0):
         print ("  ** user requested files: %d" %m_userFiles)
     print ("  ** AMI tag: %s" %m_amitag) 
+    print "  ** script: %s" %m_scriptName
+    if ("NONE" not in m_userDataSet):
+        print "  ** user data set: %s" %m_userDataSet
     print ("\n")
 
     return
@@ -446,22 +536,22 @@ def optParsing():
     p_amitag = m_amitag
     p_dataProject = m_dataProject
     p_physicsType = m_physicsType
-    p_usingMC = m_usingMC
-    p_mcDataSetName = m_mcDataSetName
+    p_scriptName = m_scriptName
+    p_userDataSet = m_userDataSet
 
     parser = OptionParser()
     parser.add_option("--amiTag", dest="p_amitag", help="Name of the requested AMI tag (example: r10258_r10258_p3399). Wild card is also possible. Default %s" %(p_amitag), default = p_amitag)
     parser.add_option("--dataProject", dest="p_dataProject", help="data project of the data sets (examples: data17_13TeV). Default %s" %(p_dataProject), default = p_dataProject)
+    parser.add_option("--dataSet", dest="p_userDataSet", help="User defined data set. Default %s" %(p_userDataSet), default = p_userDataSet)
     parser.add_option("--dataType", dest="p_dataType", help="User defined data type (examples: DAOD_ZMUMU, DESDM_MCP). Default %s" %(p_dataType), default = p_dataType)
-    parser.add_option("--dataSet", dest="p_mcDataSetName", help="User defined full data set name", default = p_mcDataSetName)
     parser.add_option("--EXEC", dest="p_submitExec", help="Submit the Grid jobs. Default: no submission", action="store_true", default = False)
     parser.add_option("--firstRun", dest="p_firstRun", help="First run number (inclusive). Default %s" %(p_firstRun), default = p_firstRun)    
     parser.add_option("--lastRun", dest="p_lastRun", help="Last run number (inclusive). Default %s" %(p_lastRun), default = p_lastRun)
-    parser.add_option("--MC", dest="p_usingMC", help="Use MC. The data set must be provided in full. Default: no MC", action="store_true", default = p_usingMC)
     parser.add_option("--minEvents", dest="p_minEvents", help="Minimum number of events. Default %s" %(p_minEvents), default = p_minEvents)
     parser.add_option("--nFiles", dest="p_userFiles", help="User defined number of files. Default %s = all the available files" %(p_userFiles), default = p_userFiles)
     parser.add_option("--run", dest="p_userRun", help="Run number in case of targetting a single run. Default %s" %(p_userRun), default = p_userRun)
     parser.add_option("--physicsType", dest="p_physicsType", help="Physics type to use (physics_Main, Hardprobes...) Default %s" %(p_physicsType), default = p_physicsType)
+    parser.add_option("--script", dest="p_scriptName", help="Name of the python script to be executed. Default %s" %p_scriptName, default = p_scriptName)
     parser.add_option("--userLabel", dest="p_userLabel", help="User defined label. Default %s" %(p_userLabel), default = p_userLabel)
 
     (config, sys.argv[1:]) = parser.parse_args(sys.argv[1:])
@@ -517,8 +607,8 @@ if __name__ == '__main__':
     m_amitag = config.p_amitag
     m_dataProject = config.p_dataProject
     m_physicsType = config.p_physicsType
-    m_usingMC = config.p_usingMC
-    m_mcDataSetName = config.p_mcDataSetName
+    m_scriptName = config.p_scriptName
+    m_userDataSet = config.p_userDataSet
 
     welcomeBanner ()
     preliminaries ()
diff --git a/InnerDetector/InDetMonitoring/InDetPerformanceMonitoring/share/runzmumu_UserConstants.py b/InnerDetector/InDetMonitoring/InDetPerformanceMonitoring/share/runzmumu_UserConstants.py
index 9f1d3b64697b0afda64a8db608e99d58cce3cb6c..bb7d8fcd999d0fc623eec4d31527e66b0f044e5b 100644
--- a/InnerDetector/InDetMonitoring/InDetPerformanceMonitoring/share/runzmumu_UserConstants.py
+++ b/InnerDetector/InDetMonitoring/InDetPerformanceMonitoring/share/runzmumu_UserConstants.py
@@ -32,7 +32,7 @@ monitoringAllTracks = True
 useGRL = False
 
 # MC
-MC_bool = False
+MC_bool = True
 
 # do Trigger
 DoTrigger = False
@@ -41,15 +41,15 @@ DoTrigger = False
 grid_bool = True
 
 # handle input constants
-readPool = False # default True
+readPool = True # default True
 readLocalDynamicDB = False # default False
 
-#inputConstants = "step8_Iter1_AlignmentConstants.root"
-#inputdb = "step8_Iter1_mycool.db"
-inputConstants = "ReAlign_2018_L6_Step28_L3.root"
+#inputConstants = "ReAlign_2018_L6_Step28_L3.root"
+#inputConstants = "step27_358395_AlignmentConstants_Iter0_Block00.root"
+inputConstants = "MisalignmentSet11_p01.pool.root"
 inputdb = "Javi_Test_mycool.db"
-#inputConstants = "MisalignmentSet11_p01.pool.root"
-#inputConstants = "AlignmentConstants_Galo_2018BaseLine.root"
+#inputdb = "step8_Iter1_mycool.db"
+
 if (readPool):
     print " readPool = True file: %s" %(inputConstants)
     import socket
@@ -87,7 +87,8 @@ print ' ========= runzmumu === config == end == '
 #include("InDetSimpleVisual/GetDetectorPositions.py")
 
 if (grid_bool):
-    PoolInput = ["/afs/cern.ch/user/m/martis/mywork/ZmumuNtuples/InputFileForGridJobs/data18_13TeV.00348354.physics_Main.merge.DESDM_ZMUMU.f920_m1831_f920_m1951._0001.1"]
+    # PoolInput = ["/afs/cern.ch/user/m/martis/mywork/ZmumuNtuples/InputFileForGridJobs/data18_13TeV.00348354.physics_Main.merge.DESDM_ZMUMU.f920_m1831_f920_m1951._0001.1"]
+    PoolInput = ["/eos/user/m/martis/data/data18_13TeV/data18_13TeV.00352436.physics_Main.merge.DAOD_ZMUMU.f938_m1831_f938_m1982._0027.1"]
 if (MC_bool): 
     PoolInput = ["/eos/user/m/martis/data/mc16_13TeV/folder_mc16_13TeV.361107.PowhegPythia8EvtGen_AZNLOCTEQ6L1_Zmumu.recon.ESD.e3601_s3126_r10201/ESD.13642341._000503.pool.root.1"]
 
@@ -189,6 +190,7 @@ DetFlags.Calo_setOff()
 DetFlags.Muon_setOn()
 
 
+print " == runzmumu == user may define his favourite alignment == start == "
 from IOVDbSvc.CondDB import conddb
 if (useIDADynamicFolders):
     if (False):
@@ -196,7 +198,7 @@ if (useIDADynamicFolders):
         conddb.addOverride("/Indet/AlignL1/ID" ,"IndetAlignL1ID-R2dynamic_2018_ReAlign_Initial")
         conddb.addOverride("/Indet/AlignL2/PIX" ,"IndetAlignL2PIX-R2dynamic_2018_ReAlign_Initial")
         conddb.addOverride("/Indet/AlignL2/SCT" ,"IndetAlignL2SCT-R2dynamic_2018_ReAlign_Initial")
-        conddb.addOverride("/Indet/AlignL3" ,"IndetIBLDist-R2dynamic_2018_ReAlign_Initial")
+        conddb.addOverride("/Indet/AlignL3" ,"IndetAlignL3-R2dynamic_2018_ReAlign_Initial")
         conddb.addOverride("/Indet/IBLDist", "IndetAlignL3-R2dynamic_2018_ReAlign_Initial")
         conddb.addOverride("/TRT/AlignL1/TRT", "TRTAlignL1-R2dynamic_2018_ReAlign_Initial")
         conddb.addOverride("/TRT/AlignL2", "TRTAlignL2-R2dynamic_2018_ReAlign_Initial")
@@ -228,19 +230,56 @@ if (useIDADynamicFolders):
             conddb.blockFolder("/Indet/AlignL3")
             conddb.blockFolder("/TRT/AlignL2")
         
+    if (False):
+        print (" == runzmumu == configuring 2018 ReAlign family (using RunSet2)") 
+        conddb.addOverride("/Indet/AlignL1/ID" ,"InDetAlignL1_ID_R2dynamic_data18_2ndBatch_Initial")
+        conddb.addOverride("/Indet/AlignL2/PIX" ,"InDetAlignL2PIX_R2dynamic_data18_2ndBatch_Initial")
+        conddb.addOverride("/Indet/AlignL2/SCT" ,"InDetAlignL2SCT_R2dynamic_data18_2ndBatch_Initial")
+        conddb.addOverride("/Indet/IBLDist", "InDetAlignIBLDist_R2dynamic_data18_2ndBatch_Initial")
+        conddb.addOverride("/TRT/AlignL1/TRT", "TRTAlignL1_R2dynamic_data18_2ndBatch_Initial")
+        #conddb.blockFolder("/Indet/AlignL3")
+        #conddb.blockFolder("/TRT/AlignL2")
+        #conddb.addOverride("/Indet/AlignL3" ,"IndetAlignL3-R2dynamic_2018_ReAlign_Initial")
+        #conddb.addOverride("/Indet/AlignL3" ,"InDetAlignL3_R2dynamic_data18_2ndBatch_Initial")
+        conddb.addOverride("/TRT/AlignL2", "TRTAlignL2_R2dynamic_data18_2ndBatch_Initial")
+
+    if (True):
+        print (" == runzmumu == configuring 2018 Salva test")
+        conddb.addOverride("/Indet/AlignL1/ID", "IndetAlignL1ID-R2dynamic-SALVA-2018-step27-TEST0")
+        conddb.addOverride("/Indet/AlignL2/PIX", "IndetAlignL2PIX-R2dynamic-SALVA-2018-step27-TEST0")
+        conddb.addOverride("/Indet/AlignL2/SCT", "IndetAlignL2SCT-R2dynamic-SALVA-2018-step27-TEST0")
+        conddb.addOverride("/Indet/IBLDist", "IndetAlignIBLDIST-R2dynamic-SALVA-2018-step27-TEST0")
+        conddb.addOverride("/Indet/AlignL3", "IndetAlignL3-R2dynamic-SALVA-2018-step27-TEST0")
+        conddb.addOverride("/TRT/AlignL1/TRT", "TRTAlignL1-R2dynamic-SALVA-2018-step27-TEST0")
+        conddb.addOverride("/TRT/AlignL2", "TRTAlignL2-R2dynamic-SALVA-2018-step27-TEST0")
+        if readPool and False:
+            print " == runzmumu == configuring 2018 Salva test == conddb.blockFolder(/Indet/AlignL3)"
+            conddb.blockFolder("/Indet/AlignL3")
+            conddb.blockFolder("/TRT/AlignL2")
+    if (False):
+        print (" == runzmumu == configuring 2018 Stefano test")
+        conddb.addOverride("/Indet/AlignL1/ID",  "InDetAlignL1_ID_R2dynamic_data18_2ndBatch_Initial")
+        conddb.addOverride("/Indet/AlignL2/PIX", "InDetAlignL2PIX_R2dynamic_data18_2ndBatch_Initial")
+        conddb.addOverride("/Indet/AlignL2/SCT", "InDetAlignL2SCT_R2dynamic_data18_2ndBatch_Initial")
+        conddb.addOverride("/Indet/IBLDist",     "InDetAlignIBLDist_R2dynamic_data18_2ndBatch_Initial")
+        conddb.addOverride("/Indet/AlignL3",     "InDetAlignL3_R2dynamic_data18_2ndBatch_Initial") 
+        conddb.addOverride("/TRT/AlignL1/TRT",   "TRTAlignL1_R2dynamic_data18_2ndBatch_Initial")
+        conddb.addOverride("/TRT/AlignL2",       "TRTAlignL2_R2dynamic_data18_2ndBatch_Initial") 
+        if readPool and False:
+            conddb.blockFolder("/Indet/AlignL3")
+            conddb.blockFolder("/TRT/AlignL2")
+print " == runzmumu == user may define his favourite alignment == completed == "
 ##
 
 
-blocked_folders = [
-     '/LAR/ElecCalibMC/AutoCorr',
-     ]
+blocked_folders = ['/LAR/ElecCalibMC/AutoCorr']
 for f in blocked_folders:
     conddb.blockFolder (f)
     print " == runzmumu == folder ",f," --> blocked "
 
 
 inputCollectons =[]
-if 'inputConstants' in dir():
+if 'inputConstants' in dir() and readPool:
     inputCollections = [inputConstants]
     print " == runzmumu == inputConstants in dir() == ", inputConstants
 
diff --git a/InnerDetector/InDetValidation/InDetPhysValMonitoring/share/postInclude.SiHitAnalysis.py b/InnerDetector/InDetValidation/InDetPhysValMonitoring/share/postInclude.SiHitAnalysis.py
index e6d37f65690e654139b7aeffb16b31fbb8718442..8f0f2f7967d9a2359b92fd59743d8edb81187d85 100644
--- a/InnerDetector/InDetValidation/InDetPhysValMonitoring/share/postInclude.SiHitAnalysis.py
+++ b/InnerDetector/InDetValidation/InDetPhysValMonitoring/share/postInclude.SiHitAnalysis.py
@@ -1,4 +1,4 @@
-from AtlasGeoModel.InDetGMJobProperties import InDetGeometryFlags
+from AtlasGeoModel.InDetGMJobProperties import GeometryFlags as InDetGeometryFlags
 from HitAnalysis.HitAnalysisConf import SiHitAnalysis
 topSequence += SiHitAnalysis('PixelHitAnalysis')
 topSequence.PixelHitAnalysis.CollectionName='PixelHits'
diff --git a/PhysicsAnalysis/DerivationFramework/DerivationFrameworkInDet/DerivationFrameworkInDet/PixelNtupleMaker.h b/PhysicsAnalysis/DerivationFramework/DerivationFrameworkInDet/DerivationFrameworkInDet/PixelNtupleMaker.h
index ff8ad29b146bc1f12bbb26189fc68c4f77eebd97..a15e08c8cdb929b6421bd5928733e74d5a1d38e8 100644
--- a/PhysicsAnalysis/DerivationFramework/DerivationFrameworkInDet/DerivationFrameworkInDet/PixelNtupleMaker.h
+++ b/PhysicsAnalysis/DerivationFramework/DerivationFrameworkInDet/DerivationFrameworkInDet/PixelNtupleMaker.h
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
 */
 
 #ifndef DERIVATIONFRAMEWORK_PIXELNTUPLEMAKER_H
diff --git a/PhysicsAnalysis/DerivationFramework/DerivationFrameworkInDet/src/PixelNtupleMaker.cxx b/PhysicsAnalysis/DerivationFramework/DerivationFrameworkInDet/src/PixelNtupleMaker.cxx
index f3bac6a615452eda31285883e9a41aecec5d7b0b..a0e23fbbd86bd4dad7ce3d2283b611f6d4d2f3c2 100644
--- a/PhysicsAnalysis/DerivationFramework/DerivationFrameworkInDet/src/PixelNtupleMaker.cxx
+++ b/PhysicsAnalysis/DerivationFramework/DerivationFrameworkInDet/src/PixelNtupleMaker.cxx
@@ -117,6 +117,8 @@ bool DerivationFramework::PixelNtupleMaker::eventPassesFilter() const {
       std::vector<int> clusterIsolation20x4;
       std::vector<int> numTotalClustersPerModule;
       std::vector<int> numTotalPixelsPerModule;
+      std::vector<int>   moduleBSerr;
+      std::vector<int>   moduleDCSstate;
       std::vector<float> moduleBiasVoltage;
       std::vector<float> moduleTemperature;
       std::vector<float> moduleLorentzShift;
@@ -179,6 +181,16 @@ bool DerivationFramework::PixelNtupleMaker::eventPassesFilter() const {
               unbiasedResidualY.push_back(msos->unbiasedResidualY());
               unbiasedPullX.push_back(msos->auxdata<float>("unbiasedPullX"));
               unbiasedPullY.push_back(msos->auxdata<float>("unbiasedPullY"));
+
+              moduleBSerr.push_back((*clus_itr)->auxdata<int>("isBSError"));
+
+              if ((*clus_itr)->auxdata<std::string>("DCSState")=="ON" || (*clus_itr)->auxdata<std::string>("DCSState")=="READY") {
+                moduleDCSstate.push_back(0);
+              }
+              else {
+                moduleDCSstate.push_back(1);
+              }
+
               moduleBiasVoltage.push_back((*clus_itr)->auxdata<float>("BiasVoltage"));
               moduleTemperature.push_back((*clus_itr)->auxdata<float>("Temperature"));
               moduleLorentzShift.push_back((*clus_itr)->auxdata<float>("LorentzShift"));
@@ -405,6 +417,8 @@ bool DerivationFramework::PixelNtupleMaker::eventPassesFilter() const {
       static SG::AuxElement::Decorator<std::vector<int>>   ClusterIsolation20x4("ClusterIsolation20x4");
       static SG::AuxElement::Decorator<std::vector<int>>   NumTotalClustersPerModule("NumTotalClustersPerModule");
       static SG::AuxElement::Decorator<std::vector<int>>   NumTotalPixelsPerModule("NumTotalPixelsPerModule");
+      static SG::AuxElement::Decorator<std::vector<int>>   ModuleBSError("ModuleBSError");
+      static SG::AuxElement::Decorator<std::vector<int>>   ModuleDCSState("ModuleDCSState");
       static SG::AuxElement::Decorator<std::vector<float>> ModuleBiasVoltage("ModuleBiasVoltage");
       static SG::AuxElement::Decorator<std::vector<float>> ModuleTemperature("ModuleTemperature");
       static SG::AuxElement::Decorator<std::vector<float>> ModuleLorentzShift("ModuleLorentzShift");
@@ -421,8 +435,8 @@ bool DerivationFramework::PixelNtupleMaker::eventPassesFilter() const {
       static SG::AuxElement::Decorator<std::vector<std::vector<float>>> SiHitEndPosY("SiHitEndPosY");
       static SG::AuxElement::Decorator<std::vector<std::vector<float>>> SiHitEnergyDeposit("SiHitEnergyDeposit");
 
-      d0err(*tp)             = (*trk)->definingParametersCovMatrixVec().at(0);
-      z0err(*tp)             = (*trk)->definingParametersCovMatrixVec().at(2);
+      d0err(*tp)             = TMath::Sqrt((*trk)->definingParametersCovMatrix()(0,0));
+      z0err(*tp)             = TMath::Sqrt((*trk)->definingParametersCovMatrix()(1,1));
       qOverPerr(*tp)         = TMath::Sqrt((*trk)->definingParametersCovMatrix()(4,4));
 
       HoleIndex(*tp)         = holeIndex;
@@ -458,6 +472,8 @@ bool DerivationFramework::PixelNtupleMaker::eventPassesFilter() const {
       ClusterIsolation20x4(*tp) = clusterIsolation20x4;
       NumTotalClustersPerModule(*tp) = numTotalClustersPerModule;
       NumTotalPixelsPerModule(*tp)   = numTotalPixelsPerModule;
+      ModuleBSError(*tp)      = moduleBSerr;
+      ModuleDCSState(*tp)     = moduleDCSstate;
       ModuleBiasVoltage(*tp)  = moduleBiasVoltage;
       ModuleTemperature(*tp)  = moduleTemperature;
       ModuleLorentzShift(*tp) = moduleLorentzShift;
diff --git a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/LongLivedParticleDPDMaker/KinkTrkSingleJetMetFilterTool.h b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/LongLivedParticleDPDMaker/KinkTrkSingleJetMetFilterTool.h
index 8dedf66cd6fc572857455dad113a368d0346f514..3ee9418a6226b02636fd289295f5eb79609acffc 100644
--- a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/LongLivedParticleDPDMaker/KinkTrkSingleJetMetFilterTool.h
+++ b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/LongLivedParticleDPDMaker/KinkTrkSingleJetMetFilterTool.h
@@ -40,6 +40,9 @@ namespace DerivationFramework {
   private:
     mutable unsigned int m_ntot;
     mutable unsigned int m_npass;
+
+    ToolHandle<CP::IMuonSelectionTool> m_muonSelectionTool;
+
     bool m_passAll;
     bool m_LeptonVeto;
     bool m_isolatedTrack;
@@ -47,7 +50,6 @@ namespace DerivationFramework {
     std::string m_jetSGKey;
     std::string m_metSGKey;
     std::string m_metTerm;
-    ToolHandle<CP::IMuonSelectionTool> m_muonSelectionTool;
     std::string m_muonSGKey;
     std::string m_muonIDKey;
     std::string m_electronSGKey;
diff --git a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/DVFlags.py b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/DVFlags.py
index 5215478b403b7d02864fbd0258434e747d6e9097..576407591e3786777e1894c034179bc03784509c 100644
--- a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/DVFlags.py
+++ b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/DVFlags.py
@@ -10,6 +10,7 @@ primRPVLLDESDM=jobproperties.PrimaryDPDFlags_RPVLLStream
 ## TriggerAPI ##
 from LongLivedParticleDPDMaker.RPVLLTriggers import RPVLLTriggers
 apitriggers = RPVLLTriggers()
+from LongLivedParticleDPDMaker.RPVLLTriggers import rpvllTrig
 
 
 class DV_containerFlags(JobProperty):
@@ -37,7 +38,7 @@ class DV_MultiJetTriggerFlags(JobProperty):
 			"HLT_7j25_gsc50_boffperf_split_L14J20" ]#gsc 
     triggers += ["HLT_2j275_j140","HLT_2j250_j120","HLT_2j220_j120"]#3jet
     triggers += ["HLT_5j85_L14J15"] # 2018 5 jet
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         triggers += apitriggers.getDVMultiJetTriggers() ## TriggerAPI
     pass
 primRPVLLDESDM.add_JobProperty(DV_MultiJetTriggerFlags)
@@ -164,7 +165,7 @@ class DV_MuonBarrelFilterFlags(JobProperty):
     cutEtMin=60.0*Units.GeV
     cutEtaMax=1.1
     triggers=["HLT_mu60_0eta105_msonly"]
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         triggers += apitriggers.getDVMuonBarrelTriggers() ## TriggerAPI
     nPassed=1
     pass
@@ -177,7 +178,7 @@ class DV_MuonFullMSFilterFlags(JobProperty):
     cutEtMin=80.0*Units.GeV
     cutEtaMax=2.5
     triggers=["HLT_mu80_msonly_3layersEC"]
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         triggers += apitriggers.getDVMuonFullMSTriggers() ## TriggerAPI
     nPassed=1
     pass
@@ -192,7 +193,7 @@ class DV_PhotonFilterFlags(JobProperty):
     cutIsEM="Loose"
     triggers=["HLT_g140_loose"]
     triggers+=["HLT_g140_tight","HLT_g200_loose"]#2017
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         triggers += apitriggers.getDVPhotonTriggers() ## TriggerAPI
     prescale=20
     nPassed=1
@@ -225,7 +226,7 @@ class DV_METFilterFlags(JobProperty):
     triggers+=[ "HLT_xe110_pufit_xe70_L1XE50" ] #2018 primary
     triggers+=[ "HLT_xe110_pufit_xe65_L1XE55","HLT_xe100_pufit_xe75_L1XE60", 
                 "HLT_xe110_pufit_xe65_L1XE60" ] #2018 backup
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         triggers += apitriggers.getDVMETTriggers() ## TriggerAPI
     pass
 primRPVLLDESDM.add_JobProperty(DV_METFilterFlags)
diff --git a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/DiLepFlags.py b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/DiLepFlags.py
index 1a8e1c94d728049ca137b4c8beda123ad20a8a51..f510b356b47b06eadf4d89d9ec0ab1856468becb 100644
--- a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/DiLepFlags.py
+++ b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/DiLepFlags.py
@@ -10,6 +10,8 @@ primRPVLLDESDM = jobproperties.PrimaryDPDFlags_RPVLLStream
 ## TriggerAPI ##
 from LongLivedParticleDPDMaker.RPVLLTriggers import RPVLLTriggers
 apitriggers = RPVLLTriggers()
+from LongLivedParticleDPDMaker.RPVLLTriggers import rpvllTrig
+
 
 class DiLep_FilterFlags(JobProperty):
     statusOn       = True
@@ -17,16 +19,16 @@ class DiLep_FilterFlags(JobProperty):
     StoredValue    = True
     
     SiPhTriggers    = ["HLT_g140_loose", "HLT_g200_loose", "HLT_g200_loose_L1EM24VHIM"]
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         SiPhTriggers   += apitriggers.getDiLepSiPhTriggers() ## TriggerAPI
     DiPhTriggers    = ["HLT_2g50_loose_L12EM20VH", "HLT_2g60_loose_L12EM20VH"]
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         DiPhTriggers   += apitriggers.getDiLepDiPhTriggers() ## TriggerAPI
     SiMuTriggers    = ["HLT_mu80_msonly_3layersEC"]
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         SiMuTriggers   += apitriggers.getDiLepSiMuTriggers() ## TriggerAPI
     SiMuBaTriggers  = ["HLT_mu60_0eta105_msonly"]
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         SiMuBaTriggers += apitriggers.getDiLepSiMuBaTriggers() ## TriggerAPI
     
     ElEtaMax       = 2.5
diff --git a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/EmergingFlags.py b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/EmergingFlags.py
index 9d14bc2849db8813cf79bcd8de273488ddde77f7..ae7464deaa27b7a305db397dc39a62812dae8e62 100644
--- a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/EmergingFlags.py
+++ b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/EmergingFlags.py
@@ -10,6 +10,7 @@ primRPVLLDESDM = jobproperties.PrimaryDPDFlags_RPVLLStream
 ## TriggerAPI ##
 from LongLivedParticleDPDMaker.RPVLLTriggers import RPVLLTriggers
 apitriggers = RPVLLTriggers()
+from LongLivedParticleDPDMaker.RPVLLTriggers import rpvllTrig
 
 class Emerging_FilterFlags(JobProperty):
     statusOn     = True
@@ -26,7 +27,7 @@ class Emerging_FilterFlags(JobProperty):
                     "HLT_4j140",
                     "HLT_4j150"
                     ]
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         Triggers    += apitriggers.getEmergingTriggers() ## TriggerAPI
 primRPVLLDESDM.add_JobProperty(Emerging_FilterFlags)
 
diff --git a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/HVFlags.py b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/HVFlags.py
index f18fd997220d096d57c28e39624e987d43a3bd3c..850462b1bdd4cb6b0b83c9b494e73194ae9a9bb1 100644
--- a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/HVFlags.py
+++ b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/HVFlags.py
@@ -10,6 +10,8 @@ primRPVLLDESDM=jobproperties.PrimaryDPDFlags_RPVLLStream
 ## TriggerAPI ##
 from LongLivedParticleDPDMaker.RPVLLTriggers import RPVLLTriggers
 apitriggers = RPVLLTriggers()
+from LongLivedParticleDPDMaker.RPVLLTriggers import rpvllTrig
+
 
 class HV_MuvtxTriggerFlags(JobProperty):
     statusOn = True
@@ -22,7 +24,7 @@ class HV_MuvtxTriggerFlags(JobProperty):
         #"HLT_j30_muvtx_L1MU4_UNPAIRED_ISO",
         #EMPTY and UNPAIRED_ISO triggers are currently in physics_Late, and thus not part of DRAW_RPVLL    
     ]
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         TriggerNames += apitriggers.getHVMuvtxTriggers() ## TriggerAPI
     pass
 primRPVLLDESDM.add_JobProperty(HV_MuvtxTriggerFlags)
@@ -34,7 +36,7 @@ class HV_prescaledMuvtxTriggerFlags(JobProperty):
     TriggerNames = [
         "HLT_j30_muvtx_noiso",
     ]
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         TriggerNames += apitriggers.getHVprescaledMuvtxTriggers() ## TriggerAPI
     Prescale = 1
     pass
@@ -71,7 +73,7 @@ class HV_CalRatioTriggerFlags(JobProperty):
         #"HLT_j30_jes_cleanLLP_PS_llp_noiso_L1TAU8_UNPAIRED_ISO"
         #EMPTY and UNPAIRED_ISO triggers are currently in physics_Late, and thus not part of DRAW_RPVLL    
     ]
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         TriggerNames += apitriggers.getHVCalRatioTriggers() ## TriggerAPI
 
     pass
diff --git a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/HipsFlags.py b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/HipsFlags.py
index 1f8e749bfef38fd157fa9ea487749b3d99923e69..8ecb80538bb839c2303a7cab6327ba3841a0dd17 100644
--- a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/HipsFlags.py
+++ b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/HipsFlags.py
@@ -15,13 +15,15 @@ primHIPsDESD=jobproperties.PrimaryDPDFlags_HIPsStream
 ## TriggerAPI ##
 from LongLivedParticleDPDMaker.RPVLLTriggers import RPVLLTriggers
 apitriggers = RPVLLTriggers()
+from LongLivedParticleDPDMaker.RPVLLTriggers import rpvllTrig
+
 
 class HipsTriggerFilterExpression(JobProperty):
     statusOn = True
     allowedTypes = ['bool']
     StoredValue = True
     triggers = ['HLT_g0_hiptrt_L1EM18VH', 'HLT_g0_hiptrt_L1EM20VH', 'HLT_g0_hiptrt_L1EM20VHI', 'HLT_g0_hiptrt_L1EM22VHI', 'HLT_g0_hiptrt_L1EM24VHI', 'HLT_g0_hiptrt_L1EM24VHIM']
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         triggers += apitriggers.getHIPsTriggers() # TriggerAPI
     pass
 primHIPsDESD.add_JobProperty(HipsTriggerFilterExpression)
diff --git a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/KinkedTrackFlags.py b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/KinkedTrackFlags.py
index b86cc799d17682d5ba54f850a887e46ecdad7c02..309ae989064c319261d6e6eeaeefdf2afaeacf39 100644
--- a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/KinkedTrackFlags.py
+++ b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/KinkedTrackFlags.py
@@ -24,6 +24,8 @@ primRPVLLDESDM=jobproperties.PrimaryDPDFlags_RPVLLStream
 ## TriggerAPI ##
 from LongLivedParticleDPDMaker.RPVLLTriggers import RPVLLTriggers
 apitriggers = RPVLLTriggers()
+from LongLivedParticleDPDMaker.RPVLLTriggers import rpvllTrig
+
 
 class KinkedTrack_containerFlags(JobProperty):
     statusOn = True
@@ -151,6 +153,7 @@ class KinkedTrack_singleJetMetFilterFlags(JobProperty):
         'HLT_xe110_pufit_xe70_L1XE50',
         'HLT_xe110_pufit_xe65_L1XE55',
         'HLT_xe110_pufit_xe65_L1XE60',
+        'HLT_xe110_pufit_xe65_L1XE50',
         'HLT_xe110_L1XE60',
         'HLT_xe110_mht_L1XE60',
         'HLT_xe110_mht_L1XE50',
@@ -174,7 +177,7 @@ class KinkedTrack_singleJetMetFilterFlags(JobProperty):
         'HLT_xe120_pufit_L1XE60',
         'HLT_xe130_mht_L1XE50'        
         ]
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         triggerNames += apitriggers.getKinkedTrackJetMetTriggers() ## TriggerAPI
     cutsEtMin = [80.0*Units.GeV, 40.0*Units.GeV]
     cutsEtMinForStublet = [90.0*Units.GeV, 40.0*Units.GeV]
@@ -184,7 +187,7 @@ class KinkedTrack_singleJetMetFilterFlags(JobProperty):
     cutEtaMax = 3.2
     doLeptonVeto = True
     requireIsolatedTrack = True
-    electronIDKey = "Tight"
+    electronIDKey = "LHTight"
     muonIDKey = "Medium"
     leptonPtMax = 20.0*Units.GeV
     leptonEtaMax = 2.5
@@ -235,13 +238,15 @@ class KinkedTrack_ZeeFilterFlags(JobProperty):
         'HLT_e28_lhtight_ivarloose',
         'HLT_e28_lhtight_nod0_iloose',
         'HLT_e28_lhtight_nod0_ivarloose',
+        'HLT_e60_lhmedium_nod0',
+        'HLT_e140_lhloose_nod0'
         ]
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         triggerNames += apitriggers.getKinkedTrackZeeTriggers() ## TriggerAPI
     doTriggerMatch = False
     electronPtMin = 40*Units.GeV
     electronEtaMax = 2.5
-    electronIDKeys = ["Tight"]
+    electronIDKeys = ["LHTight"]
     clusterEtMin = 15*Units.GeV
     clusterEtaMax = 2.5
     diElectronMassLow = (91.1876-40)*Units.GeV
@@ -270,7 +275,7 @@ class KinkedTrack_ZmumuFilterFlags(JobProperty):
         'HLT_mu26_imedium',
         'HLT_mu26_ivarmedium',
         ]
-    if apitriggers.doTriggerAPI:
+    if rpvllTrig.doRPVLLTriggerAPI:
         triggerNames += apitriggers.getKinkedTrackZmumuTriggers() ## TriggerAPI
     doTriggerMatch = False
     muonPtMin = 40*Units.GeV
diff --git a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/RPVLLTriggers.py b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/RPVLLTriggers.py
index d3ceb0a0b9974ea61c295c08707d4d4ceade5a43..7dd246e9a379161d6f8c33d5540de5563c88ab9e 100644
--- a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/RPVLLTriggers.py
+++ b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/python/RPVLLTriggers.py
@@ -1,6 +1,11 @@
 from TriggerMenu.api.TriggerAPI import TriggerAPI
 from TriggerMenu.api.TriggerEnums import TriggerPeriod, TriggerType
 
+from AthenaCommon.JobProperties import JobProperty, JobPropertyContainer
+from AthenaCommon.JobProperties import jobproperties
+
+import AthenaCommon.SystemOfUnits as Units
+
 
 # general function to get current menu unprescaled triggers for given trigger type
 #def getTriggerList( trigger_type, matching_pattern="", rejection_pattern="", test=[] ):
@@ -155,5 +160,19 @@ class RPVLLTriggers:
         HIPsList = getTriggerList( TriggerType.exotics, "hiptrt" )
         return HIPsList
 
-    # on / off switch
-    doTriggerAPI = True
+
+    
+# Flags to turn RPVLL TriggerAPI implementation on/off
+class RPVLLTriggerAPIFlags(JobPropertyContainer):
+    """ RPV/LL TriggerAPI flag container """
+
+jobproperties.add_Container(RPVLLTriggerAPIFlags)
+
+rpvllTrig=jobproperties.RPVLLTriggerAPIFlags
+
+class doRPVLLTriggerAPI(JobProperty):
+    statusOn = True
+    allowedTypes = ["bool"]
+    StoredValue = True
+    pass
+rpvllTrig.add_JobProperty(doRPVLLTriggerAPI)
diff --git a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/share/PhysDESDM_HNL.py b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/share/PhysDESDM_HNL.py
index b4074f11b12abe1a40239bd1ab7d8473669004ae..975752ba7af4b0072432bd9adb0e9d158035be4a 100644
--- a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/share/PhysDESDM_HNL.py
+++ b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/share/PhysDESDM_HNL.py
@@ -6,8 +6,9 @@ from DerivationFrameworkCore.DerivationFrameworkCoreConf import DerivationFramew
 ## TriggerAPI ##
 from LongLivedParticleDPDMaker.RPVLLTriggers import RPVLLTriggers
 apitriggers = RPVLLTriggers()
+from LongLivedParticleDPDMaker.RPVLLTriggers import rpvllTrig
 apitriggerlist = []
-if apitriggers.doTriggerAPI:
+if rpvllTrig.doRPVLLTriggerAPI:
     apitriggerlist = apitriggers.getHNLTriggers()
 
 HnlFilterTool = skimtool( name = "HnlFilterTool",
diff --git a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/share/PhysDESDM_KinkedTrack.py b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/share/PhysDESDM_KinkedTrack.py
index d3053c4a4900a412dad2defed3d51621b88870a7..4103cc92d345b8af6e62ecc152ea91b4810e70b5 100644
--- a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/share/PhysDESDM_KinkedTrack.py
+++ b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/share/PhysDESDM_KinkedTrack.py
@@ -33,9 +33,6 @@ def KinkTrkTriggerFilterString(flags):
 
     return selectionString
 
-
-
-
 #====================================================================
 # JetMetFilter
 #====================================================================
diff --git a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/KinkTrkSingleJetMetFilterTool.cxx b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/KinkTrkSingleJetMetFilterTool.cxx
index 03d0ceb3f535d4bd80302ed5b53c72d8146d90a5..ecb7aed4ec54f19146df3a9ec1bc4a412baf5ee4 100644
--- a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/KinkTrkSingleJetMetFilterTool.cxx
+++ b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/KinkTrkSingleJetMetFilterTool.cxx
@@ -25,6 +25,7 @@ DerivationFramework::KinkTrkSingleJetMetFilterTool::KinkTrkSingleJetMetFilterToo
   AthAlgTool(t,n,p),
   m_ntot(0),
   m_npass(0),
+  m_muonSelectionTool("CP::MuonSelectionTool/MuonSelectionTool"),
   m_passAll(false),
   m_LeptonVeto(false),
   m_isolatedTrack(false),
@@ -32,11 +33,10 @@ DerivationFramework::KinkTrkSingleJetMetFilterTool::KinkTrkSingleJetMetFilterToo
   m_jetSGKey("AntiKt4LCTopoJets"),
   m_metSGKey("MET_RefFinal"),
   m_metTerm("Final"),
-  m_muonSelectionTool("CP::MuonSelectionTool/MuonSelectionTool"),
   m_muonSGKey("Muons"),
   m_muonIDKey("Medium"),
   m_electronSGKey("ElectronCollection"),
-  m_electronIDKey("Tight"),
+  m_electronIDKey("LHTight"),
   m_metCut(-1),
   m_jetPtCuts(std::vector<float>()),
   m_jetEtaMax(3.2),
@@ -78,6 +78,7 @@ StatusCode DerivationFramework::KinkTrkSingleJetMetFilterTool::initialize()
 {
   ATH_MSG_VERBOSE("initialize() ...");
 
+  // Muon selection
   CHECK(m_muonSelectionTool.retrieve());
 
   return StatusCode::SUCCESS;
@@ -170,26 +171,10 @@ bool DerivationFramework::KinkTrkSingleJetMetFilterTool::eventPassesFilter() con
     // Retrieve muon container	
     const xAOD::MuonContainer* muons(0);
     ATH_CHECK( evtStore()->retrieve(muons, m_muonSGKey) );	
-    int qflag(0);
-    if (m_muonIDKey == "VeryLoose") {
-      qflag = xAOD::Muon::VeryLoose;
-    } else if (m_muonIDKey == "Loose") {
-      qflag = xAOD::Muon::Loose;
-    } else if (m_muonIDKey == "Medium") {
-      qflag = xAOD::Muon::Medium;
-    } else if (m_muonIDKey == "Tight") {
-      qflag = xAOD::Muon::Tight;
-    } else {
-      ATH_MSG_WARNING("Cannot find the muon quality flag " << m_muonIDKey << ". Use Medium instead.");
-      qflag = xAOD::Muon::Medium;
-    }
-
     for (auto muon: *muons) {
-      bool passID(false);
-      if (m_muonSelectionTool->getQuality(*muon) <= qflag) {
-        passID = true;
-      }
-      if (muon->pt() > m_leptonPtCut && fabs(muon->eta()) < m_leptonEtaMax && passID) {
+      if( !m_muonSelectionTool->passedMuonCuts(*muon) ) continue;
+      if( muon->muonType() != xAOD::Muon::Combined    ) continue;
+      if(muon->pt() > m_leptonPtCut && fabs(muon->eta()) < m_leptonEtaMax ) {
         return acceptEvent; 
       }
     }
@@ -199,8 +184,8 @@ bool DerivationFramework::KinkTrkSingleJetMetFilterTool::eventPassesFilter() con
     ATH_CHECK(evtStore()->retrieve(electrons, m_electronSGKey));	
     for (auto ele: *electrons) {
       bool passID(false);
-      if (!ele->passSelection(passID, m_electronIDKey)) {
-        ATH_MSG_WARNING("Cannot find the electron quality flag " << m_muonIDKey);
+      if( !ele->passSelection(passID,m_electronIDKey) ){
+        ATH_MSG_WARNING("Cannot find the electron quality flag " << m_electronIDKey);
       }
       if (ele->pt() > m_leptonPtCut && fabs(ele->eta()) < m_leptonEtaMax && passID) {
         return acceptEvent; 
@@ -210,13 +195,27 @@ bool DerivationFramework::KinkTrkSingleJetMetFilterTool::eventPassesFilter() con
 
   // at least 1 isolated pixel tracklet OR standard track
   if(m_isolatedTrack){
-    //if(true){
+
     // Find IsolatedTracklet
-    bool passIsolatedTracklet = false;
     const xAOD::TrackParticleContainer *pixelTrackletContainer=NULL;
     ATH_CHECK( evtStore()->retrieve(pixelTrackletContainer, "InDetPixelPrdAssociationTrackParticles") );
-    
-    for(auto Tracklet : *pixelTrackletContainer){
+
+    const xAOD::VertexContainer* vertices(0);
+    ATH_CHECK( evtStore()->retrieve(vertices, "PrimaryVertices") );
+    const xAOD::Vertex* pv = 0;
+    for( const auto& v: *vertices ){
+      if( v->vertexType() == xAOD::VxType::PriVtx ){
+        pv = v;
+        break;
+      }
+    }
+    if( !pv ){
+      ATH_MSG_WARNING("Cannot find a PV in the event; reject it!");
+      return false;
+    }
+
+    bool passIsolatedTracklet = false;
+    for(const auto& Tracklet : *pixelTrackletContainer){
       passIsolatedTracklet = true;
       for(unsigned int i=0;i<goodJets.size();i++){
 	double deltaPhi = (fabs(Tracklet->phi() - goodJets.at(i)->phi()) > M_PI) ? 2.0*M_PI-fabs(Tracklet->phi()-goodJets.at(i)->phi()) : fabs(Tracklet->phi()-goodJets.at(i)->phi());
@@ -231,6 +230,11 @@ bool DerivationFramework::KinkTrkSingleJetMetFilterTool::eventPassesFilter() con
       
       if(passIsolatedTracklet==false)
 	continue;
+
+      if( Tracklet->pt() < 20000.0 ){
+        passIsolatedTracklet = false;
+        continue;
+      } 
       
       if(TMath::Abs(Tracklet->eta()) < 0.1 || TMath::Abs(Tracklet->eta()) > 1.9){
 	passIsolatedTracklet = false;
@@ -242,7 +246,7 @@ bool DerivationFramework::KinkTrkSingleJetMetFilterTool::eventPassesFilter() con
 	continue;
       }
 
-      if(Tracklet->auxdata<UChar_t>("numberOfContribPixelLayers")<4){
+      if(Tracklet->auxdata<UChar_t>("numberOfContribPixelLayers")<3){
 	passIsolatedTracklet = false;
 	continue;
       }
@@ -251,8 +255,16 @@ bool DerivationFramework::KinkTrkSingleJetMetFilterTool::eventPassesFilter() con
 	passIsolatedTracklet = false;
 	continue;
       }
+
+      double z0SinTheta = (Tracklet->z0() + Tracklet->vz() - pv->z() ) * TMath::Sin(Tracklet->p4().Theta());
+      if( fabs(z0SinTheta) > 5.0 ){
+        passIsolatedTracklet = false;
+        continue;
+      }
+
       
       if(passIsolatedTracklet)	break;
+
     }// for Tracklet
     
     if(passIsolatedTracklet==false){
@@ -262,7 +274,7 @@ bool DerivationFramework::KinkTrkSingleJetMetFilterTool::eventPassesFilter() con
       const xAOD::TrackParticleContainer *standardTrackContainer=NULL;
       ATH_CHECK( evtStore()->retrieve(standardTrackContainer, "InDetTrackParticles") );
       
-      for(auto StdTrack : *standardTrackContainer){
+      for(const auto& StdTrack : *standardTrackContainer){
 	if(StdTrack->pt()/1000.0 < 20.0)
 	  continue;
 
diff --git a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/KinkTrkZeeTagTool.cxx b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/KinkTrkZeeTagTool.cxx
index 44988ef6ca1de73702a2e60dcd05be9598390d10..ddcf1ebb463064974b0b6b4bf04d46442767088d 100644
--- a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/KinkTrkZeeTagTool.cxx
+++ b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/KinkTrkZeeTagTool.cxx
@@ -176,8 +176,8 @@ bool DerivationFramework::KinkTrkZeeTagTool::checkEleClusPair(const xAOD::Electr
 
 bool DerivationFramework::KinkTrkZeeTagTool::passElectronQuality(const xAOD::Electron *ele) const
 {
-  if (ele->pt() < m_electronPtCut) return false;
-  if (fabs(ele->eta()) > m_electronEtaMax) return false;
+  if( ele->pt() < m_electronPtCut           ) return false;
+  if( fabs(ele->eta()) > m_electronEtaMax   ) return false;
   bool passID(false);
   for (unsigned int i=0; i<m_electronIDKeys.size(); i++) {
     if (ele->passSelection(passID, m_electronIDKeys[i])) {
@@ -187,6 +187,7 @@ bool DerivationFramework::KinkTrkZeeTagTool::passElectronQuality(const xAOD::Ele
     }
   }
   if (!passID) return false;
+
   return true;
 }
 
diff --git a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/KinkTrkZmumuTagTool.cxx b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/KinkTrkZmumuTagTool.cxx
index b6cc0a3909aff952b36ab1eec054564b730bd43d..8c1ddf647a7ac70390355779c960a8cdadfeb30d 100644
--- a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/KinkTrkZmumuTagTool.cxx
+++ b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/KinkTrkZmumuTagTool.cxx
@@ -184,32 +184,12 @@ bool DerivationFramework::KinkTrkZmumuTagTool::checkMuonTrackPair(const xAOD::Mu
 
 bool DerivationFramework::KinkTrkZmumuTagTool::passMuonQuality(const xAOD::Muon *muon) const
 {
-  if (muon->pt() < m_muonPtCut) return false;
-  if (fabs(muon->eta()) > m_muonEtaMax) return false;
-
-  bool passID(false);
-  for (unsigned int i=0; i<m_muonIDKeys.size(); i++) {
-    int qflag(0);
-    if (m_muonIDKeys[i] == "VeryLoose") {
-      qflag = xAOD::Muon::VeryLoose;
-    } else if (m_muonIDKeys[i] == "Loose") {
-      qflag = xAOD::Muon::Loose;
-    } else if (m_muonIDKeys[i] == "Medium" ) {
-      qflag = xAOD::Muon::Medium;
-    } else if (m_muonIDKeys[i] == "Tight") { 
-      qflag = xAOD::Muon::Tight;
-    } else {
-      ATH_MSG_WARNING("Cannot find the muon quality flag " << m_muonIDKeys[i] << ". Use Medium instead.");
-      qflag = xAOD::Muon::Medium;
-    }
-
-    if ( m_muonSelectionTool->getQuality(*muon) <= qflag ) {
-      passID = true;
-      break;
-    }
-  }
-  if (!passID) return false;
+  if( muon->pt() < m_muonPtCut                    ) return false;
+  if( fabs(muon->eta()) > m_muonEtaMax            ) return false;
+  if( !m_muonSelectionTool->passedMuonCuts(*muon) ) return false;
+  if( muon->muonType() != xAOD::Muon::Combined    ) return false;
 
+  // Good muon!
   return true;
 }
 
diff --git a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/VHLowTrackJetFilterTool.cxx b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/VHLowTrackJetFilterTool.cxx
index 81f81036e8caf3dd89d8b9758a0f23ee808a92de..bd4600238aa5ec9ddb5a53207b1fab3f19b20ac7 100644
--- a/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/VHLowTrackJetFilterTool.cxx
+++ b/PhysicsAnalysis/SUSYPhys/LongLivedParticleDPDMaker/src/VHLowTrackJetFilterTool.cxx
@@ -7,7 +7,6 @@
 ///////////////////////////////////////////////////////////////////
 
 #include "LongLivedParticleDPDMaker/VHLowTrackJetFilterTool.h"
-
 #include "xAODEgamma/ElectronContainer.h"
 #include "xAODEventInfo/EventInfo.h"
 #include "xAODJet/JetContainer.h"
@@ -41,7 +40,7 @@ m_AlphaMaxCut(0.03),
 m_CHFCut(0.3),
 m_nJetsReq(0),
 m_electronSGKey("Electrons"),
-m_electronIDKey("Medium"),
+m_electronIDKey("LHMedium"),
 m_electronPtCut(0),
 m_muonSelectionTool("CP::MuonSelectionTool/MuonSelectionTool"),
 m_muonSGKey("Muons"),
@@ -74,7 +73,6 @@ m_muonPtCut(0)
 }
 
 // Athena finalize
-
 StatusCode DerivationFramework::VHLowTrackJetFilterTool::finalize()
 {
   ATH_MSG_VERBOSE("finalize() ...");
@@ -92,7 +90,6 @@ StatusCode DerivationFramework::VHLowTrackJetFilterTool::finalize()
 // The filter itself
 bool DerivationFramework::VHLowTrackJetFilterTool::eventPassesFilter() const
 {
-
   typedef std::vector<const xAOD::TrackParticle*> Particles;
   typedef ElementLink<xAOD::TrackParticleContainer> TrackLink;
   typedef std::vector<TrackLink> TrackLinks;
@@ -115,7 +112,8 @@ bool DerivationFramework::VHLowTrackJetFilterTool::eventPassesFilter() const
   }
   
   //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-  
+
+
   //electron portion
   const xAOD::ElectronContainer* electrons(0);
   sc = evtStore()->retrieve(electrons,m_electronSGKey);
@@ -124,12 +122,14 @@ bool DerivationFramework::VHLowTrackJetFilterTool::eventPassesFilter() const
     return false;
   }
   
+
   // loop over the electrons in the container
   for (auto electron : *electrons){
-    
+
     if(electron->pt()<m_electronPtCut) continue;
     if( (fabs(electron->caloCluster()->etaBE(2))>1.37 && fabs(electron->caloCluster()->etaBE(2)<1.52))
        || fabs(electron->caloCluster()->etaBE(2))>2.47 ) continue;
+
     if(electron->isolation(xAOD::Iso::topoetcone20)/electron->pt()>0.2) continue;
     
     bool passID=false;
@@ -147,7 +147,7 @@ bool DerivationFramework::VHLowTrackJetFilterTool::eventPassesFilter() const
         double delta_z0 = tp->z0() + tp->vz() - vertex->z();
         double theta = tp->theta();
         float z0sintheta = fabs(delta_z0 * sin(theta));
-        
+
         if (sigd0>5) continue;
         if (z0sintheta>0.5) continue;
         
@@ -155,6 +155,7 @@ bool DerivationFramework::VHLowTrackJetFilterTool::eventPassesFilter() const
         break;
       }
     }
+
     if (passesEl){
       m_nEventsPassElectron++;
       break;
@@ -165,31 +166,21 @@ bool DerivationFramework::VHLowTrackJetFilterTool::eventPassesFilter() const
   //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
   
   //muon portion
-  int qflag=0;
-  if (m_muonIDKey == "VeryLoose") {
-    qflag = xAOD::Muon::VeryLoose;
-  } else if (m_muonIDKey == "Loose") {
-    qflag = xAOD::Muon::Loose;
-  } else if (m_muonIDKey == "Medium") {
-    qflag = xAOD::Muon::Medium;
-  } else if (m_muonIDKey == "Tight") {
-    qflag = xAOD::Muon::Tight;
-  } else {
-    ATH_MSG_FATAL("Cannot find the muon quality flag " << m_muonIDKey << ".");
-    return false;
-  }
-  
+
   const xAOD::MuonContainer* muons(0);
   sc = evtStore()->retrieve(muons,m_muonSGKey);
   if (sc.isFailure()) {
     ATH_MSG_FATAL("No muon collection with name " << m_muonSGKey << " found in StoreGate!");
     return false;
   }
+
   
   for(auto muon : *muons){
+
     if (muon->pt()<m_muonPtCut) continue;
     if (fabs(muon->eta())>2.5) continue;
-    if (!(m_muonSelectionTool->getQuality(*muon) <= qflag)) continue;
+    if (!(m_muonSelectionTool->passedMuonCuts(*muon))) continue;
+    if (muon->muonType()!=xAOD::Muon::Combined) continue;
     if (muon->isolation(xAOD::Iso::topoetcone20)/muon->pt()>0.3) continue;
     
     for (auto vertex : *vertices) {	// Select good primary vertex
@@ -197,17 +188,20 @@ bool DerivationFramework::VHLowTrackJetFilterTool::eventPassesFilter() const
         const xAOD::TrackParticle* tp = muon->primaryTrackParticle() ; //your input track particle from the electron
         
         double d0sig = xAOD::TrackingHelpers::d0significance( tp, eventInfo->beamPosSigmaX(), eventInfo->beamPosSigmaY(), eventInfo->beamPosSigmaXY() );
+
         if (fabs(d0sig)>3) continue;
           
 	float delta_z0 = tp->z0() + tp->vz() - vertex->z();
         float theta = tp->theta();
         double z0sintheta = delta_z0 * sin(theta);
+
         if (fabs(z0sintheta)>0.5) continue;
-        
+       
         passesMu = true;
         break;
       }
     }
+
     if (passesMu) {
       m_nEventsPassMuon++;
       break;
@@ -228,18 +222,37 @@ bool DerivationFramework::VHLowTrackJetFilterTool::eventPassesFilter() const
   
   std::vector<const xAOD::Jet*> goodJets;
   for (auto jet : *jets) {
+
+    if (jet->pt() < m_jetPtCut) continue;
+    if (fabs(jet->eta()) > m_jetEtaCut) continue;
+
     TLorentzVector VJet = TLorentzVector(0.0,0.0,0.0,0.0);
     VJet.SetPtEtaPhiE(jet->pt(), jet->eta(), jet->phi(), jet->e());
-    
+
     float minDeltaR = 100;
+
     for (auto electron : *electrons ){
-      if (!electron->passSelection("Loose")) continue;
+
+      if (electron->pt()<20000) continue;
+      if (fabs(electron->eta())>2.47) continue;
+
+      bool passLoose=false;
+      if (!electron->passSelection(passLoose, "LHLoose")){
+	ATH_MSG_WARNING("Cannot find the LHLoose electron quality flag");
+	continue;
+      }
+      if (!passLoose) continue;
+
       TLorentzVector VElec=electron->p4();
       float deltaR = VJet.DeltaR(VElec);
+
       if (deltaR<minDeltaR) minDeltaR=deltaR;
     }
+    
     if (minDeltaR<0.2) continue;
+
     goodJets.push_back(jet);
+
   }
   
   
@@ -248,9 +261,6 @@ bool DerivationFramework::VHLowTrackJetFilterTool::eventPassesFilter() const
     jetNo++;
     if (jetNo>=3) break;  //Only consider two leading jets
     
-    if (jet->pt() < m_jetPtCut) continue;
-    if (fabs(jet->eta()) > m_jetEtaCut) continue;
-    
     TLorentzVector CHFNum = TLorentzVector(0.0,0.0,0.0,0.0);
     const xAOD::BTagging *bjet(nullptr);
     bjet = jet->btagging();
@@ -274,7 +284,7 @@ bool DerivationFramework::VHLowTrackJetFilterTool::eventPassesFilter() const
         TLorentzVector VTrack = TLorentzVector(0.0,0.0,0.0,0.0);
         VTrack.SetPtEtaPhiE(track->pt(),track->eta(), track->phi(), track->e());
         alphaDen=alphaDen+VTrack;
-        if (track->d0() > m_TrackD0Max) continue;
+        if (fabs(track->d0()) > m_TrackD0Max) continue;
         
         float z0 = track->z0() + track->vz() - vertex->z();
         float theta = track->theta();
@@ -291,19 +301,23 @@ bool DerivationFramework::VHLowTrackJetFilterTool::eventPassesFilter() const
     CHFNum = TLorentzVector(0.0,0.0,0.0,0.0);
     for(auto track : goodTracks) {
       if (track->pt() < m_TrackMinPt) continue;
-      if (track->d0() > m_TrackD0Max) continue;
+      if (fabs(track->d0()) > m_TrackD0Max) continue;
       
       TLorentzVector VTrack = TLorentzVector(0.0,0.0,0.0,0.0);
       VTrack.SetPtEtaPhiE(track->pt(),track->eta(), track->phi(), track->e());
       CHFNum=CHFNum+VTrack;
+
     }
+
     float chf = CHFNum.Pt()/jet->pt();
     
     if (alpha_max < m_AlphaMaxCut) m_nJetsPassAlphaMax++;
     if (chf < m_CHFCut) m_nJetsPassCHF++;
     
     if (chf > m_CHFCut && alpha_max > m_AlphaMaxCut) continue;
+
     nJetsPassed++;
+
   }
   
   if (nJetsPassed >= m_nJetsReq){
@@ -312,7 +326,7 @@ bool DerivationFramework::VHLowTrackJetFilterTool::eventPassesFilter() const
   }
   
   //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-  
+
   if (passesJet && (passesEl || passesMu)){
     m_nEventsPass++;
     return true;
diff --git a/Reconstruction/RecExample/RecExCommon/share/RecExCommon_topOptions.py b/Reconstruction/RecExample/RecExCommon/share/RecExCommon_topOptions.py
index d8408fb750c2cb1fad3d653078ba56c872fb5d7a..121783b1ce01957117506c182e33340916a04566 100755
--- a/Reconstruction/RecExample/RecExCommon/share/RecExCommon_topOptions.py
+++ b/Reconstruction/RecExample/RecExCommon/share/RecExCommon_topOptions.py
@@ -1106,13 +1106,14 @@ if rec.doFileMetaData():
 ###=== Only run reco on events that pass selected triggers
 ##--------------------------------------------------------
 if rec.doTrigger and rec.doTriggerFilter() and globalflags.DataSource() == 'data' and globalflags.InputFormat == 'bytestream':
+    logRecExCommon_topOptions.info('Setting up trigger filtering')
     try:
 ### seq will be our filter sequence
         from AthenaCommon.AlgSequence import AthSequencer
         seq=AthSequencer("AthFilterSeq")
         seq+=CfgMgr.EventCounterAlg("AllExecutedEventsAthFilterSeq")
         seq+=topSequence.TrigConfDataIOVChanger
-        seq+=topSequence.RoIBResultToAOD
+        seq+=topSequence.RoIBResultToxAOD
         seq+=topSequence.TrigBSExtraction
         seq+=topSequence.TrigDecMaker
 
@@ -1120,9 +1121,9 @@ if rec.doTrigger and rec.doTriggerFilter() and globalflags.DataSource() == 'data
         seq += TriggerSelectorAlg('TriggerAlg1')
         seq.TriggerAlg1.TriggerSelection = rec.triggerFilterList()
         pass
-    except:
+    except Exception, e:
+        logRecExCommon_topOptions.error('Trigger filtering not set up, reason: ' + `e`)
         pass
-    pass
 ##--------------------------------------------------------
 
 
diff --git a/Reconstruction/RecJobTransforms/share/skeleton.RAWtoALL_tf.py b/Reconstruction/RecJobTransforms/share/skeleton.RAWtoALL_tf.py
index a33f1f71daec30b8353fa25bd6944fcafaedc47c..7aab49cfa5b4e936901c57d0665e64eb72edfad0 100644
--- a/Reconstruction/RecJobTransforms/share/skeleton.RAWtoALL_tf.py
+++ b/Reconstruction/RecJobTransforms/share/skeleton.RAWtoALL_tf.py
@@ -49,7 +49,7 @@ if hasattr(runArgs,"inputEVNTFile"):
 ## Output
 if hasattr(runArgs,"trigFilterList"):
     rec.doTriggerFilter.set_Value_and_Lock(True)
-    rec.triggerFilterList = "|".join(runArgs.trigFilterList)
+    rec.triggerFilterList = "||".join(runArgs.trigFilterList)
     
 if hasattr(runArgs,"outputESDFile"):
     rec.doESD.set_Value_and_Lock( True )
diff --git a/Reconstruction/RecJobTransforms/share/skeleton.RAWtoESD_tf.py b/Reconstruction/RecJobTransforms/share/skeleton.RAWtoESD_tf.py
index 6b382d41b834067625f687bff18f4af486c0438f..36e8f5e19f5d2d071c56ea8d01b941c1e12e67c4 100644
--- a/Reconstruction/RecJobTransforms/share/skeleton.RAWtoESD_tf.py
+++ b/Reconstruction/RecJobTransforms/share/skeleton.RAWtoESD_tf.py
@@ -121,7 +121,7 @@ if hasattr(runArgs,"outputDRAW_WMUNUFile"):
 
 if hasattr(runArgs,"trigFilterList"):
     rec.doTriggerFilter.set_Value_and_Lock(True)
-    rec.triggerFilterList = "|".join(runArgs.trigFilterList)
+    rec.triggerFilterList = "||".join(runArgs.trigFilterList)
 
 if hasattr(runArgs,"outputESDFile"):
     rec.doESD.set_Value_and_Lock( True )
diff --git a/Reconstruction/VKalVrt/VrtSecInclusive/VrtSecInclusive/VrtSecInclusive.h b/Reconstruction/VKalVrt/VrtSecInclusive/VrtSecInclusive/VrtSecInclusive.h
index dddd4ca71265e6cbcdb7fd3e8956ce791e98ff49..0e785d28934f464f31071462c89450b92d543c02 100755
--- a/Reconstruction/VKalVrt/VrtSecInclusive/VrtSecInclusive/VrtSecInclusive.h
+++ b/Reconstruction/VKalVrt/VrtSecInclusive/VrtSecInclusive/VrtSecInclusive.h
@@ -165,6 +165,9 @@ namespace VKalVrtAthena {
       int  CutTRTHits; // Kazuki
       int  CutTightSCTHits;
       int  CutTightTRTHits;
+     
+      /* track extrpolator; 1==VKalGetImpact, 2==m_trackToVertexTool*/
+      int trkExtrapolator;
       
       // Vertex reconstruction
       bool   doPVcompatibilityCut;
@@ -198,7 +201,7 @@ namespace VKalVrtAthena {
       double mergeByShufflingAllowance;
       
       double improveChi2ProbThreshold;
-      
+
       // vertexing using muons (test implementation)
       bool doSelectTracksFromMuons;
       bool doSelectTracksFromElectrons;
@@ -384,6 +387,12 @@ namespace VKalVrtAthena {
     
     /** finalization of the vertex and store to xAOD::VertexContainer */
     StatusCode refitAndSelectGoodQualityVertices( std::vector<WrkVrt>* );
+
+    /** get secondary vertex impact parameters **/
+    bool getSVImpactParameters(const xAOD::TrackParticle* trk, Amg::Vector3D vertex, std::vector<double>& impactParameters, std::vector<double>& impactParErrors); 
+
+    enum TrkParameter    { k_d0=0, k_z0=1, k_theta=2, k_phi=3, k_qOverP=4 ,k_nTP=5 };
+    enum TrkParameterUnc { k_d0d0=0, k_z0z0=1, k_nTPU=2 };
     
     using vertexingAlg = StatusCode (VrtSecInclusive::*)( std::vector<WrkVrt>* );
     std::vector< std::pair<std::string, vertexingAlg> > m_vertexingAlgorithms;
diff --git a/Reconstruction/VKalVrt/VrtSecInclusive/src/Utilities.cxx b/Reconstruction/VKalVrt/VrtSecInclusive/src/Utilities.cxx
index c32ede1a1141264898902dfdcbccf53159675707..c3997da004d35c159ab6d99a6c006a53aaa0c09f 100644
--- a/Reconstruction/VKalVrt/VrtSecInclusive/src/Utilities.cxx
+++ b/Reconstruction/VKalVrt/VrtSecInclusive/src/Utilities.cxx
@@ -726,6 +726,8 @@ namespace VKalVrtAthena {
     declareProperty("CutTRTHits",                      m_jp.CutTRTHits                      = 0                             );
     declareProperty("CutTightSCTHits",                 m_jp.CutTightSCTHits                 = 7                             );
     declareProperty("CutTightTRTHits",                 m_jp.CutTightTRTHits                 = 20                            );
+ 
+    declareProperty("TrkExtrapolator",                 m_jp.trkExtrapolator                 = 2                             );
     
     declareProperty("doReassembleVertices",            m_jp.doReassembleVertices            = false                         );
     declareProperty("doMergeByShuffling",              m_jp.doMergeByShuffling              = false                         );
diff --git a/Reconstruction/VKalVrt/VrtSecInclusive/src/VertexingAlgs.cxx b/Reconstruction/VKalVrt/VrtSecInclusive/src/VertexingAlgs.cxx
index 15072afd0a800bb04195bc77f1aad7085266de5d..b8f337a07cb5b15c513bc4cabdf61764c90908bb 100644
--- a/Reconstruction/VKalVrt/VrtSecInclusive/src/VertexingAlgs.cxx
+++ b/Reconstruction/VKalVrt/VrtSecInclusive/src/VertexingAlgs.cxx
@@ -113,15 +113,16 @@ namespace VKalVrtAthena {
         std::vector<double> impactParameters;
         std::vector<double> impactParErrors;
         
-        m_fitSvc->VKalGetImpact( *itrk, initVertex, static_cast<long int>( (*itrk)->charge() ), impactParameters, impactParErrors);
-        const auto roughD0_itrk = impactParameters.at(0);
-        const auto roughZ0_itrk = impactParameters.at(1);
-        if( fabs( impactParameters.at(0) ) > roughD0Cut || fabs( impactParameters.at(1) ) > roughZ0Cut ) {
+        if( !getSVImpactParameters( *itrk, initVertex, impactParameters, impactParErrors) ) continue;
+        const auto roughD0_itrk = impactParameters.at(TrkParameter::k_d0);
+        const auto roughZ0_itrk = impactParameters.at(TrkParameter::k_z0);
+        if( fabs( impactParameters.at(0)) > roughD0Cut || fabs( impactParameters.at(1) ) > roughZ0Cut ) {
           continue;
         }
-        m_fitSvc->VKalGetImpact( *jtrk, initVertex, static_cast<long int>( (*jtrk)->charge() ), impactParameters, impactParErrors);
-        const auto roughD0_jtrk = impactParameters.at(0);
-        const auto roughZ0_jtrk = impactParameters.at(1);
+
+        if( !getSVImpactParameters( *jtrk, initVertex, impactParameters, impactParErrors) ) continue;
+        const auto roughD0_jtrk = impactParameters.at(TrkParameter::k_d0);
+        const auto roughZ0_jtrk = impactParameters.at(TrkParameter::k_z0);
         if( fabs( impactParameters.at(0) ) > roughD0Cut || fabs( impactParameters.at(1) ) > roughZ0Cut ) {
           continue;
         }
@@ -944,15 +945,13 @@ namespace VKalVrtAthena {
           std::vector<double> impactParameters;
           std::vector<double> impactParErrors;
         
-          m_fitSvc->VKalGetImpact(trk, targetVertex.vertex, static_cast<int>( trk->charge() ), impactParameters, impactParErrors);
-          
-          enum { k_d0, k_z0, k_theta, k_phi, k_qOverP };
-          
-          const auto& distance = hypot( impactParameters.at(k_d0), impactParameters.at(k_z0) );
+          if( !getSVImpactParameters(trk,targetVertex.vertex,impactParameters,impactParErrors) ) continue;
+
+          const auto& distance = hypot( impactParameters.at(0), impactParameters.at(1) );
           distances.emplace_back( distance );
           
-          if( fabs( impactParameters.at(k_d0) > m_jp.reassembleMaxImpactParameterD0 ) ) continue;
-          if( fabs( impactParameters.at(k_z0) > m_jp.reassembleMaxImpactParameterZ0 ) ) continue;
+          if( fabs( impactParameters.at(0) ) > m_jp.reassembleMaxImpactParameterD0 ) continue;
+          if( fabs( impactParameters.at(1) ) > m_jp.reassembleMaxImpactParameterZ0 ) continue;
           
           mergiableVertex[index] = ritr;
           mergiableVerticesSet.emplace( ritr );
@@ -1097,13 +1096,10 @@ namespace VKalVrtAthena {
         std::vector<double> impactParameters;
         std::vector<double> impactParErrors;
         
-        m_fitSvc->VKalGetImpact(trk, vertexPos, static_cast<int>( trk->charge() ), impactParameters, impactParErrors);
-        
-        enum { k_d0, k_z0, k_theta, k_phi, k_qOverP };
-        enum { k_d0d0, k_d0z0, k_z0z0 };
-        
-        if( fabs( impactParameters.at(k_d0) ) / sqrt( impactParErrors.at(k_d0d0) ) > m_jp.associateMaxD0Signif ) continue;
-        if( fabs( impactParameters.at(k_z0) ) / sqrt( impactParErrors.at(k_z0z0) ) > m_jp.associateMaxZ0Signif ) continue;
+        if( !getSVImpactParameters( trk, vertexPos, impactParameters, impactParErrors) ) continue;
+
+        if( fabs( impactParameters.at(0) ) / sqrt( impactParErrors.at(0) ) > m_jp.associateMaxD0Signif ) continue;
+        if( fabs( impactParameters.at(1) ) / sqrt( impactParErrors.at(1) ) > m_jp.associateMaxZ0Signif ) continue;
         
         ATH_MSG_DEBUG( " > " << __FUNCTION__ << ": trk " << trk
                        << ": d0 to vtx = " << impactParameters.at(k_d0)
@@ -1987,5 +1983,35 @@ namespace VKalVrtAthena {
     return StatusCode::SUCCESS;
   }
   
+  //____________________________________________________________________________________________________
+  bool VrtSecInclusive::getSVImpactParameters(const xAOD::TrackParticle* trk, Amg::Vector3D vertex,
+                                              std::vector<double>& impactParameters,
+                                              std::vector<double>& impactParErrors){
+
+    impactParameters.clear();
+    impactParErrors.clear();
+    
+    if( m_jp.trkExtrapolator==1 ){
+      m_fitSvc->VKalGetImpact(trk, vertex, static_cast<int>( trk->charge() ), impactParameters, impactParErrors);
+    }
+    else if( m_jp.trkExtrapolator==2 ){
+      const Trk::Perigee* sv_perigee = m_trackToVertexTool->perigeeAtVertex( *trk, vertex );
+      if( !sv_perigee ) return false;
+      impactParameters.push_back(sv_perigee->parameters() [Trk::d0]);
+      impactParameters.push_back(sv_perigee->parameters() [Trk::z0]);
+      impactParErrors.push_back((*sv_perigee->covariance())( Trk::d0, Trk::d0 ));
+      impactParErrors.push_back((*sv_perigee->covariance())( Trk::z0, Trk::z0 ));
+      delete sv_perigee;
+    }
+    else{
+      ATH_MSG_WARNING( " > " << __FUNCTION__ << ": Unknown track extrapolator " << m_jp.trkExtrapolator   );
+      return false;
+    }
+
+    return true;
+
+  } // getSVImpactParameters
+
+
 
 } // end of namespace VKalVrtAthena
diff --git a/Simulation/G4Utilities/MCTruthSimAlgs/src/MergeHijingParsTool.cxx b/Simulation/G4Utilities/MCTruthSimAlgs/src/MergeHijingParsTool.cxx
index 2a18a7247dc0fa4e312bb5ae0384d94c9f12fcda..054404ed4fdae5b0243de81a630b198691917532 100644
--- a/Simulation/G4Utilities/MCTruthSimAlgs/src/MergeHijingParsTool.cxx
+++ b/Simulation/G4Utilities/MCTruthSimAlgs/src/MergeHijingParsTool.cxx
@@ -39,7 +39,7 @@ StatusCode MergeHijingParsTool::processBunchXing(int,
   SubEventIterator iEvt = bSubEvents;
   for (; iEvt!=eSubEvents; iEvt++)
     {
-      StoreGateSvc& seStore(*bSubEvents->ptr()->evtStore());
+      StoreGateSvc& seStore(*iEvt->ptr()->evtStore());
       if (seStore.contains<HijingEventParams>(m_outputObject.name()))
         {
           const HijingEventParams *hijing_pars(nullptr);
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/CMakeLists.txt b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/CMakeLists.txt
index 0ae7cd4c62957f2f2d2c0c77107df897482adcbc..8987393052a7cab80a8cf6373dc5928f6427f9e2 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/CMakeLists.txt
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/CMakeLists.txt
@@ -36,9 +36,13 @@ atlas_add_root_dictionary( ISF_FastCaloSimEvent _dictSource
                            ISF_FastCaloSimEvent/TFCS1DFunctionRegression.h 
                            ISF_FastCaloSimEvent/TFCS1DFunctionRegressionTF.h 
                            ISF_FastCaloSimEvent/TFCS1DFunctionSpline.h 
+                           ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHelpers.h 
                            ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHistogram.h 
+                           ISF_FastCaloSimEvent/TFCS1DFunctionTemplateInterpolationHistogram.h 
                            ISF_FastCaloSimEvent/TFCS2DFunction.h 
                            ISF_FastCaloSimEvent/TFCS2DFunctionHistogram.h 
+                           ISF_FastCaloSimEvent/TFCS2DFunctionTemplateHistogram.h 
+                           ISF_FastCaloSimEvent/TFCS2DFunctionTemplateInterpolationHistogram.h 
                            ISF_FastCaloSimEvent/TFCSParametrizationBase.h 
                            ISF_FastCaloSimEvent/TFCSParametrizationPlaceholder.h
                            ISF_FastCaloSimEvent/TFCSParametrization.h 
@@ -61,13 +65,18 @@ atlas_add_root_dictionary( ISF_FastCaloSimEvent _dictSource
                            ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitBase.h
                            ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitChain.h
                            ISF_FastCaloSimEvent/TFCSCenterPositionCalculation.h
+                           ISF_FastCaloSimEvent/TFCSFlatLateralShapeParametrization.h
                            ISF_FastCaloSimEvent/TFCSHistoLateralShapeParametrization.h
                            ISF_FastCaloSimEvent/TFCSHistoLateralShapeParametrizationFCal.h
+                           ISF_FastCaloSimEvent/TFCS2DFunctionLateralShapeParametrization.h
+                           ISF_FastCaloSimEvent/TFCSHistoLateralShapeWeight.h
+                           ISF_FastCaloSimEvent/TFCSHistoLateralShapeWeightHitAndMiss.h
                            ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitNumberFromE.h
                            ISF_FastCaloSimEvent/TFCSHitCellMapping.h
                            ISF_FastCaloSimEvent/TFCSHitCellMappingFCal.h
                            ISF_FastCaloSimEvent/TFCSHitCellMappingWiggle.h
                            ISF_FastCaloSimEvent/TFCSHitCellMappingWiggleEMB.h
+                           ISF_FastCaloSimEvent/TFCSEnergyRenormalization.h
                            ISF_FastCaloSimEvent/TFCSExtrapolationState.h 
                            ISF_FastCaloSimEvent/TFCSSimulationState.h 
                            ISF_FastCaloSimEvent/TFCSTruthState.h 
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/LinkDef.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/LinkDef.h
index ffce63d8d40aac7be68aa41367d975c00201c813..cf6a205e893629cdbe3b52ac82dd201104e99580 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/LinkDef.h
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/LinkDef.h
@@ -12,9 +12,13 @@
 #include "ISF_FastCaloSimEvent/TFCS1DFunctionRegression.h"
 #include "ISF_FastCaloSimEvent/TFCS1DFunctionRegressionTF.h"
 #include "ISF_FastCaloSimEvent/TFCS1DFunctionSpline.h"
+#include "ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHelpers.h"
 #include "ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHistogram.h"
+#include "ISF_FastCaloSimEvent/TFCS1DFunctionTemplateInterpolationHistogram.h"
 #include "ISF_FastCaloSimEvent/TFCS2DFunction.h"
 #include "ISF_FastCaloSimEvent/TFCS2DFunctionHistogram.h"
+#include "ISF_FastCaloSimEvent/TFCS2DFunctionTemplateHistogram.h"
+#include "ISF_FastCaloSimEvent/TFCS2DFunctionTemplateInterpolationHistogram.h"
 
 #include "ISF_FastCaloSimEvent/TFCSParametrizationBase.h"
 #include "ISF_FastCaloSimEvent/TFCSParametrizationPlaceholder.h"
@@ -40,13 +44,18 @@
 #include "ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitBase.h"
 #include "ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitChain.h"
 #include "ISF_FastCaloSimEvent/TFCSCenterPositionCalculation.h"
+#include "ISF_FastCaloSimEvent/TFCSFlatLateralShapeParametrization.h"
 #include "ISF_FastCaloSimEvent/TFCSHistoLateralShapeParametrization.h"
 #include "ISF_FastCaloSimEvent/TFCSHistoLateralShapeParametrizationFCal.h"
+#include "ISF_FastCaloSimEvent/TFCS2DFunctionLateralShapeParametrization.h"
+#include "ISF_FastCaloSimEvent/TFCSHistoLateralShapeWeight.h"
+#include "ISF_FastCaloSimEvent/TFCSHistoLateralShapeWeightHitAndMiss.h"
 #include "ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitNumberFromE.h"
 #include "ISF_FastCaloSimEvent/TFCSHitCellMapping.h"
 #include "ISF_FastCaloSimEvent/TFCSHitCellMappingFCal.h"
 #include "ISF_FastCaloSimEvent/TFCSHitCellMappingWiggle.h"
 #include "ISF_FastCaloSimEvent/TFCSHitCellMappingWiggleEMB.h"
+#include "ISF_FastCaloSimEvent/TFCSEnergyRenormalization.h"
 
 #include "ISF_FastCaloSimEvent/TFCSTruthState.h"
 #include "ISF_FastCaloSimEvent/TFCSExtrapolationState.h"
@@ -64,7 +73,7 @@
 #pragma link C++ class TFCS1DFunctionRegressionTF+;
 #pragma link C++ class TFCS1DFunctionSpline+;
 
-///Linkdefs needed for template based histograms
+///Linkdefs needed for common classes for template based histograms
 #pragma link C++ class TFCS1DFunction_Numeric<uint8_t,float>+;
 #pragma link C++ class TFCS1DFunction_Numeric<uint16_t,float>+;
 #pragma link C++ class TFCS1DFunction_Numeric<uint32_t,float>+;
@@ -99,6 +108,7 @@
 #pragma link C++ class TFCS1DFunction_HistogramFloatBinEdges+;
 #pragma link C++ class TFCS1DFunction_HistogramDoubleBinEdges+;
 
+///Linkdefs needed for 1D template based histograms
 #pragma link C++ class TFCS1DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint8_t,float>+;
 #pragma link C++ class TFCS1DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint16_t,float>+;
 #pragma link C++ class TFCS1DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint32_t,float>+;
@@ -120,6 +130,52 @@
 #pragma link C++ class TFCS1DFunctionInt8Int16InterpolationHistogram+;
 #pragma link C++ class TFCS1DFunctionInt16Int16InterpolationHistogram+;
 #pragma link C++ class TFCS1DFunctionInt16Int32InterpolationHistogram+;
+
+///Linkdefs needed for 2D template based histograms
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt8BinEdges, uint8_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt8BinEdges, uint16_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt8BinEdges, uint32_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint8_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint16_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint32_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt16BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint8_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt16BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint16_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt16BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint32_t,float>+;
+
+#pragma link C++ class TFCS2DFunctionInt8Int8Int8Histogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int8Int16Histogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int8Int32Histogram+;
+
+#pragma link C++ class TFCS2DFunctionInt8Int16Int8Histogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int16Int16Histogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int16Int32Histogram+;
+
+#pragma link C++ class TFCS2DFunctionInt16Int16Int8Histogram+;
+#pragma link C++ class TFCS2DFunctionInt16Int16Int16Histogram+;
+#pragma link C++ class TFCS2DFunctionInt16Int16Int32Histogram+;
+
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt8BinEdges, uint8_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt8BinEdges, uint16_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt8BinEdges, uint32_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint8_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint16_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint32_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint8_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint16_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint32_t,float>+;
+
+#pragma link C++ class TFCS2DFunctionInt8Int8Int8InterpolationHistogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int8Int16InterpolationHistogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int8Int32InterpolationHistogram+;
+
+#pragma link C++ class TFCS2DFunctionInt8Int16Int8InterpolationHistogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int16Int16InterpolationHistogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int16Int32InterpolationHistogram+;
+
+#pragma link C++ class TFCS2DFunctionInt16Int16Int8InterpolationHistogram+;
+#pragma link C++ class TFCS2DFunctionInt16Int16Int16InterpolationHistogram+;
+#pragma link C++ class TFCS2DFunctionInt16Int16Int32InterpolationHistogram+;
+
 ///End Linkdefs needed for template based histograms
 
 #pragma link C++ class TFCS2DFunction+;
@@ -148,13 +204,18 @@
 #pragma link C++ class TFCSLateralShapeParametrizationHitBase+;
 #pragma link C++ class TFCSLateralShapeParametrizationHitChain+;
 #pragma link C++ class TFCSCenterPositionCalculation+;
+#pragma link C++ class TFCSFlatLateralShapeParametrization+;
 #pragma link C++ class TFCSHistoLateralShapeParametrization+;
 #pragma link C++ class TFCSHistoLateralShapeParametrizationFCal+;
+#pragma link C++ class TFCS2DFunctionLateralShapeParametrization+;
+#pragma link C++ class TFCSHistoLateralShapeWeight+;
+#pragma link C++ class TFCSHistoLateralShapeWeightHitAndMiss+;
 #pragma link C++ class TFCSLateralShapeParametrizationHitNumberFromE+;
 #pragma link C++ class TFCSHitCellMapping+;
 #pragma link C++ class TFCSHitCellMappingFCal+;
 #pragma link C++ class TFCSHitCellMappingWiggle+;
 #pragma link C++ class TFCSHitCellMappingWiggleEMB+;
+#pragma link C++ class TFCSEnergyRenormalization+;
 
 #pragma link C++ class TFCSTruthState+;
 #pragma link C++ class TFCSExtrapolationState+;
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHelpers.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHelpers.h
new file mode 100644
index 0000000000000000000000000000000000000000..9c7179dd99b9b63992c16f8bcd241e4a6c64cfeb
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHelpers.h
@@ -0,0 +1,436 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef ISF_FASTCALOSIMEVENT_TFCS1DFunctionTemplateHelpers_h
+#define ISF_FASTCALOSIMEVENT_TFCS1DFunctionTemplateHelpers_h
+
+#include "TBuffer.h"
+
+#include <vector>
+#include <cstring>
+#include <algorithm>
+#include <cmath>
+
+//For the purpose of FastCaloSim, 32bit are sufficient for bin counting
+typedef uint32_t TFCS1DFunction_size_t;
+
+///Converter functions that does nothing for floats
+template<typename T,typename Tfloat=float> class TFCS1DFunction_Numeric {
+public:
+  static constexpr T MaxValue = 1;
+  static constexpr Tfloat MaxValueFloat = MaxValue;
+
+  static inline T MaxCeilOnlyForInt(const Tfloat value) {return value;};
+  static inline T ExpandToMaxRange(const Tfloat value) {return value;};
+  static inline Tfloat ToNormalizedRange(const T value) {return value;};
+};  
+
+///Converter functions to store/retrive float numbers in integers
+template<typename Tfloat> class TFCS1DFunction_Numeric<uint8_t,Tfloat> {
+public:
+  static constexpr uint8_t MaxValue = UINT8_MAX;
+  static constexpr Tfloat MaxValueFloat = MaxValue;
+
+  static inline uint8_t MaxCeilOnlyForInt(const Tfloat value) {return std::ceil(MaxValueFloat*value);};
+  static inline uint8_t ExpandToMaxRange(const Tfloat value) {return value*(1+MaxValueFloat);};
+  static inline Tfloat ToNormalizedRange(const uint8_t value) {return value/(1+MaxValueFloat);};
+};  
+
+///Converter functions to store/retrive float numbers in integers
+template<typename Tfloat> class TFCS1DFunction_Numeric<uint16_t,Tfloat> {
+public:
+  static constexpr uint16_t MaxValue = UINT16_MAX;
+  static constexpr Tfloat MaxValueFloat = MaxValue;
+
+  static inline uint16_t MaxCeilOnlyForInt(const Tfloat value) {return std::ceil(MaxValueFloat*value);};
+  static inline uint16_t ExpandToMaxRange(const Tfloat value) {return value*(1+MaxValueFloat);};
+  static inline Tfloat ToNormalizedRange(const uint16_t value) {return value/(1+MaxValueFloat);};
+};  
+
+///Converter functions to store/retrive float numbers in integers
+template<typename Tfloat> class TFCS1DFunction_Numeric<uint32_t,Tfloat> {
+public:
+  static constexpr uint32_t MaxValue = UINT32_MAX;
+  static constexpr Tfloat MaxValueFloat = MaxValue;
+
+  static inline uint32_t MaxCeilOnlyForInt(const Tfloat value) {return std::ceil(MaxValueFloat*value);};
+  static inline uint32_t ExpandToMaxRange(const Tfloat value) {return value*(1+MaxValueFloat);};
+  static inline Tfloat ToNormalizedRange(const uint32_t value) {return value/(1+MaxValueFloat);};
+};  
+
+template<typename T> class TFCS1DFunction_Array
+{
+  public:
+    typedef TFCS1DFunction_size_t size_t;
+    
+    TFCS1DFunction_Array() {};
+    TFCS1DFunction_Array(size_t count) {resize(count);};
+    ~TFCS1DFunction_Array() {if(m_content) delete[] m_content;};
+
+    std::size_t MemorySizeArray() const {return size()*sizeof(T);};
+    std::size_t MemorySize() const {return sizeof(*this)+MemorySizeArray();};
+
+    inline size_t size() const {return m_size;};
+    
+    ///resize to given count, copying old content
+    void resize(size_t count) {
+      T* new_cont=nullptr;
+      if(count>0) new_cont=new T[count];
+      if(m_content && new_cont) {
+        size_t ncopy=count;
+        if(size()<ncopy) ncopy=size();
+        ncopy*=sizeof(T);
+        std::memcpy(new_cont,m_content,ncopy);
+      }
+      if(m_content) delete[] m_content;
+      m_size=count;
+      m_content=new_cont;
+    };
+    
+    ///Direct data pointer
+    inline T* data() {return m_content;};
+    inline const T* data() const {return m_content;};
+    
+    ///Direct access operators. Values are in the range [0,TFCS1DFunction_Numeric<T>::MaxValue]
+    inline T&       operator[]( size_t pos ) {return m_content[pos];};
+    inline const T& operator[]( size_t pos ) const {return m_content[pos];};
+    
+    ///begin() iterators
+    inline T* begin() {return m_content;};
+    inline const T* begin() const {return m_content;};
+
+    ///end() iterators
+    inline T* end() {return m_content+size();};
+    inline const T* end() const {return m_content+size();};
+    
+  private:
+    T* m_content{nullptr};//!
+    size_t m_size{0};
+
+  //Use ClassDef without virtual functions. Saves 8 bytes per instance
+  ClassDefNV(TFCS1DFunction_Array,1)  //TFCS1DFunction_Array
+};
+
+///Streamer method to persitify objects of type TFCS1DFunction_Array<T>
+template<typename T> void TFCS1DFunction_Array<T>::Streamer(TBuffer &b) {
+  if (b.IsReading()) {
+    size_t count;
+    b >> count;
+    resize(count);
+    if(m_size>0) b.ReadFastArray(m_content, m_size);
+  } else {
+    b << m_size;
+    if(m_size>0) b.WriteFastArray(m_content, m_size);
+  }
+}
+
+//Class to represent histogram content. Trandom should be a type with a floating point range [0,1]
+template<typename T,typename Trandom=float> class TFCS1DFunction_HistogramContent
+{
+  public:
+    typedef TFCS1DFunction_size_t size_t;
+    typedef T value_type;
+    typedef Trandom random_type;
+
+    TFCS1DFunction_HistogramContent(size_t nbins=0):m_array(nbins >= 1 ? nbins-1 : 0) {};
+
+    std::size_t MemorySizeArray() const {return m_array.MemorySizeArray();};
+    std::size_t MemorySize() const {return sizeof(*this)+MemorySizeArray();};
+
+    ///Set the content of bin pos to a given value, where value is in the range [0,1]
+    void set_fraction( size_t pos, Trandom value ) {
+      if(pos>=size()) return;
+      m_array[pos]=TFCS1DFunction_Numeric<T,Trandom>::MaxCeilOnlyForInt(value);
+    };
+    
+    ///Get the cumulative content at bin pos as fraction in the range [0,1]
+    inline Trandom get_fraction( size_t pos ) const {
+      if(pos>=size()) return 1;
+      return m_array[pos]/TFCS1DFunction_Numeric<T,Trandom>::MaxValueFloat;
+    };
+    
+    ///Get the content at bin pos as fraction in the range [0,1]
+    inline Trandom get_binfraction( size_t pos ) const {
+      if(pos==0) return m_array[pos]/TFCS1DFunction_Numeric<T,Trandom>::MaxValueFloat;
+      if(pos==size()) return 1-(m_array[size()-1]/TFCS1DFunction_Numeric<T,Trandom>::MaxValueFloat);
+      return (m_array[pos]-m_array[pos-1])/TFCS1DFunction_Numeric<T,Trandom>::MaxValueFloat;
+    };
+    
+    ///set number of bins. 
+    ///The actualy alocated size is one smaller than count, as the last bin is fixed with the range [get_fraction(size()-1,1]
+    void set_nbins(size_t nbins) {m_array.resize(nbins >= 1 ? nbins-1 : 0);};
+
+    ///return number of bins. 
+    ///This is one larger than size, as the last bin is fixed with the range [get_fraction(size()-1,1]
+    inline size_t get_nbins() const {return size()+1;};
+
+    ///Get the matching bin for a given random value in the range [0,1). 
+    ///A residual random number to calculate a position inside this bin is returned in residual_rnd
+    size_t get_bin(Trandom drnd,Trandom& residual_rnd) const {
+      if(drnd<=0) drnd=0;
+      if(drnd>=1) drnd=std::nextafter((Trandom)1.0,(Trandom)0.0);
+      if(size()==0) {
+        residual_rnd=drnd;
+        return 0;
+      }  
+      T rnd=TFCS1DFunction_Numeric<T,Trandom>::MaxValueFloat*drnd;
+      auto it = std::upper_bound(m_array.begin(),m_array.end(),rnd);
+
+      T basecont=0;
+      if(it!=m_array.begin()) basecont=*(it-1);
+
+      T fullcont=TFCS1DFunction_Numeric<T,Trandom>::MaxValue;
+      if(it!=m_array.end()) fullcont=*it;
+
+      T dcont=fullcont-basecont;
+      if(dcont>0) {
+        residual_rnd=((Trandom)(rnd-basecont))/dcont;
+        if(residual_rnd>1) residual_rnd=std::nextafter((Trandom)1.0,(Trandom)0.0);
+      } else {
+        residual_rnd=0.5;
+      }
+
+      if(it==m_array.end()) return size();
+      return std::distance(m_array.begin(),it);
+    }; 
+
+  private:
+    TFCS1DFunction_Array<T> m_array;
+    inline size_t size() const {return m_array.size();};
+
+  //Use ClassDef without virtual functions. Saves 8 bytes per instance
+  ClassDefNV(TFCS1DFunction_HistogramContent,1)  //TFCS1DFunction_HistogramContent
+};
+
+//Class to represent histogram bin edges. Trandom should be a type with a floating point range [0,1]
+template<typename T,typename Trandom=float> class TFCS1DFunction_HistogramBinEdges
+{
+  public:
+    typedef TFCS1DFunction_size_t size_t;
+    typedef T value_type;
+    typedef Trandom random_type;
+
+    TFCS1DFunction_HistogramBinEdges(size_t nbins=0):m_array(nbins+1) {};
+    ~TFCS1DFunction_HistogramBinEdges() {};
+
+    std::size_t MemorySizeArray() const {return m_array.MemorySizeArray();};
+    std::size_t MemorySize() const {return sizeof(*this)+MemorySizeArray();};
+
+    ///set number of bins
+    void set_nbins(size_t nbins) {m_array.resize(nbins+1);};
+
+    ///return number of bins
+    inline size_t get_nbins() const {return size()-1;};
+    
+    ///set position of lower edge of bins
+    void SetBinLowEdge(size_t pos,const T& value) {m_array[pos]=value;};
+    
+    ///get position of lower edge of bins
+    inline const T& GetBinLowEdge(size_t pos) const {return m_array[pos];};
+    
+    ///get the length of a bin
+    inline const T GetBinLength(size_t pos) const {return GetBinLowEdge(pos+1)-GetBinLowEdge(pos);};
+    
+    ///set and get minimum
+    void SetMin(const T& value) {m_array[0]=value;};
+    inline const T& GetMin() const {return m_array[0];};
+
+    ///set and get maximum
+    void SetMax(const T& value) {m_array[get_nbins()]=value;};
+    inline const T& GetMax() const {return m_array[get_nbins()];};
+
+    ///set minimum and maximum
+    void SetMinMax(const T& valuemin,const T& valuemax) {SetMin(valuemin);SetMax(valuemax);};
+    
+    ///Get length of interval of all bins
+    inline const T Length() const {return GetMax()-GetMin();};
+    
+    ///return linear interpolated position for bin pos. Interpolation is done with a random value in the range [0,1]
+    inline T position(size_t pos,const Trandom& drnd) const {
+      return (1-drnd)*m_array[pos] + drnd*m_array[pos+1];
+    };
+
+    ///return interpolated position for bin pos, such that histograming the position gives a linear slope m,
+    ///where m is in units of the bin width for bin pos. Interpolation is done with a random value in the range [0,1]
+    inline T position(size_t pos,Trandom m,const Trandom& drnd) const {
+      if(m>2) m=2;
+      if(m<-2) m=-2;
+      Trandom x=fabs(m)>0.001 ? (0.5*std::sqrt(m*(m+8*drnd-4)+4)-1)/m+0.5 : drnd;
+      return (1-x)*m_array[pos] + x*m_array[pos+1];
+    };
+
+  private:
+    TFCS1DFunction_Array<T> m_array;
+    inline size_t size() const {return m_array.size();};
+
+  //Use ClassDef without virtual functions. Saves 8 bytes per instance
+  ClassDefNV(TFCS1DFunction_HistogramBinEdges,1)  //TFCS1DFunction_HistogramBinEdges
+};
+
+class TFCS1DFunction_HistogramFloatBinEdges: public TFCS1DFunction_HistogramBinEdges<float,float>
+{
+  public:
+    TFCS1DFunction_HistogramFloatBinEdges(size_t nbins=0):TFCS1DFunction_HistogramBinEdges<float,float>(nbins) {};
+
+  ClassDefNV(TFCS1DFunction_HistogramFloatBinEdges,1)  //TFCS1DFunction_HistogramFloatBinEdges
+};
+
+class TFCS1DFunction_HistogramDoubleBinEdges: public TFCS1DFunction_HistogramBinEdges<double,double>
+{
+  public:
+    TFCS1DFunction_HistogramDoubleBinEdges(size_t nbins=0):TFCS1DFunction_HistogramBinEdges<double,double>(nbins) {};
+
+  ClassDefNV(TFCS1DFunction_HistogramDoubleBinEdges,1)  //TFCS1DFunction_HistogramDoubleBinEdges
+};
+
+//Class to represent histogram bin edges, where the interval between GetMin() and GetMax() 
+//can be stored as a different (smaller) data type Tint
+//Trandom should be a type with a floating point range [0,1]
+template<typename T,typename Tint,typename Trandom=float> class TFCS1DFunction_HistogramCompactBinEdges
+{
+  public:
+    typedef TFCS1DFunction_size_t size_t;
+    typedef T value_type;
+    typedef Trandom random_type;
+    typedef Tint internal_storage_type;
+    
+    TFCS1DFunction_HistogramCompactBinEdges(size_t nbins=0):m_array(nbins >= 1 ? nbins-1 : 0) {};
+    ~TFCS1DFunction_HistogramCompactBinEdges() {};
+
+    std::size_t MemorySizeArray() const {return m_array.MemorySizeArray();};
+    std::size_t MemorySize() const {return sizeof(*this)+MemorySizeArray();};
+
+    ///set number of bins
+    void set_nbins(size_t nbins) {m_array.resize(nbins >= 1 ? nbins-1 : 0);};
+
+    ///return number of bins
+    inline size_t get_nbins() const {return size()+1;};
+    
+    ///set position of lower edge of bins. Requires GetMin() and GetMax() to be set and may not be changed!
+    void SetBinLowEdge(size_t pos,const T& value) {
+      if(pos==0) {
+        SetMin(value);
+        return;
+      }  
+      if(pos>=get_nbins()) {
+        SetMax(value);
+        return;
+      }  
+      m_array[pos-1]=TFCS1DFunction_Numeric<Tint,T>::ExpandToMaxRange((value-GetMin())/Length());
+    };
+
+    ///get position of lower edge of bins. Requires GetMin() and GetMax() to be set and may not be changed!
+    inline const T GetBinLowEdge(size_t pos) const {
+      if(pos==0) return GetMin();
+      if(pos>=get_nbins()) return GetMax();
+      return GetMin()+Length()*TFCS1DFunction_Numeric<Tint,T>::ToNormalizedRange(m_array[pos-1]);
+    };
+
+    ///get the length of a bin
+    inline const T GetBinLength(size_t pos) const {return GetBinLowEdge(pos+1)-GetBinLowEdge(pos);};
+    
+    ///set and get minimum
+    void SetMin(const T& value) {m_Min=value;};
+    inline const T& GetMin() const {return m_Min;};
+
+    ///set and get maximum
+    void SetMax(const T& value) {m_Max=value;};
+    inline const T& GetMax() const {return m_Max;};
+    
+    ///set minimum and maximum
+    void SetMinMax(const T& valuemin,const T& valuemax) {SetMin(valuemin);SetMax(valuemax);};
+    
+    ///Get length of interval of all bins
+    inline const T Length() const {return GetMax()-GetMin();};
+    
+    ///return linear interpolated position for bin pos. Interpolation is done with a random value in the range [0,1]
+    inline T position(size_t pos,const Trandom& drnd) const {
+      T pos1=GetBinLowEdge(pos);
+      T pos2=GetBinLowEdge(pos+1);
+      return (1-drnd)*pos1 + drnd*pos2;
+    };
+    
+    ///return interpolated position for bin pos, such that histograming the position gives a linear slope m,
+    ///where m is in units of the bin width for bin pos. Interpolation is done with a random value in the range [0,1]
+    inline T position(size_t pos,Trandom m,const Trandom& drnd) const {
+      if(m>2) m=2;
+      if(m<-2) m=-2;
+      Trandom x=fabs(m)>0.001 ? (0.5*std::sqrt(m*(m+8*drnd-4)+4)-1)/m+0.5 : drnd;
+      T pos1=GetBinLowEdge(pos);
+      T pos2=GetBinLowEdge(pos+1);
+      return (1-x)*pos1 + x*pos2;
+    };
+    
+  private:
+    TFCS1DFunction_Array<Tint> m_array;
+    inline size_t size() const {return m_array.size();};
+    T m_Min{0};
+    T m_Max{0};  
+
+  ClassDefNV(TFCS1DFunction_HistogramCompactBinEdges,1)  //TFCS1DFunction_HistogramCompactBinEdges
+};
+
+class TFCS1DFunction_HistogramInt8BinEdges: public TFCS1DFunction_HistogramCompactBinEdges<float,uint8_t,float>
+{
+  public:
+    TFCS1DFunction_HistogramInt8BinEdges(size_t nbins=0):TFCS1DFunction_HistogramCompactBinEdges<float,uint8_t,float>(nbins) {};
+
+  ClassDefNV(TFCS1DFunction_HistogramInt8BinEdges,1)  //TFCS1DFunction_HistogramInt8BinEdges
+};
+
+class TFCS1DFunction_HistogramInt16BinEdges: public TFCS1DFunction_HistogramCompactBinEdges<float,uint16_t,float>
+{
+  public:
+    TFCS1DFunction_HistogramInt16BinEdges(size_t nbins=0):TFCS1DFunction_HistogramCompactBinEdges<float,uint16_t,float>(nbins) {};
+
+  ClassDefNV(TFCS1DFunction_HistogramInt16BinEdges,1)  //TFCS1DFunction_HistogramInt16BinEdges
+};
+
+class TFCS1DFunction_HistogramInt32BinEdges: public TFCS1DFunction_HistogramCompactBinEdges<float,uint32_t,float>
+{
+  public:
+    TFCS1DFunction_HistogramInt32BinEdges(size_t nbins=0):TFCS1DFunction_HistogramCompactBinEdges<float,uint32_t,float>(nbins) {};
+
+  ClassDefNV(TFCS1DFunction_HistogramInt32BinEdges,1)  //TFCS1DFunction_HistogramInt32BinEdges
+};
+
+
+#if defined(__ROOTCLING__) && defined(__FastCaloSimStandAlone__)
+#pragma link C++ class TFCS1DFunction_Numeric<uint8_t,float>+;
+#pragma link C++ class TFCS1DFunction_Numeric<uint16_t,float>+;
+#pragma link C++ class TFCS1DFunction_Numeric<uint32_t,float>+;
+#pragma link C++ class TFCS1DFunction_Numeric<float,float>+;
+#pragma link C++ class TFCS1DFunction_Numeric<double,float>+;
+#pragma link C++ class TFCS1DFunction_Numeric<double,double>+;
+
+#pragma link C++ class TFCS1DFunction_Array<float>-;
+#pragma link C++ class TFCS1DFunction_Array<double>-;
+#pragma link C++ class TFCS1DFunction_Array<uint8_t>-;
+#pragma link C++ class TFCS1DFunction_Array<uint16_t>-;
+#pragma link C++ class TFCS1DFunction_Array<uint32_t>-;
+
+#pragma link C++ class TFCS1DFunction_HistogramContent<float,float>+;
+#pragma link C++ class TFCS1DFunction_HistogramContent<double,float>+;
+#pragma link C++ class TFCS1DFunction_HistogramContent<double,double>+;
+#pragma link C++ class TFCS1DFunction_HistogramContent<uint8_t,float>+;
+#pragma link C++ class TFCS1DFunction_HistogramContent<uint16_t,float>+;
+#pragma link C++ class TFCS1DFunction_HistogramContent<uint32_t,float>+;
+
+#pragma link C++ class TFCS1DFunction_HistogramBinEdges<float,float>+;
+#pragma link C++ class TFCS1DFunction_HistogramBinEdges<double,float>+;
+#pragma link C++ class TFCS1DFunction_HistogramBinEdges<double,double>+;
+
+#pragma link C++ class TFCS1DFunction_HistogramCompactBinEdges<float,uint8_t,float>+;
+#pragma link C++ class TFCS1DFunction_HistogramCompactBinEdges<float,uint16_t,float>+;
+#pragma link C++ class TFCS1DFunction_HistogramCompactBinEdges<float,uint32_t,float>+;
+
+#pragma link C++ class TFCS1DFunction_HistogramInt8BinEdges+;
+#pragma link C++ class TFCS1DFunction_HistogramInt16BinEdges+;
+#pragma link C++ class TFCS1DFunction_HistogramInt32BinEdges+;
+#pragma link C++ class TFCS1DFunction_HistogramFloatBinEdges+;
+#pragma link C++ class TFCS1DFunction_HistogramDoubleBinEdges+;
+
+#endif
+
+#endif
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHistogram.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHistogram.h
index 939f21594669f063a88979c14de6ee2c1cb75114..405fae06b479c2711abda5b0bb29fe5fcf700b91 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHistogram.h
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHistogram.h
@@ -1,404 +1,16 @@
 /*
-  Copyright (C) 2002-2018 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
 */
 
 #ifndef ISF_FASTCALOSIMEVENT_TFCS1DFunctionTemplateHistogram_h
 #define ISF_FASTCALOSIMEVENT_TFCS1DFunctionTemplateHistogram_h
 
 #include "ISF_FastCaloSimEvent/TFCS1DFunction.h"
-#include <vector>
-#include "TH1.h"
-#include "TBuffer.h"
-#include <cstring>
-#include <algorithm>
-#include <cmath>
-
+#include "ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHelpers.h"
 
+#include "TH1.h"
 #include <iostream>
 
-//For the purpose of FastCaloSim, 32bit are sufficient for bin counting
-typedef uint32_t TFCS1DFunction_size_t;
-
-///Converter functions that does nothing for floats
-template<typename T,typename Tfloat=float> class TFCS1DFunction_Numeric {
-public:
-  static constexpr T MaxValue = 1;
-  static constexpr Tfloat MaxValueFloat = MaxValue;
-
-  static inline T MaxCeilOnlyForInt(const Tfloat value) {return value;};
-  static inline T ExpandToMaxRange(const Tfloat value) {return value;};
-  static inline Tfloat ToNormalizedRange(const T value) {return value;};
-};  
-
-///Converter functions to store/retrive float numbers in integers
-template<typename Tfloat> class TFCS1DFunction_Numeric<uint8_t,Tfloat> {
-public:
-  static constexpr uint8_t MaxValue = UINT8_MAX;
-  static constexpr Tfloat MaxValueFloat = MaxValue;
-
-  static inline uint8_t MaxCeilOnlyForInt(const Tfloat value) {return std::ceil(MaxValueFloat*value);};
-  static inline uint8_t ExpandToMaxRange(const Tfloat value) {return value*(1+MaxValueFloat);};
-  static inline Tfloat ToNormalizedRange(const uint8_t value) {return value/(1+MaxValueFloat);};
-};  
-
-///Converter functions to store/retrive float numbers in integers
-template<typename Tfloat> class TFCS1DFunction_Numeric<uint16_t,Tfloat> {
-public:
-  static constexpr uint16_t MaxValue = UINT16_MAX;
-  static constexpr Tfloat MaxValueFloat = MaxValue;
-
-  static inline uint16_t MaxCeilOnlyForInt(const Tfloat value) {return std::ceil(MaxValueFloat*value);};
-  static inline uint16_t ExpandToMaxRange(const Tfloat value) {return value*(1+MaxValueFloat);};
-  static inline Tfloat ToNormalizedRange(const uint16_t value) {return value/(1+MaxValueFloat);};
-};  
-
-///Converter functions to store/retrive float numbers in integers
-template<typename Tfloat> class TFCS1DFunction_Numeric<uint32_t,Tfloat> {
-public:
-  static constexpr uint32_t MaxValue = UINT32_MAX;
-  static constexpr Tfloat MaxValueFloat = MaxValue;
-
-  static inline uint32_t MaxCeilOnlyForInt(const Tfloat value) {return std::ceil(MaxValueFloat*value);};
-  static inline uint32_t ExpandToMaxRange(const Tfloat value) {return value*(1+MaxValueFloat);};
-  static inline Tfloat ToNormalizedRange(const uint32_t value) {return value/(1+MaxValueFloat);};
-};  
-
-template<typename T> class TFCS1DFunction_Array
-{
-  public:
-    typedef TFCS1DFunction_size_t size_t;
-    
-    TFCS1DFunction_Array() {};
-    TFCS1DFunction_Array(size_t count) {resize(count);};
-    ~TFCS1DFunction_Array() {if(m_content) delete[] m_content;};
-
-    std::size_t MemorySizeArray() const {return size()*sizeof(T);};
-    std::size_t MemorySize() const {return sizeof(*this)+MemorySizeArray();};
-
-    inline size_t size() const {return m_size;};
-    
-    ///resize to given count, copying old content
-    void resize(size_t count) {
-      T* new_cont=nullptr;
-      if(count>0) new_cont=new T[count];
-      if(m_content && new_cont) {
-        size_t ncopy=count;
-        if(size()<ncopy) ncopy=size();
-        ncopy*=sizeof(T);
-        std::memcpy(new_cont,m_content,ncopy);
-      }
-      if(m_content) delete[] m_content;
-      m_size=count;
-      m_content=new_cont;
-    };
-    
-    ///Direct data pointer
-    inline T* data() {return m_content;};
-    inline const T* data() const {return m_content;};
-    
-    ///Direct access operators. Values are in the range [0,TFCS1DFunction_Numeric<T>::MaxValue]
-    inline T&       operator[]( size_t pos ) {return m_content[pos];};
-    inline const T& operator[]( size_t pos ) const {return m_content[pos];};
-    
-    ///begin() iterators
-    inline T* begin() {return m_content;};
-    inline const T* begin() const {return m_content;};
-
-    ///end() iterators
-    inline T* end() {return m_content+size();};
-    inline const T* end() const {return m_content+size();};
-    
-  private:
-    T* m_content{nullptr};//!
-    size_t m_size{0};
-
-  //Use ClassDef without virtual functions. Saves 8 bytes per instance
-  ClassDefNV(TFCS1DFunction_Array,1)  //TFCS1DFunction_Array
-};
-
-///Streamer method to persitify objects of type TFCS1DFunction_Array<T>
-template<typename T> void TFCS1DFunction_Array<T>::Streamer(TBuffer &b) {
-  if (b.IsReading()) {
-    size_t count;
-    b >> count;
-    resize(count);
-    if(m_size>0) b.ReadFastArray(m_content, m_size);
-  } else {
-    b << m_size;
-    if(m_size>0) b.WriteFastArray(m_content, m_size);
-  }
-}
-
-
-//Class to represent histogram content. Trandom should be a type with a floating point range [0,1]
-template<typename T,typename Trandom=float> class TFCS1DFunction_HistogramContent
-{
-  public:
-    typedef TFCS1DFunction_size_t size_t;
-    typedef T value_type;
-    typedef Trandom random_type;
-
-    TFCS1DFunction_HistogramContent(size_t nbins=0):m_array(nbins >= 1 ? nbins-1 : 0) {};
-
-    std::size_t MemorySizeArray() const {return m_array.MemorySizeArray();};
-    std::size_t MemorySize() const {return sizeof(*this)+MemorySizeArray();};
-
-    ///Set the content of bin pos to a given value, where value is in the range [0,1]
-    void set_fraction( size_t pos, Trandom value ) {
-      if(pos>=size()) return;
-      m_array[pos]=TFCS1DFunction_Numeric<T,Trandom>::MaxCeilOnlyForInt(value);
-    };
-    
-    ///Get the cumulative content at bin pos as fraction in the range [0,1]
-    inline Trandom get_fraction( size_t pos ) const {
-      if(pos>=size()) return 1;
-      return m_array[pos]/TFCS1DFunction_Numeric<T,Trandom>::MaxValueFloat;
-    };
-    
-    ///Get the content at bin pos as fraction in the range [0,1]
-    inline Trandom get_binfraction( size_t pos ) const {
-      if(pos==0) return m_array[pos]/TFCS1DFunction_Numeric<T,Trandom>::MaxValueFloat;
-      if(pos==size()) return 1-(m_array[size()-1]/TFCS1DFunction_Numeric<T,Trandom>::MaxValueFloat);
-      return (m_array[pos]-m_array[pos-1])/TFCS1DFunction_Numeric<T,Trandom>::MaxValueFloat;
-    };
-    
-    ///set number of bins. 
-    ///The actualy alocated size is one smaller than count, as the last bin is fixed with the range [get_fraction(size()-1,1]
-    void set_nbins(size_t nbins) {m_array.resize(nbins >= 1 ? nbins-1 : 0);};
-
-    ///return number of bins. 
-    ///This is one larger than size, as the last bin is fixed with the range [get_fraction(size()-1,1]
-    inline size_t get_nbins() const {return size()+1;};
-
-    ///Get the matching bin for a given random value in the range [0,1). 
-    ///A residual random number to calculate a position inside this bin is returned in residual_rnd
-    size_t get_bin(const Trandom& drnd,Trandom& residual_rnd) const {
-      if(size()==0) {
-        residual_rnd=drnd;
-        return 0;
-      }  
-      T rnd=TFCS1DFunction_Numeric<T,Trandom>::MaxValueFloat*drnd;
-      auto it = std::upper_bound(m_array.begin(),m_array.end(),rnd);
-
-      T basecont=0;
-      if(it!=m_array.begin()) basecont=*(it-1);
-
-      T fullcont=TFCS1DFunction_Numeric<T,Trandom>::MaxValue;
-      if(it!=m_array.end()) fullcont=*it;
-
-      T dcont=fullcont-basecont;
-      if(dcont>0) {
-        residual_rnd=(TFCS1DFunction_Numeric<T,Trandom>::MaxValueFloat*drnd-basecont) / dcont;
-      } else {
-        residual_rnd=0.5;
-      }
-
-      if(it==m_array.end()) return size();
-      return std::distance(m_array.begin(),it);
-    }; 
-
-  private:
-    TFCS1DFunction_Array<T> m_array;
-    inline size_t size() const {return m_array.size();};
-
-  //Use ClassDef without virtual functions. Saves 8 bytes per instance
-  ClassDefNV(TFCS1DFunction_HistogramContent,1)  //TFCS1DFunction_HistogramContent
-};
-
-//Class to represent histogram bin edges. Trandom should be a type with a floating point range [0,1]
-template<typename T,typename Trandom=float> class TFCS1DFunction_HistogramBinEdges
-{
-  public:
-    typedef TFCS1DFunction_size_t size_t;
-    typedef T value_type;
-    typedef Trandom random_type;
-
-    TFCS1DFunction_HistogramBinEdges(size_t nbins=0):m_array(nbins+1) {};
-    ~TFCS1DFunction_HistogramBinEdges() {};
-
-    std::size_t MemorySizeArray() const {return m_array.MemorySizeArray();};
-    std::size_t MemorySize() const {return sizeof(*this)+MemorySizeArray();};
-
-    ///set number of bins
-    void set_nbins(size_t nbins) {m_array.resize(nbins+1);};
-
-    ///return number of bins
-    inline size_t get_nbins() const {return size()-1;};
-    
-    ///set position of lower edge of bins
-    void SetBinLowEdge(size_t pos,const T& value) {m_array[pos]=value;};
-    
-    ///get position of lower edge of bins
-    inline const T& GetBinLowEdge(size_t pos) const {return m_array[pos];};
-    
-    ///get the length of a bin
-    inline const T GetBinLength(size_t pos) const {return GetBinLowEdge(pos+1)-GetBinLowEdge(pos);};
-    
-    ///set and get minimum
-    void SetMin(const T& value) {m_array[0]=value;};
-    inline const T& GetMin() const {return m_array[0];};
-
-    ///set and get maximum
-    void SetMax(const T& value) {m_array[get_nbins()]=value;};
-    inline const T& GetMax() const {return m_array[get_nbins()];};
-
-    ///set minimum and maximum
-    void SetMinMax(const T& valuemin,const T& valuemax) {SetMin(valuemin);SetMax(valuemax);};
-    
-    ///Get length of interval of all bins
-    inline const T Length() const {return GetMax()-GetMin();};
-    
-    ///return linear interpolated position for bin pos. Interpolation is done with a random value in the range [0,1]
-    inline T position(size_t pos,const Trandom& drnd) const {
-      return (1-drnd)*m_array[pos] + drnd*m_array[pos+1];
-    };
-
-    ///return interpolated position for bin pos, such that histograming the position gives a linear slope m,
-    ///where m is in units of the bin width for bin pos. Interpolation is done with a random value in the range [0,1]
-    inline T position(size_t pos,Trandom m,const Trandom& drnd) const {
-      if(m>2) m=2;
-      if(m<-2) m=-2;
-      Trandom x=fabs(m)>0.001 ? (0.5*std::sqrt(m*(m+8*drnd-4)+4)-1)/m+0.5 : drnd;
-      return (1-x)*m_array[pos] + x*m_array[pos+1];
-    };
-
-  private:
-    TFCS1DFunction_Array<T> m_array;
-    inline size_t size() const {return m_array.size();};
-
-  //Use ClassDef without virtual functions. Saves 8 bytes per instance
-  ClassDefNV(TFCS1DFunction_HistogramBinEdges,1)  //TFCS1DFunction_HistogramBinEdges
-};
-
-class TFCS1DFunction_HistogramFloatBinEdges: public TFCS1DFunction_HistogramBinEdges<float,float>
-{
-  public:
-    TFCS1DFunction_HistogramFloatBinEdges(size_t nbins=0):TFCS1DFunction_HistogramBinEdges<float,float>(nbins) {};
-
-  ClassDefNV(TFCS1DFunction_HistogramFloatBinEdges,1)  //TFCS1DFunction_HistogramFloatBinEdges
-};
-
-class TFCS1DFunction_HistogramDoubleBinEdges: public TFCS1DFunction_HistogramBinEdges<double,double>
-{
-  public:
-    TFCS1DFunction_HistogramDoubleBinEdges(size_t nbins=0):TFCS1DFunction_HistogramBinEdges<double,double>(nbins) {};
-
-  ClassDefNV(TFCS1DFunction_HistogramDoubleBinEdges,1)  //TFCS1DFunction_HistogramDoubleBinEdges
-};
-
-
-
-//Class to represent histogram bin edges, where the interval between GetMin() and GetMax() 
-//can be stored as a different (smaller) data type Tint
-//Trandom should be a type with a floating point range [0,1]
-template<typename T,typename Tint,typename Trandom=float> class TFCS1DFunction_HistogramCompactBinEdges
-{
-  public:
-    typedef TFCS1DFunction_size_t size_t;
-    typedef T value_type;
-    typedef Trandom random_type;
-    typedef Tint internal_storage_type;
-    
-    TFCS1DFunction_HistogramCompactBinEdges(size_t nbins=0):m_array(nbins >= 1 ? nbins-1 : 0) {};
-    ~TFCS1DFunction_HistogramCompactBinEdges() {};
-
-    std::size_t MemorySizeArray() const {return m_array.MemorySizeArray();};
-    std::size_t MemorySize() const {return sizeof(*this)+MemorySizeArray();};
-
-    ///set number of bins
-    void set_nbins(size_t nbins) {m_array.resize(nbins >= 1 ? nbins-1 : 0);};
-
-    ///return number of bins
-    inline size_t get_nbins() const {return size()+1;};
-    
-    ///set position of lower edge of bins. Requires GetMin() and GetMax() to be set and may not be changed!
-    void SetBinLowEdge(size_t pos,const T& value) {
-      if(pos==0) {
-        SetMin(value);
-        return;
-      }  
-      if(pos>=get_nbins()) {
-        SetMax(value);
-        return;
-      }  
-      m_array[pos-1]=TFCS1DFunction_Numeric<Tint,T>::ExpandToMaxRange((value-GetMin())/Length());
-    };
-
-    ///get position of lower edge of bins. Requires GetMin() and GetMax() to be set and may not be changed!
-    inline const T GetBinLowEdge(size_t pos) const {
-      if(pos==0) return GetMin();
-      if(pos>=get_nbins()) return GetMax();
-      return GetMin()+Length()*TFCS1DFunction_Numeric<Tint,T>::ToNormalizedRange(m_array[pos-1]);
-    };
-
-    ///get the length of a bin
-    inline const T GetBinLength(size_t pos) const {return GetBinLowEdge(pos+1)-GetBinLowEdge(pos);};
-    
-    ///set and get minimum
-    void SetMin(const T& value) {m_Min=value;};
-    inline const T& GetMin() const {return m_Min;};
-
-    ///set and get maximum
-    void SetMax(const T& value) {m_Max=value;};
-    inline const T& GetMax() const {return m_Max;};
-    
-    ///set minimum and maximum
-    void SetMinMax(const T& valuemin,const T& valuemax) {SetMin(valuemin);SetMax(valuemax);};
-    
-    ///Get length of interval of all bins
-    inline const T Length() const {return GetMax()-GetMin();};
-    
-    ///return linear interpolated position for bin pos. Interpolation is done with a random value in the range [0,1]
-    inline T position(size_t pos,const Trandom& drnd) const {
-      T pos1=GetBinLowEdge(pos);
-      T pos2=GetBinLowEdge(pos+1);
-      return (1-drnd)*pos1 + drnd*pos2;
-    };
-    
-    ///return interpolated position for bin pos, such that histograming the position gives a linear slope m,
-    ///where m is in units of the bin width for bin pos. Interpolation is done with a random value in the range [0,1]
-    inline T position(size_t pos,Trandom m,const Trandom& drnd) const {
-      if(m>2) m=2;
-      if(m<-2) m=-2;
-      Trandom x=(0.5*std::sqrt(m*(m+8*drnd-4)+4)-1)/m+0.5;
-      T pos1=GetBinLowEdge(pos);
-      T pos2=GetBinLowEdge(pos+1);
-      return (1-x)*pos1 + x*pos2;
-    };
-    
-  private:
-    TFCS1DFunction_Array<Tint> m_array;
-    inline size_t size() const {return m_array.size();};
-    T m_Min{0};
-    T m_Max{0};  
-
-  ClassDefNV(TFCS1DFunction_HistogramCompactBinEdges,1)  //TFCS1DFunction_HistogramCompactBinEdges
-};
-
-class TFCS1DFunction_HistogramInt8BinEdges: public TFCS1DFunction_HistogramCompactBinEdges<float,uint8_t,float>
-{
-  public:
-    TFCS1DFunction_HistogramInt8BinEdges(size_t nbins=0):TFCS1DFunction_HistogramCompactBinEdges<float,uint8_t,float>(nbins) {};
-
-  ClassDefNV(TFCS1DFunction_HistogramInt8BinEdges,1)  //TFCS1DFunction_HistogramInt8BinEdges
-};
-
-class TFCS1DFunction_HistogramInt16BinEdges: public TFCS1DFunction_HistogramCompactBinEdges<float,uint16_t,float>
-{
-  public:
-    TFCS1DFunction_HistogramInt16BinEdges(size_t nbins=0):TFCS1DFunction_HistogramCompactBinEdges<float,uint16_t,float>(nbins) {};
-
-  ClassDefNV(TFCS1DFunction_HistogramInt16BinEdges,1)  //TFCS1DFunction_HistogramInt16BinEdges
-};
-
-class TFCS1DFunction_HistogramInt32BinEdges: public TFCS1DFunction_HistogramCompactBinEdges<float,uint32_t,float>
-{
-  public:
-    TFCS1DFunction_HistogramInt32BinEdges(size_t nbins=0):TFCS1DFunction_HistogramCompactBinEdges<float,uint32_t,float>(nbins) {};
-
-  ClassDefNV(TFCS1DFunction_HistogramInt32BinEdges,1)  //TFCS1DFunction_HistogramInt32BinEdges
-};
-
 template <typename Txvec, typename Ty,typename Trandom=float> class TFCS1DFunctionTemplateHistogram:public TFCS1DFunction
 {
   public:
@@ -486,78 +98,6 @@ template <typename Txvec, typename Ty,typename Trandom=float> class TFCS1DFuncti
   ClassDef(TFCS1DFunctionTemplateHistogram,1)  //TFCS1DFunctionTemplateHistogram
 };
 
-template <typename Txvec, typename Ty,typename Trandom=float> class TFCS1DFunctionTemplateInterpolationHistogram:public TFCS1DFunctionTemplateHistogram<Txvec,Ty,Trandom>
-{
-  public:
-    TFCS1DFunctionTemplateInterpolationHistogram(TH1* hist=nullptr):TFCS1DFunctionTemplateHistogram<Txvec,Ty,Trandom>(hist) {};
-
-    using TFCS1DFunction::rnd_to_fct;
-    using TFCS1DFunctionTemplateHistogram<Txvec,Ty,Trandom>::get_nbins;
-    using TFCS1DFunctionTemplateHistogram<Txvec,Ty,Trandom>::m_HistoContents;
-    using TFCS1DFunctionTemplateHistogram<Txvec,Ty,Trandom>::m_HistoBorders;
-
-    ///Function gets random number rnd in the range [0,1) as argument 
-    ///and returns function value according to a histogram distribution.
-    ///A linear interpolation is done within each bin
-    virtual double rnd_to_fct(double rnd) const {
-      size_t nbins=get_nbins();
-      if(nbins==0) return 0;
-      Trandom residual_rnd;
-      size_t ibin=m_HistoContents.get_bin(rnd,residual_rnd);
-      
-      Trandom frac=m_HistoContents.get_fraction(ibin);
-  
-      Trandom dfrac;
-      auto l=m_HistoBorders.GetBinLength(ibin);
-      Trandom dfracprev;
-      auto lprev=l;
-      if(ibin>0) {
-        Trandom fracprev=m_HistoContents.get_fraction(ibin-1);
-        dfrac=frac-fracprev;
-        if(ibin>1) dfracprev=fracprev-m_HistoContents.get_fraction(ibin-2);
-         else dfracprev=fracprev;
-        lprev=m_HistoBorders.GetBinLength(ibin-1);
-      } else {
-        dfrac=frac;
-        dfracprev=dfrac;
-      }
-      
-      Trandom dfracnext;
-      auto lnext=l;
-      if(ibin<nbins-1) {
-        dfracnext=m_HistoContents.get_fraction(ibin+1)-frac;
-        lnext=m_HistoBorders.GetBinLength(ibin+1);
-      } else {
-        dfracnext=dfrac;
-      }  
-      
-      Trandom m;
-      if(dfrac>0) {
-        /*
-        //approximately readable version of the code below
-        //density in bin is dfracX/lX
-        Trandom mnext=dfracnext/lnext-dfrac/l; //change in density to next bin
-        mnext/=0.5*(l+lnext); //normalize change in density to distance between bins
-        Trandom mprev=dfrac/l-dfracprev/lprev; //change in density to previous bin
-        mprev/=0.5*(l+lprev); //normalize change in density to distance between bins
-        m=0.5*(mnext+mprev); //average the two
-        m*=l; //m is needed inside this bin, so multiply by size of this bin (matches normalization to distance between bins above)
-        m/=dfrac/l; //finally, m is the slope in this bin, given that this bin was already selected. So overall normalize to density inside this bin
-        */
-        Trandom mnext=dfracnext/lnext-dfrac/l;
-        mnext/=l+lnext;
-        Trandom mprev=dfrac/l-dfracprev/lprev;
-        mprev/=l+lprev;
-        m=(mnext+mprev)*l*l/dfrac;
-      } else m=0;
-      
-      //std::cout<<"fbin="<<ibin<<" fx="<<m_HistoBorders.GetBinLowEdge(ibin)<<" frac="<<m_HistoContents.get_fraction(ibin)<<" dfracprev="<<dfracprev<<" dfrac="<<dfrac<<" dfracnext="<<dfracnext<<" dfracprev-dfrac="<<dfracprev-dfrac<<" dfracnext-dfrac="<<dfracnext-dfrac<<" m="<<m<<" residual_rnd="<<residual_rnd<<std::endl;
-      return m_HistoBorders.position(ibin,m,residual_rnd);
-    }
-
-  ClassDef(TFCS1DFunctionTemplateInterpolationHistogram,1)  //TFCS1DFunctionTemplateInterpolationHistogram
-};
-
 class TFCS1DFunctionInt8Int8Histogram: public TFCS1DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint8_t,float>
 {
   public:
@@ -598,76 +138,8 @@ class TFCS1DFunctionInt16Int32Histogram: public TFCS1DFunctionTemplateHistogram<
   ClassDef(TFCS1DFunctionInt16Int32Histogram,1)  //TFCS1DFunctionInt16Int32Histogram
 };
 
-class TFCS1DFunctionInt8Int8InterpolationHistogram: public TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint8_t,float>
-{
-  public:
-    TFCS1DFunctionInt8Int8InterpolationHistogram(TH1* h=nullptr):TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint8_t,float>(h) {};
-
-  ClassDef(TFCS1DFunctionInt8Int8InterpolationHistogram,1)  //TFCS1DFunctionInt8Int8InterpolationHistogram
-};
-
-class TFCS1DFunctionInt8Int16InterpolationHistogram: public TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint16_t,float>
-{
-  public:
-    TFCS1DFunctionInt8Int16InterpolationHistogram(TH1* h=nullptr):TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint16_t,float>(h) {};
-
-  ClassDef(TFCS1DFunctionInt8Int16InterpolationHistogram,1)  //TFCS1DFunctionInt8Int16InterpolationHistogram
-};
-
-class TFCS1DFunctionInt16Int16InterpolationHistogram: public TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,uint16_t,float>
-{
-  public:
-    TFCS1DFunctionInt16Int16InterpolationHistogram(TH1* h=nullptr):TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,uint16_t,float>(h) {};
-
-  ClassDef(TFCS1DFunctionInt16Int16InterpolationHistogram,1)  //TFCS1DFunctionInt16Int16InterpolationHistogram
-};
-
-class TFCS1DFunctionInt16Int32InterpolationHistogram: public TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,uint32_t,float>
-{
-  public:
-    TFCS1DFunctionInt16Int32InterpolationHistogram(TH1* h=nullptr):TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,uint32_t,float>(h) {};
-
-  ClassDef(TFCS1DFunctionInt16Int32InterpolationHistogram,1)  //TFCS1DFunctionInt16Int32InterpolationHistogram
-};
-
-
-
 
 #if defined(__ROOTCLING__) && defined(__FastCaloSimStandAlone__)
-#pragma link C++ class TFCS1DFunction_Numeric<uint8_t,float>+;
-#pragma link C++ class TFCS1DFunction_Numeric<uint16_t,float>+;
-#pragma link C++ class TFCS1DFunction_Numeric<uint32_t,float>+;
-#pragma link C++ class TFCS1DFunction_Numeric<float,float>+;
-#pragma link C++ class TFCS1DFunction_Numeric<double,float>+;
-#pragma link C++ class TFCS1DFunction_Numeric<double,double>+;
-
-#pragma link C++ class TFCS1DFunction_Array<float>-;
-#pragma link C++ class TFCS1DFunction_Array<double>-;
-#pragma link C++ class TFCS1DFunction_Array<uint8_t>-;
-#pragma link C++ class TFCS1DFunction_Array<uint16_t>-;
-#pragma link C++ class TFCS1DFunction_Array<uint32_t>-;
-
-#pragma link C++ class TFCS1DFunction_HistogramContent<float,float>+;
-#pragma link C++ class TFCS1DFunction_HistogramContent<double,float>+;
-#pragma link C++ class TFCS1DFunction_HistogramContent<double,double>+;
-#pragma link C++ class TFCS1DFunction_HistogramContent<uint8_t,float>+;
-#pragma link C++ class TFCS1DFunction_HistogramContent<uint16_t,float>+;
-#pragma link C++ class TFCS1DFunction_HistogramContent<uint32_t,float>+;
-
-#pragma link C++ class TFCS1DFunction_HistogramBinEdges<float,float>+;
-#pragma link C++ class TFCS1DFunction_HistogramBinEdges<double,float>+;
-#pragma link C++ class TFCS1DFunction_HistogramBinEdges<double,double>+;
-
-#pragma link C++ class TFCS1DFunction_HistogramCompactBinEdges<float,uint8_t,float>+;
-#pragma link C++ class TFCS1DFunction_HistogramCompactBinEdges<float,uint16_t,float>+;
-#pragma link C++ class TFCS1DFunction_HistogramCompactBinEdges<float,uint32_t,float>+;
-
-#pragma link C++ class TFCS1DFunction_HistogramInt8BinEdges+;
-#pragma link C++ class TFCS1DFunction_HistogramInt16BinEdges+;
-#pragma link C++ class TFCS1DFunction_HistogramInt32BinEdges+;
-#pragma link C++ class TFCS1DFunction_HistogramFloatBinEdges+;
-#pragma link C++ class TFCS1DFunction_HistogramDoubleBinEdges+;
-
 #pragma link C++ class TFCS1DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint8_t,float>+;
 #pragma link C++ class TFCS1DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint16_t,float>+;
 #pragma link C++ class TFCS1DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint32_t,float>+;
@@ -680,16 +152,6 @@ class TFCS1DFunctionInt16Int32InterpolationHistogram: public TFCS1DFunctionTempl
 #pragma link C++ class TFCS1DFunctionInt16Int16Histogram+;
 #pragma link C++ class TFCS1DFunctionInt16Int32Histogram+;
 
-#pragma link C++ class TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint8_t,float>+;
-#pragma link C++ class TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint16_t,float>+;
-#pragma link C++ class TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,uint16_t,float>+;
-#pragma link C++ class TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,uint32_t,float>+;
-
-#pragma link C++ class TFCS1DFunctionInt8Int8InterpolationHistogram+;
-#pragma link C++ class TFCS1DFunctionInt8Int16InterpolationHistogram+;
-#pragma link C++ class TFCS1DFunctionInt16Int16InterpolationHistogram+;
-#pragma link C++ class TFCS1DFunctionInt16Int32InterpolationHistogram+;
-
 #endif
 
 #endif
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS1DFunctionTemplateInterpolationHistogram.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS1DFunctionTemplateInterpolationHistogram.h
new file mode 100644
index 0000000000000000000000000000000000000000..a9ec021859f7c9373098ea5fae3bebae530bca46
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS1DFunctionTemplateInterpolationHistogram.h
@@ -0,0 +1,128 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef ISF_FASTCALOSIMEVENT_TFCS1DFunctionTemplateInterpolationHistogram_h
+#define ISF_FASTCALOSIMEVENT_TFCS1DFunctionTemplateInterpolationHistogram_h
+
+#include "ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHistogram.h"
+
+template <typename Txvec, typename Ty,typename Trandom=float> class TFCS1DFunctionTemplateInterpolationHistogram:public TFCS1DFunctionTemplateHistogram<Txvec,Ty,Trandom>
+{
+  public:
+    TFCS1DFunctionTemplateInterpolationHistogram(TH1* hist=nullptr):TFCS1DFunctionTemplateHistogram<Txvec,Ty,Trandom>(hist) {};
+
+    using TFCS1DFunction::rnd_to_fct;
+    using TFCS1DFunctionTemplateHistogram<Txvec,Ty,Trandom>::get_nbins;
+    using TFCS1DFunctionTemplateHistogram<Txvec,Ty,Trandom>::m_HistoContents;
+    using TFCS1DFunctionTemplateHistogram<Txvec,Ty,Trandom>::m_HistoBorders;
+
+    ///Function gets random number rnd in the range [0,1) as argument 
+    ///and returns function value according to a histogram distribution.
+    ///A linear interpolation is done within each bin
+    virtual double rnd_to_fct(double rnd) const {
+      size_t nbins=get_nbins();
+      if(nbins==0) return 0;
+      Trandom residual_rnd;
+      size_t ibin=m_HistoContents.get_bin(rnd,residual_rnd);
+      
+      Trandom frac=m_HistoContents.get_fraction(ibin);
+  
+      Trandom dfrac;
+      auto l=m_HistoBorders.GetBinLength(ibin);
+      Trandom dfracprev;
+      auto lprev=l;
+      if(ibin>0) {
+        Trandom fracprev=m_HistoContents.get_fraction(ibin-1);
+        dfrac=frac-fracprev;
+        if(ibin>1) dfracprev=fracprev-m_HistoContents.get_fraction(ibin-2);
+         else dfracprev=fracprev;
+        lprev=m_HistoBorders.GetBinLength(ibin-1);
+      } else {
+        dfrac=frac;
+        dfracprev=dfrac;
+      }
+      
+      Trandom dfracnext;
+      auto lnext=l;
+      if(ibin<nbins-1) {
+        dfracnext=m_HistoContents.get_fraction(ibin+1)-frac;
+        lnext=m_HistoBorders.GetBinLength(ibin+1);
+      } else {
+        dfracnext=dfrac;
+      }  
+      
+      Trandom m;
+      if(dfrac>0) {
+        /*
+        //approximately readable version of the code below
+        //density in bin is dfracX/lX
+        Trandom mnext=dfracnext/lnext-dfrac/l; //change in density to next bin
+        mnext/=0.5*(l+lnext); //normalize change in density to distance between bins
+        Trandom mprev=dfrac/l-dfracprev/lprev; //change in density to previous bin
+        mprev/=0.5*(l+lprev); //normalize change in density to distance between bins
+        m=0.5*(mnext+mprev); //average the two
+        m*=l; //m is needed inside this bin, so multiply by size of this bin (matches normalization to distance between bins above)
+        m/=dfrac/l; //finally, m is the slope in this bin, given that this bin was already selected. So overall normalize to density inside this bin
+        */
+        Trandom mnext=dfracnext/lnext-dfrac/l;
+        mnext/=l+lnext;
+        Trandom mprev=dfrac/l-dfracprev/lprev;
+        mprev/=l+lprev;
+        m=(mnext+mprev)*l*l/dfrac;
+      } else m=0;
+      
+      //std::cout<<"fbin="<<ibin<<" fx="<<m_HistoBorders.GetBinLowEdge(ibin)<<" frac="<<m_HistoContents.get_fraction(ibin)<<" dfracprev="<<dfracprev<<" dfrac="<<dfrac<<" dfracnext="<<dfracnext<<" dfracprev-dfrac="<<dfracprev-dfrac<<" dfracnext-dfrac="<<dfracnext-dfrac<<" m="<<m<<" residual_rnd="<<residual_rnd<<std::endl;
+      return m_HistoBorders.position(ibin,m,residual_rnd);
+    }
+
+  ClassDef(TFCS1DFunctionTemplateInterpolationHistogram,1)  //TFCS1DFunctionTemplateInterpolationHistogram
+};
+
+class TFCS1DFunctionInt8Int8InterpolationHistogram: public TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint8_t,float>
+{
+  public:
+    TFCS1DFunctionInt8Int8InterpolationHistogram(TH1* h=nullptr):TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint8_t,float>(h) {};
+
+  ClassDef(TFCS1DFunctionInt8Int8InterpolationHistogram,1)  //TFCS1DFunctionInt8Int8InterpolationHistogram
+};
+
+class TFCS1DFunctionInt8Int16InterpolationHistogram: public TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint16_t,float>
+{
+  public:
+    TFCS1DFunctionInt8Int16InterpolationHistogram(TH1* h=nullptr):TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint16_t,float>(h) {};
+
+  ClassDef(TFCS1DFunctionInt8Int16InterpolationHistogram,1)  //TFCS1DFunctionInt8Int16InterpolationHistogram
+};
+
+class TFCS1DFunctionInt16Int16InterpolationHistogram: public TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,uint16_t,float>
+{
+  public:
+    TFCS1DFunctionInt16Int16InterpolationHistogram(TH1* h=nullptr):TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,uint16_t,float>(h) {};
+
+  ClassDef(TFCS1DFunctionInt16Int16InterpolationHistogram,1)  //TFCS1DFunctionInt16Int16InterpolationHistogram
+};
+
+class TFCS1DFunctionInt16Int32InterpolationHistogram: public TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,uint32_t,float>
+{
+  public:
+    TFCS1DFunctionInt16Int32InterpolationHistogram(TH1* h=nullptr):TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,uint32_t,float>(h) {};
+
+  ClassDef(TFCS1DFunctionInt16Int32InterpolationHistogram,1)  //TFCS1DFunctionInt16Int32InterpolationHistogram
+};
+
+
+#if defined(__ROOTCLING__) && defined(__FastCaloSimStandAlone__)
+#pragma link C++ class TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint8_t,float>+;
+#pragma link C++ class TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,uint16_t,float>+;
+#pragma link C++ class TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,uint16_t,float>+;
+#pragma link C++ class TFCS1DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,uint32_t,float>+;
+
+#pragma link C++ class TFCS1DFunctionInt8Int8InterpolationHistogram+;
+#pragma link C++ class TFCS1DFunctionInt8Int16InterpolationHistogram+;
+#pragma link C++ class TFCS1DFunctionInt16Int16InterpolationHistogram+;
+#pragma link C++ class TFCS1DFunctionInt16Int32InterpolationHistogram+;
+
+#endif
+
+#endif
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS2DFunction.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS2DFunction.h
index d42cd394eaca50ab24da28858bcc42f75e684215..23001019539c6ae39ee8ecb601238bcc81bf9dbc 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS2DFunction.h
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS2DFunction.h
@@ -6,6 +6,7 @@
 #define ISF_FASTCALOSIMEVENT_TFCS2DFunction_h
 
 #include "ISF_FastCaloSimEvent/TFCSFunction.h"
+#include <vector>
 
 class TH2;
 
@@ -20,6 +21,10 @@ class TFCS2DFunction:public TFCSFunction
     virtual void rnd_to_fct(float& valuex,float& valuey,float rnd0,float rnd1) const = 0;
     virtual void rnd_to_fct(float value[],const float rnd[]) const;
 
+  	static double CheckAndIntegrate2DHistogram(const TH2* hist, std::vector<double>& integral_vec,int& first,int& last);
+
+    static void unit_test(TH2* hist=nullptr,TFCS2DFunction* rtof=nullptr,const char* outfilename="TFCS2DFunction_unit_test.root",int nrnd=10000000);
+    static void unit_tests(TH2* hist=nullptr,const char* outfilename="TFCS2DFunction_unit_test.root",int nrnd=10000000);
   private:
 
   ClassDef(TFCS2DFunction,1)  //TFCS2DFunction
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS2DFunctionLateralShapeParametrization.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS2DFunctionLateralShapeParametrization.h
new file mode 100644
index 0000000000000000000000000000000000000000..0cd3392f9cf0999c3046a940f68ddbe86ee3e94b
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS2DFunctionLateralShapeParametrization.h
@@ -0,0 +1,66 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TFCS2DFunctionLateralShapeParametrization_h
+#define TFCS2DFunctionLateralShapeParametrization_h
+
+#include "ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitBase.h"
+#include "ISF_FastCaloSimEvent/TFCS2DFunction.h"
+#include "ISF_FastCaloSimEvent/TFCSTruthState.h"
+
+class TH2;
+
+class TFCS2DFunctionLateralShapeParametrization:public TFCSLateralShapeParametrizationHitBase {
+public:
+  TFCS2DFunctionLateralShapeParametrization(const char* name=nullptr, const char* title=nullptr);
+  ~TFCS2DFunctionLateralShapeParametrization();
+
+  ///Status bit for FCS needs
+  enum FCSStatusBits {
+     k_phi_symmetric = BIT(15) ///< Set this bit to simulate phi symmetric histograms
+  };
+
+  bool is_phi_symmetric() const {return TestBit(k_phi_symmetric);};
+  virtual void set_phi_symmetric() {SetBit(k_phi_symmetric);};
+  virtual void reset_phi_symmetric() {ResetBit(k_phi_symmetric);};
+
+  /// set the desired number of hits
+  void set_number_of_hits(float nhits);
+
+  float get_number_of_expected_hits() const {return m_nhits;};
+  
+  ///default for this class is to simulate get_number_of_expected_hits() hits, 
+  ///which gives fluctuations sigma^2=1/get_number_of_expected_hits()
+  virtual double get_sigma2_fluctuation(TFCSSimulationState& simulstate,const TFCSTruthState* truth, const TFCSExtrapolationState* extrapol) const override;
+
+  /// default for this class is to simulate get_number_of_expected_hits() hits
+  virtual int get_number_of_hits(TFCSSimulationState& simulstate,const TFCSTruthState* truth, const TFCSExtrapolationState* extrapol) const override;
+
+  /// simulated one hit position with weight that should be put into simulstate
+  /// sometime later all hit weights should be resacled such that their final sum is simulstate->E(sample)
+  /// someone also needs to map all hits into cells
+  virtual FCSReturnCode simulate_hit(Hit& hit,TFCSSimulationState& simulstate,const TFCSTruthState* truth, const TFCSExtrapolationState* extrapol) override;
+
+  /// Init from function
+  bool Initialize(TFCS2DFunction* func,float nhits=-1);
+  
+  TFCS2DFunction* function() {return m_function;};
+  const TFCS2DFunction* function() const {return m_function;};
+  
+  virtual void Print(Option_t *option = "") const override;
+protected:
+  /// Histogram to be used for the shape simulation
+  TFCS2DFunction* m_function;
+  float m_nhits;
+
+private:
+
+  ClassDefOverride(TFCS2DFunctionLateralShapeParametrization,2)  //TFCS2DFunctionLateralShapeParametrization
+};
+
+#if defined(__ROOTCLING__) && defined(__FastCaloSimStandAlone__)
+#pragma link C++ class TFCS2DFunctionLateralShapeParametrization+;
+#endif
+
+#endif
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS2DFunctionTemplateHistogram.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS2DFunctionTemplateHistogram.h
new file mode 100644
index 0000000000000000000000000000000000000000..2420053690bc7f135ae0cb6b7a8766aede3353b2
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS2DFunctionTemplateHistogram.h
@@ -0,0 +1,287 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef ISF_FASTCALOSIMEVENT_TFCS2DFunctionTemplateHistogram_h
+#define ISF_FASTCALOSIMEVENT_TFCS2DFunctionTemplateHistogram_h
+
+#include "TFCS2DFunction.h"
+#include "ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHelpers.h"
+#include "TH1.h"
+#include "TH2.h"
+#include "TBuffer.h"
+
+#include <iostream>
+
+template <typename Txvec, typename Tyvec, typename Tz, typename Trandom=float> class TFCS2DFunctionTemplateHistogram:public TFCS2DFunction
+{
+  public:
+    typedef TFCS1DFunction_size_t size_t;
+    typedef Trandom random_type;
+    typedef Txvec xvec_type;
+    typedef Tyvec yvec_type;
+    typedef Tz z_value_type;
+
+    TFCS2DFunctionTemplateHistogram(TH2* hist=nullptr) {if(hist) Initialize(hist);};
+    ~TFCS2DFunctionTemplateHistogram() {};
+
+    std::size_t MemorySizeArray() const {return m_HistoBordersx.MemorySizeArray()+m_HistoBordersy.MemorySizeArray()+m_HistoContents.MemorySizeArray();};
+    std::size_t MemorySize() const {return sizeof(*this)+MemorySizeArray();};
+
+    ///set number of bins
+    void set_nbins(size_t nbinsx, size_t nbinsy) {m_HistoBordersx.set_nbins(nbinsx); m_HistoBordersy.set_nbins(nbinsy); m_HistoContents.set_nbins(nbinsx*nbinsy);};
+
+    ///return number of bins
+    inline size_t get_nbins() const {return m_HistoContents.get_nbins();};
+   // inline size_t get_nbinsx() const {return m_HistoBordersx.get_nbins();};
+   // inline size_t get_nbinsy() const {return m_HistoBordersy.get_nbins();};
+    
+
+    ///Initialize from root histogram. Depending on the precision of the x- and y-axis, bins are merged if numerical identical
+    virtual void Initialize(const TH2* hist) {
+	Int_t nbinsx = hist->GetNbinsX();
+	Int_t nbinsy = hist->GetNbinsY();
+      std::vector<double> temp_HistoContents;
+      int first,last;
+      double integral=CheckAndIntegrate2DHistogram(hist, temp_HistoContents,first,last);
+	printf("initializing 2D template...");
+      if(integral<=0) {
+        set_nbins(0, 0);
+      } else {
+        set_nbins( nbinsx, nbinsy );
+
+        //m_HistoBordersx.SetMinMax(hist->GetXaxis()->GetBinLowEdge(first+1),hist->GetXaxis()->GetBinUpEdge(last+1));
+		m_HistoBordersx.SetMinMax(hist->GetXaxis()->GetBinLowEdge(1),hist->GetXaxis()->GetBinUpEdge(nbinsx) );
+		m_HistoBordersy.SetMinMax(hist->GetYaxis()->GetBinLowEdge(1),hist->GetYaxis()->GetBinUpEdge(nbinsy) );
+        //Int_t ihist=0;
+        for( Int_t ibinx=1; ibinx<=hist->GetNbinsX(); ++ibinx ) {
+          m_HistoBordersx.SetBinLowEdge(ibinx,hist->GetXaxis()->GetBinLowEdge(ibinx+1));
+//          if(ihist>0) if(m_HistoBorders.GetBinLowEdge(ihist-1)==m_HistoBorders.GetBinLowEdge(ihist)) {
+//          std::cout<<"Skip bin="<<ibin+1<<" x="<<hist->GetXaxis()->GetBinLowEdge(ibin+1)<<" fx="<<m_HistoBorders.GetBinLowEdge(ihist)<<std::endl;
+//          --ihist;
+//          std::cout<<"     bin="<<ibin  <<" x="<<hist->GetXaxis()->GetBinLowEdge(ibin  )<<" fx="<<m_HistoBorders.GetBinLowEdge(ihist)<<std::endl;
+//          }
+	}  //for
+	for( Int_t ibiny=1;ibiny<=hist->GetNbinsY();++ibiny ) {
+          m_HistoBordersy.SetBinLowEdge(ibiny,hist->GetYaxis()->GetBinLowEdge(ibiny+1));
+//          if(ihist>0) if(m_HistoBorders.GetBinLowEdge(ihist-1)==m_HistoBorders.GetBinLowEdge(ihist)) {
+//          std::cout<<"Skip bin="<<ibin+1<<" x="<<hist->GetXaxis()->GetBinLowEdge(ibin+1)<<" fx="<<m_HistoBorders.GetBinLowEdge(ihist)<<std::endl;
+//          --ihist;
+//          std::cout<<"     bin="<<ibin  <<" x="<<hist->GetXaxis()->GetBinLowEdge(ibin  )<<" fx="<<m_HistoBorders.GetBinLowEdge(ihist)<<std::endl;
+//          }
+	} //for
+		
+	for( Int_t ibinx=1; ibinx<=hist->GetNbinsX(); ++ibinx )
+	for( Int_t ibiny=1; ibiny<=hist->GetNbinsY(); ++ibiny )
+	  {
+	  int globalbin = (ibinx-1)*hist->GetNbinsY() + ibiny - 1;
+          m_HistoContents.set_fraction(globalbin, temp_HistoContents[globalbin]/integral);
+//          if(ihist>0) if(m_HistoContents.get_fraction(ihist-1)==m_HistoContents.get_fraction(ihist)) {
+//            std::cout<<"Skip fbin="<<ihist<<" fx="<<m_HistoBorders.GetBinLowEdge(ihist)<<" frac="<<m_HistoContents.get_fraction(ihist)<<std::endl;
+//            --ihist;
+//            std::cout<<"     fbin="<<ihist<<" fx="<<m_HistoBorders.GetBinLowEdge(ihist)<<" frac="<<m_HistoContents.get_fraction(ihist)<<std::endl;
+//          }
+
+//          std::cout<<"bin="<<ibin+1<<" fbin="<<ihist<<"/"<<m_HistoBorders.get_nbins()<<" x=["<<hist->GetXaxis()->GetBinLowEdge(ibin+1)<<","<<hist->GetXaxis()->GetBinUpEdge(ibin+1)<<"] fx="<<m_HistoBorders.GetBinLowEdge(ihist)<<" int="<<temp_HistoContents[ibin]/integral<<" frac="<<m_HistoContents.get_fraction(ihist)<<std::endl;
+          
+//          ++ihist;
+		   }  
+//        if(ihist!=nbins) {
+//          set_nbins(ihist);
+//          m_HistoBorders.SetMinMax(hist->GetXaxis()->GetBinLowEdge(first+1),hist->GetXaxis()->GetBinUpEdge(last+1));
+//        }
+	}//for ibinx  
+	}//for ibiny  
+
+/*
+	for( Int_t ibiny=1; ibiny<=hist->GetNbinsY(); ++ibiny )
+	for( Int_t ibinx=1; ibinx<=hist->GetNbinsX(); ++ibinx )
+	  {
+	   int globalbin = (ibiny-1)*hist->GetNbinsX() + ibinx - 1;
+           m_HistoContents.set_fraction(globalbin, temp_HistoContents[globalbin]/integral);
+//          if(ihist>0) if(m_HistoContents.get_fraction(ihist-1)==m_HistoContents.get_fraction(ihist)) {
+//            std::cout<<"Skip fbin="<<ihist<<" fx="<<m_HistoBorders.GetBinLowEdge(ihist)<<" frac="<<m_HistoContents.get_fraction(ihist)<<std::endl;
+//            --ihist;
+//            std::cout<<"     fbin="<<ihist<<" fx="<<m_HistoBorders.GetBinLowEdge(ihist)<<" frac="<<m_HistoContents.get_fraction(ihist)<<std::endl;
+//          }
+
+//          std::cout<<"bin="<<ibin+1<<" fbin="<<ihist<<"/"<<m_HistoBorders.get_nbins()<<" x=["<<hist->GetXaxis()->GetBinLowEdge(ibin+1)<<","<<hist->GetXaxis()->GetBinUpEdge(ibin+1)<<"] fx="<<m_HistoBorders.GetBinLowEdge(ihist)<<" int="<<temp_HistoContents[ibin]/integral<<" frac="<<m_HistoContents.get_fraction(ihist)<<std::endl;
+          
+//          ++ihist;
+		   }  
+//        if(ihist!=nbins) {
+//          set_nbins(ihist);
+//          m_HistoBorders.SetMinMax(hist->GetXaxis()->GetBinLowEdge(first+1),hist->GetXaxis()->GetBinUpEdge(last+1));
+//        }
+	}//for ibinx  
+	}//for ibiny  
+*/
+   // using TFCS2DFunction::rnd_to_fct;
+    
+    ///Function gets random number rnd in the range [0,1) as argument 
+    ///and returns function value according to a histogram distribution
+
+    using TFCS2DFunction::rnd_to_fct;
+
+    virtual void rnd_to_fct(float& valuex,float& valuey,float rnd) const {
+	      if(m_HistoContents.get_nbins()==0) { valuex = 0.0; valuey = 0.0; }
+	      Trandom residual_rnd;
+	      size_t ibin=m_HistoContents.get_bin(rnd,residual_rnd);
+	      int ibiny = ibin % m_HistoBordersy.get_nbins() ;
+	      int ibinx = ibin / m_HistoBordersy.get_nbins() ;
+		valuex = m_HistoBordersx.position(ibinx, 0.5);
+		valuey = m_HistoBordersy.position(ibiny,residual_rnd); 
+		//valuex = 2.0+ 0.3*rnd;
+		//valuey = 1200.0 + rnd*500.0;
+	}
+
+    virtual void rnd_to_fct(float& valuex,float& valuey,float rnd, float rnd2) const {
+	      if(m_HistoContents.get_nbins()==0) { valuex = 0.0; valuey = 0.0; }
+	      Trandom residual_rnd;
+	      size_t ibin=m_HistoContents.get_bin(rnd,residual_rnd);
+	      int ibiny = ibin % m_HistoBordersy.get_nbins();     //printf("%d ", m_HistoBordersy.get_nbins() );
+	      int ibinx = ibin / m_HistoBordersy.get_nbins();     //printf("%d ", m_HistoBordersx.get_nbins() ); getchar();
+		valuex = m_HistoBordersx.position(ibinx, rnd2);
+		valuey = m_HistoBordersy.position(ibiny,residual_rnd);
+		//valuex = 2.0+ rnd;
+		//valuey = 1200.0 + rnd2*500.0; 
+	}
+	/*
+	virtual void rnd_to_fct(float& valuex,float& valuey,float rnd, float rnd2) const {
+	      if(m_HistoContents.get_nbins()==0) { valuex = 0.0; valuey = 0.0; }
+	      Trandom residual_rnd;
+	      size_t ibin=m_HistoContents.get_bin(rnd,residual_rnd);
+	      int ibiny = ibin / m_HistoBordersx.get_nbins();     //printf("%d ", m_HistoBordersy.get_nbins() );
+	      int ibinx = ibin % m_HistoBordersx.get_nbins();     //printf("%d ", m_HistoBordersx.get_nbins() ); getchar();
+		valuex = m_HistoBordersx.position(ibinx, rnd2);
+		valuey = m_HistoBordersy.position(ibiny,residual_rnd);
+		//valuex = 2.0+ rnd;
+		//valuey = 1200.0 + rnd2*500.0; 
+	}*/
+
+    inline const Txvec& get_HistoBordersx() const {return m_HistoBordersx;};
+    inline const Tyvec& get_HistoBordersy() const {return m_HistoBordersy;};
+
+    /////inline void get_XYZfromGlobal(int &ix, int &iy, int &iz, int global) const {hist;};
+
+    inline Txvec& get_HistoBordersx() {return m_HistoBordersx;};
+    inline Tyvec& get_HistoBordersy() {return m_HistoBordersy;};
+    
+    inline const TFCS1DFunction_HistogramContent<Tz,Trandom>& get_HistoContents() const {return m_HistoContents;};
+    inline TFCS1DFunction_HistogramContent<Tz,Trandom>& get_HistoContents() {return m_HistoContents;};
+    
+  protected:
+    Txvec m_HistoBordersx;
+    Tyvec m_HistoBordersy;
+    TFCS1DFunction_HistogramContent<Tz,Trandom> m_HistoContents;
+
+  private:
+
+  ClassDef(TFCS2DFunctionTemplateHistogram,1)  //TFCS2DFunctionTemplateHistogram
+};
+
+
+
+class TFCS2DFunctionInt8Int8Int8Histogram: public TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt8BinEdges,uint8_t,float>
+{
+  public:
+    TFCS2DFunctionInt8Int8Int8Histogram(TH2* h=nullptr):TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt8BinEdges,uint8_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt8Int8Int8Histogram,1)  //TFCS2DFunctionInt8Int8Int8Histogram
+};
+
+class TFCS2DFunctionInt8Int8Int16Histogram: public TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt8BinEdges,uint16_t,float>
+{
+  public:
+    TFCS2DFunctionInt8Int8Int16Histogram(TH2* h=nullptr):TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt8BinEdges,uint16_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt8Int8Int16Histogram,1)  //TFCS2DFunctionInt8Int8Int16Histogram
+};
+
+class TFCS2DFunctionInt8Int8Int32Histogram: public TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt8BinEdges,uint32_t,float>
+{
+  public:
+    TFCS2DFunctionInt8Int8Int32Histogram(TH2* h=nullptr):TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt8BinEdges,uint32_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt8Int8Int32Histogram,1)  //TFCS2DFunctionInt8Int8Int32Histogram
+};
+
+
+
+class TFCS2DFunctionInt8Int16Int8Histogram: public TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint8_t,float>
+{
+  public:
+    TFCS2DFunctionInt8Int16Int8Histogram(TH2* h=nullptr):TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint8_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt8Int16Int8Histogram,1)  //TFCS2DFunctionInt8Int16Int8Histogram
+};
+
+class TFCS2DFunctionInt8Int16Int16Histogram: public TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint16_t,float>
+{
+  public:
+    TFCS2DFunctionInt8Int16Int16Histogram(TH2* h=nullptr):TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint16_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt8Int16Int16Histogram,1)  //TFCS2DFunctionInt8Int16Int16Histogram
+};
+
+class TFCS2DFunctionInt8Int16Int32Histogram: public TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint32_t,float>
+{
+  public:
+    TFCS2DFunctionInt8Int16Int32Histogram(TH2* h=nullptr):TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint32_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt8Int16Int32Histogram,1)  //TFCS2DFunctionInt8Int16Int32Histogram
+};
+
+
+
+class TFCS2DFunctionInt16Int16Int8Histogram: public TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt16BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint8_t,float>
+{
+  public:
+    TFCS2DFunctionInt16Int16Int8Histogram(TH2* h=nullptr):TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt16BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint8_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt16Int16Int8Histogram,1)  //TFCS2DFunctionInt16Int16Int8Histogram
+};
+
+class TFCS2DFunctionInt16Int16Int16Histogram: public TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt16BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint16_t,float>
+{
+  public:
+    TFCS2DFunctionInt16Int16Int16Histogram(TH2* h=nullptr):TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt16BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint16_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt16Int16Int16Histogram,1)  //TFCS2DFunctionInt16Int16Int16Histogram
+};
+
+class TFCS2DFunctionInt16Int16Int32Histogram: public TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt16BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint32_t,float>
+{
+  public:
+    TFCS2DFunctionInt16Int16Int32Histogram(TH2* h=nullptr):TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt16BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint32_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt16Int16Int32Histogram,1)  //TFCS2DFunctionInt16Int16Int32Histogram
+};
+
+
+#if defined(__ROOTCLING__) && defined(__FastCaloSimStandAlone__)
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt8BinEdges, uint8_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt8BinEdges, uint16_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt8BinEdges, uint32_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint8_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint16_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint32_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt16BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint8_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt16BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint16_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateHistogram<TFCS1DFunction_HistogramInt16BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint32_t,float>+;
+
+#pragma link C++ class TFCS2DFunctionInt8Int8Int8Histogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int8Int16Histogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int8Int32Histogram+;
+
+#pragma link C++ class TFCS2DFunctionInt8Int16Int8Histogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int16Int16Histogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int16Int32Histogram+;
+
+#pragma link C++ class TFCS2DFunctionInt16Int16Int8Histogram+;
+#pragma link C++ class TFCS2DFunctionInt16Int16Int16Histogram+;
+#pragma link C++ class TFCS2DFunctionInt16Int16Int32Histogram+;
+
+#endif
+
+#endif
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS2DFunctionTemplateInterpolationHistogram.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS2DFunctionTemplateInterpolationHistogram.h
new file mode 100644
index 0000000000000000000000000000000000000000..ac376b24dcebcea16ecbaf72ac928278d8e2a169
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCS2DFunctionTemplateInterpolationHistogram.h
@@ -0,0 +1,189 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef ISF_FASTCALOSIMEVENT_TFCS2DFunctionTemplateInterpolationHistogram_h
+#define ISF_FASTCALOSIMEVENT_TFCS2DFunctionTemplateInterpolationHistogram_h
+
+#include "TFCS2DFunction.h"
+#include "ISF_FastCaloSimEvent/TFCS2DFunctionTemplateHistogram.h"
+#include "TH1.h"
+#include "TH2.h"
+#include "TBuffer.h"
+
+#include <iostream>
+
+template <typename Txvec, typename Tyvec, typename Tz ,typename Trandom=float> class TFCS2DFunctionTemplateInterpolationHistogram:public TFCS2DFunctionTemplateHistogram<Txvec,Tyvec,Tz,Trandom>
+{
+  public:
+    TFCS2DFunctionTemplateInterpolationHistogram(TH2* hist=nullptr):TFCS2DFunctionTemplateHistogram<Txvec,Tyvec,Tz,Trandom>(hist) {};
+
+    using TFCS2DFunctionTemplateHistogram<Txvec,Tyvec,Tz,Trandom>::rnd_to_fct;
+    using TFCS2DFunctionTemplateHistogram<Txvec,Tyvec,Tz,Trandom>::get_nbins;
+    using TFCS2DFunctionTemplateHistogram<Txvec,Tyvec,Tz,Trandom>::m_HistoContents;
+    using TFCS2DFunctionTemplateHistogram<Txvec,Tyvec,Tz,Trandom>::m_HistoBordersx;
+    using TFCS2DFunctionTemplateHistogram<Txvec,Tyvec,Tz,Trandom>::m_HistoBordersy;
+
+    ///Function gets random number rnd in the range [0,1) as argument 
+    ///and returns function value according to a histogram distribution.
+    ///A linear interpolation is done within each bin
+    virtual void rnd_to_fct(float& valuex,float& valuey,float rnd, float rnd2) const {
+      size_t nbins=get_nbins();
+      if(nbins==0) {valuex = 0.0; valuey = 0.0; return;}
+      Trandom residual_rnd;
+      size_t ibin=m_HistoContents.get_bin(rnd,residual_rnd);
+      
+      Trandom frac=m_HistoContents.get_fraction(ibin);
+
+      size_t ibiny = ibin % m_HistoBordersy.get_nbins();     //printf("%d ", m_HistoBordersy.get_nbins() );
+      size_t ibinx = ibin / m_HistoBordersy.get_nbins();
+      //size_t nbinsx = m_HistoBordersx.get_nbins();
+      size_t nbinsy = m_HistoBordersy.get_nbins();
+  
+      Trandom dfrac;
+      auto l=m_HistoBordersy.GetBinLength(ibiny);
+      Trandom dfracprev;
+      auto lprev=l;
+      if(ibiny>0) {
+        Trandom fracprev=m_HistoContents.get_fraction(ibin-1);
+        dfrac=frac-fracprev;
+        if(ibiny>1) {
+          dfracprev=fracprev-m_HistoContents.get_fraction(ibin-2);
+        } else {
+          dfracprev=fracprev;
+          if(ibinx>0) dfracprev-=m_HistoContents.get_fraction(ibin-2);
+        }  
+        lprev=m_HistoBordersy.GetBinLength(ibiny-1);
+      } else {
+        dfrac=frac;
+        if(ibinx>0) dfrac-=m_HistoContents.get_fraction(ibin-1);
+        dfracprev=dfrac;
+      }
+      
+      Trandom dfracnext;
+      auto lnext=l;
+      if(ibiny<nbinsy-1) {
+        dfracnext=m_HistoContents.get_fraction(ibin+1)-frac;
+        lnext=m_HistoBordersy.GetBinLength(ibiny+1);
+      } else {
+        dfracnext=dfrac;
+      }  
+      
+      Trandom m;
+      if(dfrac>0) {
+        Trandom mnext=dfracnext/lnext-dfrac/l;
+        mnext/=l+lnext;
+        Trandom mprev=dfrac/l-dfracprev/lprev;
+        mprev/=l+lprev;
+        m=(mnext+mprev)*l*l/dfrac;
+      } else m=0;
+      
+      valuey = m_HistoBordersy.position(ibiny,m,residual_rnd);
+      valuex = m_HistoBordersx.position(ibinx, rnd2);
+    }
+
+  ClassDef(TFCS2DFunctionTemplateInterpolationHistogram,1)  //TFCS1DFunctionTemplateInterpolationHistogram
+};
+
+class TFCS2DFunctionInt8Int8Int8InterpolationHistogram: public TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt8BinEdges,uint8_t,float>
+{
+  public:
+    TFCS2DFunctionInt8Int8Int8InterpolationHistogram(TH2* h=nullptr):TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt8BinEdges,uint8_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt8Int8Int8InterpolationHistogram,1)  //TFCS2DFunctionInt8Int8Int8InterpolationHistogram
+};
+
+class TFCS2DFunctionInt8Int8Int16InterpolationHistogram: public TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt8BinEdges,uint16_t,float>
+{
+  public:
+    TFCS2DFunctionInt8Int8Int16InterpolationHistogram(TH2* h=nullptr):TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt8BinEdges,uint16_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt8Int8Int16InterpolationHistogram,1)  //TFCS2DFunctionInt8Int8Int16InterpolationHistogram
+};
+
+class TFCS2DFunctionInt8Int8Int32InterpolationHistogram: public TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt8BinEdges,uint32_t,float>
+{
+  public:
+    TFCS2DFunctionInt8Int8Int32InterpolationHistogram(TH2* h=nullptr):TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt8BinEdges,uint32_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt8Int8Int32InterpolationHistogram,1)  //TFCS2DFunctionInt8Int8Int32InterpolationHistogram
+};
+
+
+
+class TFCS2DFunctionInt8Int16Int8InterpolationHistogram: public TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint8_t,float>
+{
+  public:
+    TFCS2DFunctionInt8Int16Int8InterpolationHistogram(TH2* h=nullptr):TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint8_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt8Int16Int8InterpolationHistogram,1)  //TFCS2DFunctionInt8Int16Int8InterpolationHistogram
+};
+
+class TFCS2DFunctionInt8Int16Int16InterpolationHistogram: public TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint16_t,float>
+{
+  public:
+    TFCS2DFunctionInt8Int16Int16InterpolationHistogram(TH2* h=nullptr):TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint16_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt8Int16Int16InterpolationHistogram,1)  //TFCS2DFunctionInt8Int16Int16InterpolationHistogram
+};
+
+class TFCS2DFunctionInt8Int16Int32InterpolationHistogram: public TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint32_t,float>
+{
+  public:
+    TFCS2DFunctionInt8Int16Int32InterpolationHistogram(TH2* h=nullptr):TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint32_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt8Int16Int32InterpolationHistogram,1)  //TFCS2DFunctionInt8Int16Int32InterpolationHistogram
+};
+
+
+
+class TFCS2DFunctionInt16Int16Int8InterpolationHistogram: public TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint8_t,float>
+{
+  public:
+    TFCS2DFunctionInt16Int16Int8InterpolationHistogram(TH2* h=nullptr):TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint8_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt16Int16Int8InterpolationHistogram,1)  //TFCS2DFunctionInt16Int16Int8InterpolationHistogram
+};
+
+class TFCS2DFunctionInt16Int16Int16InterpolationHistogram: public TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint16_t,float>
+{
+  public:
+    TFCS2DFunctionInt16Int16Int16InterpolationHistogram(TH2* h=nullptr):TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint16_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt16Int16Int16InterpolationHistogram,1)  //TFCS2DFunctionInt16Int16Int16InterpolationHistogram
+};
+
+class TFCS2DFunctionInt16Int16Int32InterpolationHistogram: public TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint32_t,float>
+{
+  public:
+    TFCS2DFunctionInt16Int16Int32InterpolationHistogram(TH2* h=nullptr):TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges,TFCS1DFunction_HistogramInt16BinEdges,uint32_t,float>(h) {};
+
+  ClassDef(TFCS2DFunctionInt16Int16Int32InterpolationHistogram,1)  //TFCS2DFunctionInt16Int16Int32InterpolationHistogram
+};
+
+#if defined(__ROOTCLING__) && defined(__FastCaloSimStandAlone__)
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt8BinEdges, uint8_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt8BinEdges, uint16_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt8BinEdges, uint32_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint8_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint16_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt8BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint32_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint8_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint16_t,float>+;
+#pragma link C++ class TFCS2DFunctionTemplateInterpolationHistogram<TFCS1DFunction_HistogramInt16BinEdges, TFCS1DFunction_HistogramInt16BinEdges, uint32_t,float>+;
+
+#pragma link C++ class TFCS2DFunctionInt8Int8Int8InterpolationHistogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int8Int16InterpolationHistogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int8Int32InterpolationHistogram+;
+
+#pragma link C++ class TFCS2DFunctionInt8Int16Int8InterpolationHistogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int16Int16InterpolationHistogram+;
+#pragma link C++ class TFCS2DFunctionInt8Int16Int32InterpolationHistogram+;
+
+#pragma link C++ class TFCS2DFunctionInt16Int16Int8InterpolationHistogram+;
+#pragma link C++ class TFCS2DFunctionInt16Int16Int16InterpolationHistogram+;
+#pragma link C++ class TFCS2DFunctionInt16Int16Int32InterpolationHistogram+;
+
+#endif
+
+#endif
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSEnergyRenormalization.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSEnergyRenormalization.h
new file mode 100644
index 0000000000000000000000000000000000000000..47966d9d6d3482fcf89015ff65ba34cc43186024
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSEnergyRenormalization.h
@@ -0,0 +1,42 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef ISF_FASTCALOSIMEVENT_TFCSEnergyRenormalization_h
+#define ISF_FASTCALOSIMEVENT_TFCSEnergyRenormalization_h
+
+#include "ISF_FastCaloSimEvent/TFCSParametrization.h"
+
+/** The class TFCSEnergyRenormalization ensures that the sum of cell energies in every calorimeter layer 
+    matches the output of energy simulation
+*/
+
+class TFCSEnergyRenormalization:public TFCSParametrization {
+public:
+  TFCSEnergyRenormalization(const char* name=nullptr, const char* title=nullptr);
+  virtual ~TFCSEnergyRenormalization();
+
+  virtual bool is_match_Ekin_bin(int /*Ekin_bin*/) const override;
+  virtual bool is_match_calosample(int /*calosample*/) const override;
+
+  virtual FCSReturnCode simulate(TFCSSimulationState& simulstate,const TFCSTruthState* /*truth*/, const  TFCSExtrapolationState* /*extrapol*/) override;
+private:
+
+  ClassDefOverride(TFCSEnergyRenormalization,1)  //TFCSEnergyRenormalization
+};
+
+inline bool TFCSEnergyRenormalization::is_match_Ekin_bin(int /*Ekin_bin*/) const
+{
+  return true;
+}
+
+inline bool TFCSEnergyRenormalization::is_match_calosample(int /*calosample*/) const
+{
+  return true;
+}
+
+#if defined(__ROOTCLING__) && defined(__FastCaloSimStandAlone__)
+#pragma link C++ class TFCSEnergyRenormalization+;
+#endif
+
+#endif
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSFlatLateralShapeParametrization.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSFlatLateralShapeParametrization.h
new file mode 100644
index 0000000000000000000000000000000000000000..fd516c4efd1f140c991296f6563f2b4ac4380580
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSFlatLateralShapeParametrization.h
@@ -0,0 +1,73 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TFCSFlatLateralShapeParametrization_h
+#define TFCSFlatLateralShapeParametrization_h
+
+#include "ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitBase.h"
+#include "ISF_FastCaloSimEvent/TFCS2DFunctionHistogram.h"
+#include "ISF_FastCaloSimEvent/TFCSTruthState.h"
+
+class TH2;
+
+class TFCSFlatLateralShapeParametrization:public TFCSLateralShapeParametrizationHitBase {
+public:
+  TFCSFlatLateralShapeParametrization(const char* name=nullptr, const char* title=nullptr);
+  virtual ~TFCSFlatLateralShapeParametrization();
+
+  /// set the integral of the histogram to the desired number of hits
+  void set_number_of_hits(float nhits);
+
+  float get_number_of_expected_hits() const;
+
+  /// default for this class is to simulate poisson(integral histogram) hits
+  virtual int get_number_of_hits(TFCSSimulationState& simulstate,const TFCSTruthState* truth, const TFCSExtrapolationState* extrapol) const override;
+
+  /// set the radius in which hits should be generated
+  void set_dR(float _dR);
+
+  float dR() const;
+
+  /// set the radius in which hits should be generated
+  void set_scale(float _scale);
+
+  float scale() const;
+
+  /// simulated one hit position with weight that should be put into simulstate
+  /// sometime later all hit weights should be resacled such that their final sum is simulstate->E(sample)
+  /// someone also needs to map all hits into cells
+  virtual FCSReturnCode simulate_hit(Hit& hit,TFCSSimulationState& simulstate,const TFCSTruthState* truth, const TFCSExtrapolationState* extrapol) override;
+
+  virtual void Print(Option_t *option = "") const override;
+protected:
+  /// Simulate hits flat in radius dR
+  float m_dR;
+  float m_nhits;
+  float m_scale;
+
+private:
+
+  ClassDefOverride(TFCSFlatLateralShapeParametrization,1)  //TFCSFlatLateralShapeParametrization
+};
+
+inline float TFCSFlatLateralShapeParametrization::get_number_of_expected_hits() const 
+{
+  return m_nhits;
+}
+
+inline float TFCSFlatLateralShapeParametrization::dR() const 
+{
+  return m_dR;
+}
+
+inline float TFCSFlatLateralShapeParametrization::scale() const 
+{
+  return m_scale;
+}
+
+#if defined(__ROOTCLING__) && defined(__FastCaloSimStandAlone__)
+#pragma link C++ class TFCSFlatLateralShapeParametrization+;
+#endif
+
+#endif
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSHistoLateralShapeParametrization.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSHistoLateralShapeParametrization.h
index f6ecbe2e4f6c615bb1aca035083733e7e6e00a85..50bd8fa5041efa9330e35e8195c1625f80b6fb84 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSHistoLateralShapeParametrization.h
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSHistoLateralShapeParametrization.h
@@ -29,8 +29,20 @@ public:
   void set_number_of_hits(float nhits);
 
   float get_number_of_expected_hits() const {return m_nhits;};
+  
+  ///set an offset in r on the simulated histogram
+  void set_r_offset(float r_offset) {m_r_offset=r_offset;};
+  float r_offset() const {return m_r_offset;};
+  
+  ///set an scale factor for r on the simulated histogram
+  void set_r_scale(float r_scale) {m_r_scale=r_scale;};
+  float r_scale() const {return m_r_scale;};
+
+  ///default for this class is to simulate get_number_of_expected_hits() hits, 
+  ///which gives fluctuations sigma^2=1/get_number_of_expected_hits()
+  virtual double get_sigma2_fluctuation(TFCSSimulationState& simulstate,const TFCSTruthState* truth, const TFCSExtrapolationState* extrapol) const override;
 
-  /// default for this class is to simulate poisson(integral histogram) hits
+  /// default for this class is to simulate get_number_of_expected_hits() hits
   int get_number_of_hits(TFCSSimulationState& simulstate,const TFCSTruthState* truth, const TFCSExtrapolationState* extrapol) const override;
 
   /// simulated one hit position with weight that should be put into simulstate
@@ -50,10 +62,12 @@ protected:
   /// Histogram to be used for the shape simulation
   TFCS2DFunctionHistogram m_hist;
   float m_nhits;
+  float m_r_offset;
+  float m_r_scale;
 
 private:
 
-  ClassDefOverride(TFCSHistoLateralShapeParametrization,1)  //TFCSHistoLateralShapeParametrization
+  ClassDefOverride(TFCSHistoLateralShapeParametrization,2)  //TFCSHistoLateralShapeParametrization
 };
 
 #if defined(__ROOTCLING__) && defined(__FastCaloSimStandAlone__)
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSHistoLateralShapeWeight.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSHistoLateralShapeWeight.h
new file mode 100644
index 0000000000000000000000000000000000000000..dc650c2ca08d400c17315eca14fb1d8eeebd6e1a
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSHistoLateralShapeWeight.h
@@ -0,0 +1,36 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TFCSHistoLateralShapeWeight_h
+#define TFCSHistoLateralShapeWeight_h
+
+#include "ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitBase.h"
+
+class TH1;
+
+class TFCSHistoLateralShapeWeight:public TFCSLateralShapeParametrizationHitBase {
+public:
+  TFCSHistoLateralShapeWeight(const char* name=nullptr, const char* title=nullptr);
+  virtual ~TFCSHistoLateralShapeWeight();
+
+  /// weight the energy of one hit in order to generate fluctuations
+  virtual FCSReturnCode simulate_hit(Hit& hit,TFCSSimulationState& simulstate,const TFCSTruthState* truth, const TFCSExtrapolationState* extrapol) override;
+
+  /// Init from histogram. The integral of the histogram is used as number of expected hits to be generated
+  bool Initialize(TH1* hist);
+  
+  virtual void Print(Option_t *option = "") const override;
+protected:
+  /// Histogram to be used for the shape simulation
+  /// The histogram x-axis should be in dR^2=deta^2+dphi^2
+  TH1* m_hist{nullptr};
+
+  ClassDefOverride(TFCSHistoLateralShapeWeight,1)  //TFCSHistoLateralShapeWeight
+};
+
+#if defined(__ROOTCLING__) && defined(__FastCaloSimStandAlone__)
+#pragma link C++ class TFCSHistoLateralShapeWeight+;
+#endif
+
+#endif
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSHistoLateralShapeWeightHitAndMiss.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSHistoLateralShapeWeightHitAndMiss.h
new file mode 100644
index 0000000000000000000000000000000000000000..ebdce230bf4960dc0fb7dd0dba17c7d10b0c7ac2
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSHistoLateralShapeWeightHitAndMiss.h
@@ -0,0 +1,29 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TFCSHistoLateralShapeWeightHitAndMiss_h
+#define TFCSHistoLateralShapeWeightHitAndMiss_h
+
+#include "ISF_FastCaloSimEvent/TFCSHistoLateralShapeWeight.h"
+
+class TH1;
+
+class TFCSHistoLateralShapeWeightHitAndMiss:public TFCSHistoLateralShapeWeight {
+public:
+  TFCSHistoLateralShapeWeightHitAndMiss(const char* name=nullptr, const char* title=nullptr);
+  virtual ~TFCSHistoLateralShapeWeightHitAndMiss();
+
+  /// weight the energy of one hit in order to generate fluctuations. If the hit energy is 0, discard the hit
+  virtual FCSReturnCode simulate_hit(Hit& hit,TFCSSimulationState& simulstate,const TFCSTruthState* truth, const TFCSExtrapolationState* extrapol) override;
+
+private:
+
+  ClassDefOverride(TFCSHistoLateralShapeWeightHitAndMiss,1)  //TFCSHistoLateralShapeWeightHitAndMiss
+};
+
+#if defined(__ROOTCLING__) && defined(__FastCaloSimStandAlone__)
+#pragma link C++ class TFCSHistoLateralShapeWeightHitAndMiss+;
+#endif
+
+#endif
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSHitCellMappingWiggle.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSHitCellMappingWiggle.h
index 59374d81bb3af36ef281911bdb811b10ddbe02f2..5804324671cb64db0fc57baf0f870c64adee111f 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSHitCellMappingWiggle.h
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSHitCellMappingWiggle.h
@@ -26,7 +26,7 @@ public:
   inline double get_bin_low_edge(int bin) const {return m_bin_low_edge[bin];};
   inline double get_bin_up_edge(int bin) const {return m_bin_low_edge[bin+1];};
   
-  inline const TFCS1DFunction* get_function(int bin) {return m_functions[bin];};
+  inline const TFCS1DFunction* get_function(int bin) const {return m_functions[bin];};
   const std::vector< const TFCS1DFunction* > get_functions() {return m_functions;};
   const std::vector< float > get_bin_low_edges() {return m_bin_low_edge;};
 
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitBase.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitBase.h
index 89f1c721aac2eb21c18b262ef184c25564d6e484..44635409f11c09d9d3bc5b98339d0d3a23f4aea8 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitBase.h
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitBase.h
@@ -13,7 +13,10 @@ class TFCSLateralShapeParametrizationHitBase:public TFCSLateralShapeParametrizat
 public:
   TFCSLateralShapeParametrizationHitBase(const char* name=nullptr, const char* title=nullptr);
 
-  ///Call get_number_of_hits() only once per shower simulation, as it could be calculated with random numbers and give different results each time
+  ///Give the effective size sigma^2 of the fluctuations that should be generated by the amount of generated hits. Return a value <0 if this instance can't determine
+  virtual double get_sigma2_fluctuation(TFCSSimulationState& simulstate,const TFCSTruthState* truth, const TFCSExtrapolationState* extrapol) const;
+
+  ///Call get_number_of_hits() only once per shower simulation, as it could be calculated with random numbers and give different results each time. Return a value of -1 if this instance can't determine
   virtual int get_number_of_hits(TFCSSimulationState& simulstate,const TFCSTruthState* truth, const TFCSExtrapolationState* extrapol) const;
 
   class Hit
@@ -38,6 +41,13 @@ public:
       m_useXYZ=true;
     }
 
+    inline void reset_center(){
+      m_center_r=0;
+      m_center_z=0;
+      m_center_eta=0;
+      m_center_phi=0;
+    }
+
     inline void reset(){
       m_eta_x=0.;
       m_phi_y=0.;
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitChain.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitChain.h
index 4a912ace0c96c4d832a4fd45f0fb5a54b1b9111a..497e1e448ade5a8e83c80ed344ab8f2b3ddf2ef9 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitChain.h
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitChain.h
@@ -25,7 +25,10 @@ public:
   const Chain_t& chain() const {return m_chain;};
   Chain_t& chain() {return m_chain;};
   void push_back( const Chain_t::value_type& value ) {m_chain.push_back(value);};
-  
+  //TODO: add generic functionality to determine the number of hits or center position only once
+  // and not for every iteration of the hit chain
+
+  /// set which instance should determine the number of hits
   virtual void set_number_of_hits_simul(TFCSLateralShapeParametrizationHitBase* sim) {m_number_of_hits_simul=sim;};
   
   /// Call get_number_of_hits() only once, as it could contain a random number
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitNumberFromE.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitNumberFromE.h
index cbe0e39996096567ab2a9130c8647e5eb5a545bb..e43420da0ad2c14a0ee7c470118631db7ba7696a 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitNumberFromE.h
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSLateralShapeParametrizationHitNumberFromE.h
@@ -26,6 +26,9 @@ public:
   ///    constant=0.035;
   TFCSLateralShapeParametrizationHitNumberFromE(const char* name=nullptr, const char* title=nullptr,double stochastic=0.1,double constant=0);
 
+  ///Give the effective size sigma^2 of the fluctuations from the stochastic and constant term
+  double get_sigma2_fluctuation(TFCSSimulationState& simulstate,const TFCSTruthState* truth, const TFCSExtrapolationState* extrapol) const override;
+
   int get_number_of_hits(TFCSSimulationState& simulstate,const TFCSTruthState* truth, const TFCSExtrapolationState* extrapol) const override;
 
   void Print(Option_t *option = "") const override;
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSParametrizationBase.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSParametrizationBase.h
index 0ee503bffd36648a59a25ca1636bc27353cffc1e..9ac1b7cf1761ef1f4bca8f1b1401d2169f7dd8d5 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSParametrizationBase.h
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/ISF_FastCaloSimEvent/TFCSParametrizationBase.h
@@ -141,12 +141,12 @@ public:
   static void DoCleanup();
 
 protected:
-  const double init_Ekin_nominal=0;
-  const double init_Ekin_min=0;
-  const double init_Ekin_max=14000000;
-  const double init_eta_nominal=0;
-  const double init_eta_min=-100;
-  const double init_eta_max=100;
+  static constexpr double init_Ekin_nominal=0;//! Do not persistify!
+  static constexpr double init_Ekin_min=0;//! Do not persistify!
+  static constexpr double init_Ekin_max=14000000;//! Do not persistify!
+  static constexpr double init_eta_nominal=0;//! Do not persistify!
+  static constexpr double init_eta_min=-100;//! Do not persistify!
+  static constexpr double init_eta_max=100;//! Do not persistify!
 
   static std::vector< TFCSParametrizationBase* > s_cleanup_list;
 
@@ -196,7 +196,7 @@ private:
 private:
   static std::set< int > s_no_pdgid;
 
-  ClassDef(TFCSParametrizationBase,1)  //TFCSParametrizationBase
+  ClassDef(TFCSParametrizationBase,2)  //TFCSParametrizationBase
 };
 
 #if defined(__ROOTCLING__) && defined(__FastCaloSimStandAlone__)
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunction.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunction.cxx
index 8a9ecda8a593b641822ec3dde9be0cf3bf92eadd..b53faf24a837d33aeb7ad4ddb38aae4d3ba7b8f2 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunction.cxx
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunction.cxx
@@ -113,7 +113,7 @@ void TFCS1DFunction::unit_test(TH1* hist,TFCS1DFunction* rtof,int nrnd,TH1* hist
     std::cout<<"rnd0="<<rnd[0]<<" -> x="<<value[0]<<std::endl;
   }
 
-  TH1* hist_val;
+  TH1* hist_val=nullptr;
   if(histfine) hist_val=(TH1*)histfine->Clone(TString(hist->GetName())+"hist_val");
    else hist_val=(TH1*)hist->Clone(TString(hist->GetName())+"hist_val");
   double weightfine=hist_val->Integral()/nrnd;
@@ -142,7 +142,6 @@ void TFCS1DFunction::unit_test(TH1* hist,TFCS1DFunction* rtof,int nrnd,TH1* hist
     float val=hist_diff->GetBinContent(ix);
     float err=hist_diff->GetBinError(ix);
     if(err>0) hist_pull->Fill(val/err);
-    //std::cout<<"x="<<hist->GetBinCenter(ix)<<" : pull val="<<val<<" err="<<err<<std::endl;
   }
   
 //Screen output in athena won't make sense and would require linking of additional libraries
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunctionInt32Histogram.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunctionInt32Histogram.cxx
index 62fc8c186d65419f0c663e4db530316f1ae515af..1402bd4277fb76d4263bfe430679cb2e0cd9be65 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunctionInt32Histogram.cxx
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunctionInt32Histogram.cxx
@@ -53,8 +53,7 @@ void TFCS1DFunctionInt32Histogram::Initialize(const TH1* hist)
   
   for(ibin=0;ibin<nbins;++ibin) {
     m_HistoContents[ibin]=s_MaxValue*(temp_HistoContents[ibin]/integral);
-    //std::cout<<"bin="<<ibin<<" val="<<m_HistoContents[ibin]<<std::endl;
-  }  
+  }
 }
 
 double TFCS1DFunctionInt32Histogram::rnd_to_fct(double rnd) const
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunctionTemplateHelpers.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunctionTemplateHelpers.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..cd7bf32500b783e0afa0cc323836dcc30b96318c
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunctionTemplateHelpers.cxx
@@ -0,0 +1,10 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "ISF_FastCaloSimEvent/TFCS1DFunctionTemplateHelpers.h"
+
+//=============================================
+//======= TFCS1DFunctionTemplateHelpers =========
+//=============================================
+
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunctionTemplateHistogram.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunctionTemplateHistogram.cxx
index 27273e697e30b73f529973ffb052cdaab853ce51..168353d57a93a918eb4866a1173822383e28a404 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunctionTemplateHistogram.cxx
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunctionTemplateHistogram.cxx
@@ -7,4 +7,3 @@
 //=============================================
 //======= TFCS1DFunctionTemplateHistogram =========
 //=============================================
-
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunctionTemplateInterpolationHistogram.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunctionTemplateInterpolationHistogram.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..12295ce68f2e549c5b184519736aee26691508a0
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS1DFunctionTemplateInterpolationHistogram.cxx
@@ -0,0 +1,9 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "ISF_FastCaloSimEvent/TFCS1DFunctionTemplateInterpolationHistogram.h"
+
+//=============================================
+//======= TFCS1DFunctionTemplateInterpolationHistogram =========
+//=============================================
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS2DFunction.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS2DFunction.cxx
index f657c8fd3f9849eea4a499cc13ceef6f6283bc55..e1cc3a6d1a3f43c04af837a5b2430dcdabd492b6 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS2DFunction.cxx
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS2DFunction.cxx
@@ -3,6 +3,15 @@
 */
 
 #include "ISF_FastCaloSimEvent/TFCS2DFunction.h"
+#include "ISF_FastCaloSimEvent/TFCS2DFunctionHistogram.h"
+#include "ISF_FastCaloSimEvent/TFCS2DFunctionTemplateHistogram.h"
+#include "TH2.h"
+#include "TCanvas.h"
+#include "TH2F.h"
+#include "TRandom.h"
+#include "TFile.h"
+
+#include <iostream>
 
 //=============================================
 //======= TFCS2DFunction =========
@@ -12,3 +21,138 @@ void TFCS2DFunction::rnd_to_fct(float value[],const float rnd[]) const
 {
   rnd_to_fct(value[0],value[1],rnd[0],rnd[1]);
 }
+
+//================================================================================================================================
+
+double TFCS2DFunction::CheckAndIntegrate2DHistogram(const TH2* hist, std::vector<double>& integral_vec, int& first, int& last) 
+{
+  Int_t nbinsx = hist->GetNbinsX();
+  Int_t nbinsy = hist->GetNbinsY();
+  Int_t nbins = nbinsx*nbinsy;
+
+  float integral=0.0;
+  float hint = hist->Integral();
+  integral_vec.resize(nbins);
+
+
+  for (int ix=1; ix<=nbinsx; ix++) {
+    for (int iy=1; iy<=nbinsy; iy++) {
+      int globalbin = (ix-1)*nbinsy + iy - 1;   
+	    float binval = hist->GetBinContent(ix,iy);
+      if(binval<0) {
+        //Can't work if a bin is negative, forcing bins to 0 in this case
+        double fraction=binval/hint;
+        if(TMath::Abs(fraction)>1e-5) {
+          std::cout<<"WARNING: bin content is negative in histogram "<<hist->GetName()<<" : "<<hist->GetTitle()<<" binval="<<binval<<" "<<fraction*100<<"% of integral="<<hist->Integral()<<". Forcing bin to 0."<<std::endl;
+        }  
+        binval=0;
+      }
+      integral+=binval;
+      integral_vec[globalbin]=integral;
+    }
+  }
+
+  for(first=0; first<nbins; first++) if(integral_vec[first]!=0) break;
+  for(last=nbins-1; last>0; last--) if(integral_vec[last]!=integral) break;
+  last++;
+  
+  if(integral<=0) {
+    std::cout<<"ERROR: histogram "<<hist->GetName()<<" : "<<hist->GetTitle()<<" integral="<<integral<<" is <=0"<<std::endl;
+  }
+  
+  return integral;
+}
+
+TH2* create_random_TH2(int nbinsx=64,int nbinsy=64)
+{
+  TH2* hist=new TH2F("test2D","test2D",nbinsx,0,1,nbinsy,0,1);
+  hist->Sumw2();
+  for(int ix=1;ix<=nbinsx;++ix) {
+    for(int iy=1;iy<=nbinsy;++iy) {
+      hist->SetBinContent(ix,iy,(0.5+gRandom->Rndm())*(nbinsx+ix)*(nbinsy*nbinsy/2+iy*iy));
+      if(gRandom->Rndm()<0.1) hist->SetBinContent(ix,iy,0);
+      hist->SetBinError(ix,iy,0);
+    }
+  }
+  return hist;
+}
+
+void TFCS2DFunction::unit_test(TH2* hist,TFCS2DFunction* rtof,const char* outfilename,int nrnd)
+{
+  if(hist==nullptr) hist=create_random_TH2();
+  if(rtof==nullptr) rtof=new TFCS2DFunctionHistogram(hist);
+
+  int nbinsx=hist->GetNbinsX();
+  int nbinsy=hist->GetNbinsY();
+  
+  float value[2];
+  float rnd[2];
+  for(rnd[0]=0;rnd[0]<0.9999;rnd[0]+=0.25) {
+    for(rnd[1]=0;rnd[1]<0.9999;rnd[1]+=0.25) {
+      rtof->rnd_to_fct(value,rnd);
+      std::cout<<"rnd0="<<rnd[0]<<" rnd1="<<rnd[1]<<" -> x="<<value[0]<<" y="<<value[1]<<std::endl;
+    }  
+  }
+
+//  TH2F* hist_val=new TH2F("val2D","val2D",16,hist->GetXaxis()->GetXmin(),hist->GetXaxis()->GetXmax(),
+//                                          16,hist->GetYaxis()->GetXmin(),hist->GetYaxis()->GetXmax());
+  TH2F* hist_val=(TH2F*)hist->Clone(TString(hist->GetName())+"_"+rtof->ClassName());
+  hist_val->Reset();
+  
+  float weight=hist->Integral()/nrnd;
+  hist_val->Sumw2();
+  for(int i=0;i<nrnd;++i) {
+    rnd[0]=gRandom->Rndm();
+    rnd[1]=gRandom->Rndm();
+    rtof->rnd_to_fct(value,rnd);
+    hist_val->Fill(value[0],value[1],weight);
+  } 
+  hist_val->Add(hist,-1);
+
+  TH1F* hist_pull=new TH1F(TString("pull_")+rtof->ClassName(),TString("pull for ")+rtof->ClassName(),80,-4,4);
+  for(int ix=1;ix<=nbinsx;++ix) {
+    for(int iy=1;iy<=nbinsy;++iy) {
+      float val=hist_val->GetBinContent(ix,iy);
+      float err=hist_val->GetBinError(ix,iy);
+      if(err>0) hist_pull->Fill(val/err);
+      //std::cout<<"val="<<val<<" err="<<err<<std::endl;
+    }
+  }
+  
+  std::unique_ptr<TFile> outputfile(TFile::Open( outfilename, "UPDATE" ));
+  if (outputfile != NULL) {
+    hist->Write();
+    hist_val->Write();
+    hist_pull->Write();
+    outputfile->ls();
+  }
+
+//Screen output in athena won't make sense and would require linking of additional libraries
+#if defined(__FastCaloSimStandAlone__)
+  new TCanvas(hist->GetName(),hist->GetTitle());
+  hist->Draw("colz");
+  
+  new TCanvas(hist_val->GetName(),hist_val->GetTitle());
+  hist_val->Draw("colz");
+
+  new TCanvas(hist_pull->GetName(),hist_pull->GetTitle());
+  hist_pull->Draw();  
+#endif
+}
+
+void TFCS2DFunction::unit_tests(TH2* hist,const char* outfilename,int nrnd)
+{
+  if(hist==nullptr) hist=create_random_TH2(16,16);
+  
+  const int ntest=4;
+  TFCS2DFunction* tests[ntest];
+  tests[0]=new TFCS2DFunctionHistogram(hist);
+  tests[1]=new TFCS2DFunctionInt8Int8Int8Histogram(hist);
+  tests[2]=new TFCS2DFunctionInt8Int8Int16Histogram(hist);
+  tests[3]=new TFCS2DFunctionInt8Int8Int32Histogram(hist);
+  
+  for(int i=0;i<ntest;++i) {
+    unit_test(hist,tests[i],outfilename,nrnd);
+  }
+}
+
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS2DFunctionLateralShapeParametrization.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS2DFunctionLateralShapeParametrization.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..3f555ab2f37163c927dfe0fa5fdddc6f367bc1ef
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS2DFunctionLateralShapeParametrization.cxx
@@ -0,0 +1,150 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "CLHEP/Random/RandFlat.h"
+#include "CLHEP/Random/RandPoisson.h"
+
+#include "ISF_FastCaloSimEvent/TFCS2DFunctionLateralShapeParametrization.h"
+#include "ISF_FastCaloSimEvent/FastCaloSim_CaloCell_ID.h"
+#include "ISF_FastCaloSimEvent/TFCSSimulationState.h"
+#include "ISF_FastCaloSimEvent/TFCSExtrapolationState.h"
+
+#include "TFile.h"
+#include "TMath.h"
+#include "TH2.h"
+
+#include "HepPDT/ParticleData.hh"
+#include "HepPDT/ParticleDataTable.hh"
+
+//=============================================
+//======= TFCS2DFunctionLateralShapeParametrization =========
+//=============================================
+
+TFCS2DFunctionLateralShapeParametrization::TFCS2DFunctionLateralShapeParametrization(const char* name, const char* title) :
+  TFCSLateralShapeParametrizationHitBase(name,title),m_function(nullptr),m_nhits(0)
+{
+  reset_phi_symmetric();
+}
+
+TFCS2DFunctionLateralShapeParametrization::~TFCS2DFunctionLateralShapeParametrization()
+{
+  if(m_function) delete m_function;
+  m_function=nullptr;
+}
+
+double TFCS2DFunctionLateralShapeParametrization::get_sigma2_fluctuation(TFCSSimulationState& /*simulstate*/,const TFCSTruthState* /*truth*/, const TFCSExtrapolationState* /*extrapol*/) const
+{
+  return 1.0/m_nhits;
+}
+
+int TFCS2DFunctionLateralShapeParametrization::get_number_of_hits(TFCSSimulationState &simulstate, const TFCSTruthState* /*truth*/, const TFCSExtrapolationState* /*extrapol*/) const
+{
+  if (!simulstate.randomEngine()) {
+    return -1;
+  }
+
+  return CLHEP::RandPoisson::shoot(simulstate.randomEngine(), m_nhits);
+}
+
+void TFCS2DFunctionLateralShapeParametrization::set_number_of_hits(float nhits)
+{
+  m_nhits=nhits;
+}
+
+FCSReturnCode TFCS2DFunctionLateralShapeParametrization::simulate_hit(Hit &hit, TFCSSimulationState &simulstate, const TFCSTruthState* truth, const TFCSExtrapolationState* /*extrapol*/)
+{
+  if (!simulstate.randomEngine()) {
+    return FCSFatal;
+  }
+  if (!m_function) {
+    return FCSFatal;
+  }
+  
+  const int     pdgId    = truth->pdgid();
+  const double  charge   = HepPDT::ParticleID(pdgId).charge();
+
+  const int cs=calosample();
+  const double center_eta = hit.center_eta(); 
+  const double center_phi = hit.center_phi();
+  const double center_r   = hit.center_r();
+  const double center_z   = hit.center_z();
+
+  if (TMath::IsNaN(center_r) or TMath::IsNaN(center_z) or TMath::IsNaN(center_eta) or TMath::IsNaN(center_phi)) { //Check if extrapolation fails
+    return FCSFatal;
+  }
+
+  float alpha, r, rnd1, rnd2;
+  rnd1 = CLHEP::RandFlat::shoot(simulstate.randomEngine());
+  rnd2 = CLHEP::RandFlat::shoot(simulstate.randomEngine());
+  if(is_phi_symmetric()) {
+    if(rnd2>=0.5) { //Fill negative phi half of shape
+      rnd2-=0.5;
+      rnd2*=2;
+      m_function->rnd_to_fct(alpha,r,rnd1,rnd2);
+      alpha=-alpha;
+    } else { //Fill positive phi half of shape
+      rnd2*=2;
+      m_function->rnd_to_fct(alpha,r,rnd1,rnd2);
+    }
+  } else {
+    m_function->rnd_to_fct(alpha,r,rnd1,rnd2);
+  }
+  if(TMath::IsNaN(alpha) || TMath::IsNaN(r)) {
+    ATH_MSG_ERROR("  2D function, #hits="<<m_nhits<<" alpha="<<alpha<<" r="<<r<<" rnd1="<<rnd1<<" rnd2="<<rnd2);
+    alpha=0;
+    r=0.001;
+
+    ATH_MSG_ERROR("  This error could probably be retried");
+    return FCSFatal;
+  }
+  
+  float delta_eta_mm = r * cos(alpha);
+  float delta_phi_mm = r * sin(alpha);
+  
+  // Particles with negative eta are expected to have the same shape as those with positive eta after transformation: delta_eta --> -delta_eta
+  if(center_eta<0.)delta_eta_mm = -delta_eta_mm;
+  // Particle with negative charge are expected to have the same shape as positively charged particles after transformation: delta_phi --> -delta_phi
+  if(charge < 0.)  delta_phi_mm = -delta_phi_mm;
+
+  const float dist000    = TMath::Sqrt(center_r * center_r + center_z * center_z);
+  const float eta_jakobi = TMath::Abs(2.0 * TMath::Exp(-center_eta) / (1.0 + TMath::Exp(-2 * center_eta)));
+
+  const float delta_eta = delta_eta_mm / eta_jakobi / dist000;
+  const float delta_phi = delta_phi_mm / center_r;
+
+  hit.setEtaPhiZE(center_eta + delta_eta,center_phi + delta_phi,center_z, hit.E());
+
+  ATH_MSG_DEBUG("HIT: E="<<hit.E()<<" cs="<<cs<<" eta="<<hit.eta()<<" phi="<<hit.phi()<< " z="<<hit.z()<<" r="<<r<<" alpha="<<alpha);
+
+  return FCSSuccess;
+}
+
+
+bool TFCS2DFunctionLateralShapeParametrization::Initialize(TFCS2DFunction* func,float nhits)
+{
+  if(!func) return false;
+  if(m_function) delete m_function;
+  m_function=func;
+
+  if(nhits>0) set_number_of_hits(nhits);
+
+  return true;
+}
+
+void TFCS2DFunctionLateralShapeParametrization::Print(Option_t *option) const
+{
+  TString opt(option);
+  bool shortprint=opt.Index("short")>=0;
+  bool longprint=msgLvl(MSG::DEBUG) || (msgLvl(MSG::INFO) && !shortprint);
+  TString optprint=opt;optprint.ReplaceAll("short","");
+  TFCSLateralShapeParametrizationHitBase::Print(option);
+
+  if(longprint) {
+    if(is_phi_symmetric()) {
+      ATH_MSG_INFO(optprint <<"  2D function, #hits="<<m_nhits<<" (phi symmetric)");
+    } else {
+      ATH_MSG_INFO(optprint <<"  2D function, #hits="<<m_nhits<<" (not phi symmetric)");
+    }
+  }  
+}
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS2DFunctionTemplateHistogram.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS2DFunctionTemplateHistogram.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..bd33fd5b6de273fb74166fe66405d56d81782dbb
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS2DFunctionTemplateHistogram.cxx
@@ -0,0 +1,10 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "ISF_FastCaloSimEvent/TFCS2DFunctionTemplateHistogram.h"
+
+//=============================================
+//======= TFCS2DFunctionTemplateHistogram =========
+//=============================================
+
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS2DFunctionTemplateInterpolationHistogram.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS2DFunctionTemplateInterpolationHistogram.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..c6ac1716666233a91cb4e2790d84c468d9dd9815
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCS2DFunctionTemplateInterpolationHistogram.cxx
@@ -0,0 +1,10 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "ISF_FastCaloSimEvent/TFCS2DFunctionTemplateInterpolationHistogram.h"
+
+//=============================================
+//======= TFCS2DFunctionTemplateInterpolationHistogram =========
+//=============================================
+
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSEnergyRenormalization.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSEnergyRenormalization.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..daa099180361761fd5b151cb1fb5c44ac22a58cf
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSEnergyRenormalization.cxx
@@ -0,0 +1,55 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "ISF_FastCaloSimEvent/TFCSEnergyRenormalization.h"
+#include "ISF_FastCaloSimEvent/TFCSSimulationState.h"
+#include "ISF_FastCaloSimEvent/FastCaloSim_CaloCell_ID.h"
+#include "CaloDetDescr/CaloDetDescrElement.h"
+
+//=============================================
+//======= TFCSEnergyRenormalization =========
+//=============================================
+
+TFCSEnergyRenormalization::TFCSEnergyRenormalization(const char* name, const char* title):TFCSParametrization(name,title)
+{
+}
+
+TFCSEnergyRenormalization::~TFCSEnergyRenormalization()
+{
+}
+
+FCSReturnCode TFCSEnergyRenormalization::simulate(TFCSSimulationState& simulstate,const TFCSTruthState* /*truth*/, const TFCSExtrapolationState* /*extrapol*/)
+{
+  std::vector< float > energies(CaloCell_ID_FCS::MaxSample,0);
+
+  //Loop over all cells and sum up energies
+  for(const auto& iter : simulstate.cells()) {
+    const CaloDetDescrElement* theDDE=iter.first;
+    int layer=theDDE->getSampling();
+    energies[layer]+=iter.second;
+  }
+
+  std::vector< float > scalefactor(CaloCell_ID_FCS::MaxSample,1);
+  
+  for(int layer=0;layer<CaloCell_ID_FCS::MaxSample;++layer) {
+    if(energies[layer]!=0) scalefactor[layer]=simulstate.E(layer)/energies[layer];
+  }
+
+  //Loop over all cells and apply the scalefactor
+  for(auto& iter : simulstate.cells()) {
+    const CaloDetDescrElement* theDDE=iter.first;
+    int layer=theDDE->getSampling();
+    iter.second*=scalefactor[layer];
+  }
+
+  if(msgLvl(MSG::DEBUG)) {
+    ATH_MSG_DEBUG("Apply scale factors : ");
+    for (int layer=0;layer<CaloCell_ID_FCS::MaxSample;++layer) {
+      ATH_MSG_DEBUG("  "<<layer<<" *= "<<scalefactor[layer]<<" : "<<energies[layer]<<" -> "<<simulstate.E(layer));
+    }  
+  }
+
+  return FCSSuccess;
+}
+
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSFlatLateralShapeParametrization.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSFlatLateralShapeParametrization.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a84df0607ec92e269a7a43304deee3bb26c7708e
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSFlatLateralShapeParametrization.cxx
@@ -0,0 +1,97 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "CLHEP/Random/RandFlat.h"
+#include "CLHEP/Random/RandPoisson.h"
+
+#include "ISF_FastCaloSimEvent/TFCSFlatLateralShapeParametrization.h"
+#include "ISF_FastCaloSimEvent/FastCaloSim_CaloCell_ID.h"
+#include "ISF_FastCaloSimEvent/TFCSSimulationState.h"
+#include "ISF_FastCaloSimEvent/TFCSExtrapolationState.h"
+
+#include "TFile.h"
+#include "TMath.h"
+#include "TH2.h"
+
+//=============================================
+//======= TFCSFlatLateralShapeParametrization =========
+//=============================================
+
+TFCSFlatLateralShapeParametrization::TFCSFlatLateralShapeParametrization(const char* name, const char* title) :
+  TFCSLateralShapeParametrizationHitBase(name,title),m_nhits(0),m_scale(1)
+{
+}
+
+TFCSFlatLateralShapeParametrization::~TFCSFlatLateralShapeParametrization()
+{
+}
+
+int TFCSFlatLateralShapeParametrization::get_number_of_hits(TFCSSimulationState &simulstate, const TFCSTruthState* /*truth*/, const TFCSExtrapolationState* /*extrapol*/) const
+{
+  if (!simulstate.randomEngine()) {
+    return -1;
+  }
+
+  return CLHEP::RandPoisson::shoot(simulstate.randomEngine(), m_nhits);
+}
+
+void TFCSFlatLateralShapeParametrization::set_number_of_hits(float nhits)
+{
+  m_nhits=nhits;
+}
+
+void TFCSFlatLateralShapeParametrization::set_dR(float _dR) 
+{
+  m_dR=_dR;
+}
+
+void TFCSFlatLateralShapeParametrization::set_scale(float _scale)
+{
+  m_scale=_scale;
+}
+
+FCSReturnCode TFCSFlatLateralShapeParametrization::simulate_hit(Hit &hit, TFCSSimulationState &simulstate, const TFCSTruthState* /*truth*/, const TFCSExtrapolationState* /*extrapol*/)
+{
+  if (!simulstate.randomEngine()) {
+    return FCSFatal;
+  }
+
+  const int cs=calosample();
+  const double center_eta = hit.center_eta(); 
+  const double center_phi = hit.center_phi();
+  const double center_r   = hit.center_r();
+  const double center_z   = hit.center_z();
+
+  if (TMath::IsNaN(center_r) or TMath::IsNaN(center_z) or TMath::IsNaN(center_eta) or TMath::IsNaN(center_phi)) { //Check if extrapolation fails
+    return FCSFatal;
+  }
+
+  float alpha, r;
+  
+  alpha=2*TMath::Pi()*CLHEP::RandFlat::shoot(simulstate.randomEngine());
+  r=m_dR*CLHEP::RandFlat::shoot(simulstate.randomEngine());
+  
+  float delta_eta = r*cos(alpha);
+  float delta_phi = r*sin(alpha);
+
+  hit.setEtaPhiZE(center_eta + delta_eta,center_phi + delta_phi,center_z, hit.E()*m_scale);
+
+  ATH_MSG_DEBUG("HIT: E="<<hit.E()<<" cs="<<cs<<" eta="<<hit.eta()<<" phi="<<hit.phi()<< " z="<<hit.z()<<" r="<<r<<" alpha="<<alpha);
+
+  return FCSSuccess;
+}
+
+
+void TFCSFlatLateralShapeParametrization::Print(Option_t *option) const
+{
+  TString opt(option);
+  bool shortprint=opt.Index("short")>=0;
+  bool longprint=msgLvl(MSG::DEBUG) || (msgLvl(MSG::INFO) && !shortprint);
+  TString optprint=opt;optprint.ReplaceAll("short","");
+  TFCSLateralShapeParametrizationHitBase::Print(option);
+
+  if(longprint) {
+    ATH_MSG_INFO(optprint <<"  dR="<<m_dR<<" scale factor="<<m_scale<<", #hits="<<m_nhits);
+  }  
+}
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSHistoLateralShapeParametrization.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSHistoLateralShapeParametrization.cxx
index e7b62b40d5d01c535faa81a1fc7ba48fe7570121..860e87f933a8e7f5b9fd789ddfec14293661a336 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSHistoLateralShapeParametrization.cxx
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSHistoLateralShapeParametrization.cxx
@@ -22,7 +22,7 @@
 //=============================================
 
 TFCSHistoLateralShapeParametrization::TFCSHistoLateralShapeParametrization(const char* name, const char* title) :
-  TFCSLateralShapeParametrizationHitBase(name,title),m_nhits(0)
+  TFCSLateralShapeParametrizationHitBase(name,title),m_nhits(0),m_r_offset(0),m_r_scale(1.0)
 {
   reset_phi_symmetric();
 }
@@ -31,6 +31,13 @@ TFCSHistoLateralShapeParametrization::~TFCSHistoLateralShapeParametrization()
 {
 }
 
+double TFCSHistoLateralShapeParametrization::get_sigma2_fluctuation(TFCSSimulationState& /*simulstate*/,const TFCSTruthState* /*truth*/, const TFCSExtrapolationState* /*extrapol*/) const
+{
+  //Limit to factor 1000 fluctuations
+  if(m_nhits<0.001) return 1000;
+  return 1.0/m_nhits;
+}
+
 int TFCSHistoLateralShapeParametrization::get_number_of_hits(TFCSSimulationState &simulstate, const TFCSTruthState* /*truth*/, const TFCSExtrapolationState* /*extrapol*/) const
 {
   if (!simulstate.randomEngine()) {
@@ -91,6 +98,9 @@ FCSReturnCode TFCSHistoLateralShapeParametrization::simulate_hit(Hit &hit, TFCSS
     return FCSFatal;
   }
   
+  r*=m_r_scale;
+  r+=m_r_offset;
+  if(r<0) r=0;
   
   float delta_eta_mm = r * cos(alpha);
   float delta_phi_mm = r * sin(alpha);
@@ -152,9 +162,9 @@ void TFCSHistoLateralShapeParametrization::Print(Option_t *option) const
 
   if(longprint) {
     if(is_phi_symmetric()) {
-      ATH_MSG_INFO(optprint <<"  Histo: "<<m_hist.get_HistoBordersx().size()-1<<"*"<<m_hist.get_HistoBordersy().size()-1<<" bins, #hits="<<m_nhits<<" (phi symmetric)");
+      ATH_MSG_INFO(optprint <<"  Histo: "<<m_hist.get_HistoBordersx().size()-1<<"*"<<m_hist.get_HistoBordersy().size()-1<<" bins, #hits="<<m_nhits<<", r scale="<<m_r_scale<<", r offset="<<m_r_offset<<"mm (phi symmetric)");
     } else {
-      ATH_MSG_INFO(optprint <<"  Histo: "<<m_hist.get_HistoBordersx().size()-1<<"*"<<m_hist.get_HistoBordersy().size()-1<<" bins, #hits="<<m_nhits<<" (not phi symmetric)");
+      ATH_MSG_INFO(optprint <<"  Histo: "<<m_hist.get_HistoBordersx().size()-1<<"*"<<m_hist.get_HistoBordersy().size()-1<<" bins, #hits="<<m_nhits<<", r scale="<<m_r_scale<<", r offset="<<m_r_offset<<"mm (not phi symmetric)");
     }
   }  
 }
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSHistoLateralShapeWeight.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSHistoLateralShapeWeight.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..8700119c028761ab11bcbd527654b65d104f2be3
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSHistoLateralShapeWeight.cxx
@@ -0,0 +1,84 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "CLHEP/Random/RandFlat.h"
+
+#include "ISF_FastCaloSimEvent/TFCSHistoLateralShapeWeight.h"
+#include "ISF_FastCaloSimEvent/TFCSSimulationState.h"
+
+#include "TH1.h"
+#include "TVector2.h"
+#include "TMath.h"
+
+//=============================================
+//======= TFCSHistoLateralShapeWeight =========
+//=============================================
+
+TFCSHistoLateralShapeWeight::TFCSHistoLateralShapeWeight(const char* name, const char* title) :
+  TFCSLateralShapeParametrizationHitBase(name,title)
+{
+}
+
+TFCSHistoLateralShapeWeight::~TFCSHistoLateralShapeWeight()
+{
+  if(m_hist) delete m_hist;
+}
+
+FCSReturnCode TFCSHistoLateralShapeWeight::simulate_hit(Hit& hit,TFCSSimulationState& simulstate,const TFCSTruthState* /*truth*/, const TFCSExtrapolationState* /*extrapol*/)
+{
+  if (!simulstate.randomEngine()) {
+    return FCSFatal;
+  }
+
+  const double center_eta = hit.center_eta(); 
+  const double center_phi = hit.center_phi();
+  const double center_r   = hit.center_r();
+  const double center_z   = hit.center_z();
+
+  const float dist000    = TMath::Sqrt(center_r * center_r + center_z * center_z);
+  const float eta_jakobi = TMath::Abs(2.0 * TMath::Exp(-center_eta) / (1.0 + TMath::Exp(-2 * center_eta)));
+
+  const float delta_eta = hit.eta()-center_eta;
+  const float delta_phi = hit.phi()-center_phi;
+  const float delta_eta_mm = delta_eta * eta_jakobi * dist000;
+  const float delta_phi_mm = delta_phi * center_r;
+  const float delta_r_mm = TMath::Sqrt(delta_eta_mm*delta_eta_mm+delta_phi_mm*delta_phi_mm);
+  
+  //TODO: delta_r_mm should perhaps be cached in hit
+
+  Int_t bin=m_hist->FindBin(delta_r_mm);
+  if(bin<1) bin=1;
+  if(bin>m_hist->GetNbinsX()) bin=m_hist->GetNbinsX();
+  float weight=m_hist->GetBinContent(bin);
+  hit.E()*=weight;
+
+  ATH_MSG_DEBUG("HIT: E="<<hit.E()<<" dR_mm="<<delta_r_mm<<" weight="<<weight);
+  return FCSSuccess;
+}
+
+
+bool TFCSHistoLateralShapeWeight::Initialize(TH1* hist)
+{
+  if(!hist) return false;
+  if(m_hist) delete m_hist;
+	m_hist=(TH1*)hist->Clone(TString("TFCSHistoLateralShapeWeight_")+hist->GetName());
+	m_hist->SetDirectory(0);
+
+  return true;
+}
+
+void TFCSHistoLateralShapeWeight::Print(Option_t *option) const
+{
+  TString opt(option);
+  bool shortprint=opt.Index("short")>=0;
+  bool longprint=msgLvl(MSG::DEBUG) || (msgLvl(MSG::INFO) && !shortprint);
+  TString optprint=opt;optprint.ReplaceAll("short","");
+  TFCSLateralShapeParametrizationHitBase::Print(option);
+
+  if(longprint) {
+    if(m_hist) ATH_MSG_INFO(optprint <<"  Histogram: "<<m_hist->GetNbinsX()<<" bins ["<<m_hist->GetXaxis()->GetXmin()<<","<<m_hist->GetXaxis()->GetXmax()<<"]");
+     else ATH_MSG_INFO(optprint <<"  no Histogram");
+  }  
+}
+
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSHistoLateralShapeWeightHitAndMiss.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSHistoLateralShapeWeightHitAndMiss.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..63726341a0873ad01ff03543e61c4dcde2e354a8
--- /dev/null
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSHistoLateralShapeWeightHitAndMiss.cxx
@@ -0,0 +1,69 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "CLHEP/Random/RandFlat.h"
+
+#include "ISF_FastCaloSimEvent/TFCSHistoLateralShapeWeightHitAndMiss.h"
+#include "ISF_FastCaloSimEvent/TFCSSimulationState.h"
+
+#include "TH1.h"
+#include "TVector2.h"
+#include "TMath.h"
+
+//=============================================
+//======= TFCSHistoLateralShapeWeightHitAndMiss =========
+//=============================================
+
+TFCSHistoLateralShapeWeightHitAndMiss::TFCSHistoLateralShapeWeightHitAndMiss(const char* name, const char* title):TFCSHistoLateralShapeWeight(name,title)
+{
+}
+
+TFCSHistoLateralShapeWeightHitAndMiss::~TFCSHistoLateralShapeWeightHitAndMiss()
+{
+}
+
+FCSReturnCode TFCSHistoLateralShapeWeightHitAndMiss::simulate_hit(Hit& hit,TFCSSimulationState& simulstate,const TFCSTruthState* /*truth*/, const TFCSExtrapolationState* /*extrapol*/)
+{
+  if (!simulstate.randomEngine()) {
+    return FCSFatal;
+  }
+
+  const double center_eta = hit.center_eta(); 
+  const double center_phi = hit.center_phi();
+  const double center_r   = hit.center_r();
+  const double center_z   = hit.center_z();
+
+  const float dist000    = TMath::Sqrt(center_r * center_r + center_z * center_z);
+  const float eta_jakobi = TMath::Abs(2.0 * TMath::Exp(-center_eta) / (1.0 + TMath::Exp(-2 * center_eta)));
+
+  const float delta_eta = hit.eta()-center_eta;
+  const float delta_phi = hit.phi()-center_phi;
+  const float delta_eta_mm = delta_eta * eta_jakobi * dist000;
+  const float delta_phi_mm = delta_phi * center_r;
+  const float delta_r_mm = TMath::Sqrt(delta_eta_mm*delta_eta_mm+delta_phi_mm*delta_phi_mm);
+  
+  //TODO: delta_r_mm should perhaps be cached in hit
+
+  Int_t bin=m_hist->FindBin(delta_r_mm);
+  if(bin<1) bin=1;
+  if(bin>m_hist->GetNbinsX()) bin=m_hist->GetNbinsX();
+  float weight=m_hist->GetBinContent(bin);
+  if(weight<=1) {
+    //if weight<=1, give lower energy to hit. 
+    //TFCSLateralShapeParametrizationHitChain needs to be able to generate more hits in this case
+    hit.E()*=weight;
+  } else {
+    //if weight>1, accept only 1/weight events, but give them a higher energy increased by weight. 
+    //This leads to larger fluctuations, while keeping the shape unchanged.
+    float prob=1.0/weight;
+    float rnd=CLHEP::RandFlat::shoot(simulstate.randomEngine());
+    if(rnd<prob) hit.E()*=weight;
+     else hit.E()=0;
+  }  
+
+  ATH_MSG_DEBUG("HIT: E="<<hit.E()<<" dR_mm="<<delta_r_mm<<" weight="<<weight);
+  return FCSSuccess;
+}
+
+
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSHitCellMappingWiggle.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSHitCellMappingWiggle.cxx
index 72136bb2ac19d8a2412822a3b36f24d28d7511ae..94a6db4f3eca14c3d096f26c720b47f0154980d4 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSHitCellMappingWiggle.cxx
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSHitCellMappingWiggle.cxx
@@ -47,7 +47,7 @@ void TFCSHitCellMappingWiggle::initialize(TFCS1DFunction* func)
 void TFCSHitCellMappingWiggle::initialize(const std::vector< const TFCS1DFunction* >& functions, const std::vector< float >& bin_low_edges)
 {
   if(functions.size()+1!=bin_low_edges.size()) {
-    ATH_MSG_ERROR("Using "<<functions.size()<<" functions needs "<<functions.size()+1<<" bins, but got "<<bin_low_edges.size()<<"bins");
+    ATH_MSG_ERROR("Using "<<functions.size()<<" functions needs "<<functions.size()+1<<" bin low edges, but got "<<bin_low_edges.size()<<"bins");
     return;
   }
   for(auto function : m_functions) if(function) delete function;
@@ -125,9 +125,9 @@ void TFCSHitCellMappingWiggle::Print(Option_t *option) const
   TString optprint=opt;optprint.ReplaceAll("short","");
   
   if(longprint) {
-    ATH_MSG(INFO) << optprint <<"  "<<get_number_of_bins()<<" functions in [";
-    for (unsigned int i=0;i<get_number_of_bins();++i) msg()<<get_bin_low_edge(i)<<", ";
-    msg()<<get_bin_up_edge(get_number_of_bins()-1)<<"]"<< endmsg;
+    ATH_MSG(INFO) << optprint <<"  "<<get_number_of_bins()<<" functions : ";
+    for (unsigned int i=0;i<get_number_of_bins();++i) msg()<<get_bin_low_edge(i)<<" < ("<<get_function(i)<<") < ";
+    msg()<<get_bin_up_edge(get_number_of_bins()-1)<< endmsg;
   }  
 }
 
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSLateralShapeParametrizationHitBase.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSLateralShapeParametrizationHitBase.cxx
index ace709d6cc37beff91c039906f7a6ea15db0fe88..8e2badda702e5ace27a7c1bd36ed34f75adadfc5 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSLateralShapeParametrizationHitBase.cxx
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSLateralShapeParametrizationHitBase.cxx
@@ -16,6 +16,11 @@ TFCSLateralShapeParametrizationHitBase::TFCSLateralShapeParametrizationHitBase(c
 {
 }
 
+double TFCSLateralShapeParametrizationHitBase::get_sigma2_fluctuation(TFCSSimulationState& /*simulstate*/,const TFCSTruthState* /*truth*/, const TFCSExtrapolationState* /*extrapol*/) const
+{
+  return -1;
+}
+
 int TFCSLateralShapeParametrizationHitBase::get_number_of_hits(TFCSSimulationState& /*simulstate*/,const TFCSTruthState* /*truth*/, const TFCSExtrapolationState* /*extrapol*/) const
 {
   return -1;
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSLateralShapeParametrizationHitChain.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSLateralShapeParametrizationHitChain.cxx
index 7e3e400a63bb1a052c92b8d8e7956a56a40c0894..409255c124ebc741077af603fb7ed84a7a1f9ad7 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSLateralShapeParametrizationHitChain.cxx
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSLateralShapeParametrizationHitChain.cxx
@@ -52,38 +52,52 @@ FCSReturnCode TFCSLateralShapeParametrizationHitChain::simulate(TFCSSimulationSt
     return FCSFatal;
   }
 
-  float Ehit=simulstate.E(calosample())/nhit;
+  float Elayer=simulstate.E(calosample());
+  float Ehit=Elayer/nhit;
+  float sumEhit=0;
 
   bool debug = msgLvl(MSG::DEBUG);
   if (debug) {
-    ATH_MSG_DEBUG("E("<<calosample()<<")="<<simulstate.E(calosample())<<" #hits="<<nhit);
+    ATH_MSG_DEBUG("E("<<calosample()<<")="<<simulstate.E(calosample())<<" #hits~"<<nhit);
   }
 
-  for (int i = 0; i < nhit; ++i) {
-    TFCSLateralShapeParametrizationHitBase::Hit hit; 
+  int ihit=0;
+  TFCSLateralShapeParametrizationHitBase::Hit hit;
+  hit.reset_center();
+  do {
+    hit.reset();
     hit.E()=Ehit;
     for(TFCSLateralShapeParametrizationHitBase* hitsim : m_chain) {
       if (debug) {
-        if (i < 2) hitsim->setLevel(MSG::DEBUG);
+        if (ihit < 2) hitsim->setLevel(MSG::DEBUG);
         else hitsim->setLevel(MSG::INFO);
       }
 
       for (int i = 0; i <= FCS_RETRY_COUNT; i++) {
+        //TODO: potentially change logic in case of a retry to redo the whole hit chain from an empty hit instead of just redoing one step in the hit chain
         if (i > 0) ATH_MSG_WARNING("TFCSLateralShapeParametrizationHitChain::simulate(): Retry simulate_hit call " << i << "/" << FCS_RETRY_COUNT);
   
         FCSReturnCode status = hitsim->simulate_hit(hit, simulstate, truth, extrapol);
 
-        if (status == FCSSuccess)
+        if (status == FCSSuccess) {
+          if(sumEhit+hit.E()>Elayer) hit.E()=Elayer-sumEhit;//sum of all hit energies needs to be Elayer: correct last hit accordingly
           break;
-        else if (status == FCSFatal)
-          return FCSFatal;
+        } else {
+          if (status == FCSFatal) return FCSFatal;
+        }    
 
         if (i == FCS_RETRY_COUNT) {
           ATH_MSG_ERROR("TFCSLateralShapeParametrizationHitChain::simulate(): simulate_hit call failed after " << FCS_RETRY_COUNT << "retries");
         }
       }
     }
-  }
+    sumEhit+=hit.E();
+    ++ihit;
+    if(ihit>10*nhit) {
+      ATH_MSG_WARNING("TFCSLateralShapeParametrizationHitChain::simulate(): aborting hit chain, iterated " << 10*nhit << " times, expected " << nhit<<" times. Deposited E("<<calosample()<<")="<<sumEhit);
+      break;
+    }  
+  } while (sumEhit<Elayer);
 
   return FCSSuccess;
 }
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSLateralShapeParametrizationHitNumberFromE.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSLateralShapeParametrizationHitNumberFromE.cxx
index 55a3ddf360baab2d91608d45c7623fa368373dd2..6d48a5a7601f0165669727c7318b965470dd0375 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSLateralShapeParametrizationHitNumberFromE.cxx
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimEvent/src/TFCSLateralShapeParametrizationHitNumberFromE.cxx
@@ -19,12 +19,8 @@ TFCSLateralShapeParametrizationHitNumberFromE::TFCSLateralShapeParametrizationHi
   set_match_all_pdgid();
 }
 
-int TFCSLateralShapeParametrizationHitNumberFromE::get_number_of_hits(TFCSSimulationState& simulstate,const TFCSTruthState* /*truth*/, const TFCSExtrapolationState* /*extrapol*/) const
+double TFCSLateralShapeParametrizationHitNumberFromE::get_sigma2_fluctuation(TFCSSimulationState& simulstate,const TFCSTruthState* /*truth*/, const TFCSExtrapolationState* /*extrapol*/) const
 {
-  if (!simulstate.randomEngine()) {
-    return -1;
-  }
-
   int cs=calosample();
   double energy=simulstate.E(cs);
 
@@ -38,7 +34,21 @@ int TFCSLateralShapeParametrizationHitNumberFromE::get_number_of_hits(TFCSSimula
   }
   
   double sigma_stochastic=m_stochastic/sqrt(energy/1000.0);
-  int hits = CLHEP::RandPoisson::shoot(simulstate.randomEngine(), 1.0 / (sigma_stochastic*sigma_stochastic + m_constant*m_constant));
+  double sigma2 = sigma_stochastic*sigma_stochastic + m_constant*m_constant;
+
+  ATH_MSG_DEBUG("sigma^2 fluctuation="<<sigma2);
+
+  return sigma2;
+}
+
+int TFCSLateralShapeParametrizationHitNumberFromE::get_number_of_hits(TFCSSimulationState& simulstate,const TFCSTruthState* truth, const TFCSExtrapolationState* extrapol) const
+{
+  if (!simulstate.randomEngine()) {
+    return -1;
+  }
+
+  double sigma2=get_sigma2_fluctuation(simulstate,truth,extrapol);
+  int hits = CLHEP::RandPoisson::shoot(simulstate.randomEngine(), 1.0 / sigma2);
 
   ATH_MSG_DEBUG("#hits="<<hits);
   
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimParametrization/CMakeLists.txt b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimParametrization/CMakeLists.txt
index 8a185b740c7c967c00ba2719577c4263964ab3d6..3179d9f870795455eff3e26d481e2dc0c78d902e 100644
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimParametrization/CMakeLists.txt
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimParametrization/CMakeLists.txt
@@ -27,6 +27,8 @@ atlas_depends_on_subdirs( PUBLIC
                           Calorimeter/CaloEvent
                           Calorimeter/CaloIdentifier
                           Calorimeter/CaloTrackingGeometry
+                          Simulation/G4Sim/TrackRecord
+                          Simulation/G4SimCnv/G4SimTPCnv
                           Database/AthenaPOOL/AthenaPoolUtilities
                           DetectorDescription/GeoModel/GeoAdaptors
                           DetectorDescription/GeoModel/GeoModelInterfaces
@@ -61,7 +63,7 @@ atlas_add_root_dictionary( ISF_FastCaloSimParametrizationLib
                            ISF_FastCaloSimParametrization/TreeReader.h
                            ISF_FastCaloSimParametrization/FCS_Cell.h
                            ISF_FastCaloSimParametrization/CaloGeometry.h
-                           ISF_FastCaloSimParametrization/CaloGeometryLookup.h 
+                           ISF_FastCaloSimParametrization/CaloGeometryLookup.h
                            Root/LinkDef.h
                            EXTERNAL_PACKAGES  ROOT HepPDT XercesC CLHEP HepMC Geant4 )
 
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimParametrization/ISF_FastCaloSimParametrization/ISF_HitAnalysis.h b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimParametrization/ISF_FastCaloSimParametrization/ISF_HitAnalysis.h
index 3cc869ae945c75240bccf0f72a741cf539474419..accc9cc1a72f476018b97db8801496011b36366c 100755
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimParametrization/ISF_FastCaloSimParametrization/ISF_HitAnalysis.h
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimParametrization/ISF_FastCaloSimParametrization/ISF_HitAnalysis.h
@@ -195,6 +195,16 @@ class ISF_HitAnalysis : public AthAlgorithm {
    std::vector<float>* m_newTTC_Angle3D;
    std::vector<float>* m_newTTC_AngleEta;
 
+
+   std::vector<float>* m_MuonEntryLayer_E;
+   std::vector<float>* m_MuonEntryLayer_px;
+   std::vector<float>* m_MuonEntryLayer_py;
+   std::vector<float>* m_MuonEntryLayer_pz;
+   std::vector<float>* m_MuonEntryLayer_x;
+   std::vector<float>* m_MuonEntryLayer_y;
+   std::vector<float>* m_MuonEntryLayer_z;
+   std::vector<int>* m_MuonEntryLayer_pdg;
+
    /** The new Extrapolator setup */
    ToolHandle<Trk::ITimedExtrapolator>  m_extrapolator;
    ToolHandle<ICaloSurfaceHelper>       m_caloSurfaceHelper;
diff --git a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimParametrization/src/ISF_HitAnalysis.cxx b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimParametrization/src/ISF_HitAnalysis.cxx
index d94aa5036613b41718f94149917b5c9e17d8bede..e4c330305ba2b354053ee98885dcf9c6e3f4fdf4 100755
--- a/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimParametrization/src/ISF_HitAnalysis.cxx
+++ b/Simulation/ISF/ISF_FastCaloSim/ISF_FastCaloSimParametrization/src/ISF_HitAnalysis.cxx
@@ -26,6 +26,9 @@
 #include "TileSimEvent/TileHit.h"
 #include "TileSimEvent/TileHitVector.h"
 
+//Track Record
+#include "TrackRecord/TrackRecordCollection.h"
+
 //CaloCell
 #include "CaloEvent/CaloCellContainer.h"
 
@@ -163,6 +166,15 @@ ISF_HitAnalysis::ISF_HitAnalysis(const std::string& name, ISvcLocator* pSvcLocat
    , m_newTTC_Angle3D(0)
    , m_newTTC_AngleEta(0)
 
+   , m_MuonEntryLayer_E(0)
+   , m_MuonEntryLayer_px(0)
+   , m_MuonEntryLayer_py(0)
+   , m_MuonEntryLayer_pz(0)
+   , m_MuonEntryLayer_x(0)
+   , m_MuonEntryLayer_y(0)
+   , m_MuonEntryLayer_z(0)
+   , m_MuonEntryLayer_pdg(0)
+
    , m_caloEntrance(0)
    , m_calo_tb_coord(0)
    , m_sample_calo_surf(CaloCell_ID_FCS::noSample)
@@ -546,6 +558,16 @@ StatusCode ISF_HitAnalysis::initialize()
   m_newTTC_Angle3D = new std::vector<float>;
   m_newTTC_AngleEta = new std::vector<float>;
 
+  m_MuonEntryLayer_E = new std::vector<float>;
+  m_MuonEntryLayer_px = new std::vector<float>;
+  m_MuonEntryLayer_py = new std::vector<float>;
+  m_MuonEntryLayer_pz = new std::vector<float>;
+  m_MuonEntryLayer_x = new std::vector<float>;
+  m_MuonEntryLayer_y = new std::vector<float>;
+  m_MuonEntryLayer_z = new std::vector<float>;
+  m_MuonEntryLayer_pdg = new std::vector<int>;
+
+
   // Optional branches
   if(m_saveAllBranches){
     m_tree->Branch("HitX",                 &m_hit_x);
@@ -637,6 +659,18 @@ StatusCode ISF_HitAnalysis::initialize()
   m_tree->Branch("newTTC_Angle3D",&m_newTTC_Angle3D);
   m_tree->Branch("newTTC_AngleEta",&m_newTTC_AngleEta);
 
+
+
+  m_tree->Branch("MuonEntryLayer_E",&m_MuonEntryLayer_E);
+  m_tree->Branch("MuonEntryLayer_px",&m_MuonEntryLayer_px);
+  m_tree->Branch("MuonEntryLayer_py",&m_MuonEntryLayer_py);
+  m_tree->Branch("MuonEntryLayer_pz",&m_MuonEntryLayer_pz);
+  m_tree->Branch("MuonEntryLayer_x",&m_MuonEntryLayer_x);
+  m_tree->Branch("MuonEntryLayer_y",&m_MuonEntryLayer_y);
+  m_tree->Branch("MuonEntryLayer_z",&m_MuonEntryLayer_z);
+  m_tree->Branch("MuonEntryLayer_pdg",&m_MuonEntryLayer_pdg);
+
+
  }
  dummyFile->Close();
  return StatusCode::SUCCESS;
@@ -833,6 +867,16 @@ StatusCode ISF_HitAnalysis::execute()
  m_newTTC_IDCaloBoundary_z->clear();
  m_newTTC_Angle3D->clear();
  m_newTTC_AngleEta->clear();
+
+
+ m_MuonEntryLayer_E->clear();
+ m_MuonEntryLayer_x->clear();
+ m_MuonEntryLayer_y->clear();
+ m_MuonEntryLayer_z->clear();
+ m_MuonEntryLayer_px->clear();
+ m_MuonEntryLayer_py->clear();
+ m_MuonEntryLayer_pz->clear();
+
  //##########################
 
  //Get the FastCaloSim step info collection from store
@@ -1083,6 +1127,30 @@ StatusCode ISF_HitAnalysis::execute()
    } //mcEvent
  }//truth event
 
+
+
+ //Retrieve and save MuonEntryLayer information 
+ const TrackRecordCollection *MuonEntry = nullptr;
+ ATH_CHECK(evtStore()->retrieve(MuonEntry, "MuonEntryLayer"));
+ if (sc.isFailure())
+ {
+ ATH_MSG_WARNING( "Couldn't read MuonEntry from StoreGate");
+ //return NULL;
+ }
+ else{
+  for ( const TrackRecord &record : *MuonEntry){
+    m_MuonEntryLayer_E->push_back((record).GetEnergy());
+    m_MuonEntryLayer_px->push_back((record).GetMomentum().getX());
+    m_MuonEntryLayer_py->push_back((record).GetMomentum().getY());
+    m_MuonEntryLayer_pz->push_back((record).GetMomentum().getZ());
+    m_MuonEntryLayer_x->push_back((record).GetPosition().getX());
+    m_MuonEntryLayer_y->push_back((record).GetPosition().getY());
+    m_MuonEntryLayer_z->push_back((record).GetPosition().getZ());
+    m_MuonEntryLayer_pdg->push_back((record).GetPDGCode());
+  }
+ }
+
+
  //Get reco cells if available
  const CaloCellContainer *cellColl = 0;
  sc = evtStore()->retrieve(cellColl, "AllCalo");
diff --git a/Simulation/ISF/ISF_HepMC/ISF_HepMC_Tools/src/GenParticleSimWhiteList.cxx b/Simulation/ISF/ISF_HepMC/ISF_HepMC_Tools/src/GenParticleSimWhiteList.cxx
index 04f55760e26d3de5ef6c5cb3fcadf7e10e7adee2..b75e219631f7d2008d607a59b8da5157d2587928 100644
--- a/Simulation/ISF/ISF_HepMC/ISF_HepMC_Tools/src/GenParticleSimWhiteList.cxx
+++ b/Simulation/ISF/ISF_HepMC/ISF_HepMC_Tools/src/GenParticleSimWhiteList.cxx
@@ -68,6 +68,9 @@ StatusCode  ISF::GenParticleSimWhiteList::initialize()
 /** passes through to the private version of the filter */
 bool ISF::GenParticleSimWhiteList::pass(const HepMC::GenParticle& particle) const
 {
+
+  ATH_MSG_VERBOSE( "Checking whether " << particle << " passes the filter." );
+
   static std::vector<int> vertices(500);
   vertices.clear();
   bool so_far_so_good = pass( particle , vertices );
@@ -81,6 +84,8 @@ bool ISF::GenParticleSimWhiteList::pass(const HepMC::GenParticle& particle) cons
       // Check this particle
       vertices.clear();
       bool parent_all_clear = pass( **it , vertices );
+      ATH_MSG_VERBOSE( "Parent all clear: " << parent_all_clear <<
+         "\nIf true, will not pass the daughter because it should have been picked up through the parent already (to avoid multi-counting)." );
       so_far_so_good = so_far_so_good && !parent_all_clear;
     } // Loop over parents
   } // particle had parents
@@ -95,7 +100,6 @@ bool ISF::GenParticleSimWhiteList::pass(const HepMC::GenParticle& particle , std
   bool passFilter = std::binary_search( m_pdgId.begin() , m_pdgId.end() , particle.pdg_id() ) || MC::PID::isNucleus( particle.pdg_id() );
   // Remove documentation particles
   passFilter = passFilter && particle.status()!=3;
-
   // Test all daughter particles
   if (particle.end_vertex() && m_qs && passFilter){
     // Break loops
@@ -104,7 +108,10 @@ bool ISF::GenParticleSimWhiteList::pass(const HepMC::GenParticle& particle , std
       for (HepMC::GenVertex::particle_iterator it = particle.end_vertex()->particles_begin(HepMC::children);
                                                it != particle.end_vertex()->particles_end(HepMC::children); ++it){
         passFilter = passFilter && pass( **it , used_vertices );
-        if (!passFilter) break;
+        if (!passFilter) {
+          ATH_MSG_VERBOSE( "Daughter particle " << **it << " does not pass." );
+          break;
+        }
       } // Loop over daughters
     } // Break loops
   } // particle had daughters
diff --git a/Simulation/Tests/DigitizationTests/test/test_Digi_tf_mc15_2015_heavy_ion.sh b/Simulation/Tests/DigitizationTests/test/test_Digi_tf_mc15_2015_heavy_ion.sh
new file mode 100755
index 0000000000000000000000000000000000000000..f5a27bc056ce6234891550fc987cc7cb6c1e3ece
--- /dev/null
+++ b/Simulation/Tests/DigitizationTests/test/test_Digi_tf_mc15_2015_heavy_ion.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# art-description: Run digitization combining a heavy ion sample produced with MC15 using 2015 geometry and conditions with Zmumu events
+# art-include: 21.0/Athena
+# art-include: 21.3/Athena
+# art-include: 21.9/Athena
+# art-include: master/Athena
+# art-type: grid
+# art-output: mc15_2015_heavyIon.RDO.pool.root
+
+DigiOutFileName="mc15_2015_heavyIon.RDO.pool.root"
+ZmumuHITSFileName="/cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/DigitizationTests/mc15_pPb8TeV.361107.PowhegPythia8EvtGen_AZNLOCTEQ6L1_Zmumu.simul.HITS.e5367_s3164.HITS.11308894._000005.pool.root.1"
+HeavyIonHITSFileName="/cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/DigitizationTests/mc15_pPb8TeV.420112.Hijing_MinBiasDiff_pPb_8TeV.merge.HITS.e5328_s3148_s3153.HITS.11232927._000249.pool.root.1"
+
+Digi_tf.py \
+--inputHITSFile ${ZmumuHITSFileName} \
+--inputCavernHitsFile ${HeavyIonHITSFileName} \
+--outputRDOFile ${DigiOutFileName} \
+--maxEvents 10 \
+--skipEvents 0 \
+--numberOfCavernBkg 1 \
+--preExec 'all:rec.doHIP.set_Value_and_Lock(True);from AthenaCommon.BeamFlags import jobproperties;jobproperties.Beam.numberOfCollisions.set_Value_and_Lock(0.0);jobproperties.Beam.bunchSpacing.set_Value_and_Lock(25);from LArROD.LArRODFlags import larRODFlags;larRODFlags.nSamples.set_Value_and_Lock(4);from LArDigitization.LArDigitizationFlags import jobproperties;jobproperties.LArDigitizationFlags.useFcalHighGain.set_Value_and_Lock(True)' 'HITtoRDO:from Digitization.DigitizationFlags import digitizationFlags;digitizationFlags.overrideMetadata+=["PhysicsList"]' \
+--preInclude 'HITtoRDO:Digitization/ForceUseOfPileUpTools.py' \
+--postExec 'all:CfgMgr.MessageSvc().setError+=["HepMcParticleLink"]' 'HITtoRDO:from AthenaCommon import CfgGetter;mergeMcEventCollTool=CfgGetter.getPublicTool("MergeMcEventCollTool").DoSlimming=False' \
+--postInclude 'all:PyJobTransforms/UseFrontier.py,SimulationJobOptions/postInclude.HijingPars.py' \
+--geometryVersion ATLAS-R2-2015-03-01-00 \
+--conditionsTag all:OFLCOND-MC15c-SDR-15 \
+--DataRunNumber 295000 \
+--pileupInitialBunch 0 \
+--pileupFinalBunch 0 \
+--bunchSpacing 100 \
+--imf False
+
+echo  "art-result: $? Digi_tf.py"
diff --git a/Simulation/Tests/SimExoticsTests/CMakeLists.txt b/Simulation/Tests/SimExoticsTests/CMakeLists.txt
index 7c7a8a939c6db5371acce476ab8b1f0d8f50e314..515b6508a8f4baa68eb284298b7f2ac0f282a269 100644
--- a/Simulation/Tests/SimExoticsTests/CMakeLists.txt
+++ b/Simulation/Tests/SimExoticsTests/CMakeLists.txt
@@ -10,5 +10,5 @@ atlas_depends_on_subdirs( PRIVATE
                           TestPolicy )
 
 # Install files from the package:
-atlas_install_runtime( test/SimExoticsTests_TestConfiguration.xml )
+atlas_install_runtime( test/*.sh )
 
diff --git a/Simulation/Tests/SimExoticsTests/test/SimExoticsTests_TestConfiguration.xml b/Simulation/Tests/SimExoticsTests/test/SimExoticsTests_TestConfiguration.xml
deleted file mode 100644
index 10e2e2f5bef28f4d30f1f100134c676654848dd8..0000000000000000000000000000000000000000
--- a/Simulation/Tests/SimExoticsTests/test/SimExoticsTests_TestConfiguration.xml
+++ /dev/null
@@ -1,293 +0,0 @@
-<!DOCTYPE unifiedTestConfiguration SYSTEM "http://www.hep.ucl.ac.uk/atlas/AtlasTesting/DTD/unifiedTestConfiguration.dtd">
-<unifiedTestConfiguration>
-  <rtt xmlns="http://www.hep.ucl.ac.uk/atlas/AtlasTesting/rtt">
-    <rttContactPerson>Andreas Schaelicke (andreas.schaelicke@cern.ch)</rttContactPerson>
-    <mailto>atlas-simulation-testreports@cern.ch</mailto>
-    <refRelease>17.2.7</refRelease>
-
-<!--
-         don't forget to check the xml file with
-         python /afs/cern.ch/user/r/rtt/public/validateXML.py SimExoticsTests_TestConfiguration.xml
-         and to update the twiki page:
-         https://twiki.cern.ch/twiki/bin/viewauth/Atlas/SimExoticsTests
--->
-
-    <jobList>
-
-      <classification>
-        <displayClass>OfflineValidation</displayClass>
-        <displayProcess>Simul</displayProcess>
-        <displayComponent>Athena-Core</displayComponent>
-      </classification>
-     <chain>
-        <chainName>StableRHadrons114865</chainName>
-        <sequential>
-           <chainElement>
-             <jobTransform userJobId="StableRHadronEvgen114865">
-               <doc>PYTHIA6 R-Hadrons stable generic stop 600GeV</doc>
-               <jobTransformJobName>StableRHadronEvgen114865</jobTransformJobName>
-               <jobTransformCmd>
-export DATAPATH=/afs/cern.ch/atlas/groups/Generators/MC14JobOptions/latest/share/tests:$DATAPATH; Generate_tf.py --ecmEnergy='8000' --runNumber='114865' --firstEvent='1' --maxEvents='6000' --randomSeed='50097493' --jobConfig='/afs/cern.ch/atlas/groups/Generators/MC14JobOptions/latest/share/tests/MC14.114865.PythiaRhad_AUET2B_CTEQ6L1_regge_stop_600GeV.py' --outputEVNTFile='evgen.pool.root'
-</jobTransformCmd>
-               <group>ExoticGenJobTransforms</group>
-               <queue>short</queue>
-             </jobTransform>
-             <chainfileout>evgen.pool.root</chainfileout>
-           </chainElement>
-           <chainElement>
-             <jobTransform userJobId="StableRHadronSim114865">
-               <doc>PYTHIA6 R-Hadrons stable generic stop 600GeV - Simulation</doc>
-               <jobTransformJobName>StableRHadronSim114865</jobTransformJobName>
-               <jobTransformCmd>
-AtlasG4_tf.py --inputEVNTFile evgen.pool.root --outputHITSFile test.HITS.pool.root --conditionsTag 'OFLCOND-RUN12-SDR-19' --physicsList FTFP_BERT_ATL --maxEvents 10 --geometryVersion 'ATLAS-R2-2015-03-01-00' --DataRunNumber '222525' --randomSeed 9375655
-               </jobTransformCmd>
-               <group>ExoticGenJobTransforms</group>
-               <chaindataset_info>
-                 <jobTransformData />
-                 <chaindatasetName>evgen.pool.root</chaindatasetName>
-                 <dataset_info>
-                   <jobTransformData />
-                   <datasetName>/eos/atlas/atlascerngroupdisk/proj-sit/simulation/validation/RTT/referenceFiles/18.X.0/StableRHadrons114865.EVNT.pool.root</datasetName>
-                 </dataset_info>
-               </chaindataset_info>
-               <queue>long</queue>
-               <batchWallTime>300</batchWallTime>
-             </jobTransform>
-             <chainfileout>test.HITS.pool.root</chainfileout>
-           </chainElement>
-           <chainElement>
-             <jobTransform userJobId="StableRHadronSim114865_Reg">
-               <doc>Regression test between releases</doc>
-               <jobTransformJobName>StableRHadronSim114865_Reg</jobTransformJobName>
-               <jobTransformCmd>sim_reg_test.py StableRHadronSim114865 test.HITS.pool.root HITS.pool</jobTransformCmd>
-               <group>SimCoreTests:SimCoreRegressionTests</group>
-               <queue>short</queue>
-             </jobTransform>
-           </chainElement>
-        </sequential>
-     </chain>
-     <chain>
-        <chainName>StableRHadrons114875</chainName>
-        <sequential>
-           <chainElement>
-             <jobTransform userJobId="StableRHadronEvgen114875">
-               <doc>PYTHIA6 R-Hadrons stable generic sbottom 600GeV</doc>
-               <jobTransformJobName>StableRHadronEvgen114875</jobTransformJobName>
-               <jobTransformCmd>
-export DATAPATH=/afs/cern.ch/atlas/groups/Generators/MC14JobOptions/latest/share/tests:$DATAPATH; Generate_tf.py --ecmEnergy='8000' --runNumber='114875' --firstEvent='1' --maxEvents='6000' --randomSeed='50097493' --jobConfig='/afs/cern.ch/atlas/groups/Generators/MC14JobOptions/latest/share/tests/MC14.114875.PythiaRhad_AUET2B_CTEQ6L1_regge_sbottom_600GeV.py' --outputEVNTFile='evgen.pool.root'
-               </jobTransformCmd>
-               <group>ExoticGenJobTransforms</group>
-               <queue>short</queue>
-             </jobTransform>
-             <chainfileout>evgen.pool.root</chainfileout>
-           </chainElement>
-           <chainElement>
-             <jobTransform userJobId="StableRHadronSim114875">
-               <doc>PYTHIA6 R-Hadrons stable generic sbottom 600GeV - Simulation</doc>
-               <jobTransformJobName>StableRHadronSim114875</jobTransformJobName>
-               <jobTransformCmd>
-AtlasG4_tf.py --inputEVNTFile evgen.pool.root --outputHITSFile test.HITS.pool.root --conditionsTag 'OFLCOND-RUN12-SDR-19' --physicsList FTFP_BERT_ATL --maxEvents 10 --geometryVersion 'ATLAS-R2-2015-03-01-00' --DataRunNumber '222525' --randomSeed 9375655
-               </jobTransformCmd>
-               <group>ExoticGenJobTransforms</group>
-               <chaindataset_info>
-                 <jobTransformData />
-                 <chaindatasetName>evgen.pool.root</chaindatasetName>
-                 <dataset_info>
-                   <jobTransformData />
-                   <datasetName>/eos/atlas/atlascerngroupdisk/proj-sit/simulation/validation/RTT/referenceFiles/18.X.0/StableRHadrons114875.EVNT.pool.root</datasetName>
-                 </dataset_info>
-               </chaindataset_info>
-               <queue>long</queue>
-               <batchWallTime>300</batchWallTime>
-             </jobTransform>
-             <chainfileout>test.HITS.pool.root</chainfileout>
-           </chainElement>
-           <chainElement>
-             <jobTransform userJobId="StableRHadronSim114875_Reg">
-               <doc>Regression test between releases</doc>
-               <jobTransformJobName>StableRHadronSim114875_Reg</jobTransformJobName>
-               <jobTransformCmd>sim_reg_test.py StableRHadronSim114875 test.HITS.pool.root HITS.pool</jobTransformCmd>
-               <group>SimCoreTests:SimCoreRegressionTests</group>
-               <queue>short</queue>
-             </jobTransform>
-           </chainElement>
-        </sequential>
-     </chain>
-     <chain>
-        <chainName>StableRHadrons176089</chainName>
-        <sequential>
-           <chainElement>
-             <jobTransform userJobId="StableRHadronEvgen176089">
-               <doc>PYTHIA6 R-Hadrons stable generic gluino gball0.1 1000GeV</doc>
-               <jobTransformJobName>StableRHadronEvgen176089</jobTransformJobName>
-               <jobTransformCmd>
-export DATAPATH=/afs/cern.ch/atlas/groups/Generators/MC14JobOptions/latest/share/tests:$DATAPATH; Generate_tf.py --ecmEnergy='8000' --runNumber='176089' --firstEvent='1' --maxEvents='6000' --randomSeed='50097493' --jobConfig='/afs/cern.ch/atlas/groups/Generators/MC14JobOptions/latest/share/tests/MC14.176089.PythiaRhad_AUET2B_CTEQ6L1_gener_gluino_0p1_1000GeV.py' --outputEVNTFile='evgen.pool.root'
-               </jobTransformCmd>
-               <group>ExoticGenJobTransforms</group>
-               <queue>short</queue>
-             </jobTransform>
-             <chainfileout>evgen.pool.root</chainfileout>
-           </chainElement>
-           <chainElement>
-             <jobTransform userJobId="StableRHadronSim176089">
-               <doc>PYTHIA6 R-Hadrons stable generic gluino gball0.1 1000GeV - Simulation</doc>
-               <jobTransformJobName>StableRHadronSim176089</jobTransformJobName>
-               <jobTransformCmd>
-AtlasG4_tf.py --inputEVNTFile evgen.pool.root --outputHITSFile test.HITS.pool.root --conditionsTag 'OFLCOND-RUN12-SDR-19' --physicsList FTFP_BERT_ATL --maxEvents 10 --geometryVersion 'ATLAS-R2-2015-03-01-00' --DataRunNumber '222525' --randomSeed 9375655
-               </jobTransformCmd>
-               <group>ExoticGenJobTransforms</group>
-               <chaindataset_info>
-                 <jobTransformData />
-                 <chaindatasetName>evgen.pool.root</chaindatasetName>
-                 <dataset_info>
-                   <jobTransformData />
-                   <datasetName>/eos/atlas/atlascerngroupdisk/proj-sit/simulation/validation/RTT/referenceFiles/18.X.0/StableRHadrons176089.EVNT.pool.root</datasetName>
-                 </dataset_info>
-               </chaindataset_info>
-               <queue>long</queue>
-               <batchWallTime>300</batchWallTime>
-             </jobTransform>
-             <chainfileout>test.HITS.pool.root</chainfileout>
-           </chainElement>
-           <chainElement>
-             <jobTransform userJobId="StableRHadronSim176089_Reg">
-               <doc>Regression test between releases</doc>
-               <jobTransformJobName>StableRHadronSim176089_Reg</jobTransformJobName>
-               <jobTransformCmd>sim_reg_test.py StableRHadronSim176089 test.HITS.pool.root HITS.pool</jobTransformCmd>
-               <group>SimCoreTests:SimCoreRegressionTests</group>
-               <queue>short</queue>
-             </jobTransform>
-           </chainElement>
-        </sequential>
-      </chain>
-
-      <chain>
-        <chainName>AMSB_500GeV_Chargino_1ns</chainName>
-        <sequential>
-           <chainElement>
-             <jobTransform userJobId="AMSB_500GeV_Chargino_1nsEvgen">
-               <doc>Evgen AMSB 500GeV Charginos 1ns lifetime</doc>
-               <jobTransformJobName>AMSB_500GeV_Chargino_1nsEvgen</jobTransformJobName>
-               <jobTransformCmd>
-export DATAPATH=/afs/cern.ch/atlas/groups/Generators/MC14JobOptions/latest/share/tests:$DATAPATH; Generate_tf.py --ecmEnergy=8000 --runNumber=174309 --firstEvent=1 --randomSeed=1 --jobConfig=/afs/cern.ch/atlas/groups/Generators/MC14JobOptions/latest/share/tests/MC14.174309.Herwigpp_UEEE3_CTEQ6L1_mAMSB_EW_1000_175000_5_P_LL1p0_1jet.py --outputEVNTFile=evgen.pool.root --maxEvents=500
-               </jobTransformCmd>
-               <group>ExoticGenJobTransforms</group>
-               <queue>short</queue>
-             </jobTransform>
-             <chainfileout>evgen.pool.root</chainfileout>
-           </chainElement>
-           <chainElement>
-             <jobTransform userJobId="AMSB_500GeV_Chargino_1nsSim">
-               <doc>Simulation AMSB 500GeV Charginos 1ns lifetime</doc>
-               <jobTransformJobName>AMSB_500GeV_Chargino_1nsSim</jobTransformJobName>
-               <jobTransformCmd>
-AtlasG4_tf.py --inputEVNTFile evgen.pool.root --outputHITSFile test.HITS.pool.root --conditionsTag 'OFLCOND-RUN12-SDR-19' --physicsList FTFP_BERT_ATL --maxEvents 100 --geometryVersion 'ATLAS-R2-2015-03-01-00' --DataRunNumber '222525' --randomSeed 10
-               </jobTransformCmd>
-               <group>ExoticGenJobTransforms</group>
-               <chaindataset_info>
-                 <jobTransformData />
-                 <chaindatasetName>evgen.pool.root</chaindatasetName>
-                 <dataset_info>
-                   <jobTransformData />
-                   <datasetName>/eos/atlas/atlascerngroupdisk/proj-sit/simulation/validation/RTT/referenceFiles/18.X.0/AMSB_500GeV_Chargino_1ns.EVNT.pool.root</datasetName>
-                 </dataset_info>
-               </chaindataset_info>
-               <queue>long</queue>
-               <batchWallTime>300</batchWallTime>
-             </jobTransform>
-             <chainfileout>test.HITS.pool.root</chainfileout>
-           </chainElement>
-           <chainElement>
-             <jobTransform userJobId="AMSB_500GeV_Chargino_1nsSim_Reg">
-               <doc>Regression test between releases</doc>
-               <jobTransformJobName>AMSB_500GeV_Chargino_1nsSim_Reg</jobTransformJobName>
-               <jobTransformCmd>sim_reg_test.py AMSB_500GeV_Chargino_1nsSim test.HITS.pool.root HITS.pool</jobTransformCmd>
-               <group>SimCoreTests:SimCoreRegressionTests</group>
-               <queue>short</queue>
-             </jobTransform>
-           </chainElement>
-        </sequential>
-      </chain>
-
-      <chain>
-        <chainName>Monopole</chainName>
-        <sequential>
-           <chainElement>
-             <jobTransform userJobId="Monopole_Evgen">
-               <doc>Evgen Monopole</doc>
-               <jobTransformJobName>Monopole_Evgen</jobTransformJobName>
-               <jobTransformCmd>
-export DATAPATH=/afs/cern.ch/atlas/groups/Generators/MC14JobOptions/latest/share/tests:$DATAPATH; Generate_tf.py --ecmEnergy='8000' --runNumber='188762' --firstEvent='1' --maxEvents='5000' --randomSeed='1' --jobConfig='/afs/cern.ch/atlas/groups/Generators/MC14JobOptions/latest/share/tests/MC14.188762.ParticleGenerator_SingleMonopole.py' --outputEVNTFile='evgen.pool.root'
-               </jobTransformCmd>
-               <group>ExoticGenJobTransforms</group>
-               <queue>short</queue>
-             </jobTransform>
-             <chainfileout>evgen.pool.root</chainfileout>
-           </chainElement>
-           <chainElement>
-             <jobTransform userJobId="Monopole_Sim">
-               <doc>Simulation Monopole</doc>
-               <jobTransformJobName>Monopole_Sim</jobTransformJobName>
-               <jobTransformCmd>
-AtlasG4_tf.py --inputEVNTFile evgen.pool.root --outputHITSFile test.HITS.pool.root --conditionsTag 'OFLCOND-RUN12-SDR-19' --physicsList FTFP_BERT_ATL --maxEvents 100 --geometryVersion 'ATLAS-R2-2015-03-01-00' --DataRunNumber '222525' --randomSeed 10
-               </jobTransformCmd>
-               <group>ExoticGenJobTransforms</group>
-               <chaindataset_info>
-                 <jobTransformData />
-                 <chaindatasetName>evgen.pool.root</chaindatasetName>
-                 <dataset_info>
-                   <jobTransformData />
-                   <datasetName>/eos/atlas/atlascerngroupdisk/proj-sit/simulation/validation/RTT/referenceFiles/18.X.0/Monopole.EVNT.pool.root</datasetName>
-                 </dataset_info>
-               </chaindataset_info>
-               <queue>long</queue>
-               <batchWallTime>300</batchWallTime>
-             </jobTransform>
-             <chainfileout>test.HITS.pool.root</chainfileout>
-           </chainElement>
-           <chainElement>
-             <jobTransform userJobId="Monopole_Sim_Reg">
-               <doc>Regression test between releases</doc>
-               <jobTransformJobName>Monopole_Sim_Reg</jobTransformJobName>
-               <jobTransformCmd>sim_reg_test.py Monopole_Sim test.HITS.pool.root HITS.pool</jobTransformCmd>
-               <group>SimCoreTests:SimCoreRegressionTests</group>
-               <queue>short</queue>
-             </jobTransform>
-           </chainElement>
-        </sequential>
-      </chain>
-
-      <!--   NOTE:: make -omitvalidation to start with two "-"  -->
-    </jobList>
-
-    <jobGroups>
-      <!-- note: the job groups have also be known to Factory_ScriptWriter.py in order to work in RTT -->
-      <jobGroup name="ExoticJobTransform" parent="Top">
-        <keepFilePattern>fileGrepper_results.txt</keepFilePattern>
-        <keepFilePattern>*.root</keepFilePattern>
-        <keepFilePattern>*.txt</keepFilePattern>
-        <keepFilePattern>log.*</keepFilePattern>
-        <keepFilePattern>*.log</keepFilePattern>
-        <keepFilePattern>*.diffPool</keepFilePattern>
-      </jobGroup>
-
-
-      <jobGroup name="ExoticGenJobTransforms" parent="ExoticJobTransform">
-        <!-- nothing to configure (yet) -->
-      </jobGroup>
-
-      <jobGroup name="ExoticSimJobTransforms" parent="ExoticJobTransform">
-        <!-- nothing to configure (yet) -->
-      </jobGroup>
-
-
-      <jobGroup name="ExoticDigiJobTransforms" parent="ExoticJobTransform">
-        <!-- nothing to configure (yet) -->
-      </jobGroup>
-
-    </jobGroups>
-
-
-  </rtt>
-
-</unifiedTestConfiguration>
diff --git a/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingCharginos.sh b/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingCharginos.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a8e7c5ba15ec7ebf4e46784b846e3252794c3d92
--- /dev/null
+++ b/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingCharginos.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# art-description: MC16-style simulation of decaying Charginos using FullG4 (tests Charginos and Gauginos packages)
+# art-type: grid
+# art-include: 21.0/Athena
+# art-include: 21.0/AthSimulation
+# art-include: 21.3/Athena
+# art-include: 21.9/Athena
+# art-include: master/Athena
+# art-include: master/AthSimulation
+
+# MC16 setup
+# ATLAS-R2-2016-01-00-01 and OFLCOND-MC16-SDR-14
+Sim_tf.py \
+--conditionsTag 'default:OFLCOND-MC16-SDR-14' \
+--physicsList 'FTFP_BERT_ATL' \
+--truthStrategy 'MC15aPlusLLP' \
+--simulator 'FullG4' \
+--postInclude 'default:PyJobTransforms/UseFrontier.py' \
+--preInclude 'EVNTtoHITS:SimulationJobOptions/preInclude.BeamPipeKill.py,SimulationJobOptions/preInclude.FrozenShowersFCalOnly.py' \
+--DataRunNumber '284500' \
+--geometryVersion 'default:ATLAS-R2-2016-01-00-01' \
+--inputEVNTFile "/cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/SimCoreTests/mc15_13TeV.448307.MGPy8EG_A14N23LO_mAMSB_C1C1_5000_208000_LL4p0_MET60.evgen.EVNT.e6962.EVNT.15631425._000001.pool.root.1" \
+--outputHITSFile "Hits.pool.root" \
+--maxEvents 10 \
+--imf False
+
+rc=$?
+echo  "art-result: $rc simulation"
+rc2=-9999
+if [ $rc -eq 0 ]
+then
+    ArtPackage=$1
+    ArtJobName=$2
+    art.py compare grid --entries 10 ${ArtPackage} ${ArtJobName} --mode=summary
+    rc2=$?
+fi
+echo  "art-result: $rc2 regression"
diff --git a/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingCharginos_busy.sh b/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingCharginos_busy.sh
new file mode 100755
index 0000000000000000000000000000000000000000..e9a64b838b588f21509608137e078c2052a0327d
--- /dev/null
+++ b/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingCharginos_busy.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# art-description: MC16-style simulation of decaying Charginos (busy events) using FullG4 (tests Charginos and Gauginos packages)
+# art-type: grid
+# art-include: 21.0/Athena
+# art-include: 21.0/AthSimulation
+# art-include: 21.3/Athena
+# art-include: 21.9/Athena
+# art-include: master/Athena
+# art-include: master/AthSimulation
+
+# MC16 setup
+# ATLAS-R2-2016-01-00-01 and OFLCOND-MC16-SDR-14
+Sim_tf.py \
+--conditionsTag 'default:OFLCOND-MC16-SDR-14' \
+--physicsList 'FTFP_BERT_ATL' \
+--truthStrategy 'MC15aPlusLLP' \
+--simulator 'FullG4' \
+--postInclude 'default:PyJobTransforms/UseFrontier.py' \
+--preInclude 'EVNTtoHITS:SimulationJobOptions/preInclude.BeamPipeKill.py,SimulationJobOptions/preInclude.FrozenShowersFCalOnly.py' \
+--DataRunNumber '284500' \
+--geometryVersion 'default:ATLAS-R2-2016-01-00-01' \
+--inputEVNTFile "/cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/SimCoreTests/mc15_13TeV.448300.MGPy8EG_A14N23LO_GG_mixedC1LLP_0p2_1400_1200.evgen.EVNT.e7183.EVNT.16706750._000001.pool.root.1" \
+--outputHITSFile "Hits.pool.root" \
+--maxEvents 4 \
+--imf False
+
+rc=$?
+echo  "art-result: $rc simulation"
+rc2=-9999
+if [ $rc -eq 0 ]
+then
+    ArtPackage=$1
+    ArtJobName=$2
+    art.py compare grid --entries 4 ${ArtPackage} ${ArtJobName} --mode=summary
+    rc2=$?
+fi
+echo  "art-result: $rc2 regression"
diff --git a/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingLightSleptons.sh b/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingLightSleptons.sh
new file mode 100755
index 0000000000000000000000000000000000000000..b4c25825aa061c30058899975efa2e2d4faa64f2
--- /dev/null
+++ b/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingLightSleptons.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# art-description: MC16-style simulation of decaying light sleptons using FullG4 (tests Sleptons + Gauginos)
+# art-type: grid
+# art-include: 21.0/Athena
+# art-include: 21.0/AthSimulation
+# art-include: 21.3/Athena
+# art-include: 21.9/Athena
+# art-include: master/Athena
+# art-include: master/AthSimulation
+
+# MC16 setup
+# ATLAS-R2-2016-01-00-01 and OFLCOND-MC16-SDR-14
+Sim_tf.py \
+--conditionsTag 'default:OFLCOND-MC16-SDR-14' \
+--physicsList 'FTFP_BERT_ATL' \
+--truthStrategy 'MC15aPlusLLP' \
+--simulator 'FullG4' \
+--postInclude 'default:PyJobTransforms/UseFrontier.py' \
+--preInclude 'EVNTtoHITS:SimulationJobOptions/preInclude.BeamPipeKill.py,SimulationJobOptions/preInclude.FrozenShowersFCalOnly.py' \
+--DataRunNumber '284500' \
+--geometryVersion 'default:ATLAS-R2-2016-01-00-01' \
+--inputEVNTFile "/cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/SimCoreTests/mc15_13TeV.399030.MGPy8EG_A14NNPDF23LO_SlepSlep_directLLP_100_0_0p01ns.evgen.EVNT.e7067.EVNT.16242732._000001.pool.root.1" \
+--outputHITSFile "Hits.pool.root" \
+--maxEvents 10 \
+--imf False
+
+rc=$?
+echo  "art-result: $rc simulation"
+rc2=-9999
+if [ $rc -eq 0 ]
+then
+    ArtPackage=$1
+    ArtJobName=$2
+    art.py compare grid --entries 10 ${ArtPackage} ${ArtJobName} --mode=summary
+    rc2=$?
+fi
+echo  "art-result: $rc2 regression"
diff --git a/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingNeutralinos.sh b/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingNeutralinos.sh
new file mode 100755
index 0000000000000000000000000000000000000000..8b60764fc8b52534ef7677965ea2994bd883702c
--- /dev/null
+++ b/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingNeutralinos.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# art-description: MC16-style simulation of decaying Neutralinos using FullG4 (tests sim response to many displaced “primary” particles)
+# art-type: grid
+# art-include: 21.0/Athena
+# art-include: 21.0/AthSimulation
+# art-include: 21.3/Athena
+# art-include: 21.9/Athena
+# art-include: master/Athena
+# art-include: master/AthSimulation
+
+# MC16 setup
+# ATLAS-R2-2016-01-00-01 and OFLCOND-MC16-SDR-14
+Sim_tf.py \
+--conditionsTag 'default:OFLCOND-MC16-SDR-14' \
+--physicsList 'FTFP_BERT_ATL' \
+--truthStrategy 'MC15aPlusLLP' \
+--simulator 'FullG4' \
+--postInclude 'default:PyJobTransforms/UseFrontier.py' \
+--preInclude 'EVNTtoHITS:SimulationJobOptions/preInclude.BeamPipeKill.py,SimulationJobOptions/preInclude.FrozenShowersFCalOnly.py' \
+--DataRunNumber '284500' \
+--geometryVersion 'default:ATLAS-R2-2016-01-00-01' \
+--inputEVNTFile "/cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/SimCoreTests/mc15_13TeV.448168.MGPy8EG_A14NNPDF23LO_GG_qqn1_2400_850_rpvLF_p01ns.evgen.EVNT.e7245.EVNT.17092338._000002.pool.root.1" \
+--outputHITSFile "Hits.pool.root" \
+--maxEvents 10 \
+--imf False
+
+rc=$?
+echo  "art-result: $rc simulation"
+rc2=-9999
+if [ $rc -eq 0 ]
+then
+    ArtPackage=$1
+    ArtJobName=$2
+    art.py compare grid --entries 10 ${ArtPackage} ${ArtJobName} --mode=summary
+    rc2=$?
+fi
+echo  "art-result: $rc2 regression"
diff --git a/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingStaus.sh b/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingStaus.sh
new file mode 100755
index 0000000000000000000000000000000000000000..18d2d005ba5154c8cce5a8d3f6d1aa5b0a09b3bb
--- /dev/null
+++ b/Simulation/Tests/SimExoticsTests/test/test_FullG4_DecayingStaus.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# art-description: MC16-style simulation of decaying staus using FullG4 (tests the Sleptons + Gauginos packages)
+# art-type: grid
+# art-include: 21.0/Athena
+# art-include: 21.0/AthSimulation
+# art-include: 21.3/Athena
+# art-include: 21.9/Athena
+# art-include: master/Athena
+# art-include: master/AthSimulation
+
+# MC16 setup
+# ATLAS-R2-2016-01-00-01 and OFLCOND-MC16-SDR-14
+Sim_tf.py \
+--conditionsTag 'default:OFLCOND-MC16-SDR-14' \
+--physicsList 'FTFP_BERT_ATL' \
+--truthStrategy 'MC15aPlusLLP' \
+--simulator 'FullG4' \
+--postInclude 'default:PyJobTransforms/UseFrontier.py' \
+--preInclude 'EVNTtoHITS:SimulationJobOptions/preInclude.BeamPipeKill.py,SimulationJobOptions/preInclude.FrozenShowersFCalOnly.py' \
+--DataRunNumber '284500' \
+--geometryVersion 'default:ATLAS-R2-2016-01-00-01' \
+--inputEVNTFile "/cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/SimCoreTests/mc15_13TeV.399007.MGPy8EG_A14NNPDF23LO_StauStau_directLLP_100_0_1ns.evgen.EVNT.e7067.EVNT.16137672._000001.pool.root.1" \
+--outputHITSFile "Hits.pool.root" \
+--maxEvents 10 \
+--imf False
+
+rc=$?
+echo  "art-result: $rc simulation"
+rc2=-9999
+if [ $rc -eq 0 ]
+then
+    ArtPackage=$1
+    ArtJobName=$2
+    art.py compare grid --entries 10 ${ArtPackage} ${ArtJobName} --mode=summary
+    rc2=$?
+fi
+echo  "art-result: $rc2 regression"
diff --git a/Simulation/Tests/SimExoticsTests/test/test_FullG4_StableCharginos.sh b/Simulation/Tests/SimExoticsTests/test/test_FullG4_StableCharginos.sh
new file mode 100755
index 0000000000000000000000000000000000000000..b9e3e953f75235fc0ddb69ce3fc4fc346c1c0175
--- /dev/null
+++ b/Simulation/Tests/SimExoticsTests/test/test_FullG4_StableCharginos.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# art-description: MC16-style simulation of stable Charginos using FullG4 (tests the Charginos package alone)
+# art-type: grid
+# art-include: 21.0/Athena
+# art-include: 21.0/AthSimulation
+# art-include: 21.3/Athena
+# art-include: 21.9/Athena
+# art-include: master/Athena
+# art-include: master/AthSimulation
+
+# MC16 setup
+# ATLAS-R2-2016-01-00-01 and OFLCOND-MC16-SDR-14
+Sim_tf.py \
+--conditionsTag 'default:OFLCOND-MC16-SDR-14' \
+--physicsList 'FTFP_BERT_ATL' \
+--truthStrategy 'MC15aPlusLLP' \
+--simulator 'FullG4' \
+--postInclude 'default:PyJobTransforms/UseFrontier.py' \
+--preInclude 'EVNTtoHITS:SimulationJobOptions/preInclude.BeamPipeKill.py,SimulationJobOptions/preInclude.FrozenShowersFCalOnly.py' \
+--DataRunNumber '284500' \
+--geometryVersion 'default:ATLAS-R2-2016-01-00-01' \
+--inputEVNTFile "/cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/SimCoreTests/mc15_13TeV.404460.MGPy8EG_A14N23LO_mAMSB_C1C1_5000_68000_Stable.evgen.EVNT.e5654.EVNT.13360885._000001.pool.root.1" \
+--outputHITSFile "Hits.pool.root" \
+--maxEvents 10 \
+--imf False
+
+rc=$?
+echo  "art-result: $rc simulation"
+rc2=-9999
+if [ $rc -eq 0 ]
+then
+    ArtPackage=$1
+    ArtJobName=$2
+    art.py compare grid --entries 10 ${ArtPackage} ${ArtJobName} --mode=summary
+    rc2=$?
+fi
+echo  "art-result: $rc2 regression"
diff --git a/Simulation/Tests/SimExoticsTests/test/test_FullG4_StableSleptons.sh b/Simulation/Tests/SimExoticsTests/test/test_FullG4_StableSleptons.sh
new file mode 100755
index 0000000000000000000000000000000000000000..225b0e886af0db5c51ba2645463aea14ba0d9991
--- /dev/null
+++ b/Simulation/Tests/SimExoticsTests/test/test_FullG4_StableSleptons.sh
@@ -0,0 +1,38 @@
+#!/bin/sh
+#
+# art-description: MC16-style simulation of stable Sleptons using FullG4 (tests Sleptons package alone)
+# art-type: grid
+# art-include: 21.0/Athena
+# art-include: 21.0/AthSimulation
+# art-include: 21.3/Athena
+# art-include: 21.9/Athena
+# art-include: master/Athena
+# art-include: master/AthSimulation
+
+# MC16 setup
+# ATLAS-R2-2016-01-00-01 and OFLCOND-MC16-SDR-14
+Sim_tf.py \
+--conditionsTag 'default:OFLCOND-MC16-SDR-14' \
+--physicsList 'FTFP_BERT_ATL' \
+--truthStrategy 'MC15aPlusLLP' \
+--simulator 'FullG4' \
+--postInclude 'default:PyJobTransforms/UseFrontier.py' \
+--preInclude 'EVNTtoHITS:SimulationJobOptions/preInclude.BeamPipeKill.py,SimulationJobOptions/preInclude.FrozenShowersFCalOnly.py' \
+--DataRunNumber '284500' \
+--geometryVersion 'default:ATLAS-R2-2016-01-00-01' \
+--inputEVNTFile "/cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/SimCoreTests/mc15_13TeV.404510.MGPy8EG_A14N23LO_GMSB_stablestau_lambda_090_tanb_10_dy.evgen.EVNT.e5652.EVNT.13360872._000001.pool.root.1" \
+--outputHITSFile "Hits.pool.root" \
+--maxEvents 10 \
+--imf False
+
+rc=$?
+echo  "art-result: $rc simulation"
+rc2=-9999
+if [ $rc -eq 0 ]
+then
+    ArtPackage=$1
+    ArtJobName=$2
+    art.py compare grid --entries 10 ${ArtPackage} ${ArtJobName} --mode=summary
+    rc2=$?
+fi
+echo  "art-result: $rc2 regression"
diff --git a/TileCalorimeter/TileExample/TileRecEx/share/jobOptions_TileCalibRec.py b/TileCalorimeter/TileExample/TileRecEx/share/jobOptions_TileCalibRec.py
index 5956005ed0fd0a54e04b6545598f04bfc014703a..8e80d3480a5ad5b7d5f05451b58829671567930d 100644
--- a/TileCalorimeter/TileExample/TileRecEx/share/jobOptions_TileCalibRec.py
+++ b/TileCalorimeter/TileExample/TileRecEx/share/jobOptions_TileCalibRec.py
@@ -466,6 +466,9 @@ if not 'doTileMF' in dir():
 if not 'doTileOF1' in dir():
     doTileOF1 = False
 
+if not 'doTileWiener' in dir():
+    doTileWiener = False
+
 if not 'doTileFit' in dir():
     doTileFit = not TileCompareMode and ReadDigits
 
@@ -511,7 +514,7 @@ if not 'OfcFromCOOL' in dir():
     else:
         OfcFromCOOL = False
 
-if useRODReco or doTileOpt2 or doTileMF or doTileOF1 or doTileOptATLAS or doTileFitCool or TileCompareMode or not 'TileUseCOOL' in dir():
+if useRODReco or doTileOpt2 or doTileMF or doTileOF1 or doTileOptATLAS or doTileWiener or doTileFitCool or TileCompareMode or not 'TileUseCOOL' in dir():
     TileUseCOOL = True
     TileUseCOOLOFC = not ReadPool or OfcFromCOOL
 
@@ -843,6 +846,12 @@ else:
     from xAODEventInfoCnv.xAODEventInfoCreator import xAODMaker__EventInfoCnvAlg
     topSequence+=xAODMaker__EventInfoCnvAlg()
 
+#============================================================
+#=== configure BunchCrossingTool
+#============================================================
+from TrigBunchCrossingTool.BunchCrossingTool import BunchCrossingTool
+ToolSvc += BunchCrossingTool("LHC" if globalflags.DataSource() == "data" else "MC")
+
 #=============================================================
 #=== read ByteStream and reconstruct data
 #=============================================================
@@ -941,6 +950,19 @@ if doTileOF1:
 
     print ToolSvc.TileRawChannelBuilderOF1    
 
+if doTileWiener:
+    if PhaseFromCOOL:
+        ToolSvc.TileRawChannelBuilderWienerFilter.TileCondToolTiming = tileInfoConfigurator.TileCondToolTiming
+        ToolSvc.TileRawChannelBuilderWienerFilter.correctTime = False # do not need to correct time with best phase
+
+    ToolSvc.TileRawChannelBuilderWienerFilter.BestPhase   = PhaseFromCOOL # Phase from COOL or assume phase=0
+    if TileCompareMode or TileEmulateDSP:
+        ToolSvc.TileRawChannelBuilderWienerFilter.EmulateDSP = True # use dsp emulation
+    ToolSvc.TileRawChannelBuilderWienerFilter.UseDSPCorrection = not TileBiGainRun
+    ToolSvc.TileRawChannelBuilderWienerFilter.MC = globalflags.DataSource() != "data"
+
+    print ToolSvc.TileRawChannelBuilderWienerFilter    
+
 if (doEventDisplay or doCreatePool):
     # create TileHit from TileRawChannel and store it in TileHitVec
     from TileRecAlgs.TileHitFromRawChGetter import *
@@ -1278,6 +1300,9 @@ if doTileMon:
             
         if doTileMF:
             theTileRawChannelMon.TileRawChannelContainer = "TileRawChannelMF"
+            
+        if doTileWiener:
+            theTileRawChannelMon.TileRawChannelContainer = "TileRawChannelWiener"
 
         if useRODReco:
             theTileRawChannelMon.TileRawChannelContainerDSP = "TileRawChannelCnt"
diff --git a/TileCalorimeter/TileIdentifier/TileIdentifier/TileFragHash.h b/TileCalorimeter/TileIdentifier/TileIdentifier/TileFragHash.h
index f94912245c66bb2bd3e837144950f6498f81a8f5..7c642fba8f3be2dda3f8348b0a645791197b0016 100755
--- a/TileCalorimeter/TileIdentifier/TileIdentifier/TileFragHash.h
+++ b/TileCalorimeter/TileIdentifier/TileIdentifier/TileFragHash.h
@@ -31,7 +31,8 @@ class TileFragHash  {
   /** initialize */
   enum TYPE {Beam=255, Default=0, Digitizer=0,
              OptFilterDsp=1, OptFilterOffline=2, OptFilterDspCompressed=3,
-             ManyAmps=4, MF=5, FitFilter=6, FitFilterCool=7, FlatFilter=8 };
+             ManyAmps=4, MF=5, FitFilter=6, FitFilterCool=7, FlatFilter=8,
+             WienerFilterOffline=9 };
 
   void initialize(const TileHWID * tileHWID, TYPE type=Default ); 
 
diff --git a/TileCalorimeter/TileRec/TileRec/TileAANtuple.h b/TileCalorimeter/TileRec/TileRec/TileAANtuple.h
index 76c8fd91444ecaf583db939acea31bffc980822b..b082bd8a77440bd2aefa09405b2a21a3ccd24591 100755
--- a/TileCalorimeter/TileRec/TileRec/TileAANtuple.h
+++ b/TileCalorimeter/TileRec/TileRec/TileAANtuple.h
@@ -22,6 +22,7 @@
 ///    TileRawChannelContainerFit       key of fit filtered RawChannels in TDS
 ///    TileRawChannelContainerFitCool   key of fit filtered RawChannels in TDS
 ///    TileRawChannelOF1                key of OF1 filtered RawChannels in TDS
+///    TileRawChannelWiener             key of Wiener filtered RawChannels in TDS
 ///    TileLaserObject         string   key of Laser Object in TDS
 ///    CalibrateEnergy         bool     If calibration should be applied to energy
 ///    CalibMode               bool     If data is in calibration mode (bi-gain input)
@@ -297,6 +298,11 @@ class TileAANtuple : public AthAlgorithm {
     float m_chi2MF[N_ROS2][N_MODULES][N_CHANS];
     float m_pedMF[N_ROS2][N_MODULES][N_CHANS];
 
+    float m_eWiener[N_ROS2][N_MODULES][N_CHANS];
+    float m_tWiener[N_ROS2][N_MODULES][N_CHANS];
+    float m_pedWiener[N_ROS2][N_MODULES][N_CHANS];
+    float m_chi2Wiener[N_ROS2][N_MODULES][N_CHANS];
+
     short m_ROD_GlobalCRC[N_ROS][N_MODULES];
     short m_ROD_BCID[N_ROS][N_MODULES];
     short m_ROD_DMUBCIDErr[N_ROS][N_MODULES][N_DMUS];
@@ -340,6 +346,7 @@ class TileAANtuple : public AthAlgorithm {
     std::string m_dspRawChannelContainer;
     std::string m_mfRawChannelContainer;
     std::string m_of1RawChannelContainer;
+    std::string m_wienerRawChannelContainer;
     std::string m_laserObject;
     std::string m_tileMuRcvRawChannelContainer; // TMDB
     std::string m_tileMuRcvDigitsContainer; // TMDB
diff --git a/TileCalorimeter/TileRec/share/TileDefaults_jobOptions.py b/TileCalorimeter/TileRec/share/TileDefaults_jobOptions.py
index 84f446c1b7cc5dbd3125b450d9dfee274d4998c6..5bec251868ec7e0c9c9f339f1cf88680891441c2 100755
--- a/TileCalorimeter/TileRec/share/TileDefaults_jobOptions.py
+++ b/TileCalorimeter/TileRec/share/TileDefaults_jobOptions.py
@@ -11,6 +11,7 @@ if ('doSim' in dir()) and doSim:
     doTileQIE = False
     doTileOptATLAS = False
     doTileMF = False
+    doTileWiener = False
     doTileFit  = False
     doTileFitCool = False
     TileCisRun = False
@@ -61,6 +62,11 @@ if not 'doTileMF' in dir():
 else:
     jobproperties.TileRecFlags.doTileMF = doTileMF
 
+if not 'doTileWiener' in dir():
+    doTileWiener = jobproperties.TileRecFlags.doTileWiener()
+else:
+    jobproperties.TileRecFlags.doTileWiener = doTileWiener
+
 if not 'doTileFit' in dir():
     doTileFit = jobproperties.TileRecFlags.doTileFit()
 else:
diff --git a/TileCalorimeter/TileRec/share/TileNtuple_jobOptions.py b/TileCalorimeter/TileRec/share/TileNtuple_jobOptions.py
index ebca0914cdd328c666d5fec366cc812d5295e1ed..46861e8bd2ca891023fa8a9a99209aae74d1b69e 100755
--- a/TileCalorimeter/TileRec/share/TileNtuple_jobOptions.py
+++ b/TileCalorimeter/TileRec/share/TileNtuple_jobOptions.py
@@ -18,6 +18,7 @@ TileNtuple.TileRawChannelContainerOpt  = ""
 TileNtuple.TileRawChannelContainerQIE  = ""
 TileNtuple.TileRawChannelContainerOF1  = ""
 TileNtuple.TileRawChannelContainerMF = ""
+TileNtuple.TileRawChannelContainerWiener = ""
 TileNtuple.TileRawChannelContainerDsp  = ""
 TileNtuple.TileLaserObject = ""
 TileNtuple.TileMuRcvRawChannelContainer= ""
@@ -87,6 +88,9 @@ else:
     if doTileMF:
         TileNtuple.TileRawChannelContainerMF = "TileRawChannelMF"
 
+    if doTileWiener:
+        TileNtuple.TileRawChannelContainerWiener = "TileRawChannelWiener"
+
     if useTMDB:
         TileNtuple.TileMuRcvRawChannelContainer = "MuRcvRawChCnt"
         TileNtuple.TileMuRcvDigitsContainer = "MuRcvDigitsCnt"
diff --git a/TileCalorimeter/TileRec/src/TileAANtuple.cxx b/TileCalorimeter/TileRec/src/TileAANtuple.cxx
index 694beb6417868e98d4474ba37d7fc6d88bf319a8..36e339bcf7ffd6ca734d59a99541e964d82f4a0b 100755
--- a/TileCalorimeter/TileRec/src/TileAANtuple.cxx
+++ b/TileCalorimeter/TileRec/src/TileAANtuple.cxx
@@ -153,6 +153,10 @@ TileAANtuple::TileAANtuple(std::string name, ISvcLocator* pSvcLocator)
 , m_tMF()
 , m_chi2MF()
 , m_pedMF()
+, m_eWiener()
+, m_tWiener()
+, m_pedWiener()
+, m_chi2Wiener()
 , m_ROD_GlobalCRC()
 , m_ROD_BCID()
 , m_ROD_DMUBCIDErr()
@@ -220,6 +224,7 @@ TileAANtuple::TileAANtuple(std::string name, ISvcLocator* pSvcLocator)
   declareProperty("TileRawChannelContainerOF1", m_of1RawChannelContainer = "");      //
   declareProperty("TileRawChannelContainerDsp", m_dspRawChannelContainer = "");      //
   declareProperty("TileRawChannelContainerMF", m_mfRawChannelContainer = "");      //
+  declareProperty("TileRawChannelContainerWiener", m_wienerRawChannelContainer = "");      //
   declareProperty("TileMuRcvRawChannelContainer", m_tileMuRcvRawChannelContainer = "MuRcvRawChCnt");// TMDB
   declareProperty("TileMuRcvDigitsContainer", m_tileMuRcvDigitsContainer = "MuRcvDigitsCnt");// TMDB
   declareProperty("TileMuRcvContainer", m_tileMuRcvContainer = "TileMuRcvCnt");// TMDB
@@ -448,14 +453,15 @@ StatusCode TileAANtuple::execute() {
   
   // store TileRawChannels
   // start from DSP channels - so we can find out what is the DSP units
-  empty &= storeRawChannels(m_dspRawChannelContainer,  m_eDsp,  m_tDsp,  m_chi2Dsp, m_pedDsp, true ).isFailure();
-  empty &= storeRawChannels(m_rawChannelContainer,     m_ene,   m_time,  m_chi2,    m_ped,    false).isFailure();
-  empty &= storeMFRawChannels(m_mfRawChannelContainer, m_eMF,   m_tMF,   m_chi2MF,  m_pedMF,  false).isFailure();
-  empty &= storeRawChannels(m_fitRawChannelContainer,  m_eFit,  m_tFit,  m_chi2Fit, m_pedFit, false).isFailure();
-  empty &= storeRawChannels(m_fitcRawChannelContainer, m_eFitc, m_tFitc, m_chi2Fitc,m_pedFitc,false).isFailure();
-  empty &= storeRawChannels(m_optRawChannelContainer,  m_eOpt,  m_tOpt,  m_chi2Opt, m_pedOpt, false).isFailure();
-  empty &= storeRawChannels(m_qieRawChannelContainer,  m_eQIE,  m_tQIE,  m_chi2QIE, m_pedQIE, false).isFailure();
-  empty &= storeRawChannels(m_of1RawChannelContainer,  m_eOF1,  m_tOF1,  m_chi2OF1, m_pedOF1, false).isFailure();
+  empty &= storeRawChannels(m_dspRawChannelContainer,    m_eDsp,     m_tDsp,     m_chi2Dsp,    m_pedDsp,    true ).isFailure();
+  empty &= storeRawChannels(m_rawChannelContainer,       m_ene,      m_time,     m_chi2,       m_ped,       false).isFailure();
+  empty &= storeMFRawChannels(m_mfRawChannelContainer,   m_eMF,      m_tMF,      m_chi2MF,     m_pedMF,     false).isFailure();
+  empty &= storeRawChannels(m_fitRawChannelContainer,    m_eFit,     m_tFit,     m_chi2Fit,    m_pedFit,    false).isFailure();
+  empty &= storeRawChannels(m_fitcRawChannelContainer,   m_eFitc,    m_tFitc,    m_chi2Fitc,   m_pedFitc,   false).isFailure();
+  empty &= storeRawChannels(m_optRawChannelContainer,    m_eOpt,     m_tOpt,     m_chi2Opt,    m_pedOpt,    false).isFailure();
+  empty &= storeRawChannels(m_qieRawChannelContainer,    m_eQIE,     m_tQIE,     m_chi2QIE,    m_pedQIE,    false).isFailure();
+  empty &= storeRawChannels(m_of1RawChannelContainer,    m_eOF1,     m_tOF1,     m_chi2OF1,    m_pedOF1,    false).isFailure();
+  empty &= storeRawChannels(m_wienerRawChannelContainer, m_eWiener,  m_tWiener,  m_chi2Wiener, m_pedWiener, false).isFailure();
   
   // store TMDB data
   //
@@ -2061,6 +2067,13 @@ void TileAANtuple::DIGI_addBranch(void)
       m_ntuplePtr->Branch(NAME2("chi2MF",f_suf), m_chi2MF[ir],     NAME3("chi2MF",f_suf,"[4][64][48]/F")); // float
       m_ntuplePtr->Branch(NAME2("pedMF",f_suf),  m_pedMF[ir],      NAME3("pedMF",f_suf,"[4][64][48]/F")); // float
     }
+
+    if (m_wienerRawChannelContainer.size() > 0) {
+      m_ntuplePtr->Branch(NAME2("eWiener",f_suf),    m_eWiener[ir],        NAME3("eWiener",f_suf,"[4][64][48]/F")); // float
+      m_ntuplePtr->Branch(NAME2("tWiener",f_suf),    m_tWiener[ir],        NAME3("tWiener",f_suf,"[4][64][48]/F")); // float
+      m_ntuplePtr->Branch(NAME2("pedWiener",f_suf),  m_pedWiener[ir],    NAME3("pedWiener",f_suf,"[4][64][48]/F")); // float
+      m_ntuplePtr->Branch(NAME2("chi2Wiener",f_suf), m_chi2Wiener[ir],  NAME3("chi2Wiener",f_suf,"[4][64][48]/F")); // float
+    }
     
     if (m_bsInput) {
       if (i == imin) { // common for low and high gain
@@ -2190,6 +2203,13 @@ void TileAANtuple::DIGI_clearBranch(void) {
     CLEAR2(m_chi2MF, size);
     CLEAR2(m_pedMF, size);
   }
+
+  if (m_wienerRawChannelContainer.size() > 0) {
+    CLEAR2(m_eWiener, size);
+    CLEAR2(m_tWiener, size);
+    CLEAR2(m_pedWiener, size);
+    CLEAR2(m_chi2Wiener, size);
+  }
   
   if (m_bsInput) {
     CLEAR1(m_ROD_GlobalCRC);
diff --git a/TileCalorimeter/TileRecUtils/CMakeLists.txt b/TileCalorimeter/TileRecUtils/CMakeLists.txt
index 35b8885701ce5f90f96fd5bd3500abec76f49911..19bd47164c8b64bb4cd9f14fc2d170327ccd376c 100644
--- a/TileCalorimeter/TileRecUtils/CMakeLists.txt
+++ b/TileCalorimeter/TileRecUtils/CMakeLists.txt
@@ -28,7 +28,8 @@ atlas_depends_on_subdirs( PUBLIC
                           Event/xAOD/xAODEventInfo
                           TileCalorimeter/TileCalib/TileCalibBlobObjs
                           TileCalorimeter/TileDetDescr
-                          Tools/PathResolver )
+                          Tools/PathResolver
+                          Trigger/TrigAnalysis/TrigAnalysisInterfaces )
 
 # External dependencies:
 find_package( Boost COMPONENTS filesystem thread system )
diff --git a/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderWienerFilter.h b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderWienerFilter.h
new file mode 100644
index 0000000000000000000000000000000000000000..137ca4819d571b3469007f9e19de2d1127efcd16
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/TileRecUtils/TileRawChannelBuilderWienerFilter.h
@@ -0,0 +1,112 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef TILERECUTILS_TILERAWCHANNELBUILDERWIENERFILTER_H
+#define TILERECUTILS_TILERAWCHANNELBUILDERWIENERFILTER_H
+
+//////////////////////////////////////////////////////////////////////
+//
+//     AUTHOR :  G. Goncalves <ginaciog@cern.ch>
+//     CREATED:  May 2019
+//
+//     TileRawChannelBuilderWienerFilter.h
+//
+//     implementation of the Wiener Filter for
+//          energy/time reconstruction in TileCal 
+//
+//////////////////////////////////////////////////////////////////////
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilder.h"
+#include "TileConditions/ITileCondToolOfc.h"
+#include "TileConditions/TileCondToolTiming.h"
+#include "TileConditions/TileCondToolNoiseSample.h"
+#include "TrigAnalysisInterfaces/IBunchCrossingTool.h"
+
+#include <vector>
+#include <string>
+
+/**
+ *
+ * @class TileRawChannelBuilderWienerFilter
+ * @brief Reconstructs Tile digitized pulses (ie, computes amplitude, time and pedestal) as a linear combination of the samples
+ *
+ * This class implements an energy reconstruction method known as Wiener
+ * Filter. It takes as input the digital samples from the digitizer boards
+ * in the front-end electronics and outputs the amplitude, time and pedestal
+ * of the pulse. Full details and fundaments of the method can be found in
+ * the ATL-TILECAL-PROC-2019-002 note. Two different versions of the algorithms
+ * are currently used: Wiener Optimal (with uses seven sets of weights, optimized
+ * for the first three, the middle and the last three BCIDs in the train) and
+ * Wiener General (one set of weights for all BCIDs).
+ */
+class TileRawChannelBuilderWienerFilter: public TileRawChannelBuilder {
+  public:
+
+    TileRawChannelBuilderWienerFilter(const std::string& type, const std::string& name,
+        const IInterface *parent); //!< Constructor
+    ~TileRawChannelBuilderWienerFilter(); //!< Destructor
+
+    // virtual methods
+    virtual StatusCode initialize(); //!< Initialize method
+    //virtual StatusCode execute();
+    virtual StatusCode finalize(); //!< Finalize method
+
+    // Inherited from TileRawChannelBuilder
+    virtual TileRawChannel* rawChannel(const TileDigits* digits);
+
+    /**
+     * AlgTool InterfaceID
+     */
+    static const InterfaceID& interfaceID();
+
+  private:
+
+    //!< Callback added to handle Data-driven GeoModel initialization
+    virtual StatusCode geoInit(IOVSVC_CALLBACK_ARGS);
+
+    ToolHandle<TileCondToolTiming> m_tileToolTiming;
+    ToolHandle<ITileCondToolOfc> m_tileCondToolOfc;
+    ToolHandle<TileCondToolNoiseSample> m_tileToolNoiseSample; //!< tool which provides noise values
+    ToolHandle<Trig::IBunchCrossingTool> m_bunchCrossingTool;
+
+    //!< Applies OF algorithm
+    double filter(int ros, int drawer, int channel, int &gain, double &pedestal, double &amplitude, double &time);
+    int findMaxDigitPosition();  //!< Finds maximum digit position in the pulse
+    //!< Gets pedestal estimation for OF1
+    float getPedestal(int ros, int drawer, int channel, int gain);
+    //!< Computes A,time,ped using OF. If iterations are required, the Iterator method is used
+    double compute(int ros, int drawer, int channel, int gain, double &pedestal, double &amplitude, double &time, double& phase);
+    //!< Gets the BCID index within the train
+    int getBCIDIndex();
+
+    int m_maxIterations; //!< maximum number of iteration to perform
+    int m_pedestalMode;  //!< pedestal mode to use
+    bool m_confTB;       //!< use testbeam configuration
+    double m_timeForConvergence; //!< minimum time difference to quit iteration procedure
+    bool m_isMC;    //!< bool variable for MC: true=> MC;  false=> data
+    bool m_minus1Iter;   //!< bool variable for whether to apply -1 iteration (initial phase guess)
+    bool m_correctAmplitude; //!< If true, resulting amplitude is corrected when using weights for tau=0 without iteration
+    bool m_correctTimeNI; //!< If true, resulting time is corrected when using method  without iteration
+
+    bool m_bestPhase; // if true, use best phase from COOL DB in "fixed phase" mode (i.e., no iterations)
+    bool m_emulateDsp; // if true, emulate DSP reconstruction algorithm
+    int m_nSignal; //!< internal counters
+    int m_nNegative;  //!< internal counters
+    int m_nCenter; //!< internal counters
+    int m_nConst;  //!< internal counters
+
+    int m_nSamples; //!< number of samples in the data
+    int m_t0SamplePosition;  //!< position of peak sample = (m_nSamples-1)/2
+    double m_maxTime; //!< max allowed time = 25*(m_nSamples-1)/2
+    double m_minTime; //!< min allowed time = -25*(m_nSamples-1)/2
+
+
+    std::vector<float> m_digits;
+
+    static const float m_gfcWiener[8]; //!< Wiener General weights
+    static const float m_ofcWiener[7][8]; //!< Wiener Optimal weights
+};
+
+#endif
diff --git a/TileCalorimeter/TileRecUtils/python/TileRawChannelGetter.py b/TileCalorimeter/TileRecUtils/python/TileRawChannelGetter.py
index 8480a5932087d5c86a97578add132052d201cc79..c43300f2df6e44f3212c8fd3e1614a4b69fe8933 100644
--- a/TileCalorimeter/TileRecUtils/python/TileRawChannelGetter.py
+++ b/TileCalorimeter/TileRecUtils/python/TileRawChannelGetter.py
@@ -143,6 +143,7 @@ class TileRawChannelGetter ( Configured)  :
             if (jobproperties.TileRecFlags.doTileMF()
                 or (not jobproperties.TileRecFlags.OfcFromCOOL()
                     and (jobproperties.TileRecFlags.doTileOF1()
+                         or jobproperties.TileRecFlags.doTileWiener()
                          or jobproperties.TileRecFlags.doTileOpt2()
                          or jobproperties.TileRecFlags.doTileOptATLAS()))):
 
@@ -154,6 +155,7 @@ class TileRawChannelGetter ( Configured)  :
 
             if jobproperties.TileRecFlags.OfcFromCOOL():
                 if (jobproperties.TileRecFlags.doTileMF()
+                    or jobproperties.TileRecFlags.doTileWiener()
                     or jobproperties.TileRecFlags.doTileOpt2()
                     or jobproperties.TileRecFlags.doTileOptATLAS()):
 
@@ -488,6 +490,39 @@ class TileRawChannelGetter ( Configured)  :
                 ToolSvc += theTileRawChannelBuilderOptATLAS
                 
                 theTileRawChannelMaker.TileRawChannelBuilder += [ToolSvc.TileRawChannelBuilderOptATLAS]
+
+            if jobproperties.TileRecFlags.doTileWiener():
+                try:
+                    from TileRecUtils.TileRecUtilsConf import TileRawChannelBuilderWienerFilter
+                    theTileRawChannelBuilderWienerFilter= TileRawChannelBuilderWienerFilter()
+                except:
+                    mlog.error("could not get handle to TileRawChannelBuilderWienerFilter Quit")
+                    print traceback.format_exc()
+                    return False
+                
+                # setup COOL to get OFCs
+                if jobproperties.TileRecFlags.OfcFromCOOL():
+                    theTileRawChannelBuilderWienerFilter.TileCondToolOfc = ToolSvc.TileCondToolOfcCool
+                    
+                #TileRawChannelBuilderWienerFilter Options:
+                jobproperties.TileRecFlags.TileRawChannelContainer           = "TileRawChannelWiener"
+                theTileRawChannelBuilderWienerFilter.TileRawChannelContainer = "TileRawChannelWiener"
+                theTileRawChannelBuilderWienerFilter.RunType                 = jobproperties.TileRecFlags.TileRunType()
+                theTileRawChannelBuilderWienerFilter.calibrateEnergy         = jobproperties.TileRecFlags.calibrateEnergy()
+                theTileRawChannelBuilderWienerFilter.correctTime             = jobproperties.TileRecFlags.correctTime()
+                theTileRawChannelBuilderWienerFilter.NoiseFilterTools        = NoiseFilterTools
+                theTileRawChannelBuilderWienerFilter.BestPhase               = False; # no point to use best phase with interations
+                theTileRawChannelBuilderWienerFilter.MC                      = False # use globalflags.DataSource()!='data'
+                theTileRawChannelBuilderWienerFilter.PedestalMode            = 1
+                theTileRawChannelBuilderWienerFilter.MaxIterations           = 5
+                theTileRawChannelBuilderWienerFilter.Minus1Iteration         = True
+                theTileRawChannelBuilderWienerFilter.AmplitudeCorrection     = False; # don't need correction after iterations
+                theTileRawChannelBuilderWienerFilter.TimeCorrection          = False; # don't need correction after iterations
+      
+                mlog.info(" adding now TileRawChannelBuilderWienerFilter to ToolSvc")   
+                ToolSvc += theTileRawChannelBuilderWienerFilter
+      
+                theTileRawChannelMaker.TileRawChannelBuilder += [ToolSvc.TileRawChannelBuilderWienerFilter]
             
 
             # now add algorithm to topSequence
@@ -527,6 +562,7 @@ class TileRawChannelGetter ( Configured)  :
             jobproperties.TileRecFlags.doTileManyAmps = False
             jobproperties.TileRecFlags.doTileMF = False
             jobproperties.TileRecFlags.doTileOF1 = False
+            jobproperties.TileRecFlags.doTileWiener = False
             jobproperties.TileRecFlags.OfcFromCOOL = False
             jobproperties.TileRecFlags.print_JobProperties('tree&value')
 
diff --git a/TileCalorimeter/TileRecUtils/python/TileRawChannelGetter_DigiHSTruth.py b/TileCalorimeter/TileRecUtils/python/TileRawChannelGetter_DigiHSTruth.py
index 642c2c572083ef6c85af958c95173a6989305cdd..1e4e960a1ae929824d68f29ab93b66b920197774 100644
--- a/TileCalorimeter/TileRecUtils/python/TileRawChannelGetter_DigiHSTruth.py
+++ b/TileCalorimeter/TileRecUtils/python/TileRawChannelGetter_DigiHSTruth.py
@@ -177,6 +177,7 @@ class TileRawChannelGetter_DigiHSTruth ( Configured)  :
             jobproperties.TileRecFlags.doTileManyAmps = False
             jobproperties.TileRecFlags.doTileMF = False
             jobproperties.TileRecFlags.doTileOF1 = False
+            jobproperties.TileRecFlags.doTileWiener = False
             jobproperties.TileRecFlags.OfcFromCOOL = False
             jobproperties.TileRecFlags.print_JobProperties('tree&value')
 
diff --git a/TileCalorimeter/TileRecUtils/python/TileRecFlags.py b/TileCalorimeter/TileRecUtils/python/TileRecFlags.py
index 8f085d31df1447807431bdca5b0b96f04e05eb69..670eda6eff58461bc2b06f9b7746186c90c236a8 100644
--- a/TileCalorimeter/TileRecUtils/python/TileRecFlags.py
+++ b/TileCalorimeter/TileRecUtils/python/TileRecFlags.py
@@ -96,6 +96,14 @@ class doTileMF(JobProperty):
     allowedTypes = ['bool']
     StoredValue  = False
 
+#
+class doTileWiener(JobProperty):
+    """ Use Wiener filter method for energy reconstruction
+    """
+    statusOn     = True
+    allowedTypes = ['bool']
+    StoredValue  = False
+
 #
 class noiseFilter(JobProperty):
     """ appply one or another noise suppression algorithm at the level of raw channels
@@ -266,6 +274,7 @@ list_jobproperties = [
     doTileManyAmps,
     doTileMF,
     doTileOptATLAS,
+    doTileWiener,
     TileRawChannelContainer,
     TileDigitsContainer,
     TileRunType,
diff --git a/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderWienerFilter.cxx b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderWienerFilter.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..a2c8454364fc84d2c1e9b660445630ddc32ae231
--- /dev/null
+++ b/TileCalorimeter/TileRecUtils/src/TileRawChannelBuilderWienerFilter.cxx
@@ -0,0 +1,472 @@
+/*
+  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "TileEvent/TileRawChannel.h"
+#include "TileCalibBlobObjs/TileCalibUtils.h"
+
+// Gaudi includes
+#include "GaudiKernel/Property.h"
+#include "GeoModelInterfaces/IGeoModelSvc.h"
+
+// Atlas includes
+#include "AthAllocators/DataPool.h"
+#include "AthenaKernel/errorcheck.h"
+#include "xAODEventInfo/EventInfo.h"
+
+// Tile includes
+#include "TileRecUtils/TileRawChannelBuilderWienerFilter.h"
+#include "TileConditions/TilePulseShapes.h"
+#include "CLHEP/Matrix/Matrix.h"
+#include "TileEvent/TileRawChannelContainer.h"
+#include "TileEvent/TileDigitsContainer.h"
+#include "TileEvent/TileDigits.h"
+#include "CaloIdentifier/TileID.h"
+#include "TileIdentifier/TileHWID.h"
+#include "TileConditions/TileInfo.h"
+
+//using namespace std;
+#include <algorithm>
+
+//interface stuff
+static const InterfaceID IID_ITileRawChannelBuilderWienerFilter("TileRawChannelBuilderWienerFilter", 1, 0);
+
+
+const InterfaceID& TileRawChannelBuilderWienerFilter::interfaceID() {
+  return IID_ITileRawChannelBuilderWienerFilter;
+}
+
+
+#define TILE_WienerFilterBUILDERVERBOSE false
+
+// initialize Wiener coefficients ( ZeroBias <mu>=40 )
+const float TileRawChannelBuilderWienerFilter::m_gfcWiener[8] = {
+  -0.194081,
+  0.467562,
+  -1.25846,
+  2.18528,
+  -1.2648,
+  0.63341,
+  -0.276375,
+  -14.2093
+};
+const float TileRawChannelBuilderWienerFilter::m_ofcWiener[7][8] = {
+  {0.04382, -0.999, 0.5212, 1.179, -0.8052, 0.4123, -0.1756, -5.393},
+  {0.1806, 0.02539, -1.253, 2.187, -1.254, 0.6214, -0.2732, -14.53},
+  {-0.3267, 0.6014, -1.346, 2.224, -1.258, 0.6043, -0.276, -14.41},
+  {-0.1914, 0.4693, -1.246, 2.169, -1.243, 0.6191, -0.2633, -15.15},
+  {-0.1831, 0.4734, -1.253, 2.241, -1.424, 0.9173, -0.5364, -15.43},
+  {-0.2245, 0.5627, -1.398, 2.433, -1.722, 1.204, -0.5617, -15.26},
+  {-0.2367, 0.617, -1.502, 2.539, -1.729, 0.7092, -0.1435, -14.15}
+};
+
+TileRawChannelBuilderWienerFilter::TileRawChannelBuilderWienerFilter(const std::string& type,
+    const std::string& name, const IInterface *parent)
+  : TileRawChannelBuilder(type, name, parent)
+  , m_tileToolTiming("TileCondToolTiming")
+  , m_tileCondToolOfc("TileCondToolOfc")
+  , m_tileToolNoiseSample("TileCondToolNoiseSample")
+  , m_bunchCrossingTool("BunchCrossingTool")
+  , m_nSignal(0)
+  , m_nNegative(0)
+  , m_nCenter(0)
+  , m_nConst(0)
+  , m_nSamples(0)
+  , m_t0SamplePosition(0)
+  , m_maxTime(0.0)
+  , m_minTime(0.0)
+{
+  //declare interfaces
+  declareInterface< TileRawChannelBuilder >( this );
+  declareInterface< TileRawChannelBuilderWienerFilter >(this);
+
+  m_TileRawChannelContainerID = "TileRawChannelWiener";
+  
+  //declare properties
+  declareProperty("TileCondToolTiming", m_tileToolTiming);
+  declareProperty("TileCondToolOfc",    m_tileCondToolOfc  ,"TileCondToolOfc");
+  declareProperty("TileCondToolNoiseSample", m_tileToolNoiseSample,"TileCondToolNoiseSample");
+  declareProperty("BunchCrossingTool", m_bunchCrossingTool);
+  declareProperty("MaxIterations",m_maxIterations = 5);
+  declareProperty("PedestalMode",m_pedestalMode = 17);
+  declareProperty("TimeForConvergence",m_timeForConvergence = 0.5);
+  declareProperty("ConfTB",m_confTB = false);
+  declareProperty("Minus1Iteration",m_minus1Iter = false);
+  declareProperty("AmplitudeCorrection",m_correctAmplitude = false);
+  declareProperty("TimeCorrection", m_correctTimeNI = false);
+  declareProperty("BestPhase",m_bestPhase = false);
+  declareProperty("EmulateDSP",m_emulateDsp = false);
+  declareProperty("MC",m_isMC = false);
+}
+
+
+TileRawChannelBuilderWienerFilter::~TileRawChannelBuilderWienerFilter() {
+}
+
+
+StatusCode TileRawChannelBuilderWienerFilter::initialize() {
+
+  ATH_MSG_INFO( "initialize()" );
+
+  m_rChType = TileFragHash::WienerFilterOffline; // type for offline Wiener Filter
+
+  // init in superclass
+  CHECK( TileRawChannelBuilder::initialize() );
+
+  if (m_maxIterations != 1) m_correctTimeNI = false;
+
+  // bits 12-15 - various options
+  // if (m_correctTimeNI)  m_bsflags |= 0x1000;
+  if (m_correctAmplitude) m_bsflags |= 0x2000;
+  if (m_maxIterations > 1) m_bsflags |= 0x4000;
+  if (m_bestPhase) m_bsflags |= 0x8000;
+
+  ATH_MSG_DEBUG( " MaxIterations=" << m_maxIterations
+              << " PedestalMode=" << m_pedestalMode
+              << " TimeForConvergence=" << m_timeForConvergence
+              << " ConfTB=" << m_confTB
+              << " Minus1Iteration=" << m_minus1Iter
+              << " AmplitudeCorrection=" << m_correctAmplitude
+              << " TimeCorrection=" << m_correctTimeNI
+              << " Best Phase " << m_bestPhase );
+
+  m_nSamples = m_tileInfo->NdigitSamples();
+  m_t0SamplePosition = m_tileInfo->ItrigSample();
+  m_maxTime = 25 * (m_nSamples - m_t0SamplePosition - 1);
+  m_minTime = -25 * m_t0SamplePosition;
+  ATH_MSG_DEBUG(" NSamples=" << m_nSamples
+             << " T0Sample=" << m_t0SamplePosition
+             << " minTime=" << m_minTime
+             << " maxTime=" << m_maxTime );
+
+  if (m_pedestalMode % 10 > 2 && m_nSamples != m_pedestalMode % 10) {
+    if (msgLvl(MSG::DEBUG)) msg(MSG::DEBUG) << "Changing PedestalMode from " << m_pedestalMode;
+    m_pedestalMode = (m_pedestalMode / 10) * 10 + m_nSamples;
+    if (msgLvl(MSG::DEBUG)) msg(MSG::DEBUG) << " to " << m_pedestalMode << endmsg;
+  }
+
+
+  if (m_nSamples != 7 && (m_pedestalMode == 71 || m_pedestalMode == 7621)) {
+    ATH_MSG_ERROR( "Incompatable pedestal mode [" << m_pedestalMode
+                   << "] and number of samples [" << m_nSamples << "]" );
+    return StatusCode::FAILURE;
+  }
+
+  m_nSignal = 0;
+  m_nNegative = 0;
+  m_nCenter = 0;
+  m_nConst = 0;
+
+  const IGeoModelSvc *geoModel = 0;
+  CHECK( service("GeoModelSvc", geoModel) );
+  
+  // dummy parameters for the callback:
+  int dummyInt = 0;
+  std::list<std::string> dummyList;
+  
+  if (geoModel->geoInitialized()) {
+    return geoInit(dummyInt, dummyList);
+  } else {
+    CHECK( detStore()->regFcn(&IGeoModelSvc::geoInit, geoModel,
+        &TileRawChannelBuilderWienerFilter::geoInit, this) );
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TileRawChannelBuilderWienerFilter::geoInit(IOVSVC_CALLBACK_ARGS) {
+  
+  //=== get TileCondToolOfc
+  CHECK( m_tileCondToolOfc.retrieve() );
+  
+  //=== get TileCondToolNoiseSample
+  CHECK( m_tileToolNoiseSample.retrieve() );
+
+  if (m_bestPhase) {
+    //=== get TileToolTiming
+    CHECK( m_tileToolTiming.retrieve() );
+  }
+
+  if (m_isMC) {
+      m_bunchCrossingTool.setTypeAndName("Trig::MCBunchCrossingTool/BunchCrossingTool");
+  } else { // is data
+      m_bunchCrossingTool.setTypeAndName("Trig::LHCBunchCrossingTool/BunchCrossingTool");
+  }
+  //=== get TrigBunchCrossingTool
+  CHECK( m_bunchCrossingTool.retrieve() );
+  
+  ATH_MSG_INFO( "initialization completed" );
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TileRawChannelBuilderWienerFilter::finalize() {
+
+  if (msgLvl(MSG::VERBOSE)) {
+    if (m_maxIterations == 1) { // Without iterations
+      msg(MSG::VERBOSE) << "Counters: Signal=" << m_nSignal
+                        << " Constant=" << m_nConst
+                        << " Total=" << m_nSignal + m_nConst << endmsg;
+    } else {
+      msg(MSG::VERBOSE) << "Counters: Signal=" << m_nSignal
+                        << " Negat=" << m_nNegative
+                        << " Center=" << m_nCenter
+                        << " Constant=" << m_nConst
+                        << " Total=" << m_nSignal + m_nNegative + m_nConst + m_nCenter << endmsg;
+    }
+  }
+
+  ATH_MSG_DEBUG( "Finalizing" );
+
+  return StatusCode::SUCCESS;
+}
+
+
+TileRawChannel * TileRawChannelBuilderWienerFilter::rawChannel(const TileDigits* digits) {
+
+  ++m_chCounter;
+
+  double pedestal = 0.;
+  double energy = 0.;
+  double time = 0.;
+  double chi2 = 0.;
+  m_digits = digits->samples();
+  const HWIdentifier adcId = digits->adc_HWID();
+  int gain = m_tileHWID->adc(adcId);
+  
+  ATH_MSG_VERBOSE( "Building Raw Channel, with WienerFilter, HWID:" << m_tileHWID->to_string(adcId)
+                << " gain=" << gain );
+
+  int ros = m_tileHWID->ros(adcId);
+  int drawer = m_tileHWID->drawer(adcId);
+  int channel = m_tileHWID->channel(adcId);
+  chi2 = filter(ros, drawer, channel, gain, pedestal, energy, time);
+  
+  if (m_calibrateEnergy) {
+    energy = m_tileInfo->CisCalib(adcId, energy);
+  }
+  
+  if (msgLvl(MSG::VERBOSE)) {
+    msg(MSG::VERBOSE) << "Creating WienerFilter RawChannel"
+                      << " a=" << energy
+                      << " t=" << time
+                      << " ped=" << pedestal
+                      << " q=" << chi2 << endmsg;
+
+    msg(MSG::VERBOSE) << "digits:";
+
+    for (unsigned int i = 0; i < m_digits.size(); ++i)
+      msg(MSG::VERBOSE) << " " << m_digits[i];
+
+    msg(MSG::VERBOSE) << " " << endmsg;
+  }
+  
+  // return new TileRawChannel
+  DataPool<TileRawChannel> tileRchPool(m_dataPoollSize);
+  TileRawChannel *rawCh = tileRchPool.nextElementPtr();
+  rawCh->assign (adcId,
+                 energy,
+                 time,
+                 chi2,
+                 pedestal);
+
+  if (m_correctTime
+      && (time != 0
+          && time < m_maxTime
+          && time > m_minTime)) {
+
+    rawCh->insertTime(m_tileInfo->TimeCalib(adcId, time));
+    ATH_MSG_VERBOSE( "Correcting time, new time=" << rawCh->time() );
+
+  }
+
+  if (TileID::HIGHGAIN == gain) {
+    ++m_nChH;
+    m_RChSumH += energy;
+  } else {
+    ++m_nChL;
+    m_RChSumL += energy;
+  }
+  
+  return rawCh;
+}
+
+
+int TileRawChannelBuilderWienerFilter::findMaxDigitPosition() {
+
+  ATH_MSG_VERBOSE( "  findMaxDigitPosition()" );
+
+  int iMaxDigit = 0;
+  float maxDigit = 0.;
+  bool saturated = false;
+  
+  for (unsigned int i = 0; i < m_digits.size(); i++) {
+    if (m_digits[i] > 1022.99) saturated = true;
+    if (maxDigit < m_digits[i]) {
+      maxDigit = m_digits[i];
+      iMaxDigit = i;
+    }
+  }
+  
+  if (msgLvl(MSG::VERBOSE)) {
+    for (unsigned int i = 0; i < m_digits.size(); i++) {
+      msg(MSG::VERBOSE) << " " << m_digits[i];
+    }
+
+    msg(MSG::VERBOSE) << "; Max: digit[" << iMaxDigit << "]=" << maxDigit << endmsg;
+
+    if (saturated)  msg(MSG::VERBOSE) << " Samples saturated" << endmsg;
+  }
+  
+  return iMaxDigit;
+}
+
+
+float TileRawChannelBuilderWienerFilter::getPedestal(int ros, int drawer, int channel, int gain) {
+  float pedestal = 0.;
+  
+  switch (m_pedestalMode) {
+    case -1:
+      // use pedestal from conditions DB
+      pedestal = m_tileToolNoiseSample->getPed(TileCalibUtils::getDrawerIdx(ros, drawer), channel, gain);
+      break;
+    case 7:
+      pedestal = m_digits[6];
+      break;
+    case 9:
+      pedestal = m_digits[8];
+      break;
+    case 12:
+      pedestal = .5 * (m_digits[0] + m_digits[1]);
+      break;
+    case 17:
+      pedestal = .5 * (m_digits[0] + m_digits[6]);
+      break;
+    case 19:
+      pedestal = .5 * (m_digits[0] + m_digits[8]);
+      break;
+    case 71:
+      pedestal = std::min(m_digits[0], m_digits[6]);
+      break;
+    case 7621:
+      pedestal = 0.5 * std::min(m_digits[0] + m_digits[1], m_digits[5] + m_digits[6]); 
+      break;
+    default:
+      pedestal = m_digits[0];
+      break;
+  }
+
+  ATH_MSG_VERBOSE("getPedestal(): pedestal=" << pedestal);
+  
+  return pedestal;
+}
+
+int TileRawChannelBuilderWienerFilter::getBCIDIndex() {
+  int bcidIndex = -1;
+
+  const xAOD::EventInfo* eventInfo(0);
+
+  if (evtStore()->retrieve(eventInfo).isSuccess()) {
+    int bcid      = eventInfo->bcid();
+    int distFront = m_bunchCrossingTool->distanceFromFront(bcid, Trig::IBunchCrossingTool::BunchCrossings);
+    int distTail  = m_bunchCrossingTool->distanceFromTail(bcid, Trig::IBunchCrossingTool::BunchCrossings);
+
+    ATH_MSG_VERBOSE( "EventInfo loaded: "
+                  << " BCID=" << bcid
+                  << " DistFront=" << distFront
+                  << " DistTail=" << distTail);
+
+    if (distFront == -1 || distTail == -1) {
+      bcidIndex = -1;
+    } else if (distFront < 3 && distTail > distFront) {
+      bcidIndex = distFront;
+    } else if (distTail < 3 && distTail < distFront) {
+      bcidIndex = 6 - distTail;
+    } else {
+      bcidIndex = 3;
+    }
+
+  } else {
+    ATH_MSG_VERBOSE("EventInfo not available");
+  }
+
+  ATH_MSG_VERBOSE("getBCIDIndex(): bcidIndex=" << bcidIndex);
+
+  return bcidIndex;
+}
+
+
+double TileRawChannelBuilderWienerFilter::filter(int ros, int drawer, int channel
+    , int &gain, double &pedestal, double &amplitude, double &time) {
+
+  ATH_MSG_VERBOSE( "filter()" );
+
+  amplitude = 0.;
+  time = 0.;
+  double chi2 = 0.;
+
+  auto minMaxDigits = std::minmax_element(m_digits.begin(), m_digits.end());
+  float minDigit = *minMaxDigits.first;
+  float maxDigit = *minMaxDigits.second;
+
+  if (maxDigit - minDigit < 0.01) { // constant value in all samples
+
+    pedestal = minDigit;
+    chi2 = 0.;
+    ATH_MSG_VERBOSE( "CASE NO SIGNAL: maxdig-mindig = " << maxDigit << "-" << minDigit
+        << " = " << maxDigit - minDigit );
+
+    m_nConst++;
+
+  } else {
+    float weights[8];
+    memset(weights, 0, sizeof(weights));
+
+    int bcidIndex = getBCIDIndex();
+
+    if (ros > 1 && channel == 1 && bcidIndex >= 0) {
+      ATH_MSG_VERBOSE( "Switch to optimal mode");
+      for (int wi = 0; wi < 8; wi++) {
+        weights[wi] = m_ofcWiener[bcidIndex][wi];
+      }
+    } else {
+      ATH_MSG_VERBOSE( "Switch to general mode");
+      for (int wi = 0; wi < 8; wi++) {
+        weights[wi] = m_gfcWiener[wi];
+      }
+    }
+
+    for (unsigned int i = 0; i < m_digits.size(); i++) {
+      amplitude += weights[i] * m_digits[i]; // Wiener coefs
+    }
+    amplitude += weights[7]; // Wiener bias
+
+    double phase = 0.;
+    pedestal = getPedestal(ros, drawer, channel, gain);
+
+    chi2 = compute(ros, drawer, channel, gain, pedestal, amplitude, time, phase);
+
+    m_nSignal++;
+  }
+
+  return chi2;
+}
+
+double TileRawChannelBuilderWienerFilter::compute(int ros, int drawer, int channel, int gain,
+    double &pedestal, double &amplitude, double &time, double& phase) {
+
+ ATH_MSG_VERBOSE( "compute();"
+               << " ros=" << ros
+               << " drawer="  << drawer
+               << " channel=" << channel
+               << " gain=" << gain
+               << " pedestal=" << pedestal
+               << " amplitude=" << amplitude
+               << " time=" << time
+               << " phase=" << phase );
+
+  double chi2 = 0.;
+  return chi2;
+}
diff --git a/TileCalorimeter/TileRecUtils/src/components/TileRecUtils_entries.cxx b/TileCalorimeter/TileRecUtils/src/components/TileRecUtils_entries.cxx
index 4e4ab60d53cf894d7a511a2acbd753a975aab28e..dcb7011dc3280e1cf4868d856c9fc69866af469c 100644
--- a/TileCalorimeter/TileRecUtils/src/components/TileRecUtils_entries.cxx
+++ b/TileCalorimeter/TileRecUtils/src/components/TileRecUtils_entries.cxx
@@ -8,6 +8,7 @@
 #include "TileRecUtils/TileRawChannelBuilderQIEFilter.h"
 #include "TileRecUtils/TileRawChannelBuilderManyAmps.h"
 #include "TileRecUtils/TileRawChannelBuilderMF.h"
+#include "TileRecUtils/TileRawChannelBuilderWienerFilter.h"
 #include "TileRecUtils/TileBeamInfoProvider.h"
 #include "TileRecUtils/TileCellBuilder.h"
 #include "TileRecUtils/TileCellFakeProb.h"
@@ -28,6 +29,7 @@ DECLARE_TOOL_FACTORY( TileRawChannelBuilderOpt2Filter )
 DECLARE_TOOL_FACTORY( TileRawChannelBuilderQIEFilter )
 DECLARE_TOOL_FACTORY( TileRawChannelBuilderManyAmps )
 DECLARE_TOOL_FACTORY( TileRawChannelBuilderMF )
+DECLARE_TOOL_FACTORY( TileRawChannelBuilderWienerFilter )
 DECLARE_TOOL_FACTORY( TileBeamInfoProvider )
 DECLARE_TOOL_FACTORY( TileCellBuilder )
 DECLARE_TOOL_FACTORY( TileCellFakeProb )
@@ -49,6 +51,7 @@ DECLARE_FACTORY_ENTRIES(TileRecUtils) {
   DECLARE_TOOL( TileRawChannelBuilderQIEFilter )
   DECLARE_TOOL( TileRawChannelBuilderManyAmps )
   DECLARE_TOOL( TileRawChannelBuilderMF )
+  DECLARE_TOOL( TileRawChannelBuilderWienerFilter )
   DECLARE_TOOL( TileBeamInfoProvider )
   DECLARE_TOOL( TileCellBuilder )
   DECLARE_TOOL( TileCellFakeProb )
diff --git a/Tools/PyJobTransforms/python/trfArgClasses.py b/Tools/PyJobTransforms/python/trfArgClasses.py
index ae5c8a52323f5557cd4257748969a5c24c1dc00f..b5a93129daf02aaf40e5186325be15fea219d8f2 100644
--- a/Tools/PyJobTransforms/python/trfArgClasses.py
+++ b/Tools/PyJobTransforms/python/trfArgClasses.py
@@ -695,6 +695,9 @@ class argFile(argList):
                 msg.debug('Found root filesystem input - activating globbing')
                 newValue = []
                 for filename in self._value:
+                    if str(filename).startswith("root"):
+                        msg.debug('Found input file name starting with "root," setting XRD_RUNFORKHANDLER=1, which enables fork handlers for xrootd in direct I/O')
+                        os.environ["XRD_RUNFORKHANDLER"] = "1"
                     if str(filename).startswith("https") or str(filename).startswith("davs") or not(str(filename).endswith('/')) and '*' not in filename and '?' not in filename:
                         msg.debug('Seems that only one file was given: {0}'.format(filename))
                         newValue.extend(([filename]))
diff --git a/Tracking/TrkVertexFitter/TrkVKalVrtFitter/src/TrkVKalVrtFitter.cxx b/Tracking/TrkVertexFitter/TrkVKalVrtFitter/src/TrkVKalVrtFitter.cxx
index dd185760f92f84319fad4709a2a6928d30f4d906..d278059f001dee888c87f97f7a8090d494cea784 100755
--- a/Tracking/TrkVertexFitter/TrkVKalVrtFitter/src/TrkVKalVrtFitter.cxx
+++ b/Tracking/TrkVertexFitter/TrkVKalVrtFitter/src/TrkVKalVrtFitter.cxx
@@ -53,7 +53,8 @@ TrkVKalVrtFitter:: TrkVKalVrtFitter(const std::string& type,
     m_useZPointingCnst(false),
     m_usePassNear(false),
     m_usePassWithTrkErr(false),
-    m_useMagFieldRotation(false)
+    m_useMagFieldRotation(false),
+    m_ErrMtx(nullptr)
    {
     declareInterface<IVertexFitter>(this);
     declareInterface<ITrkVKalVrtFitter>(this);