From 1067d40284e2dd1e482b5218ed74cd233144adda Mon Sep 17 00:00:00 2001
From: Dian YU <yudian2002@sjtu.edu.cn>
Date: Wed, 14 Aug 2024 14:52:57 +0800
Subject: [PATCH] conflicts eliminated

---
 .gitignore                                    |    2 +-
 Detector/DetCEPCv4/CMakeLists.txt             |    1 +
 .../src/calorimeter/SHcalSc04_Barrel_v04.cpp  | 1053 ++++----
 .../src/calorimeter/SHcalSc04_Endcaps_v02.cpp |  411 ++++
 Detector/DetCRD/CMakeLists.txt                |    1 +
 .../Ecal_Crystal_Barrel_Short_v02.xml         |   60 +
 .../Ecal_Crystal_Endcap_Short_v01.xml         |   68 +
 .../Ecal_Crystal_Endcap_v01_01.xml            |   49 +
 .../CRD_common_v01/OTKBarrel_v01_01.xml       |   91 +
 .../SHcalGlass_Barrel_v04_01.xml              |    2 +-
 .../CRD_common_v01/SHcalGlass_Barrel_v05.xml  |   65 +
 .../CRD_common_v01/SHcalGlass_Endcaps_v01.xml |   69 +
 .../SHcalSc04_Barrel_v04_01.xml               |   60 +-
 .../CRD_common_v01/SHcalSc04_Endcaps_v02.xml  |   69 +
 .../CRD_o1_v01/CRD_o1_v01-onlyTracker.xml     |    2 +-
 .../TDR_CaloTest/TDR_Dimensions_v01_01.xml    |  369 +++
 .../TDR_CaloTest/TDR_longEcal_GSHcal.xml      |   75 +
 .../compact/TDR_CaloTest/TDR_onlyGSHcal.xml   |   75 +
 .../compact/TDR_CaloTest/TDR_onlyPSHcal.xml   |   75 +
 .../TDR_CaloTest/TDR_onlyshortEcal.xml        |   75 +
 .../TDR_CaloTest/TDR_shortEcal_PSHcal.xml     |   75 +
 .../TDR_o1_v01/TDR_Dimensions_v01_01.xml      |    1 +
 .../DetCRD/compact/TDR_o1_v01/TDR_o1_v01.xml  |    2 +-
 .../DetCRD/scripts/TDR_o1_v01/calodigi.py     |  115 +
 .../DetCRD/scripts/TDR_o1_v01/tracking.py     |   27 +-
 .../Calorimeter/CRDEcal_Endcap_Short_v01.cpp  |  520 ++++
 .../src/Calorimeter/CRDEcal_Short_v02.cpp     |  374 +++
 .../LongCrystalBarEndcapCalorimeter_v01.cpp   |  464 ++++
 .../DetCRD/src/Muon/Muon_Barrel_v01_01.cpp    |    1 +
 .../DetCRD/src/Muon/Muon_Endcap_v01_01.cpp    |    2 +-
 .../SiTrackerStaggeredLadder_v01_geo.cpp      |    2 +-
 .../Tracker/SiTracker_otkbarrel_v01_geo.cpp   |  374 +++
 .../src/Tracker/TPC_ModularEndcap_o1_v01.cpp  |    5 +-
 .../DetCRD/src/Tracker/TPC_Simple_o1_v01.cpp  |    5 +-
 Digitisers/CMakeLists.txt                     |    1 +
 Digitisers/CaloDigi/CMakeLists.txt            |   22 +
 Digitisers/CaloDigi/src/CaloBar.h             |   54 +
 Digitisers/CaloDigi/src/CaloHit.h             |   50 +
 Digitisers/CaloDigi/src/EcalDigiAlg.cpp       |  625 +++++
 Digitisers/CaloDigi/src/EcalDigiAlg.h         |  132 +
 Digitisers/CaloDigi/src/HcalDigiAlg.cpp       |  252 ++
 Digitisers/CaloDigi/src/HcalDigiAlg.h         |  115 +
 Digitisers/CaloDigi/src/HitStep.h             |   26 +
 Digitisers/SimpleDigi/CMakeLists.txt          |    1 -
 Digitisers/SimpleDigi/src/PlanarDigiAlg.cpp   |    1 +
 Digitisers/SimpleDigi/src/PlanarDigiAlg.h     |    1 +
 Digitisers/SimpleDigi/src/TPCDigiAlg.cpp      |   91 +-
 Digitisers/SimpleDigi/src/TPCDigiAlg.h        |    2 +
 .../src/TPCPixelClusteringDigiAlg.cpp         | 1578 ------------
 .../src/TPCPixelClusteringDigiAlg.h           |  274 ---
 Examples/options/beambkgsim.py                |  127 -
 Examples/options/sim_beambkg.py               |  197 ++
 Generator/src/BeamBackgroundFileParserV1.cpp  |   30 +-
 Generator/src/BeamBackgroundFileParserV1.h    |    5 +-
 Generator/src/BeamBackgroundFileParserV2.cpp  |   52 +-
 Generator/src/BeamBackgroundFileParserV2.h    |    8 +-
 Generator/src/GtBeamBackgroundTool.cpp        |   20 +-
 Generator/src/GtBeamBackgroundTool.h          |    6 +
 Generator/src/GtGunTool.cpp                   |   13 +-
 Generator/src/GtGunTool.h                     |    3 +
 Generator/src/StdHepRdr.cpp                   |    2 +-
 Generator/src/StdHepRdr.h                     |    1 +
 README.md                                     |   19 +-
 Reconstruction/CMakeLists.txt                 |    1 +
 Reconstruction/CrystalCaloRec/CMakeLists.txt  |   68 +
 .../include/Algorithm/AxisMergingAlg.h        |   48 +
 .../include/Algorithm/ClusterMergingAlg.h     |   31 +
 .../include/Algorithm/ConeClustering2DAlg.h   |   45 +
 .../include/Algorithm/ConeClusteringAlg.h     |   36 +
 .../include/Algorithm/EnergySplittingAlg.h    |   77 +
 .../include/Algorithm/EnergyTimeMatchingAlg.h |   83 +
 .../include/Algorithm/ExampleAlg.h            |   31 +
 .../include/Algorithm/GlobalClusteringAlg.h   |   53 +
 .../include/Algorithm/HcalClusteringAlg.h     |   33 +
 .../include/Algorithm/HoughClusteringAlg.h    |   49 +
 .../include/Algorithm/LocalMaxFindingAlg.h    |   39 +
 .../include/Algorithm/PFOCreatingAlg.h        |   64 +
 .../include/Algorithm/PFOReclusteringAlg.h    |   40 +
 .../Algorithm/TrackClusterConnectingAlg.h     |   52 +
 .../include/Algorithm/TrackExtrapolatingAlg.h |   76 +
 .../include/Algorithm/TrackMatchingAlg.h      |   54 +
 .../Algorithm/TruthClusterMergingAlg.h        |   35 +
 .../include/Algorithm/TruthClusteringAlg.h    |   46 +
 .../Algorithm/TruthEnergySplittingAlg.h       |   39 +
 .../include/Algorithm/TruthMatchingAlg.h      |   52 +
 .../include/Algorithm/TruthPatternRecAlg.h    |   33 +
 .../include/Algorithm/TruthTrackMatchingAlg.h |   37 +
 .../include/Objects/Calo1DCluster.h           |   84 +
 .../include/Objects/Calo2DCluster.h           |   65 +
 .../include/Objects/Calo3DCluster.h           |  116 +
 .../include/Objects/CaloHalfCluster.h         |  108 +
 .../CrystalCaloRec/include/Objects/CaloHit.h  |   52 +
 .../CrystalCaloRec/include/Objects/CaloUnit.h |  101 +
 .../include/Objects/HoughObject.h             |   60 +
 .../include/Objects/HoughSpace.h              |   60 +
 .../CrystalCaloRec/include/Objects/PFObject.h |   44 +
 .../CrystalCaloRec/include/Objects/Track.h    |   58 +
 .../include/Objects/TrackState.h              |   33 +
 .../include/PandoraPlusDataCol.h              |   70 +
 .../CrystalCaloRec/include/PandoraPlusPFAlg.h |  321 +++
 .../CrystalCaloRec/include/Tools/Algorithm.h  |   46 +
 .../include/Tools/AlgorithmManager.h          |   66 +
 .../include/Tools/CaloHitsCreator.h           |   37 +
 .../include/Tools/MCParticleCreator.h         |   34 +
 .../include/Tools/OutputCreator.h             |   37 +
 .../include/Tools/TrackCreator.h              |   36 +
 .../include/Tools/TrackFitInEcal.h            |   59 +
 Reconstruction/CrystalCaloRec/script/digi.py  |  115 +
 Reconstruction/CrystalCaloRec/script/rec.py   |  139 ++
 Reconstruction/CrystalCaloRec/script/sim.py   |  103 +
 .../CrystalCaloRec/script/tracking.py         |  249 ++
 .../src/Algorithm/ArborClusteringAlg.cpp      |  509 ++++
 .../src/Algorithm/ArborTreeMergingAlg.cpp     |  281 +++
 .../src/Algorithm/AxisMergingAlg.cpp          |  982 ++++++++
 .../src/Algorithm/ClusterMergingAlg.cpp       |   32 +
 .../src/Algorithm/ConeClustering2DAlg.cpp     |  212 ++
 .../src/Algorithm/ConeClusteringAlg.cpp       |  220 ++
 .../src/Algorithm/EnergySplittingAlg.cpp      | 1289 ++++++++++
 .../src/Algorithm/EnergyTimeMatchingAlg.cpp   | 1445 +++++++++++
 .../src/Algorithm/ExampleAlg.cpp              |   50 +
 .../src/Algorithm/GlobalClusteringAlg.cpp     |  146 ++
 .../src/Algorithm/HcalClusteringAlg.cpp       |  175 ++
 .../src/Algorithm/HoughClusteringAlg.cpp      |  812 +++++++
 .../src/Algorithm/LocalMaxFindingAlg.cpp      |  150 ++
 .../src/Algorithm/PFOCreatingAlg.cpp          |  278 +++
 .../src/Algorithm/PFOReclusteringAlg.cpp      |  579 +++++
 .../Algorithm/TrackClusterConnectingAlg.cpp   |  371 +++
 .../src/Algorithm/TrackExtrapolatingAlg.cpp   |  450 ++++
 .../src/Algorithm/TrackMatchingAlg.cpp        |  604 +++++
 .../src/Algorithm/TruthClusterMergingAlg.cpp  |  200 ++
 .../src/Algorithm/TruthClusteringAlg.cpp      |  496 ++++
 .../src/Algorithm/TruthEnergySplittingAlg.cpp |  625 +++++
 .../src/Algorithm/TruthMatchingAlg.cpp        |  444 ++++
 .../src/Algorithm/TruthPatternRecAlg.cpp      |  213 ++
 .../src/Algorithm/TruthTrackMatchingAlg.cpp   |  271 +++
 .../src/Objects/Calo1DCluster.cc              |  249 ++
 .../src/Objects/Calo2DCluster.cc              |  122 +
 .../src/Objects/Calo3DCluster.cc              |  436 ++++
 .../src/Objects/CaloHalfCluster.cc            |  453 ++++
 .../CrystalCaloRec/src/Objects/CaloHit.cc     |   46 +
 .../CrystalCaloRec/src/Objects/CaloUnit.cc    |  188 ++
 .../CrystalCaloRec/src/Objects/HoughObject.cc |   72 +
 .../CrystalCaloRec/src/Objects/HoughSpace.cc  |  128 +
 .../CrystalCaloRec/src/Objects/PFObject.cc    |   81 +
 .../CrystalCaloRec/src/Objects/Track.cc       |  113 +
 .../CrystalCaloRec/src/PandoraPlusDataCol.cpp |   27 +
 .../CrystalCaloRec/src/PandoraPlusPFAlg.cpp   | 2158 +++++++++++++++++
 .../src/Tools/CaloHitsCreator.cpp             |  128 +
 .../src/Tools/MCParticleCreator.cpp           |   30 +
 .../src/Tools/OutputCreator.cpp               |  233 ++
 .../CrystalCaloRec/src/Tools/TrackCreator.cpp |  154 ++
 .../src/Tools/TrackFitInEcal.cpp              |  221 ++
 .../src/SiliconTrackingAlg.cpp                |   10 +-
 .../SiliconTracking/src/SiliconTrackingAlg.h  |    1 +
 .../Tracking/src/Clupatra/ClupatraAlg.cpp     |   26 +-
 .../Tracking/src/Clupatra/ClupatraAlg.h       |    7 +-
 .../FullLDCTracking/FullLDCTrackingAlg.cpp    |  198 +-
 .../src/FullLDCTracking/FullLDCTrackingAlg.h  |    5 +-
 Service/CMakeLists.txt                        |    1 +
 Service/CrystalEcalSvc/CMakeLists.txt         |   29 +
 .../include/CrystalEcalSvc/ICrystalEcalSvc.h  |   19 +
 .../src/CrystalEcalEnergyCorrectionSvc.cpp    |  110 +
 .../src/CrystalEcalEnergyCorrectionSvc.h      |   44 +
 Service/TrackSystemSvc/src/MarlinTrkUtils.cc  |   17 +-
 .../src/Edm4hepWriterAnaElemTool.cpp          |    3 -
 .../DetSimAna/src/Edm4hepWriterAnaElemTool.h  |    4 +-
 .../DetSimAna/src/ExampleAnaElemTool.cpp      |    3 +
 Simulation/DetSimAna/src/ExampleAnaElemTool.h |    2 +
 168 files changed, 24942 insertions(+), 2772 deletions(-)
 create mode 100644 Detector/DetCEPCv4/src/calorimeter/SHcalSc04_Endcaps_v02.cpp
 create mode 100644 Detector/DetCRD/compact/CRD_common_v01/Ecal_Crystal_Barrel_Short_v02.xml
 create mode 100644 Detector/DetCRD/compact/CRD_common_v01/Ecal_Crystal_Endcap_Short_v01.xml
 create mode 100755 Detector/DetCRD/compact/CRD_common_v01/Ecal_Crystal_Endcap_v01_01.xml
 create mode 100644 Detector/DetCRD/compact/CRD_common_v01/OTKBarrel_v01_01.xml
 create mode 100644 Detector/DetCRD/compact/CRD_common_v01/SHcalGlass_Barrel_v05.xml
 create mode 100644 Detector/DetCRD/compact/CRD_common_v01/SHcalGlass_Endcaps_v01.xml
 create mode 100644 Detector/DetCRD/compact/CRD_common_v01/SHcalSc04_Endcaps_v02.xml
 create mode 100644 Detector/DetCRD/compact/TDR_CaloTest/TDR_Dimensions_v01_01.xml
 create mode 100644 Detector/DetCRD/compact/TDR_CaloTest/TDR_longEcal_GSHcal.xml
 create mode 100644 Detector/DetCRD/compact/TDR_CaloTest/TDR_onlyGSHcal.xml
 create mode 100644 Detector/DetCRD/compact/TDR_CaloTest/TDR_onlyPSHcal.xml
 create mode 100644 Detector/DetCRD/compact/TDR_CaloTest/TDR_onlyshortEcal.xml
 create mode 100644 Detector/DetCRD/compact/TDR_CaloTest/TDR_shortEcal_PSHcal.xml
 create mode 100644 Detector/DetCRD/scripts/TDR_o1_v01/calodigi.py
 create mode 100644 Detector/DetCRD/src/Calorimeter/CRDEcal_Endcap_Short_v01.cpp
 create mode 100644 Detector/DetCRD/src/Calorimeter/CRDEcal_Short_v02.cpp
 create mode 100755 Detector/DetCRD/src/Calorimeter/LongCrystalBarEndcapCalorimeter_v01.cpp
 create mode 100644 Detector/DetCRD/src/Tracker/SiTracker_otkbarrel_v01_geo.cpp
 create mode 100644 Digitisers/CaloDigi/CMakeLists.txt
 create mode 100644 Digitisers/CaloDigi/src/CaloBar.h
 create mode 100644 Digitisers/CaloDigi/src/CaloHit.h
 create mode 100644 Digitisers/CaloDigi/src/EcalDigiAlg.cpp
 create mode 100644 Digitisers/CaloDigi/src/EcalDigiAlg.h
 create mode 100644 Digitisers/CaloDigi/src/HcalDigiAlg.cpp
 create mode 100644 Digitisers/CaloDigi/src/HcalDigiAlg.h
 create mode 100644 Digitisers/CaloDigi/src/HitStep.h
 delete mode 100644 Digitisers/SimpleDigi/src/TPCPixelClusteringDigiAlg.cpp
 delete mode 100644 Digitisers/SimpleDigi/src/TPCPixelClusteringDigiAlg.h
 delete mode 100644 Examples/options/beambkgsim.py
 create mode 100755 Examples/options/sim_beambkg.py
 create mode 100644 Reconstruction/CrystalCaloRec/CMakeLists.txt
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/AxisMergingAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/ClusterMergingAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/ConeClustering2DAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/ConeClusteringAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/EnergySplittingAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/EnergyTimeMatchingAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/ExampleAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/GlobalClusteringAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/HcalClusteringAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/HoughClusteringAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/LocalMaxFindingAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/PFOCreatingAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/PFOReclusteringAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/TrackClusterConnectingAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/TrackExtrapolatingAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/TrackMatchingAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/TruthClusterMergingAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/TruthClusteringAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/TruthEnergySplittingAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/TruthMatchingAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/TruthPatternRecAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Algorithm/TruthTrackMatchingAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Objects/Calo1DCluster.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Objects/Calo2DCluster.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Objects/Calo3DCluster.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Objects/CaloHalfCluster.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Objects/CaloHit.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Objects/CaloUnit.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Objects/HoughObject.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Objects/HoughSpace.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Objects/PFObject.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Objects/Track.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Objects/TrackState.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/PandoraPlusDataCol.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/PandoraPlusPFAlg.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Tools/Algorithm.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Tools/AlgorithmManager.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Tools/CaloHitsCreator.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Tools/MCParticleCreator.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Tools/OutputCreator.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Tools/TrackCreator.h
 create mode 100644 Reconstruction/CrystalCaloRec/include/Tools/TrackFitInEcal.h
 create mode 100644 Reconstruction/CrystalCaloRec/script/digi.py
 create mode 100644 Reconstruction/CrystalCaloRec/script/rec.py
 create mode 100644 Reconstruction/CrystalCaloRec/script/sim.py
 create mode 100644 Reconstruction/CrystalCaloRec/script/tracking.py
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/ArborClusteringAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/ArborTreeMergingAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/AxisMergingAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/ClusterMergingAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/ConeClustering2DAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/ConeClusteringAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/EnergySplittingAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/EnergyTimeMatchingAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/ExampleAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/GlobalClusteringAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/HcalClusteringAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/HoughClusteringAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/LocalMaxFindingAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/PFOCreatingAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/PFOReclusteringAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/TrackClusterConnectingAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/TrackExtrapolatingAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/TrackMatchingAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/TruthClusterMergingAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/TruthClusteringAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/TruthEnergySplittingAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/TruthMatchingAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/TruthPatternRecAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Algorithm/TruthTrackMatchingAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Objects/Calo1DCluster.cc
 create mode 100644 Reconstruction/CrystalCaloRec/src/Objects/Calo2DCluster.cc
 create mode 100644 Reconstruction/CrystalCaloRec/src/Objects/Calo3DCluster.cc
 create mode 100644 Reconstruction/CrystalCaloRec/src/Objects/CaloHalfCluster.cc
 create mode 100644 Reconstruction/CrystalCaloRec/src/Objects/CaloHit.cc
 create mode 100644 Reconstruction/CrystalCaloRec/src/Objects/CaloUnit.cc
 create mode 100644 Reconstruction/CrystalCaloRec/src/Objects/HoughObject.cc
 create mode 100644 Reconstruction/CrystalCaloRec/src/Objects/HoughSpace.cc
 create mode 100644 Reconstruction/CrystalCaloRec/src/Objects/PFObject.cc
 create mode 100644 Reconstruction/CrystalCaloRec/src/Objects/Track.cc
 create mode 100644 Reconstruction/CrystalCaloRec/src/PandoraPlusDataCol.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/PandoraPlusPFAlg.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Tools/CaloHitsCreator.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Tools/MCParticleCreator.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Tools/OutputCreator.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Tools/TrackCreator.cpp
 create mode 100644 Reconstruction/CrystalCaloRec/src/Tools/TrackFitInEcal.cpp
 create mode 100644 Service/CrystalEcalSvc/CMakeLists.txt
 create mode 100644 Service/CrystalEcalSvc/include/CrystalEcalSvc/ICrystalEcalSvc.h
 create mode 100755 Service/CrystalEcalSvc/src/CrystalEcalEnergyCorrectionSvc.cpp
 create mode 100755 Service/CrystalEcalSvc/src/CrystalEcalEnergyCorrectionSvc.h

diff --git a/.gitignore b/.gitignore
index ff7ac6fe..1d3c15ef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,4 @@ spack*
 ./Generator/output/
 ./Generator/options/
 
-InstallArea/
\ No newline at end of file
+InstallArea/
diff --git a/Detector/DetCEPCv4/CMakeLists.txt b/Detector/DetCEPCv4/CMakeLists.txt
index c7520576..db9c9908 100644
--- a/Detector/DetCEPCv4/CMakeLists.txt
+++ b/Detector/DetCEPCv4/CMakeLists.txt
@@ -22,6 +22,7 @@ gaudi_add_module(DetCEPCv4
                          src/calorimeter/SHcalRpc01_EndcapRing.cpp
                          src/calorimeter/SHcalSc04_Barrel_v04.cpp
                          src/calorimeter/SHcalSc04_Endcaps_v01.cpp
+                         src/calorimeter/SHcalSc04_Endcaps_v02.cpp
                          src/calorimeter/Yoke05_Barrel.cpp
                          src/calorimeter/Yoke05_Endcaps.cpp
                          src/other/BoxSupport_o1_v01_geo.cpp
diff --git a/Detector/DetCEPCv4/src/calorimeter/SHcalSc04_Barrel_v04.cpp b/Detector/DetCEPCv4/src/calorimeter/SHcalSc04_Barrel_v04.cpp
index d7273919..52396b76 100644
--- a/Detector/DetCEPCv4/src/calorimeter/SHcalSc04_Barrel_v04.cpp
+++ b/Detector/DetCEPCv4/src/calorimeter/SHcalSc04_Barrel_v04.cpp
@@ -22,10 +22,11 @@
 
 using namespace std;
 
-using dd4hep::BUILD_ENVELOPE;
+using dd4hep::_toString;
 using dd4hep::Box;
-using dd4hep::DetElement;
+using dd4hep::BUILD_ENVELOPE;
 using dd4hep::Detector;
+using dd4hep::DetElement;
 using dd4hep::IntersectionSolid;
 using dd4hep::Material;
 using dd4hep::PlacedVolume;
@@ -40,679 +41,607 @@ using dd4hep::Transform3D;
 using dd4hep::Trapezoid;
 using dd4hep::Tube;
 using dd4hep::Volume;
-using dd4hep::_toString;
 
 using dd4hep::rec::LayeredCalorimeterData;
 
 // After reading in all the necessary parameters.
 // To check the radius range and the space for placing the total layers
-static bool validateEnvelope(double rInner, double rOuter, double radiatorThickness, double layerThickness, int layerNumber, int nsymmetry){
-  
+static bool validateEnvelope(double rInner, double rOuter, double radiatorThickness, double layerThickness, int layerNumber, int nsymmetry)
+{
+
   bool Error = false;
   bool Warning = false;
-  double spaceAllowed = rOuter*cos(M_PI/nsymmetry) - rInner;
-  double spaceNeeded  = (radiatorThickness + layerThickness)* layerNumber;
-  double spaceToleranted  = (radiatorThickness + layerThickness)* (layerNumber+1);
-  double rOuterRecommaned = ( rInner + spaceNeeded )/cos(M_PI/16.);
-  int layerNumberRecommaned = floor( ( spaceAllowed ) / (radiatorThickness + layerThickness) );
-  
-  
-  if( spaceNeeded > spaceAllowed )
-    {
-      printout( dd4hep::ERROR,  "SHcalSc04_Barrel_v04", " Layer number is more than it can be built! "  ) ;
-      Error = true;
-    }
-  else if ( spaceToleranted < spaceAllowed )
-    {
-      printout( dd4hep::WARNING,  "SHcalSc04_Barrel_v04", " Layer number is less than it is able to build!" ) ;
-      Warning = true;
-    }
+  double spaceAllowed = rOuter * cos(M_PI / nsymmetry) - rInner;
+  double spaceNeeded = (radiatorThickness + layerThickness) * layerNumber;
+  double spaceToleranted = (radiatorThickness + layerThickness) * (layerNumber + 1);
+  double rOuterRecommaned = (rInner + spaceNeeded) / cos(M_PI / 16.);
+  int layerNumberRecommaned = floor((spaceAllowed) / (radiatorThickness + layerThickness));
+
+  if (spaceNeeded > spaceAllowed)
+  {
+    printout(dd4hep::ERROR, "SHcalSc04_Barrel_v04", " Layer number is more than it can be built! ");
+    Error = true;
+  }
+  else if (spaceToleranted < spaceAllowed)
+  {
+    printout(dd4hep::WARNING, "SHcalSc04_Barrel_v04", " Layer number is less than it is able to build!");
+    Warning = true;
+  }
   else
-    {
-      printout( dd4hep::DEBUG,  "SHcalSc04_Barrel_v04"," has been validated and start to build it." ) ;
-      Error = false;
-      Warning = false;
-    }
-
-  if( Error )
-    {
-      cout<<"\n ============> First Help Documentation <=============== \n"
-	  <<" When you see this message, that means you are crashing the module. \n"
-	  <<" Please take a cup of cafe, and think about what you want to do! \n"
-	  <<" Here are few FirstAid# for you. Please read them carefully. \n"
-	  <<" \n"
-	  <<" ###  FirstAid 1: ###\n"
-	  <<" If you want to build HCAL within the rInner and rOuter range, \n"
-	  <<" please reduce the layer number to " << layerNumberRecommaned <<" \n"
-	  <<" with the current layer thickness structure. \n"
-	  <<" \n"
-	  <<" You may redisgn the layer structure and thickness, too. \n"
-	  <<" \n"
-	  <<" ###  FirstAid 2: ###\n"
-	  <<" If you want to build HCAL with this layer number and the layer thickness, \n"
-	  <<" you have to update rOuter to "<< rOuterRecommaned*10. <<"*mm \n"
-	  <<" and to inform other subdetector, you need this space for building your design. \n"
-	  <<" \n"
-	  <<" ###  FirstAid 3: ###\n"
-	  <<" Do you think that you are looking for another type of HCAL driver? \n"
-	  <<" \n"
-	  <<endl; 
-      throw lcgeo::GeometryException(  "SHcalSc04_Barrel: Error: Layer number is more than it can be built!"   ) ;
-    } 
-  else if( Warning )
-    {
-      cout<<"\n ============> First Help Documentation <=============== \n"
-	  <<" When you see this warning message, that means you are changing the module. \n"
-	  <<" Please take a cup of cafe, and think about what you want to do! \n"
-	  <<" Here are few FirstAid# for you. Please read them carefully. \n"
-	  <<" \n"
-	  <<" ###  FirstAid 1: ###\n"
-	  <<" If you want to build HCAL within the rInner and rOuter range, \n"
-	  <<" You could build the layer number up to " << layerNumberRecommaned <<" \n"
-	  <<" with the current layer thickness structure. \n"
-	  <<" \n"
-	  <<" You may redisgn the layer structure and thickness, too. \n"
-	  <<" \n"
-	  <<" ###  FirstAid 2: ###\n"
-	  <<" If you want to build HCAL with this layer number and the layer thickness, \n"
-	  <<" you could reduce rOuter to "<< rOuterRecommaned*10. <<"*mm \n"
-	  <<" and to reduce the back plate thickness, which you may not need for placing layer. \n"
-	  <<" \n"
-	  <<" ###  FirstAid 3: ###\n"
-	  <<" Do you think that you are looking for another type of HCAL driver? \n"
-	  <<" \n"
-	  <<endl; 
-      return Warning;
-    }
-  else { return true; }
+  {
+    printout(dd4hep::DEBUG, "SHcalSc04_Barrel_v04", " has been validated and start to build it.");
+    Error = false;
+    Warning = false;
+  }
 
+  if (Error)
+  {
+    cout << "\n ============> First Help Documentation <=============== \n"
+         << " When you see this message, that means you are crashing the module. \n"
+         << " Please take a cup of cafe, and think about what you want to do! \n"
+         << " Here are few FirstAid# for you. Please read them carefully. \n"
+         << " \n"
+         << " ###  FirstAid 1: ###\n"
+         << " If you want to build HCAL within the rInner and rOuter range, \n"
+         << " please reduce the layer number to " << layerNumberRecommaned << " \n"
+         << " with the current layer thickness structure. \n"
+         << " \n"
+         << " You may redisgn the layer structure and thickness, too. \n"
+         << " \n"
+         << " ###  FirstAid 2: ###\n"
+         << " If you want to build HCAL with this layer number and the layer thickness, \n"
+         << " you have to update rOuter to " << rOuterRecommaned * 10. << "*mm \n"
+         << " and to inform other subdetector, you need this space for building your design. \n"
+         << " \n"
+         << " ###  FirstAid 3: ###\n"
+         << " Do you think that you are looking for another type of HCAL driver? \n"
+         << " \n"
+         << endl;
+    throw lcgeo::GeometryException("SHcalSc04_Barrel: Error: Layer number is more than it can be built!");
+  }
+  else if (Warning)
+  {
+    cout << "\n ============> First Help Documentation <=============== \n"
+         << " When you see this warning message, that means you are changing the module. \n"
+         << " Please take a cup of cafe, and think about what you want to do! \n"
+         << " Here are few FirstAid# for you. Please read them carefully. \n"
+         << " \n"
+         << " ###  FirstAid 1: ###\n"
+         << " If you want to build HCAL within the rInner and rOuter range, \n"
+         << " You could build the layer number up to " << layerNumberRecommaned << " \n"
+         << " with the current layer thickness structure. \n"
+         << " \n"
+         << " You may redisgn the layer structure and thickness, too. \n"
+         << " \n"
+         << " ###  FirstAid 2: ###\n"
+         << " If you want to build HCAL with this layer number and the layer thickness, \n"
+         << " you could reduce rOuter to " << rOuterRecommaned * 10. << "*mm \n"
+         << " and to reduce the back plate thickness, which you may not need for placing layer. \n"
+         << " \n"
+         << " ###  FirstAid 3: ###\n"
+         << " Do you think that you are looking for another type of HCAL driver? \n"
+         << " \n"
+         << endl;
+    return Warning;
+  }
+  else
+  {
+    return true;
+  }
 }
 
-static Ref_t create_detector(Detector& theDetector, xml_h element, SensitiveDetector sens)  {
+static Ref_t create_detector(Detector &theDetector, xml_h element, SensitiveDetector sens)
+{
 
   double boundarySafety = 0.0001;
 
-  xml_det_t   x_det       = element;
-  string      det_name    = x_det.nameStr();
-  int           det_id    = x_det.id();
-  DetElement  sdet( det_name, det_id );
+  xml_det_t x_det = element;
+  string det_name = x_det.nameStr();
+  int det_id = x_det.id();
+  DetElement sdet(det_name, det_id);
 
   // --- create an envelope volume and position it into the world ---------------------
-  
-  Volume envelope = dd4hep::xml::createPlacedEnvelope( theDetector,  element , sdet ) ;
-  
-  dd4hep::xml::setDetectorTypeFlag( element, sdet ) ;
-  
-  if( theDetector.buildType() == BUILD_ENVELOPE ) return sdet ;
+
+  Volume envelope = dd4hep::xml::createPlacedEnvelope(theDetector, element, sdet);
+
+  dd4hep::xml::setDetectorTypeFlag(element, sdet);
+
+  if (theDetector.buildType() == BUILD_ENVELOPE)
+    return sdet;
 
   //-----------------------------------------------------------------------------------
 
-  xml_comp_t    x_staves          = x_det.staves();
-  Material      stavesMaterial    = theDetector.material(x_staves.materialStr());
-  Material      air               = theDetector.air();
+  xml_comp_t x_staves = x_det.staves(); // absorber
+  Material stavesMaterial = theDetector.material(x_staves.materialStr());
+  Material air = theDetector.air();
 
   PlacedVolume pv;
 
   sens.setType("calorimeter");
 
-//====================================================================
-//
-// Read all the constant from ILD_o1_v05.xml
-// Use them to build HcalBarrel
-//
-//====================================================================
-  double      Hcal_inner_radius   = theDetector.constant<double>("Hcal_inner_radius");
-  double      Hcal_outer_radius   = theDetector.constant<double>("Hcal_outer_radius");
-  double      Hcal_half_length    = theDetector.constant<double>("Hcal_half_length");
-  int         Hcal_inner_symmetry = theDetector.constant<int>("Hcal_inner_symmetry");
-  int         Hcal_outer_symmetry = 0; // Fixed shape for Tube, and not allow to modify from compact xml.
-
-  double      Hcal_radiator_thickness          = theDetector.constant<double>("Hcal_radiator_thickness");
-  double      Hcal_chamber_thickness           = theDetector.constant<double>("Hcal_chamber_thickness");
-  double      Hcal_back_plate_thickness        = theDetector.constant<double>("Hcal_back_plate_thickness");
-  double      Hcal_lateral_plate_thickness     = theDetector.constant<double>("Hcal_lateral_structure_thickness");
-  double      Hcal_stave_gaps                  = theDetector.constant<double>("Hcal_stave_gaps");
-  double      Hcal_modules_gap                 = theDetector.constant<double>("Hcal_modules_gap"); 
-  double      Hcal_middle_stave_gaps           = theDetector.constant<double>("Hcal_middle_stave_gaps");
-  double      Hcal_layer_air_gap               = theDetector.constant<double>("Hcal_layer_air_gap");
-  //double      Hcal_cells_size                  = theDetector.constant<double>("Hcal_cells_size");
-
-  int         Hcal_nlayers                     = theDetector.constant<int>("Hcal_nlayers");
-
-  printout( dd4hep::INFO, "SHcalSc04_Barrel_v04", "Hcal_inner_radius : %e   - Hcal_outer_radius %e ", Hcal_inner_radius , Hcal_outer_radius);
+  //====================================================================
+  //
+  // Read all the constant from ILD_o1_v05.xml
+  // Use them to build HcalBarrel
+  //
+  //====================================================================
+  double Hcal_inner_radius = theDetector.constant<double>("Hcal_inner_radius");
+  double Hcal_outer_radius = theDetector.constant<double>("Hcal_outer_radius");
+  double Hcal_half_length = theDetector.constant<double>("Hcal_half_length");
+  int Hcal_inner_symmetry = theDetector.constant<int>("Hcal_inner_symmetry");
+  int Hcal_outer_symmetry = 0; // Fixed shape for Tube, and not allow to modify from compact xml
+  double Hcal_cell_size = theDetector.constant<double>("Hcal_cell_size");
+  double Hcal_cell_size_abnormal = theDetector.constant<double>("Hcal_cell_size_abnormal");
+  double Hcal_scintillator_air_gap = theDetector.constant<double>("Hcal_scintillator_air_gap");
+  double Hcal_scintillator_ESR_thickness = theDetector.constant<double>("Hcal_scintillator_ESR_thickness");
+  double Hcal_scintillator_thickness = theDetector.constant<double>("Hcal_scintillator_thickness");
+  double Hcal_radiator_thickness = theDetector.constant<double>("Hcal_radiator_thickness");
+  double Hcal_chamber_thickness = theDetector.constant<double>("Hcal_chamber_thickness");
+  double Hcal_back_plate_thickness = theDetector.constant<double>("Hcal_back_plate_thickness");
+  double Hcal_lateral_plate_thickness = theDetector.constant<double>("Hcal_lateral_structure_thickness");
+  double Hcal_stave_gaps = theDetector.constant<double>("Hcal_stave_gaps");
+  // double Hcal_modules_gap = theDetector.constant<double>("Hcal_modules_gap");
+  double Hcal_middle_stave_gaps = theDetector.constant<double>("Hcal_middle_stave_gaps");
+  double Hcal_layer_air_gap = theDetector.constant<double>("Hcal_layer_air_gap");
+  // double      Hcal_cells_size                  = theDetector.constant<double>("Hcal_cells_size");
+
+  int Hcal_nlayers = theDetector.constant<int>("Hcal_nlayers");
+
+  //=================================================================================
+  //  if Hcal_outer_radius stands for the radius of circumcircle, comment next line 
+  Hcal_outer_radius /= cos(M_PI / Hcal_inner_symmetry);
+  //=================================================================================
+  printout(dd4hep::INFO, "SHcalSc04_Barrel_v04", "Hcal_inner_radius : %e   - Hcal_outer_radius %e ", Hcal_inner_radius, Hcal_outer_radius);
   validateEnvelope(Hcal_inner_radius, Hcal_outer_radius, Hcal_radiator_thickness, Hcal_chamber_thickness, Hcal_nlayers, Hcal_inner_symmetry);
 
-  Readout readout = sens.readout();
-  dd4hep::Segmentation seg = readout.segmentation();
-  
-  dd4hep::DDSegmentation::BitField64 encoder = seg.decoder();
-  encoder.setValue(0) ;
-  
-
-  //fg: this is a bit tricky: we first have to check whether a multi segmentation is used and then, if so, we
-  //    will check the <subsegmentation key="" value=""/> element, for which subsegmentation to use for filling the 
-  //    DDRec:LayeredCalorimeterData information.
-  //    Additionally, we need to figure out if there is a TiledLayerGridXY instance defined -
-  //    and in case, there is more than one defined, we need to pick the correct one specified via the
-  //    <subsegmentation key="" value=""/> element.
-  //    This involves a lot of casting:  review the API in DD4hep !!
+  // fg: this is a bit tricky: we first have to check whether a multi segmentation is used and then, if so, we
+  //     will check the <subsegmentation key="" value=""/> element, for which subsegmentation to use for filling the
+  //     DDRec:LayeredCalorimeterData information.
+  //     Additionally, we need to figure out if there is a TiledLayerGridXY instance defined -
+  //     and in case, there is more than one defined, we need to pick the correct one specified via the
+  //     <subsegmentation key="" value=""/> element.
+  //     This involves a lot of casting:  review the API in DD4hep !!
 
   // check if we have a multi segmentation :
 
-  dd4hep::DDSegmentation::MultiSegmentation* multiSeg = 
-    dynamic_cast< dd4hep::DDSegmentation::MultiSegmentation*>( seg.segmentation() ) ;
-  
-  dd4hep::DDSegmentation::TiledLayerGridXY* tileSeg = 0 ;
-    
-  int sensitive_slice_number = -1 ;
-
-  if( multiSeg ){
-
-    try{ 
-      // check if we have an entry for the subsegmentation to be used 
-      xml_comp_t segxml = x_det.child( _Unicode( subsegmentation ) ) ;
-
-      std::string keyStr = segxml.attr<std::string>( _Unicode(key) ) ;
-      int keyVal = segxml.attr<int>( _Unicode(value) )  ;
-
-      encoder[ keyStr ] =  keyVal ;
-
-      // if we have a multisegmentation that uses the slice as key, we need to know for the
-      // computation of the layer parameters in LayeredCalorimeterData::Layer below
-      if( keyStr == "slice" ){
-	sensitive_slice_number = keyVal ;
-      }
-
-
-    } catch(const std::runtime_error &) {
-      throw lcgeo::GeometryException(  "SHcalSc04_Barrel: Error: MultiSegmentation specified but no "
-                                       " <subsegmentation key="" value=""/> element defined for detector ! " ) ;
-    }
-    
-    // check if we have a TiledLayerGridXY segmentation :
-    const dd4hep::DDSegmentation::TiledLayerGridXY* ts0 =
-      dynamic_cast<const dd4hep::DDSegmentation::TiledLayerGridXY*>(  &multiSeg->subsegmentation( encoder.getValue() ) ) ;
-    
-    tileSeg = const_cast<dd4hep::DDSegmentation::TiledLayerGridXY*>( ts0 ) ;
-    
-    if( ! tileSeg ){ // if the current segmentation is not a tileSeg, we see if there is another one
-      
-      for( auto s : multiSeg->subSegmentations() ){
-	const dd4hep::DDSegmentation::TiledLayerGridXY* ts =
-	  dynamic_cast<const dd4hep::DDSegmentation::TiledLayerGridXY*>( s.segmentation ) ;
-	
-	if( ts ) {
-	  tileSeg = const_cast<dd4hep::DDSegmentation::TiledLayerGridXY*>( ts ) ;
-	  break ;
-	}
-      }
-    }
-    
-  } else {
-    
-    tileSeg = 
-      dynamic_cast< dd4hep::DDSegmentation::TiledLayerGridXY*>( seg.segmentation() ) ;
-  }
-  
-  
-
-  std::vector<double> cellSizeVector = seg.cellDimensions( encoder.getValue() ); //Assume uniform cell sizes, provide dummy cellID
-  double cell_sizeX      = cellSizeVector[0];
-  double cell_sizeY      = cellSizeVector[1];
-
-
   //========== fill data for reconstruction ============================
-  LayeredCalorimeterData* caloData = new LayeredCalorimeterData ;
-  caloData->layoutType = LayeredCalorimeterData::BarrelLayout ;
-  caloData->inner_symmetry = Hcal_inner_symmetry  ;
-  caloData->outer_symmetry = Hcal_outer_symmetry  ;
-  caloData->phi0 = 0 ; // fg: also hardcoded below 
+  LayeredCalorimeterData *caloData = new LayeredCalorimeterData;
+  caloData->layoutType = LayeredCalorimeterData::BarrelLayout;
+  caloData->inner_symmetry = Hcal_inner_symmetry;
+  caloData->outer_symmetry = Hcal_outer_symmetry;
+  caloData->phi0 = 0; // fg: also hardcoded below
 
   /// extent of the calorimeter in the r-z-plane [ rmin, rmax, zmin, zmax ] in mm.
-  caloData->extent[0] = Hcal_inner_radius ;
-  caloData->extent[1] = Hcal_outer_radius ;
-  caloData->extent[2] = 0. ; // Barrel zmin is "0" by default.
-  caloData->extent[3] = Hcal_half_length ;
+  caloData->extent[0] = Hcal_inner_radius;
+  caloData->extent[1] = Hcal_outer_radius;
+  caloData->extent[2] = 0.; // Barrel zmin is "0" by default.
+  caloData->extent[3] = Hcal_half_length;
 
-//====================================================================
-//
-// general calculated parameters
-//
-//====================================================================
+  //====================================================================
+  //
+  // general calculated parameters
+  //
+  //====================================================================
 
-  double Hcal_total_dim_y   = Hcal_nlayers * (Hcal_radiator_thickness + Hcal_chamber_thickness) 
-                            + Hcal_back_plate_thickness;
+  double Hcal_total_dim_y = Hcal_nlayers * (Hcal_radiator_thickness + Hcal_chamber_thickness) + Hcal_back_plate_thickness;
 
-  double Hcal_y_dim1_for_x  = Hcal_outer_radius*cos(M_PI/Hcal_inner_symmetry) - Hcal_inner_radius;
-  double Hcal_bottom_dim_x  = 2.*Hcal_inner_radius*tan(M_PI/Hcal_inner_symmetry)- Hcal_stave_gaps;
-  double Hcal_normal_dim_z  = (2 * Hcal_half_length - Hcal_modules_gap)/2.;
+  // double Hcal_y_dim1_for_x = Hcal_outer_radius * cos(M_PI / Hcal_inner_symmetry) - Hcal_inner_radius;
+  double Hcal_bottom_dim_x = 2. * Hcal_inner_radius * tan(M_PI / Hcal_inner_symmetry) - Hcal_lateral_plate_thickness * 2 - Hcal_stave_gaps;
+  // double Hcal_normal_dim_z = (2 * Hcal_half_length - Hcal_modules_gap) / 2.;
+  double Hcal_normal_dim_z = Hcal_half_length;
 
- //only the middle has the steel plate.
-  double Hcal_regular_chamber_dim_z = Hcal_normal_dim_z - Hcal_lateral_plate_thickness;
+  // only the middle has the steel plate.
+  // double Hcal_regular_chamber_dim_z = Hcal_normal_dim_z - Hcal_lateral_plate_thickness;
+  double Hcal_regular_chamber_dim_z = Hcal_normal_dim_z;
 
-  //double Hcal_cell_dim_x            = Hcal_cells_size;
-  //double Hcal_cell_dim_z            = Hcal_regular_chamber_dim_z / floor (Hcal_regular_chamber_dim_z/Hcal_cell_dim_x);
+  // double Hcal_cell_dim_x            = Hcal_cells_size;
+  // double Hcal_cell_dim_z            = Hcal_regular_chamber_dim_z / floor (Hcal_regular_chamber_dim_z/Hcal_cell_dim_x);
+
+  // ========= Create Hcal Barrel stave   ====================================
+  //  It will be the volume for palcing the Hcal Barrel Chamber(i.e. Layers).
+  //  Itself will be placed into the world volume.
+  // ==========================================================================
 
- 
-// ========= Create Hcal Barrel stave   ====================================
-//  It will be the volume for palcing the Hcal Barrel Chamber(i.e. Layers).
-//  Itself will be placed into the world volume.
-// ==========================================================================
- 
-  double chambers_y_off_correction = 0.;
- 
   // stave modules shaper parameters
-  double BHX  = (Hcal_bottom_dim_x + Hcal_stave_gaps)/2.;
-  double THX  = (Hcal_total_dim_y + Hcal_inner_radius)*tan(M_PI/Hcal_inner_symmetry);
-  double YXH  = Hcal_total_dim_y / 2.;
-  double DHZ  = (Hcal_normal_dim_z - Hcal_lateral_plate_thickness) / 2.;
+  double BHX = (Hcal_bottom_dim_x + Hcal_lateral_plate_thickness * 2 + Hcal_stave_gaps) / 2.;
+  double THX = (Hcal_total_dim_y + Hcal_inner_radius) * tan(M_PI / Hcal_inner_symmetry);
+  double YXH = Hcal_total_dim_y / 2.;
+  // double DHZ = (Hcal_normal_dim_z - Hcal_lateral_plate_thickness) / 2.;
+  double DHZ = Hcal_normal_dim_z;
+
+  Trapezoid stave_shaper(THX, BHX, DHZ, DHZ, YXH);
 
-  Trapezoid stave_shaper(  THX, BHX, DHZ, DHZ, YXH);
+  Tube solidCaloTube(0, Hcal_outer_radius, DHZ + boundarySafety);
 
-  Tube solidCaloTube(0, Hcal_outer_radius, DHZ+boundarySafety);
-  
-  RotationZYX mrot(0,0,M_PI/2.);
+  RotationZYX mrot(0, 0, M_PI / 2.);
 
   Rotation3D mrot3D(mrot);
-  Position mxyzVec(0,0,(Hcal_inner_radius + Hcal_total_dim_y / 2.));
-  Transform3D mtran3D(mrot3D,mxyzVec);
+  Position mxyzVec(0, 0, (Hcal_inner_radius + Hcal_total_dim_y / 2.));
+  Transform3D mtran3D(mrot3D, mxyzVec);
 
   IntersectionSolid barrelModuleSolid(stave_shaper, solidCaloTube, mtran3D);
 
-  Volume  EnvLogHcalModuleBarrel(det_name+"_module",barrelModuleSolid,stavesMaterial);
-
-  EnvLogHcalModuleBarrel.setAttributes(theDetector,x_det.regionStr(),x_det.limitsStr(),x_det.visStr());
-
-
-
-
-
-  //stave modules lateral plate shaper parameters
-  double BHX_LP  = BHX;
-  double THX_LP  = THX;
-  double YXH_LP  = YXH;
+  Volume EnvLogHcalModuleBarrel(det_name + "_module", barrelModuleSolid, stavesMaterial);
 
-  //build lateral palte here to simulate lateral plate in the middle of barrel.
-  double DHZ_LP  = Hcal_lateral_plate_thickness/2.0; 
+  EnvLogHcalModuleBarrel.setAttributes(theDetector, x_staves.regionStr(), x_staves.limitsStr(), x_staves.visStr());
 
-  Trapezoid stave_shaper_LP(THX_LP, BHX_LP, DHZ_LP, DHZ_LP, YXH_LP);
+  // stave modules lateral plate shaper parameters
+  // double BHX_LP = BHX;
+  // double THX_LP = THX;
+  // double YXH_LP = YXH;
 
-  Tube solidCaloTube_LP(0, Hcal_outer_radius, DHZ_LP+boundarySafety);
+  // // build lateral palte here to simulate lateral plate in the middle of barrel.
+  // double DHZ_LP = Hcal_lateral_plate_thickness / 2.0;
 
-  IntersectionSolid Module_lateral_plate(stave_shaper_LP, solidCaloTube_LP, mtran3D);
+  // Trapezoid stave_shaper_LP(THX_LP, BHX_LP, DHZ_LP, DHZ_LP, YXH_LP);
 
-  Volume  EnvLogHcalModuleBarrel_LP(det_name+"_Module_lateral_plate",Module_lateral_plate,stavesMaterial);
+  // Tube solidCaloTube_LP(0, Hcal_outer_radius, DHZ_LP + boundarySafety);
 
-  EnvLogHcalModuleBarrel_LP.setAttributes(theDetector,x_det.regionStr(),x_det.limitsStr(),x_det.visStr());
+  // IntersectionSolid Module_lateral_plate(stave_shaper_LP, solidCaloTube_LP, mtran3D);
 
+  // Volume EnvLogHcalModuleBarrel_LP(det_name + "_Module_lateral_plate", Module_lateral_plate, stavesMaterial);
 
+  // EnvLogHcalModuleBarrel_LP.setAttributes(theDetector, x_det.regionStr(), x_det.limitsStr(), x_det.visStr());
 
 #ifdef SHCALSC04_DEBUG
-  std::cout<< " ==> Hcal_outer_radius: "<<Hcal_outer_radius <<std::endl;
+  std::cout << " ==> Hcal_outer_radius: " << Hcal_outer_radius << std::endl;
 #endif
 
-
-
-
-
-//====================================================================
-//
-// Chambers in the HCAL BARREL
-//
-//====================================================================
+  //====================================================================
+  //
+  // Chambers in the HCAL BARREL
+  //
+  //====================================================================
   // Build Layer Chamber fill with air, which include the tolarance space at the two side
   // place the slice into the Layer Chamber
   // Place the Layer Chamber into the Stave module
   // place the Stave module into the asembly Volume
   // place the module middle lateral plate into asembly Volume
 
-  double x_length; //dimension of an Hcal barrel layer on the x-axis
-  double y_height; //dimension of an Hcal barrel layer on the y-axis
-  double z_width;  //dimension of an Hcal barrel layer on the z-axis
-  x_length = 0.; // Each Layer the x_length has new value.
-  y_height = Hcal_chamber_thickness / 2.;
-  z_width  = Hcal_regular_chamber_dim_z/2.;
+  double x_halflength; // dimension of an Hcal barrel layer on the x-axis
+  double y_halfheight; // dimension of an Hcal barrel layer on the y-axis
+  double z_halfwidth;  // dimension of an Hcal barrel layer on the z-axis
+  x_halflength = 0.;   // Each Layer the x_halflength has new value.
+  y_halfheight = Hcal_chamber_thickness / 2.;
+  z_halfwidth = Hcal_regular_chamber_dim_z;
 
-  double xOffset = 0.;//the x_length of a barrel layer is calculated as a
-  //barrel x-dimension plus (bottom barrel) or minus
+  double xOffset = 0.; // the x_halflength of a barrel layer is calculated as a
+  // barrel x-dimension plus (bottom barrel) or minus
   //(top barrel) an x-offset, which depends on the angle M_PI/Hcal_inner_symmetry
 
-  double xShift = 0.;//Geant4 draws everything in the barrel related to the 
-  //center of the bottom barrel, so we need to shift the layers to
-  //the left (or to the right) with the quantity xShift
+  double xShift = 0.; // Geant4 draws everything in the barrel related to the
+  // center of the bottom barrel, so we need to shift the layers to
+  // the left (or to the right) with the quantity xShift
+
+  // read sensitive cell parameters and create it
+
+  // xml_comp_t x_scintillator = x_det.solid();
+  // Material sensitiveMaterial = theDetector.material(x_scintillator.materialStr());
+  // xml_comp_t x_ESR = x_det.shape();
+  // Material ladderMaterial = theDetector.material(x_ESR.materialStr());
+
+  Volume cell_vol;
+  Volume scintillator_vol;
+  Volume cell_vol_abnormal;
+  Volume scintillator_vol_abnormal;
+  xml_comp_t x_layer_tmp(x_det.child(_U(layer)));
+  for (xml_coll_t k(x_layer_tmp, _U(slice)); k; ++k)
+  {
+    xml_comp_t x_slice = k;
+    if (x_slice.isSensitive())
+    {
+      // child elements: ladder and sensitive
+      xml_comp_t x_sensitive(x_slice.child(_U(sensitive)));
+      xml_comp_t x_ladder(x_slice.child(_U(ladder)));
+      Material sensitiveMaterial = theDetector.material(x_sensitive.materialStr());
+      Material ladderMaterial = theDetector.material(x_ladder.materialStr());
+
+      // Store scintillator thickness
+      double cell_thickness = Hcal_scintillator_thickness + 2 * Hcal_scintillator_ESR_thickness;
+
+      double cell_size = 2 * Hcal_scintillator_ESR_thickness + Hcal_cell_size;
+      // place sensitive cell into ESR cell
+      string cell_name = "cell";
+      string scintillator_name = cell_name + "_scintillator";
+      // create sensitive cell with ESR
+      Box cell_box(cell_size / 2., cell_size / 2., cell_thickness / 2.);
+      // string cell_name=
+      // Volume cell_vol_tmp(cell_name, cell_box, ladderMaterial);
+      cell_vol = Volume(cell_name, cell_box, ladderMaterial);
+      cell_vol.setAttributes(theDetector, x_ladder.regionStr(), x_ladder.limitsStr(), x_ladder.visStr());
+      Box scintillator_box(Hcal_cell_size / 2., Hcal_cell_size / 2., Hcal_scintillator_thickness / 2.);
+      // Volume scintillator_vol_tmp(scintillator_name, scintillator_box, sensitiveMaterial);
+      scintillator_vol = Volume(scintillator_name, scintillator_box, sensitiveMaterial);
+      scintillator_vol.setAttributes(theDetector, x_sensitive.regionStr(), x_sensitive.limitsStr(), x_sensitive.visStr());
+      scintillator_vol.setSensitiveDetector(sens);
+      cell_vol.placeVolume(scintillator_vol, Position(0., 0., 0.));
+      // cout << x_ladder.visStr() << "   " << x_sensitive.visStr() << endl;
+      ///////////////////////////////
+      // abnormal cell             //
+      ///////////////////////////////
+      double cell_size_abnormal = 2 * Hcal_scintillator_ESR_thickness + Hcal_cell_size_abnormal;
+      // place sensitive cell into ESR cell
+      string cell_name_abnormal = "cell_abnormal";
+      string scintillator_name_abnormal = cell_name_abnormal + "_scintillator_abnormal";
+      // create sensitive cell with ESR
+      Box cell_box_abnormal(cell_size_abnormal / 2., cell_size / 2., cell_thickness / 2.);
+      // string cell_name=
+      // Volume cell_vol_tmp_abnormal(cell_name_abnormal, cell_box_abnormal, ladderMaterial);
+      cell_vol_abnormal = Volume(cell_name_abnormal, cell_box_abnormal, ladderMaterial);
+      cell_vol_abnormal.setAttributes(theDetector, x_ladder.regionStr(), x_ladder.limitsStr(), x_ladder.visStr());
+      Box scintillator_box_abnormal(Hcal_cell_size_abnormal / 2., Hcal_cell_size / 2., Hcal_scintillator_thickness / 2.);
+      // Volume scintillator_vol_tmp_abnormal(scintillator_name_abnormal, scintillator_box_abnormal, sensitiveMaterial);
+      scintillator_vol_abnormal = Volume(scintillator_name_abnormal, scintillator_box_abnormal, sensitiveMaterial);
+      scintillator_vol_abnormal.setAttributes(theDetector, x_sensitive.regionStr(), x_sensitive.limitsStr(), x_sensitive.visStr());
+      scintillator_vol_abnormal.setSensitiveDetector(sens);
+      cell_vol_abnormal.placeVolume(scintillator_vol_abnormal, Position(0., 0., 0.));
+    }
+  }
 
+  // sensitive cell creating done
 
   //-------------------- start loop over HCAL layers ----------------------
+  // int n_x_layer_1 = 0;
+  for (int layer_id = 1; layer_id <= (Hcal_nlayers); layer_id++)
+  {
 
-  for (int layer_id = 1; layer_id <= (2*Hcal_nlayers); layer_id++)
-    {
- 
-      double TanPiDiv8 = tan(M_PI/Hcal_inner_symmetry);
-      double x_total   = 0.;
-      double x_halfLength;
-      x_length  = 0.;
-
-      int logical_layer_id = 0;
-
-      if ( (layer_id < Hcal_nlayers)
-	   || (layer_id > Hcal_nlayers && layer_id < (2*Hcal_nlayers)) )
-	logical_layer_id = layer_id % Hcal_nlayers;
-      else if ( (layer_id == Hcal_nlayers) 
-		|| (layer_id == 2*Hcal_nlayers) ) logical_layer_id = Hcal_nlayers;
-
-      //---- bottom barrel------------------------------------------------------------
-      if( logical_layer_id *(Hcal_radiator_thickness + Hcal_chamber_thickness)
-	  < (Hcal_outer_radius * cos(M_PI/Hcal_inner_symmetry) - Hcal_inner_radius ) ) {
-	xOffset = (logical_layer_id * Hcal_radiator_thickness 
-		   + (logical_layer_id -1) * Hcal_chamber_thickness) * TanPiDiv8;
-
-	x_total  = Hcal_bottom_dim_x/2 - Hcal_middle_stave_gaps/2 + xOffset;
-	x_length = x_total - 2*Hcal_layer_air_gap;
-	x_halfLength = x_length/2.;
-
-      } else {//----- top barrel -------------------------------------------------
-	double y_layerID = logical_layer_id * (Hcal_radiator_thickness + Hcal_chamber_thickness) + Hcal_inner_radius;
-	double ro_layer = Hcal_outer_radius - Hcal_radiator_thickness;
-	
-	x_total = sqrt( ro_layer * ro_layer - y_layerID * y_layerID);
-	
-	x_length = x_total - Hcal_middle_stave_gaps;
-	
-	x_halfLength = x_length/2.;
-	
-	xOffset = (logical_layer_id * Hcal_radiator_thickness 
-		   + (logical_layer_id - 1) * Hcal_chamber_thickness - Hcal_y_dim1_for_x) / TanPiDiv8
-	  + Hcal_chamber_thickness / TanPiDiv8;
-	
-      }
+    double TanPiDiv8 = tan(M_PI / Hcal_inner_symmetry);
+    double x_total = 0.;
+    // double x_halfLength;
+    x_halflength = 0.;
 
-      double xAbsShift = (Hcal_middle_stave_gaps/2 + Hcal_layer_air_gap + x_halfLength);
-      
-      if (layer_id <= Hcal_nlayers)     xShift = - xAbsShift;
-      else if (layer_id > Hcal_nlayers) xShift = xAbsShift;
-
-      x_length = x_length/2.;
-
-      
-      //calculate the size of a fractional tile
-      //-> this sets fract_cell_dim_x
-      
-      //double fract_cell_dim_x = 0.;
-      //this->CalculateFractTileSize(2*x_length, Hcal_cell_dim_x, fract_cell_dim_x);
-      
-      //Vector newFractCellDim(fract_cell_dim_x, Hcal_chamber_thickness, Hcal_cell_dim_z);
-      //theBarrilRegSD->SetFractCellDimPerLayer(layer_id, newFractCellDim);
-
-      encoder["layer"] = logical_layer_id ;
-      cellSizeVector = seg.segmentation()->cellDimensions( encoder.getValue() ); 
-      cell_sizeX      = cellSizeVector[0];
-      cell_sizeY      = cellSizeVector[1];
-
-      LayeredCalorimeterData::Layer caloLayer ;
-      caloLayer.cellSize0 = cell_sizeX;
-      caloLayer.cellSize1 = cell_sizeY;
-
-
-      //--------------------------------------------------------------------------------
-      //  build chamber box, with the calculated dimensions 
-      //-------------------------------------------------------------------------------
-      printout( dd4hep::DEBUG,  "SHcalSc04_Barrel_v04", " \n Start to build layer chamber - layer_id: %d", layer_id ) ;
-      printout( dd4hep::DEBUG,  "SHcalSc04_Barrel_v04"," chamber x:y:z:  %e:%e:%e", x_length*2., z_width*2. , y_height*2. );
-
-      //check if x_length (scintillator length) is divisible with x_integerTileSize
-      if( layer_id <= Hcal_nlayers) {
-	double fracPart, intPart;
-	double temp = x_length*2./cell_sizeX;
-	fracPart = modf(temp, &intPart);
-	int noOfIntCells = int(temp);
-
-
-	if( tileSeg !=0 ){
-
-	  tileSeg->setBoundaryLayerX(x_length);
-	  
-	  if (fracPart == 0){ //divisible
-	    if ( noOfIntCells%2 ) {
-	      if( tileSeg !=0 ) tileSeg->setLayerOffsetX(0);
-	    }
-	    else {
-	      if( tileSeg !=0 ) tileSeg->setLayerOffsetX(1);
-	    }
-	    tileSeg->setFractCellSizeXPerLayer(0);
-	  }
-	  else if (fracPart>0){
-	    if ( noOfIntCells%2 ) {
-	      if( tileSeg !=0 ) tileSeg->setLayerOffsetX(1);
-	    }
-	    else {
-	      if( tileSeg !=0 ) tileSeg->setLayerOffsetX(0);
-	    }
-	    tileSeg->setFractCellSizeXPerLayer( (fracPart+1.0)/2.0*cell_sizeX );
-	  }
-	  
-	  if ( (int)( (z_width*2.) / cell_sizeX)%2 ){
-	    if( tileSeg !=0 ) tileSeg->setLayerOffsetY(0);
-	  }
-	  else {
-	    if( tileSeg !=0 ) tileSeg->setLayerOffsetY(1);
-	  }
-
-	}
-      }
-      Box ChamberSolid((x_length + Hcal_layer_air_gap),  //x + air gaps at two side, do not need to build air gaps individualy.
-			 z_width,   //z attention!
-			 y_height); //y attention!
+    // int layer_id = 0;
 
-      string ChamberLogical_name      = det_name+_toString(layer_id,"_layer%d");
+    // if ((layer_id < Hcal_nlayers) || (layer_id > Hcal_nlayers && layer_id < (2 * Hcal_nlayers)))
+    //   layer_id = layer_id % Hcal_nlayers;
+    // else if ((layer_id == Hcal_nlayers) || (layer_id == 2 * Hcal_nlayers))
+    //   layer_id = Hcal_nlayers;
 
-      Volume ChamberLogical(ChamberLogical_name, ChamberSolid, air);   
+    //---- bottom barrel------------------------------------------------------------
+    // if (layer_id * (Hcal_radiator_thickness + Hcal_chamber_thickness) < (Hcal_outer_radius * cos(M_PI / Hcal_inner_symmetry) - Hcal_inner_radius))
+    // {
+    xOffset = (layer_id * Hcal_radiator_thickness + (layer_id - 1) * Hcal_chamber_thickness) * TanPiDiv8;
 
+    x_total = Hcal_bottom_dim_x - Hcal_middle_stave_gaps + xOffset * 2;
+    x_halflength = x_total - 2 * Hcal_layer_air_gap;
+    // x_halfLength = x_halflength / 2.;
+    // }
+    // else
+    // { //----- top barrel -------------------------------------------------
+    //   double y_layerID = layer_id * (Hcal_radiator_thickness + Hcal_chamber_thickness) + Hcal_inner_radius;
+    //   double ro_layer = Hcal_outer_radius - Hcal_radiator_thickness;
 
+    //   x_total = sqrt(ro_layer * ro_layer - y_layerID * y_layerID);
 
-      double layer_thickness = y_height*2.;
+    //   x_halflength = x_total - Hcal_middle_stave_gaps;
 
-      double nRadiationLengths=0.;
-      double nInteractionLengths=0.;
-      double thickness_sum=0;
+    //   x_halfLength = x_halflength / 2.;
 
-      nRadiationLengths   = Hcal_radiator_thickness/(stavesMaterial.radLength());
-      nInteractionLengths = Hcal_radiator_thickness/(stavesMaterial.intLength());
-      thickness_sum       = Hcal_radiator_thickness;
+    //   xOffset = (layer_id * Hcal_radiator_thickness + (layer_id - 1) * Hcal_chamber_thickness - Hcal_y_dim1_for_x) / TanPiDiv8 + Hcal_chamber_thickness / TanPiDiv8;
+    // }
 
-//====================================================================
-// Create Hcal Barrel Chamber without radiator
-// Place into the Hcal Barrel stave, after each radiator 
-//====================================================================
-      xml_coll_t c(x_det,_U(layer));
-      xml_comp_t   x_layer = c;
-      string layer_name      = det_name+_toString(layer_id,"_layer%d");
-      
-      // Create the slices (sublayers) within the Hcal Barrel Chamber.
-      double slice_pos_z = layer_thickness/2.;
-      int slice_number = 0;
-
-      for(xml_coll_t k(x_layer,_U(slice)); k; ++k)  {
-	xml_comp_t x_slice = k;
-	string   slice_name      = layer_name + _toString(slice_number,"_slice%d");
-	double   slice_thickness = x_slice.thickness();
-	Material slice_material  = theDetector.material(x_slice.materialStr());
-	DetElement slice(layer_name,_toString(slice_number,"slice%d"),x_det.id());
-	
-	slice_pos_z -= slice_thickness/2.;
-	
-	// Slice volume & box
-	Volume slice_vol(slice_name,Box(x_length,z_width,slice_thickness/2.),slice_material);
-
-	nRadiationLengths   += slice_thickness/(2.*slice_material.radLength());
-	nInteractionLengths += slice_thickness/(2.*slice_material.intLength());
-	thickness_sum       += slice_thickness/2;
-	
-	if ( x_slice.isSensitive() ) {
-
-	  slice_vol.setSensitiveDetector(sens);
-
-	  // if we have a multisegmentation based on slices, we need to use the correct slice here
-	  if ( sensitive_slice_number<0  || sensitive_slice_number == slice_number ) {
-	
-	    //Store "inner" quantities
-	    caloLayer.inner_nRadiationLengths   = nRadiationLengths;
-	    caloLayer.inner_nInteractionLengths = nInteractionLengths;
-	    caloLayer.inner_thickness = thickness_sum;
-	    //Store scintillator thickness
-	    caloLayer.sensitive_thickness = slice_thickness;
-	    
-	    //Reset counters to measure "outside" quantitites
-	    nRadiationLengths=0.;
-	    nInteractionLengths=0.;
-	    thickness_sum = 0.;
-	  }
-	}
-
-	nRadiationLengths   += slice_thickness/(2.*slice_material.radLength());
-	nInteractionLengths += slice_thickness/(2.*slice_material.intLength());
-	thickness_sum       += slice_thickness/2;
-
-
-	// Set region, limitset, and vis.
-	slice_vol.setAttributes(theDetector,x_slice.regionStr(),x_slice.limitsStr(),x_slice.visStr());
-	// slice PlacedVolume
-	PlacedVolume slice_phv = ChamberLogical.placeVolume(slice_vol,Position(0.,0.,slice_pos_z));
-
-	slice_phv.addPhysVolID("layer",logical_layer_id).addPhysVolID("slice", slice_number );
-
-	
-	if ( x_slice.isSensitive() ) {
-	  int tower_id  = (layer_id > Hcal_nlayers)? 1:-1;
-	  slice_phv.addPhysVolID("tower",tower_id);
-	  printout( dd4hep::DEBUG,  "SHcalSc04_Barrel_v04", "  logical_layer_id:  %d  tower_id:  %d", logical_layer_id, tower_id  ) ;
-	}
-	
-	slice.setPlacement(slice_phv);
-	// Increment x position for next slice.
-	slice_pos_z -= slice_thickness/2.;
-	// Increment slice number.
-	++slice_number;             
-      }
+    x_halflength = x_halflength / 2.;
 
-      //Store "outer" quantities
-      caloLayer.outer_nRadiationLengths = nRadiationLengths;
-      caloLayer.outer_nInteractionLengths = nInteractionLengths;
-      caloLayer.outer_thickness = thickness_sum;
+    LayeredCalorimeterData::Layer caloLayer;
+    caloLayer.cellSize0 = Hcal_cell_size;
+    caloLayer.cellSize1 = Hcal_cell_size;
 
+    //--------------------------------------------------------------------------------
+    //  build chamber box, with the calculated dimensions
+    //-------------------------------------------------------------------------------
+    printout(dd4hep::DEBUG, "SHcalSc04_Barrel_v04", " \n Start to build layer chamber - layer_id: %d", layer_id);
+    printout(dd4hep::DEBUG, "SHcalSc04_Barrel_v04", " chamber x:y:z:  %e:%e:%e", x_halflength * 2., z_halfwidth * 2., y_halfheight * 2.);
 
-//---------------------------  Chamber Placements -----------------------------------------
+    // check if x_halflength (scintillator length) is divisible with x_integerTileSize
 
-      double chamber_x_offset, chamber_y_offset, chamber_z_offset;
-      chamber_x_offset = xShift;
+    Box ChamberSolid((x_halflength + Hcal_layer_air_gap), // x + air gaps at two side, do not need to build air gaps individualy.
+                     z_halfwidth,                         // z attention!
+                     y_halfheight);                       // y attention!
 
-      chamber_z_offset = 0;
+    string ChamberLogical_name = det_name + _toString(layer_id, "_layer%d");
 
+    Volume ChamberLogical(ChamberLogical_name, ChamberSolid, air);
+    ChamberLogical.setAttributes(theDetector, x_layer_tmp.regionStr(), x_layer_tmp.limitsStr(), x_layer_tmp.visStr());
 
-      chamber_y_offset = -(-Hcal_total_dim_y/2. 
-			   + (logical_layer_id-1) *(Hcal_chamber_thickness + Hcal_radiator_thickness)
-			   + Hcal_radiator_thickness + Hcal_chamber_thickness/2.);
+    double layer_thickness = y_halfheight * 2.;
 
+    double nRadiationLengths = 0.;
+    double nInteractionLengths = 0.;
+    double thickness_sum = 0;
 
-      pv =  EnvLogHcalModuleBarrel.placeVolume(ChamberLogical,
-					       Position(chamber_x_offset,
-							chamber_z_offset,
-							chamber_y_offset + chambers_y_off_correction));
-      
+    nRadiationLengths = Hcal_radiator_thickness / (stavesMaterial.radLength());
+    nInteractionLengths = Hcal_radiator_thickness / (stavesMaterial.intLength());
+    thickness_sum = Hcal_radiator_thickness;
 
+    //====================================================================
+    // Create Hcal Barrel Chamber without radiator
+    // Place into the Hcal Barrel stave, after each radiator
+    //====================================================================
+    xml_coll_t c(x_det, _U(layer));
+    xml_comp_t x_layer = c;
+    string layer_name = det_name + _toString(layer_id, "_layer%d");
 
-      //-----------------------------------------------------------------------------------------
-      if( layer_id <= Hcal_nlayers ){ // add the first set of layers to the reconstruction data
-	
-	caloLayer.distance = Hcal_inner_radius + Hcal_total_dim_y/2.0 - (chamber_y_offset + chambers_y_off_correction)
-	  - caloLayer.inner_thickness ; // Will be added later at "DDMarlinPandora/DDGeometryCreator.cc:226" to get center of sensitive element
+    // Create the slices (sublayers) within the Hcal Barrel Chamber.
+    double slice_pos_z = layer_thickness / 2.;
+    int slice_number = 0;
 
-	caloLayer.absorberThickness = Hcal_radiator_thickness ;
-	
-	caloData->layers.push_back( caloLayer ) ;
+    for (xml_coll_t k(x_layer, _U(slice)); k; ++k)
+    {
+      xml_comp_t x_slice = k;
+      string slice_name = layer_name + _toString(slice_number, "_slice%d");
+      double slice_thickness = x_slice.thickness();
+      Material slice_material = theDetector.material(x_slice.materialStr());
+      DetElement slice(layer_name, _toString(slice_number, "slice%d"), x_det.id());
+
+      slice_pos_z -= slice_thickness / 2.;
+
+      // Slice volume & box
+      Volume slice_vol(slice_name, Box(x_halflength, z_halfwidth, slice_thickness / 2.), slice_material);
+      slice_vol.setAttributes(theDetector, x_slice.regionStr(), x_slice.limitsStr(), x_slice.visStr());
+
+      nRadiationLengths += slice_thickness / (2. * slice_material.radLength());
+      nInteractionLengths += slice_thickness / (2. * slice_material.intLength());
+      thickness_sum += slice_thickness / 2;
+      if (x_slice.isSensitive())
+      {
+        // Store "inner" quantities
+        caloLayer.inner_nRadiationLengths = nRadiationLengths;
+        caloLayer.inner_nInteractionLengths = nInteractionLengths;
+        caloLayer.inner_thickness = thickness_sum;
+        // Store scintillator thickness
+        caloLayer.sensitive_thickness = Hcal_scintillator_thickness;
+
+        double cell_size = 2 * Hcal_scintillator_ESR_thickness + Hcal_cell_size;
+        double cell_size_abnormal = 2 * Hcal_scintillator_ESR_thickness + Hcal_cell_size_abnormal;
+        double cell_x_offset = 0, cell_z_offset = 0, cell_y_offset = 0;
+        // place cell into slice one by one
+        double total_cell_size = cell_size + Hcal_scintillator_air_gap;                   // include air gap
+        double total_cell_size_abnormal = cell_size_abnormal + Hcal_scintillator_air_gap; // include air gap
+        int n_x = (2 * x_halflength) / total_cell_size;
+        int n_z = (2 * z_halfwidth) / total_cell_size;
+        int nx_abnormal = (2 * x_halflength - n_x * total_cell_size) / (total_cell_size_abnormal - total_cell_size);
+        n_x -= nx_abnormal;
+        // if(layer_id==1){n_x_layer_1=n_x;}
+        // else{
+        //   n_x=(n_x-n_x_layer_1)/2;
+        //   n_x=n_x*2+n_x_layer_1;
+        // }
+        for (int index_cell_z = 0; index_cell_z < n_z; index_cell_z++)
+        {
+          cell_x_offset = (n_x * total_cell_size + nx_abnormal * total_cell_size_abnormal) / 2.;
+          cell_z_offset = (n_z / 2. - index_cell_z - 0.5) * total_cell_size;
+          for (int index_cell_x = 0; index_cell_x < n_x; index_cell_x++)
+          {
+            cell_x_offset -= total_cell_size / 2.;
+            int cell_number = index_cell_x + 100 * index_cell_z;
+            string cell_name = slice_name + _toString(cell_number, "_cell%d");
+            string scintillator_name = cell_name + "_scintillator";
+            // cell_y_offset = slice_thickness / 2.;
+            PlacedVolume cell_phv = slice_vol.placeVolume(cell_vol, Position(cell_x_offset, cell_z_offset, cell_y_offset));
+            cell_phv.addPhysVolID("tile", cell_number);
+
+            DetElement cell(cell_name, _toString(cell_number, "cell%d"), cell_number);
+            cell.setPlacement(cell_phv);
+            cell_x_offset -= total_cell_size / 2.;
+          }
+          for (int index_cell_x = 0; index_cell_x < nx_abnormal; index_cell_x++)
+          {
+            cell_x_offset -= total_cell_size_abnormal / 2.;
+            int cell_number = index_cell_x + n_x + 100 * index_cell_z;
+            string cell_name = slice_name + _toString(cell_number, "_cell%d");
+            string scintillator_name = cell_name + "_scintillator";
+            // cell_y_offset = slice_thickness / 2.;
+            PlacedVolume cell_phv = slice_vol.placeVolume(cell_vol_abnormal, Position(cell_x_offset, cell_z_offset, cell_y_offset));
+            cell_phv.addPhysVolID("tile", cell_number);
+
+            DetElement cell(cell_name, _toString(cell_number, "cell%d"), cell_number);
+            cell.setPlacement(cell_phv);
+            cell_x_offset -= total_cell_size_abnormal / 2.;
+          }
+        }
+        printf("layerID: %2d, x_length: %3.3lf, x_cell: %3.3lf, x_cell_abnormal: %3.3lf, x_dead: %3.3lf, z_length: %3.3lf,z_cell: %3.3lf, z_dead: %3.3lf,\n", layer_id,  2 * x_halflength,  n_x * total_cell_size,  nx_abnormal * total_cell_size_abnormal,  2 * x_halflength - n_x * total_cell_size - nx_abnormal * total_cell_size_abnormal,  2 * z_halfwidth,  n_z * total_cell_size,  2 * z_halfwidth - n_z * total_cell_size);
+        // Reset counters to measure "outside" quantitites
+        nRadiationLengths = 0.;
+        nInteractionLengths = 0.;
+        thickness_sum = 0.;
+        // }
       }
-      //-----------------------------------------------------------------------------------------
-
-      
-    }//end loop over HCAL nlayers;
-
-  if( tileSeg !=0 ){
-    // check the offsets directly in the TileSeg ...
-    std::vector<double> LOX = tileSeg->layerOffsetX();
-    std::vector<double> LOY = tileSeg->layerOffsetY();
-
-    std::stringstream sts ;
-    sts <<" layerOffsetX(): ";
-    for (std::vector<double>::const_iterator ix = LOX.begin(); ix != LOX.end(); ++ix)
-      sts << *ix << ' ';
-    printout( dd4hep::DEBUG,  "SHcalSc04_Barrel_v04", "%s" , sts.str().c_str() ) ;
-    sts.clear() ; sts.str("") ;
-    sts <<" layerOffsetY(): ";
-    for (std::vector<double>::const_iterator iy = LOY.begin(); iy != LOY.end(); ++iy)
-      sts << *iy << ' ';
-    printout( dd4hep::DEBUG,  "SHcalSc04_Barrel_v04", "%s" , sts.str().c_str() ) ;
+      nRadiationLengths += slice_thickness / (2. * slice_material.radLength());
+      nInteractionLengths += slice_thickness / (2. * slice_material.intLength());
+      thickness_sum += slice_thickness / 2;
+
+      // Set region, limitset, and vis.
+      // slice_vol.setAttributes(theDetector, x_slice.regionStr(), x_slice.limitsStr(), x_slice.visStr());
+      // slice PlacedVolume
+      PlacedVolume slice_phv = ChamberLogical.placeVolume(slice_vol, Position(0., 0., slice_pos_z));
+
+      slice_phv.addPhysVolID("layer", layer_id);
+      slice.setPlacement(slice_phv);
+      // Increment z position for next slice.
+      slice_pos_z -= slice_thickness / 2.;
+      // Increment slice number.
+      ++slice_number;
+    }
 
-  }
+    // Store "outer" quantities
+    caloLayer.outer_nRadiationLengths = nRadiationLengths;
+    caloLayer.outer_nInteractionLengths = nInteractionLengths;
+    caloLayer.outer_thickness = thickness_sum;
 
+    //---------------------------  Chamber Placements -----------------------------------------
 
+    double chamber_x_offset, chamber_y_offset, chamber_z_offset;
+    chamber_x_offset = xShift;
 
-//====================================================================
-// Place HCAL Barrel stave module into the envelope
-//====================================================================
-  double stave_phi_offset,  module_z_offset,  lateral_plate_z_offset;
+    chamber_z_offset = 0;
 
-  double Y = Hcal_inner_radius + Hcal_total_dim_y / 2.;
+    chamber_y_offset = -(-Hcal_total_dim_y / 2. + (layer_id - 1) * (Hcal_chamber_thickness + Hcal_radiator_thickness) + Hcal_radiator_thickness + Hcal_chamber_thickness / 2.);
 
-  stave_phi_offset = M_PI/Hcal_inner_symmetry -M_PI/2.;
+    pv = EnvLogHcalModuleBarrel.placeVolume(ChamberLogical,
+                                            Position(chamber_x_offset, chamber_z_offset, chamber_y_offset));
 
+    //-----------------------------------------------------------------------------------------
+    if (layer_id <= Hcal_nlayers)
+    { // add the first set of layers to the reconstruction data
 
-  //-------- start loop over HCAL BARREL staves ----------------------------
+      caloLayer.distance = Hcal_inner_radius + Hcal_total_dim_y / 2.0 - chamber_y_offset - caloLayer.inner_thickness;
+      // Will be added later at "DDMarlinPandora/DDGeometryCreator.cc:226" to get center of sensitive element
 
-  for (int stave_id = 1;
-       stave_id <=Hcal_inner_symmetry;
-       stave_id++)
-    {
-      module_z_offset = - (Hcal_normal_dim_z + Hcal_modules_gap + Hcal_lateral_plate_thickness)/2.;
-      lateral_plate_z_offset = - (Hcal_lateral_plate_thickness + Hcal_modules_gap)/2.;
+      caloLayer.absorberThickness = Hcal_radiator_thickness;
 
-      double phirot = stave_phi_offset;
-      RotationZYX srot(0,phirot,M_PI*0.5);
-      Rotation3D srot3D(srot);
+      caloData->layers.push_back(caloLayer);
+    }
+    //-----------------------------------------------------------------------------------------
 
-      for (int module_id = 1;
-         module_id <=2;
-         module_id++)
-	{
-	  Position sxyzVec(-Y*sin(phirot), Y*cos(phirot), module_z_offset);
-	  Transform3D stran3D(srot3D,sxyzVec);
-	  
-	  // Place Hcal Barrel volume into the envelope volume
-	  pv = envelope.placeVolume(EnvLogHcalModuleBarrel,stran3D);
-	  pv.addPhysVolID("stave",stave_id);
-	  pv.addPhysVolID("module",module_id);
-	  pv.addPhysVolID("system",det_id);
+  } // end loop over HCAL nlayers;
 
-	  const int staveCounter = (stave_id-1)*2+module_id-1;
-	  DetElement stave(sdet, _toString(staveCounter,"stave%d"), staveCounter );
-	  stave.setPlacement(pv);
+  //====================================================================
+  // Place HCAL Barrel stave module into the envelope
+  //====================================================================
+  double stave_phi_offset, module_z_offset = 0;
 
-	  Position xyzVec_LP(-Y*sin(phirot), Y*cos(phirot),lateral_plate_z_offset);
-	  Transform3D tran3D_LP(srot3D,xyzVec_LP);
-	  pv = envelope.placeVolume(EnvLogHcalModuleBarrel_LP,tran3D_LP);
+  double Y = Hcal_inner_radius + Hcal_total_dim_y / 2.;
 
-	  module_z_offset = - module_z_offset;
-	  lateral_plate_z_offset = - lateral_plate_z_offset;
-	}
+  stave_phi_offset = M_PI / Hcal_inner_symmetry - M_PI / 2.;
 
+  //-------- start loop over HCAL BARREL staves ----------------------------
 
-      stave_phi_offset -=  M_PI*2.0/Hcal_inner_symmetry;
-    }  //-------- end loop over HCAL BARREL staves ----------------------------
+  for (int stave_id = 1; stave_id <= Hcal_inner_symmetry; stave_id++)
+  {
+    double phirot = stave_phi_offset;
+    RotationZYX srot(0, phirot, M_PI * 0.5);
+    Rotation3D srot3D(srot);
 
+    // for (int module_id = 1; module_id <= 2; module_id++)
+    // {
+    Position sxyzVec(-Y * sin(phirot), Y * cos(phirot), module_z_offset);
+    Transform3D stran3D(srot3D, sxyzVec);
 
-  sdet.addExtension< LayeredCalorimeterData >( caloData ) ;
+    // Place Hcal Barrel volume into the envelope volume
+    pv = envelope.placeVolume(EnvLogHcalModuleBarrel, stran3D);
+    pv.addPhysVolID("stave", stave_id);
+    // pv.addPhysVolID("module", module_id);
+    pv.addPhysVolID("system", det_id);
 
- 
-  return sdet;
+    // const int staveCounter = (stave_id - 1) * 2 + module_id - 1;
+    const int staveCounter = stave_id - 1;
+    DetElement stave(sdet, _toString(staveCounter, "stave%d"), staveCounter);
+    stave.setPlacement(pv);
 
+    // Position xyzVec_LP(-Y * sin(phirot), Y * cos(phirot), lateral_plate_z_offset);
+    // Transform3D tran3D_LP(srot3D, xyzVec_LP);
+    // pv = envelope.placeVolume(EnvLogHcalModuleBarrel_LP, tran3D_LP);
+
+    // module_z_offset = -module_z_offset;
+    // lateral_plate_z_offset = -lateral_plate_z_offset;
+    // }
+
+    stave_phi_offset -= M_PI * 2.0 / Hcal_inner_symmetry;
+  } //-------- end loop over HCAL BARREL staves ----------------------------
+
+  sdet.addExtension<LayeredCalorimeterData>(caloData);
+
+  return sdet;
 }
 
 DECLARE_DETELEMENT(SHcalSc04_Barrel_v04, create_detector)
diff --git a/Detector/DetCEPCv4/src/calorimeter/SHcalSc04_Endcaps_v02.cpp b/Detector/DetCEPCv4/src/calorimeter/SHcalSc04_Endcaps_v02.cpp
new file mode 100644
index 00000000..06db6d6b
--- /dev/null
+++ b/Detector/DetCEPCv4/src/calorimeter/SHcalSc04_Endcaps_v02.cpp
@@ -0,0 +1,411 @@
+//================================================================================
+// Description — End-Cap AHCAL
+//--------------------------------------------------------------------------------
+// Author: Ji-Yuan CHEN (SJTU; jy_chen@sjtu.edu.cn)
+//--------------------------------------------------------------------------------
+//
+// End-cap AHCAL with 40×40×3 mm³ scintillator tiles.
+// Adding the dead materials (cassette, PCB and electronic components, ESR wrapper and steel absorber) and including the air gaps, the size of each cell becomes 40.3×40.3×27.2 mm³.
+//
+// The inner radius, end-cap thickness, unit size, etc. are directly read from XML file.
+//
+// Structure in a layer: Cassette → ESR → scintillator → ESR → PCB (including electronic components) → cassette → steel absorber.
+//
+// Default layout: The absorber is designed as a whole with some space left for the sensitive units (the cross-section is not a polygon); steel frame on the edges of each module; 16 modules (called 'staves' in this program), 48 layers; 72 rows in r direction, with uniform granularity.  For a schematic diagram, see Page 3 of  <https://indico.ihep.ac.cn/event/22010/contributions/153187/attachments/77666/96430/2024_0324_Calorimeter_Endcaps.pdf>  (the design on the left; the numbers have been modified).
+//================================================================================
+
+#include "DD4hep/DetFactoryHelper.h"
+#include "DD4hep/DD4hepUnits.h"
+#include "DD4hep/Shapes.h"
+#include "DD4hep/DetType.h"
+#include "XML/Layering.h"
+#include "XML/Utilities.h"
+#include "DDRec/DetectorData.h"
+#include "DDSegmentation/Segmentation.h"
+#include <sstream>
+#include <cassert>
+
+using std::cout;
+using std::endl;
+using std::string;
+using std::vector;
+using std::to_string;
+
+#define MYDEBUG(x) cout << __FILE__ << ":" << __LINE__ << ": " << x << endl;
+#define MYDEBUGVAL(x) cout << __FILE__ << ":" << __LINE__ << ": "<< #x << ": " << x << endl;
+
+using dd4hep::Ref_t;
+using dd4hep::Detector;
+using dd4hep::SensitiveDetector;
+using dd4hep::pi;
+using dd4hep::mm;
+using dd4hep::Material;
+using dd4hep::DetElement;
+using dd4hep::Volume;
+using dd4hep::PolyhedraRegular;
+using dd4hep::Tube;
+using dd4hep::UnionSolid;
+using dd4hep::Trapezoid;
+using dd4hep::Box;
+using dd4hep::SubtractionSolid;
+using dd4hep::PlacedVolume;
+using dd4hep::Position;
+using dd4hep::RotationX;
+using dd4hep::_toString;
+using dd4hep::Transform3D;
+using dd4hep::RotationZ;
+using dd4hep::RotationY;
+
+static Ref_t create_detector(Detector& theDetector, xml_h e, SensitiveDetector sens)
+{
+    xml_det_t x_det = e;
+
+    const string det_name = x_det.nameStr();
+    const string det_type = x_det.typeStr();
+    const int det_id = x_det.id();
+    MYDEBUGVAL(det_name)
+    MYDEBUGVAL(det_type)
+    MYDEBUGVAL(det_id)
+
+    // To prevent overlapping
+    const double boundary_safety = theDetector.constant<double>("boundary_safety");
+
+    // Global geometry
+    const double r_in = theDetector.constant<double>("hcalendcap_inner_radius");
+    const double r_out = theDetector.constant<double>("hcalendcap_outer_radius");
+    const double module_thickness = theDetector.constant<double>("hcalendcap_thickness");
+    const double pos_z = theDetector.constant<double>("hcalendcap_z");
+    const int Nmodules = theDetector.constant<int>("Nmodules");
+    const double angle = 2 * pi / Nmodules;
+
+    // Unit size
+    const double scintillator_xy = theDetector.constant<double>("scintillator_xy");
+    const double scintillator_z = theDetector.constant<double>("scintillator_z");
+    const double wrapped_scintillator_xy = theDetector.constant<double>("wrapped_scintillator_xy");
+    const double wrapped_scintillator_z = theDetector.constant<double>("wrapped_scintillator_z");
+
+    // Dead materials
+    const double cassette_thickness = theDetector.constant<double>("cassette_thickness"); 
+    const double esr_thickness = theDetector.constant<double>("esr_thickness");    // Wrapper
+    [[maybe_unused]] const double sipm_xy = theDetector.constant<double>("sipm_xy");
+    [[maybe_unused]] const double sipm_z = theDetector.constant<double>("sipm_z");
+    const double pcb_thickness = theDetector.constant<double>("pcb_thickness"); 
+    const double absorber_thickness = theDetector.constant<double>("absorber_thickness"); 
+    const double air_gap_xy = 0.5 * (wrapped_scintillator_xy - scintillator_xy) - esr_thickness;
+    [[maybe_unused]] const double air_gap_z = 0.5 * (wrapped_scintillator_z - scintillator_z) - esr_thickness;
+
+    // Odd-shaped cells
+    const int Nodd = theDetector.constant<int>("Nodd");
+    const double short_elongation_1 = theDetector.constant<double>("short_elongation_1");
+    const double short_elongation_2 = theDetector.constant<double>("short_elongation_2");
+    const double short_elongation_3 = theDetector.constant<double>("short_elongation_3");
+    const double short_elongation_4 = theDetector.constant<double>("short_elongation_4");
+    const double short_elongation_5 = theDetector.constant<double>("short_elongation_5");
+
+    const double elongation_angle_esr_out = wrapped_scintillator_xy * tan(0.5 * angle);    // Elongation of the outer edge of ESR: long side - short side
+    const double elongation_angle_esr_in = (wrapped_scintillator_xy - esr_thickness / cos(0.5 * angle)) * tan(0.5 * angle);    // Elongation of the inner edge of ESR: long side - short side
+    const double elongation_angle_sc = scintillator_xy * tan(0.5 * angle);    // Elongation of the scintillator: long side - short side
+    const vector<double> short_elongation_esr_out = { short_elongation_1, short_elongation_2, short_elongation_3, short_elongation_4, short_elongation_5 };
+    assert(Nodd == short_elongation_esr_out.size());
+
+    vector<double> short_elongation_sc(short_elongation_esr_out.size()), long_elongation_sc(short_elongation_esr_out.size()), short_elongation_esr_in(short_elongation_esr_out.size()), long_elongation_esr_in(short_elongation_esr_out.size()), long_elongation_esr_out(short_elongation_esr_out.size());
+    for (int i = 0; i < Nodd; ++i)
+    {
+        short_elongation_sc.at(i) = short_elongation_esr_out.at(i) - (air_gap_xy + esr_thickness) / cos(0.5 * angle);
+        long_elongation_sc.at(i) = short_elongation_sc.at(i) + elongation_angle_sc;
+        short_elongation_esr_in.at(i) = short_elongation_esr_out.at(i) - esr_thickness / cos(0.5 * angle);
+        long_elongation_esr_in.at(i) = short_elongation_esr_in.at(i) + elongation_angle_esr_in;
+        long_elongation_esr_out.at(i) = short_elongation_esr_out.at(i) + elongation_angle_esr_out;
+    }
+
+    // Mechanical structure
+    const double inner_structure_thickness = theDetector.constant<double>("inner_structure_thickness");
+    [[maybe_unused]] const double outer_structure_width = theDetector.constant<double>("outer_structure_width");
+    [[maybe_unused]] const double outer_structure_thickness = theDetector.constant<double>("outer_structure_thickness");
+    const double frame_thickness = theDetector.constant<double>("frame_thickness");
+
+    const double layer_thickness = wrapped_scintillator_z + pcb_thickness + 2 * cassette_thickness + absorber_thickness + 5 * boundary_safety;
+    const double non_absorber_thickness = wrapped_scintillator_z + pcb_thickness + 3 * boundary_safety;
+
+    MYDEBUGVAL(layer_thickness)
+
+    // Module size
+    const double dim_in_frame = (r_in + inner_structure_thickness + frame_thickness) * tan(0.5 * angle);
+    const double dim_out_frame = r_out * sin(0.5 * angle);
+    const double dim_in = dim_in_frame - frame_thickness / cos(0.5 * angle);
+    const double dim_out = dim_out_frame - frame_thickness / cos(0.5 * angle);
+    const double height = r_out * cos(0.5 * angle) - r_in - inner_structure_thickness;
+    const double r0 = r_in + inner_structure_thickness + 0.5 * height;    // Rotation radius for placing the modules
+
+    const int Nrows = (int) (height / (wrapped_scintillator_xy + boundary_safety));
+    const int Nlayers = (int) (module_thickness / layer_thickness);
+    int Ncells_phi;
+
+    MYDEBUGVAL(Nrows)
+    MYDEBUGVAL(Nlayers)
+
+    // Materials
+    Material mat_air(theDetector.material("Air"));
+    Material mat_PCB(theDetector.material("PCB"));
+    [[maybe_unused]] Material mat_SiPM(theDetector.material("G4_Si"));
+    Material mat_sensitive(theDetector.material( x_det.materialStr() ));
+    Material mat_ESR(theDetector.material("G4_ESR"));
+    Material mat_steel(theDetector.material("Steel235"));
+
+    // The detector and mother volumes (world)
+    DetElement AHCAL(det_name, det_id);
+    Volume motherVol = theDetector.pickMotherVolume(AHCAL);
+
+    // Create two tube-like envelopes to represent the end-cap volumes
+//    PolyhedraRegular envelope_side(Nmodules, 0.5 * angle, r_in + inner_structure_thickness, r_out, module_thickness);
+    Tube envelope_side(r_in, r_out, 0.5 * module_thickness);
+    UnionSolid envelope(envelope_side, envelope_side, Position(0, 0, 2 * pos_z));
+    Volume envelopeVol(det_name, envelope, mat_steel);
+    PlacedVolume envelopePlv = motherVol.placeVolume(envelopeVol, Position(0, 0, -pos_z));
+    envelopePlv.addPhysVolID("system", det_id);
+    envelopeVol.setVisAttributes(theDetector, "SeeThrough");
+    AHCAL.setPlacement(envelopePlv);
+    DetElement stave_det(AHCAL, "sector", det_id);
+
+    // Module (stave)
+    Trapezoid stave(dim_in, dim_out, 0.5 * module_thickness, 0.5 * module_thickness, 0.5 * height);
+    Volume stave_vol("stave_vol", stave, mat_steel);
+    stave_vol.setVisAttributes(theDetector, "BlueVis");
+
+    // Layer with only wrapped scintillators, electronic components and PCB
+    Trapezoid layer(dim_in, dim_out, 0.5 * non_absorber_thickness, 0.5 * non_absorber_thickness, 0.5 * height);
+    Volume layer_vol("layer_vol", layer, mat_steel);
+    layer_vol.setVisAttributes(theDetector, "SeeThrough");
+
+    // Scintillator, ESR, SiPM, PCB, cassette, and absorber
+    Box normal_scintillator(0.5 * scintillator_xy, 0.5 * scintillator_z, 0.5 * scintillator_xy);
+    Volume scintillator("scintillator", normal_scintillator, mat_sensitive);    // Order: phi → z → r
+    scintillator.setVisAttributes(theDetector, "SeeThrough");
+    scintillator.setSensitiveDetector(sens);
+
+    [[maybe_unused]] Volume sipm("SiPM", Box(0.5 * sipm_xy, 0.5 * sipm_xy, 0.5 * sipm_z), mat_SiPM);
+    sipm.setVisAttributes(theDetector, "CyanVis");
+
+    Box esr_out(0.5 * wrapped_scintillator_xy, 0.5 * wrapped_scintillator_z, 0.5 * wrapped_scintillator_xy);
+    Box esr_in(0.5 * wrapped_scintillator_xy - esr_thickness, 0.5 * wrapped_scintillator_z - esr_thickness, 0.5 * wrapped_scintillator_xy - esr_thickness);
+    Volume esr("esr", SubtractionSolid(esr_out, esr_in, Position(0, 0, 0)), mat_ESR);
+    esr.setVisAttributes(theDetector, "SeeThrough");
+
+    // Odd-shaped scintillators
+    Trapezoid odd_add_0_scintillator(0, long_elongation_sc.at(0) - short_elongation_sc.at(0), 0.5 * scintillator_z, 0.5 * scintillator_z, 0.5 * scintillator_xy);
+    Trapezoid odd_add_45_scintillator(short_elongation_sc.at(1), long_elongation_sc.at(1), 0.5 * scintillator_z, 0.5 * scintillator_z, 0.5 * scintillator_xy);
+    Trapezoid odd_add_9_scintillator(short_elongation_sc.at(2), long_elongation_sc.at(2), 0.5 * scintillator_z, 0.5 * scintillator_z, 0.5 * scintillator_xy);
+    Trapezoid odd_add_14_scintillator(short_elongation_sc.at(3), long_elongation_sc.at(3), 0.5 * scintillator_z, 0.5 * scintillator_z, 0.5 * scintillator_xy);
+    Trapezoid odd_add_17_scintillator(short_elongation_sc.at(4), long_elongation_sc.at(4), 0.5 * scintillator_z, 0.5 * scintillator_z, 0.5 * scintillator_xy);
+
+    Box odd_scintillator_0_rect(0.5 * (scintillator_xy + short_elongation_sc.at(0)), 0.5 * scintillator_z, 0.5 * scintillator_xy);
+
+    Volume odd_0_scintillator("odd_0_scintillator", UnionSolid(odd_scintillator_0_rect, odd_add_0_scintillator, Position(0.5 * (scintillator_xy + short_elongation_sc.at(0)), 0, 0)), mat_sensitive);
+    odd_0_scintillator.setVisAttributes(theDetector, "SeeThrough");
+    odd_0_scintillator.setSensitiveDetector(sens);
+
+    Volume odd_45_scintillator("odd_45_scintillator", UnionSolid(normal_scintillator, odd_add_45_scintillator, Position(0.5 * scintillator_xy, 0, 0)), mat_sensitive);
+    odd_45_scintillator.setVisAttributes(theDetector, "SeeThrough");
+    odd_45_scintillator.setSensitiveDetector(sens);
+
+    Volume odd_9_scintillator("odd_9_scintillator", UnionSolid(normal_scintillator, odd_add_9_scintillator, Position(0.5 * scintillator_xy, 0, 0)), mat_sensitive);
+    odd_9_scintillator.setVisAttributes(theDetector, "SeeThrough");
+    odd_9_scintillator.setSensitiveDetector(sens);
+
+    Volume odd_14_scintillator("odd_14_scintillator", UnionSolid(normal_scintillator, odd_add_14_scintillator, Position(0.5 * scintillator_xy, 0, 0)), mat_sensitive);
+    odd_14_scintillator.setVisAttributes(theDetector, "SeeThrough");
+    odd_14_scintillator.setSensitiveDetector(sens);
+
+    Volume odd_17_scintillator("odd_17_scintillator", UnionSolid(normal_scintillator, odd_add_17_scintillator, Position(0.5 * scintillator_xy, 0, 0)), mat_sensitive);
+    odd_17_scintillator.setVisAttributes(theDetector, "SeeThrough");
+    odd_17_scintillator.setSensitiveDetector(sens);
+
+    // Odd-shaped ESR
+    // Outer edge
+    Trapezoid odd_add_0_esr_out(short_elongation_esr_out.at(0), long_elongation_esr_out.at(0), 0.5 * wrapped_scintillator_z, 0.5 * wrapped_scintillator_z, 0.5 * wrapped_scintillator_xy);
+    Trapezoid odd_add_45_esr_out(short_elongation_esr_out.at(1), long_elongation_esr_out.at(1), 0.5 * wrapped_scintillator_z, 0.5 * wrapped_scintillator_z, 0.5 * wrapped_scintillator_xy);
+    Trapezoid odd_add_9_esr_out(short_elongation_esr_out.at(2), long_elongation_esr_out.at(2), 0.5 * wrapped_scintillator_z, 0.5 * wrapped_scintillator_z, 0.5 * wrapped_scintillator_xy);
+    Trapezoid odd_add_14_esr_out(short_elongation_esr_out.at(3), long_elongation_esr_out.at(3), 0.5 * wrapped_scintillator_z, 0.5 * wrapped_scintillator_z, 0.5 * wrapped_scintillator_xy);
+    Trapezoid odd_add_17_esr_out(short_elongation_esr_out.at(4), long_elongation_esr_out.at(4), 0.5 * wrapped_scintillator_z, 0.5 * wrapped_scintillator_z, 0.5 * wrapped_scintillator_xy);
+
+    UnionSolid odd_0_esr_out(esr_out, odd_add_0_esr_out, Position(0.5 * wrapped_scintillator_xy, 0, 0));
+    UnionSolid odd_45_esr_out(esr_out, odd_add_45_esr_out, Position(0.5 * wrapped_scintillator_xy, 0, 0));
+    UnionSolid odd_9_esr_out(esr_out, odd_add_9_esr_out, Position(0.5 * wrapped_scintillator_xy, 0, 0));
+    UnionSolid odd_14_esr_out(esr_out, odd_add_14_esr_out, Position(0.5 * wrapped_scintillator_xy, 0, 0));
+    UnionSolid odd_17_esr_out(esr_out, odd_add_17_esr_out, Position(0.5 * wrapped_scintillator_xy, 0, 0));
+
+    // Inner edge
+    Trapezoid odd_add_0_esr_in(0, long_elongation_esr_in.at(0) - short_elongation_esr_in.at(0), 0.5 * wrapped_scintillator_z - esr_thickness, 0.5 * wrapped_scintillator_z - esr_thickness, 0.5 * wrapped_scintillator_xy - esr_thickness);
+    Trapezoid odd_add_45_esr_in(short_elongation_esr_in.at(1), long_elongation_esr_in.at(1), 0.5 * wrapped_scintillator_z - esr_thickness, 0.5 * wrapped_scintillator_z - esr_thickness, 0.5 * wrapped_scintillator_xy - esr_thickness);
+    Trapezoid odd_add_9_esr_in(short_elongation_esr_in.at(2), long_elongation_esr_in.at(2), 0.5 * wrapped_scintillator_z - esr_thickness, 0.5 * wrapped_scintillator_z - esr_thickness, 0.5 * wrapped_scintillator_xy - esr_thickness);
+    Trapezoid odd_add_14_esr_in(short_elongation_esr_in.at(3), long_elongation_esr_in.at(3), 0.5 * wrapped_scintillator_z - esr_thickness, 0.5 * wrapped_scintillator_z - esr_thickness, 0.5 * wrapped_scintillator_xy - esr_thickness);
+    Trapezoid odd_add_17_esr_in(short_elongation_esr_in.at(4), long_elongation_esr_in.at(4), 0.5 * wrapped_scintillator_z - esr_thickness, 0.5 * wrapped_scintillator_z - esr_thickness, 0.5 * wrapped_scintillator_xy - esr_thickness);
+
+    Box odd_esr_in_0_rect(0.5 * (wrapped_scintillator_xy - 2 * esr_thickness + short_elongation_esr_in.at(0)), 0.5 * wrapped_scintillator_z - esr_thickness, 0.5 * wrapped_scintillator_xy - esr_thickness);
+
+    UnionSolid odd_0_esr_in(odd_esr_in_0_rect, odd_add_0_esr_in, Position(0.5 * (wrapped_scintillator_xy + short_elongation_esr_in.at(0)), 0, 0));
+    UnionSolid odd_45_esr_in(esr_in, odd_add_45_esr_in, Position(0.5 * wrapped_scintillator_xy, 0, 0));
+    UnionSolid odd_9_esr_in(esr_in, odd_add_9_esr_in, Position(0.5 * wrapped_scintillator_xy, 0, 0));
+    UnionSolid odd_14_esr_in(esr_in, odd_add_14_esr_in, Position(0.5 * wrapped_scintillator_xy, 0, 0));
+    UnionSolid odd_17_esr_in(esr_in, odd_add_17_esr_in, Position(0.5 * wrapped_scintillator_xy, 0, 0));
+
+    // Odd-shaped ESR, from the subtraction of outer and inner frames
+    Volume odd_0_esr("odd_0_esr", SubtractionSolid(odd_0_esr_out, odd_0_esr_in, Position(0, 0, 0)), mat_ESR);
+    Volume odd_45_esr("odd_45_esr", SubtractionSolid(odd_45_esr_out, odd_45_esr_in, Position(0, 0, 0)), mat_ESR);
+    Volume odd_9_esr("odd_9_esr", SubtractionSolid(odd_9_esr_out, odd_9_esr_in, Position(0, 0, 0)), mat_ESR);
+    Volume odd_14_esr("odd_14_esr", SubtractionSolid(odd_14_esr_out, odd_14_esr_in, Position(0, 0, 0)), mat_ESR);
+    Volume odd_17_esr("odd_17_esr", SubtractionSolid(odd_17_esr_out, odd_17_esr_in, Position(0, 0, 0)), mat_ESR);
+
+    // Positions
+    const double scintillator_pos_z = -0.5 * non_absorber_thickness + boundary_safety + 0.5 * wrapped_scintillator_z;
+    const double esr_pos_z = scintillator_pos_z;
+    const double pcb_pos_z = esr_pos_z + 0.5 * wrapped_scintillator_z + boundary_safety + 0.5 * pcb_thickness;
+
+    // Loop for placing the units in a layer
+    for (int ir = 1; ir <= Nrows; ++ir)
+    {
+        const double layer_length = dim_in + (ir - 1) * wrapped_scintillator_xy * tan(0.5 * angle);
+        Ncells_phi = (int) (2 * layer_length / wrapped_scintillator_xy);
+        const double layer_phi = Ncells_phi * wrapped_scintillator_xy;
+        const double single_elongation = layer_length - 0.5 * layer_phi;
+
+        Volume slice("slice", Trapezoid(layer_length, layer_length + elongation_angle_esr_out, 0.5 * non_absorber_thickness, 0.5 * non_absorber_thickness, 0.5 * (wrapped_scintillator_xy + boundary_safety)), mat_air);
+        slice.setVisAttributes(theDetector, "SeeThrough");
+        string slicename = "Slice_" + to_string(ir);
+        DetElement sd(stave_det, slicename, det_id);
+
+        Volume slice_pcb("slice_pcb", Box(0.5 * layer_phi, 0.5 * pcb_thickness, 0.5 * wrapped_scintillator_xy), mat_PCB);
+        slice_pcb.setVisAttributes(theDetector, "SeeThrough");
+
+        PlacedVolume pcb_unit = slice.placeVolume(slice_pcb, Position(0, pcb_pos_z, 0));
+        pcb_unit.addPhysVolID("row", ir);
+
+        for (int iphi = 1; iphi <= Ncells_phi; ++iphi)
+        {
+            PlacedVolume scintillator_unit;
+            PlacedVolume esr_unit;
+
+            Position position_scintillator((-0.5 * (Ncells_phi + 1) + iphi) * wrapped_scintillator_xy, scintillator_pos_z, 0);
+            Position position_esr((-0.5 * (Ncells_phi + 1) + iphi) * wrapped_scintillator_xy, esr_pos_z, 0);
+
+            Transform3D transform_scintillator(RotationZ(pi), position_scintillator);
+            Transform3D transform_esr(RotationZ(pi), position_esr);
+
+            if (iphi == 1)
+            {
+                if (single_elongation >= short_elongation_esr_out.at(4))
+                {
+                    scintillator_unit = slice.placeVolume(odd_17_scintillator, transform_scintillator);
+                    esr_unit = slice.placeVolume(odd_17_esr, transform_esr);
+                }
+                else if (single_elongation >= short_elongation_esr_out.at(3))
+                {
+                    scintillator_unit = slice.placeVolume(odd_14_scintillator, transform_scintillator);
+                    esr_unit = slice.placeVolume(odd_14_esr, transform_esr);
+                }
+                else if (single_elongation >= short_elongation_esr_out.at(2))
+                {
+                    scintillator_unit = slice.placeVolume(odd_9_scintillator, transform_scintillator);
+                    esr_unit = slice.placeVolume(odd_9_esr, transform_esr);
+                }
+                else if (single_elongation >= short_elongation_esr_out.at(1))
+                {
+                    scintillator_unit = slice.placeVolume(odd_45_scintillator, transform_scintillator);
+                    esr_unit = slice.placeVolume(odd_45_esr, transform_esr);
+                }
+                else if (single_elongation >= short_elongation_esr_out.at(0))
+                {
+//                    scintillator_unit = slice.placeVolume(odd_0_scintillator, transform_scintillator);
+                    scintillator_unit = slice.placeVolume(odd_0_scintillator, Transform3D(RotationZ(pi),
+                                Position((-0.5 * (Ncells_phi + 1) + iphi) * wrapped_scintillator_xy - 0.5 * short_elongation_esr_in.at(0),
+                                         scintillator_pos_z,
+                                         0)));
+                    esr_unit = slice.placeVolume(odd_0_esr, transform_esr);
+                }
+            }
+            else if (iphi == Ncells_phi)
+            {
+                if (single_elongation >= short_elongation_esr_out.at(4))
+                {
+                    scintillator_unit = slice.placeVolume(odd_17_scintillator, position_scintillator);
+                    esr_unit = slice.placeVolume(odd_17_esr, position_esr);
+                }
+                else if (single_elongation >= short_elongation_esr_out.at(3))
+                {
+                    scintillator_unit = slice.placeVolume(odd_14_scintillator, position_scintillator);
+                    esr_unit = slice.placeVolume(odd_14_esr, position_esr);
+                }
+                else if (single_elongation >= short_elongation_esr_out.at(2))
+                {
+                    scintillator_unit = slice.placeVolume(odd_9_scintillator, position_scintillator);
+                    esr_unit = slice.placeVolume(odd_9_esr, position_esr);
+                }
+                else if (single_elongation >= short_elongation_esr_out.at(1))
+                {
+                    scintillator_unit = slice.placeVolume(odd_45_scintillator, position_scintillator);
+                    esr_unit = slice.placeVolume(odd_45_esr, position_esr);
+                }
+                else if (single_elongation >= short_elongation_esr_out.at(0))
+                {
+//                    scintillator_unit = slice.placeVolume(odd_0_scintillator, position_scintillator);
+                    scintillator_unit = slice.placeVolume(odd_0_scintillator,
+                                Position((-0.5 * (Ncells_phi + 1) + iphi) * wrapped_scintillator_xy + 0.5 * short_elongation_esr_in.at(0),
+                                         scintillator_pos_z,
+                                         0));
+                    esr_unit = slice.placeVolume(odd_0_esr, position_esr);
+                }
+            }
+            else
+            {
+                scintillator_unit = slice.placeVolume(scintillator, position_scintillator);
+                esr_unit = slice.placeVolume(esr, position_esr);
+            }
+
+            scintillator_unit.addPhysVolID("row", ir).addPhysVolID("phi", iphi);
+            esr_unit.addPhysVolID("row", ir).addPhysVolID("phi", iphi);
+
+            string scintillator_name = "Scintillator_" + to_string(ir) + "_" + to_string(iphi);
+            DetElement unit(sd, scintillator_name, det_id);
+            unit.setPlacement(scintillator_unit);
+        }
+
+        PlacedVolume plv = layer_vol.placeVolume(slice, Position(0, 0, (-0.5 * (Nrows + 1) + ir) * (wrapped_scintillator_xy + boundary_safety)));
+        plv.addPhysVolID("row", ir);
+        sd.setPlacement(plv);
+    }
+
+    // Loop for placing the layers in a module
+    for (int ilayer = 1; ilayer <= Nlayers; ++ilayer)
+    {
+        PlacedVolume plv = stave_vol.placeVolume(layer_vol, Position(0, (ilayer - 1) * layer_thickness + cassette_thickness + 0.5 * non_absorber_thickness - 0.5 * module_thickness, 0));
+        plv.addPhysVolID("layer", ilayer);
+        DetElement sd(stave_det, _toString(ilayer, "layer_%3d"), det_id);
+        sd.setPlacement(plv);
+    }
+
+    // Loop for placing the modules
+    for (int i = 0; i < Nmodules; ++i)
+    {
+        const double m_rot = i * angle;
+        const double posx = -r0 * sin(m_rot);
+        const double posy = r0 * cos(m_rot);
+
+        Transform3D transform_neg(RotationZ(m_rot) * RotationX(-0.5 * pi), Position(posx, posy, 0));
+        Transform3D transform_pos(RotationZ(m_rot) * RotationY(pi) * RotationX(-0.5 * pi), Position(posx, posy, 2 * pos_z));
+        PlacedVolume plv_neg = envelopeVol.placeVolume(stave_vol, transform_neg);
+        PlacedVolume plv_pos = envelopeVol.placeVolume(stave_vol, transform_pos);
+        plv_neg.addPhysVolID("stave", i);
+        plv_pos.addPhysVolID("stave", i + Nmodules);
+        DetElement sd_neg(AHCAL, _toString(i, "sector%3d"), det_id);
+        DetElement sd_pos(AHCAL, _toString(i + Nmodules, "sector%3d"), det_id);
+        sd_neg.setPlacement(plv_neg);
+        sd_pos.setPlacement(plv_pos);
+    }
+
+    sens.setType("calorimeter");
+
+    MYDEBUG("create_detector FINISHED.");
+    return AHCAL;
+}
+
+DECLARE_DETELEMENT(SHcalSc04_Endcaps_v02, create_detector)
diff --git a/Detector/DetCRD/CMakeLists.txt b/Detector/DetCRD/CMakeLists.txt
index 1314561b..2ed4b59d 100644
--- a/Detector/DetCRD/CMakeLists.txt
+++ b/Detector/DetCRD/CMakeLists.txt
@@ -17,6 +17,7 @@ gaudi_add_module(DetCRD
                          src/Tracker/SiTrackerStaggeredLadder_v01_geo.cpp
                          src/Tracker/TPC_Simple_o1_v01.cpp
                          src/Tracker/TPC_ModularEndcap_o1_v01.cpp
+                         src/Tracker/SiTracker_otkbarrel_v01_geo.cpp
                          src/Tracker/SiTracker_otkendcap_v01_geo.cpp
 
 		 LINK ${DD4hep_COMPONENT_LIBRARIES}
diff --git a/Detector/DetCRD/compact/CRD_common_v01/Ecal_Crystal_Barrel_Short_v02.xml b/Detector/DetCRD/compact/CRD_common_v01/Ecal_Crystal_Barrel_Short_v02.xml
new file mode 100644
index 00000000..30f14a45
--- /dev/null
+++ b/Detector/DetCRD/compact/CRD_common_v01/Ecal_Crystal_Barrel_Short_v02.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd>
+    <define>
+        <constant name="ecalbarrel_inner_radius" value="Ecal_barrel_inner_radius"/>
+        <constant name="ecalbarrel_outer_radius" value="Ecal_barrel_outer_radius"/>
+        <!--<constant name="ecalbarrel_thickness" value="Ecal_barrel_thickness"/>-->
+        <constant name="ecalbarrel_thickness" value="28.5*cm"/>
+        <constant name="ecalbarrel_zlength" value="Ecal_barrel_half_length*2"/>
+        <constant name="Nmodule" value="32"/>
+        <constant name="Nblock_z" value="15"/>
+        <constant name="module_rotation" value="12*degree"/>
+
+        <constant name="crystal_r" value="4.1*cm"/>
+        <constant name="crystal_phi" value="1*cm"/>
+        <constant name="crystal_z" value="1*cm"/>
+
+        <constant name="esr_thickness" value="0.1*mm"/>
+        <constant name="sipm_r" value="0.8*mm"/>
+        <constant name="sipm_phi" value="3*mm"/>
+        <constant name="sipm_z" value="3*mm"/>
+        <constant name="pcb_thickness" value="2.2*mm"/>
+        <constant name="cu_thickness" value="1*mm"/>
+        <constant name="fibre_thickness" value="0.1*mm"/>
+
+        <constant name="collection_width" value="300*mm"/>
+        <constant name="collection_thickness" value="10*mm"/>
+
+        <constant name="boundary_safety" value="1*nm"/>
+    </define>
+
+    <regions>
+        <region name="EcalBarrelRegion">
+        </region>
+    </regions>
+
+    <detectors>
+        <detector id="DetID_ECAL"
+                  name="EcalBarrel"
+                  type="CRDEcalBarrel_Short_v02"
+                  readout="EcalBarrelCollection"
+                  vis="Invisible"
+                  sensitive="true"
+                  region="EcalBarrelRegion">
+            <!-- Use cm as unit if you want to use Pandora for reconstruction -->
+        <material name="G4_BGO"/>
+        </detector>
+    </detectors>
+
+    <readouts>
+        <readout name="EcalBarrelCollection">
+            <segmentation type="NoSegmentation"/>
+            <!--segmentation type="CartesianGridXYZ"
+                          grid_size_x="1*cm"
+                          grid_size_y="1*cm"
+                          grid_size_z="1*cm"/-->
+            <id>system:5,module:5,stave:4,layer:5,phi:6,z:6</id>
+        </readout>
+    </readouts>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/CRD_common_v01/Ecal_Crystal_Endcap_Short_v01.xml b/Detector/DetCRD/compact/CRD_common_v01/Ecal_Crystal_Endcap_Short_v01.xml
new file mode 100644
index 00000000..73c5c6e7
--- /dev/null
+++ b/Detector/DetCRD/compact/CRD_common_v01/Ecal_Crystal_Endcap_Short_v01.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd>
+    <define>
+        <constant name="ecalendcap_inner_radius" value="Ecal_endcap_inner_radius"/>
+        <constant name="ecalendcap_outer_radius" value="Ecal_endcap_outer_radius"/>
+        <constant name="ecalendcap_block_xy" value="350*mm"/>
+        <constant name="ecalendcap_thickness" value="Ecal_endcap_zmax-Ecal_endcap_zmin"/>
+        <constant name="ecalendcap_z" value="0.5*(Ecal_endcap_zmin+Ecal_endcap_zmax)"/>
+        <constant name="Nblock_xy" value="6"/>
+        <constant name="Nsectors" value="4"/>
+
+        <!-- Fill in the space at the corner-->
+        <constant name="ecalendcap_block_fill_rect_short" value="260*mm"/>
+        <constant name="ecalendcap_block_fill_sq1" value="180*mm"/>
+        <constant name="ecalendcap_block_fill_sq2" value="270*mm"/>
+
+        <constant name="Ncell_rect_short" value="26"/>
+        <constant name="Ncell_sq1_xy" value="18"/>
+        <constant name="Ncell_sq2_xy" value="27"/>
+
+        <constant name="gap_narrow" value="2*mm"/>
+        <constant name="gap_wide" value="10*mm"/>
+
+        <!-- CrystalXY = CellXY - 2 * BoundarySafety - 2 * ESRThickness-->
+        <!-- CrystalZ = CellZ - 2 * BoundarySafety - 2 * ESRThickness-->
+        <constant name="Ncell_xy" value="35"/>
+        <constant name="crystal_z" value="4.1*cm"/>
+
+        <constant name="esr_thickness" value="0.1*mm"/>
+        <constant name="sipm_x" value="3*mm"/>
+        <constant name="sipm_y" value="3*mm"/>
+        <constant name="sipm_z" value="0.8*mm"/>
+        <constant name="pcb_thickness" value="2.2*mm"/>
+        <constant name="cu_thickness" value="1*mm"/>
+        <constant name="fibre_thickness" value="0.1*mm"/>
+        <constant name="boundary_safety" value="1*nm"/>
+    </define>
+
+    <regions>
+        <region name="EcalEndcapsRegion">
+        </region>
+    </regions>
+
+    <detectors>
+        <detector id="DetID_ECAL_ENDCAP"
+                  name="EcalEndcaps"
+                  type="CRDEcalEndcap_Short_v01"
+                  readout="EcalEndcapsCollection"
+                  vis="Invisible"
+                  sensitive="true"
+                  region="EcalEndcapsRegion">
+            <!-- Use cm as unit if you want to use Pandora for reconstruction -->
+        <material name="G4_BGO"/>
+        </detector>
+    </detectors>
+
+    <readouts>
+        <readout name="EcalEndcapsCollection">
+            <segmentation type="NoSegmentation"/>
+            <!--segmentation type="CartesianGridXYZ"
+                          grid_size_x="1*cm"
+                          grid_size_y="1*cm"
+                          grid_size_z="1*cm"/-->
+            <id>system:5,module:3,stave:5,layer:5,x:6,y:6</id>
+        </readout>
+    </readouts>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/CRD_common_v01/Ecal_Crystal_Endcap_v01_01.xml b/Detector/DetCRD/compact/CRD_common_v01/Ecal_Crystal_Endcap_v01_01.xml
new file mode 100755
index 00000000..a9ab8c90
--- /dev/null
+++ b/Detector/DetCRD/compact/CRD_common_v01/Ecal_Crystal_Endcap_v01_01.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8"?> 
+<lccdd> 
+  <define>
+    <constant name="ecalendcap_inner_radius"        value="Ecal_endcap_inner_radius"/>
+    <constant name="ecalendcap_outer_radius"        value="Ecal_endcap_outer_radius"/>
+    <constant name="ecalendcap_zmin"             value="Ecal_endcap_zmin"/>
+    <constant name="ecalendcap_depth"             value="Ecal_endcap_thickness"/>
+
+    <constant name="ecalendcap_layer"               value="28" />
+    <constant name="ecalendcap_x_width"               value="350*mm" />
+    <constant name="ecalendcap_y_width"               value="350*mm" />
+    <constant name="ecalendcap_width_crystal"    value="10.2*mm"/>
+
+    <constant name="ecalendcap_crystal_wrapping"    value="0.1*mm"/>
+    <constant name="ecalendcap_length_photoelectronic"    value="0.7*mm"/> 
+    <constant name="ecalendcap_width_photoelectronic"    value="3*mm"/> 
+
+    <constant name="ecalendcap_length_carbon"    value="2.5*mm"/> 
+    <constant name="ecalendcap_length_cable"    value="0.*mm"/> 
+    <constant name="ecalendcap_length_cooling"    value="1.*mm"/> 
+    <constant name="ecalendcap_length_pcb"    value="1.2*mm"/> 
+    <constant name="ecalendcap_length_asic"    value="1.*mm"/>
+    <constant name="ecalendcap_length_back"    value="10.*mm"/>
+
+  </define> 
+
+  <regions>
+    <region name="EcalendcapRegion">
+    </region>
+  </regions>
+
+  <detectors>
+    <detector id="DetID_ECAL_ENDCAP" name="CaloDetectorEndcap" type="LongCrystalBarEndcapCalorimeter_v01" readout="EcalEndcapsCollection" vis="Invisible" sensitive="true" region="EcalendcapRegion">
+      <!-- Use cm as unit if you want to use Pandora for reconstruction -->
+    </detector>
+  </detectors>
+  
+  <readouts>
+    <readout name="EcalEndcapsCollection">
+      <segmentation type="NoSegmentation"/>
+      <!--segmentation type="CartesianGridXYZ"
+                    grid_size_x="1*cm"
+                    grid_size_y="1*cm"
+                    grid_size_z="1*cm"/-->
+      <id>system:5,module:1,stave:15,dlayer:5,slayer:1,bar:15</id>
+    </readout>
+  </readouts>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/CRD_common_v01/OTKBarrel_v01_01.xml b/Detector/DetCRD/compact/CRD_common_v01/OTKBarrel_v01_01.xml
new file mode 100644
index 00000000..0996e56b
--- /dev/null
+++ b/Detector/DetCRD/compact/CRD_common_v01/OTKBarrel_v01_01.xml
@@ -0,0 +1,91 @@
+<lccdd>
+  <info name="OTKBarrel_v01_01"
+        title="CepC OTKBarrel"
+        author="D.Yu, "
+        url="http://cepc.ihep.ac.cn"
+        contact="yudian2002@sjtu.edu.cn"
+        status="developing"
+        version="v01">
+    <comment>CepC vertex detector based on MOST2 project </comment>
+  </info>
+  <define>
+    <constant name="OTKBarrel_total_length" value="2*OTKBarrel_half_length" />
+    <constant name="OTKBarrel_ladder_total_thickness" value="15*mm" />
+    <constant name="OTKBarrel_ladder_total_width" value="160*mm" />
+    <constant name="OTKBarrel_ladder_total_length" value="OTKBarrel_total_length" />
+    <constant name="OTKBarrel_ladder_support_thickness" value="1*mm" />
+    <constant name="OTKBarrel_ladder_support_width" value="160*mm" />
+    <constant name="OTKBarrel_ladder_support_length" value="OTKBarrel_total_length" />
+    <constant name="OTKBarrel_ladder_support_height" value="OTKBarrel_ladder_support_thickness" />
+    <constant name="OTKBarrel_flex_width" value="15*mm" />
+    <constant name="OTKBarrel_sensor_length" value="140*mm" />
+    <constant name="OTKBarrel_sensor_thickness" value="500*um" />
+    <constant name="OTKBarrel_sensor_active_width" value="132*mm" />
+    <constant name="OTKBarrel_sensor_dead_width" value="28*mm" />
+    <constant name="OTKBarrel_pcb_thickness" value="500*um" />
+    <constant name="OTKBarrel_pcb_width" value="130*mm" />
+    <constant name="OTKBarrel_pcb_length" value="10*mm" />
+    <constant name="OTKBarrel_pcb_zgap" value="1*mm" />
+    <constant name="OTKBarrel_asic_thickness" value="500*um" />
+    <constant name="OTKBarrel_asic_width" value="130*mm" />
+    <constant name="OTKBarrel_asic_length" value="5*mm" />
+    <constant name="OTKBarrel_asic_zgap" value="1*mm" />
+    
+  </define>
+
+  <detectors>
+    <detector id="DetID_OTKBarrel" name="OTKBarrel" type="SiTracker_otkbarrel_v01" vis="OTKBarrelVis" readout="OTKBarrelCollection" insideTrackingVolume="true">
+      <envelope>
+      <shape type="BooleanShape" operation="Union" material="Air" >
+        <shape type="Tube" rmin="OTKBarrel_inner_radius" rmax="OTKBarrel_outer_radius" dz="OTKBarrel_half_length" />
+      </shape>
+      </envelope>
+
+      <type_flags type="DetType_TRACKER + DetType_BARREL + DetType_STRIP "/>
+
+      <global sensitive_mat="G4_Si" support_mat="G4_C" sensitive_threshold_KeV="64*keV" ladder_offset="0*mm"/>
+      <display ladder="SeeThrough" support="VXDSupportVis" flex="VXDFlexVis" sens_env="SeeThrough" sens="GrayVis" deadsensor="GreenVis"
+      pcb="GreenVis" asic="yellowVis"/>
+
+      <layer layer_id="0" ladder_radius="OTKBarrel1_inner_radius" n_ladders="45" ladder_offset="0*mm" ladder_thickness="OTKBarrel_ladder_total_thickness"
+      ladder_width="OTKBarrel_ladder_total_width" ladder_length="OTKBarrel_ladder_total_length">
+        <ladder isDoubleSided="false">
+          <ladderSupport height="OTKBarrel_ladder_support_height" length="OTKBarrel_ladder_support_length" thickness="OTKBarrel_ladder_support_thickness"
+          width="OTKBarrel_ladder_support_width" mat="CarbonFiber"/>
+          <flex n_slices="3">
+            <slice length="OTKBarrel_total_length" thickness="60*um" width="OTKBarrel_flex_width" mat="epoxy"/>
+            <slice length="OTKBarrel_total_length" thickness="74*um" width="OTKBarrel_flex_width" mat="Kapton"/>
+            <slice length="OTKBarrel_total_length" thickness="26.8*um" width="OTKBarrel_flex_width" mat="G4_Al"/>
+          </flex>
+          <sensor n_sensors="42" gap="0*mm" thickness="OTKBarrel_sensor_thickness" length="OTKBarrel_sensor_length" active_width="OTKBarrel_sensor_active_width" sensor_mat="G4_Si"
+          dead_width="OTKBarrel_sensor_dead_width"/>
+          <other pcb_thickness="OTKBarrel_pcb_thickness" pcb_width="OTKBarrel_pcb_width" pcb_length="OTKBarrel_pcb_length" pcb_zgap="OTKBarrel_pcb_zgap" pcb_mat="epoxy"
+          asic_thickness="OTKBarrel_asic_thickness" asic_width="OTKBarrel_asic_width" asic_length="OTKBarrel_asic_length" asic_zgap="OTKBarrel_asic_zgap" asic_mat="G4_Si"/>
+        </ladder>
+      </layer>
+      <layer layer_id="1" ladder_radius="OTKBarrel2_inner_radius" n_ladders="45" ladder_offset="0*mm" ladder_thickness="OTKBarrel_ladder_total_thickness"
+      ladder_width="OTKBarrel_ladder_total_width" ladder_length="OTKBarrel_ladder_total_length">
+        <ladder isDoubleSided="false">
+          <ladderSupport height="OTKBarrel_ladder_support_height" length="OTKBarrel_ladder_support_length" thickness="OTKBarrel_ladder_support_thickness"
+          width="OTKBarrel_ladder_support_width" mat="CarbonFiber"/>
+          <flex n_slices="3">
+            <slice length="OTKBarrel_total_length" thickness="60*um" width="OTKBarrel_flex_width" mat="epoxy"/>
+            <slice length="OTKBarrel_total_length" thickness="74*um" width="OTKBarrel_flex_width" mat="Kapton"/>
+            <slice length="OTKBarrel_total_length" thickness="26.8*um" width="OTKBarrel_flex_width" mat="G4_Al"/>
+          </flex>
+          <sensor n_sensors="42" gap="0*mm" thickness="OTKBarrel_sensor_thickness" length="OTKBarrel_sensor_length" active_width="OTKBarrel_sensor_active_width" sensor_mat="G4_Si"
+          dead_width="OTKBarrel_sensor_dead_width"/>
+          <other pcb_thickness="OTKBarrel_pcb_thickness" pcb_width="OTKBarrel_pcb_width" pcb_length="OTKBarrel_pcb_length" pcb_zgap="OTKBarrel_pcb_zgap" pcb_mat="epoxy"
+          asic_thickness="OTKBarrel_asic_thickness" asic_width="OTKBarrel_asic_width" asic_length="OTKBarrel_asic_length" asic_zgap="OTKBarrel_asic_zgap" asic_mat="G4_Si"/>
+        </ladder>
+      </layer>
+    </detector>
+  </detectors>
+  
+  <readouts>
+    <readout name="OTKBarrelCollection">
+      <id>system:5,side:-2,layer:9,module:8,active:8,sensor:8</id>
+    </readout>
+  </readouts>
+</lccdd>
+
diff --git a/Detector/DetCRD/compact/CRD_common_v01/SHcalGlass_Barrel_v04_01.xml b/Detector/DetCRD/compact/CRD_common_v01/SHcalGlass_Barrel_v04_01.xml
index be7ef3f1..117d3023 100644
--- a/Detector/DetCRD/compact/CRD_common_v01/SHcalGlass_Barrel_v04_01.xml
+++ b/Detector/DetCRD/compact/CRD_common_v01/SHcalGlass_Barrel_v04_01.xml
@@ -38,7 +38,7 @@
 
       <envelope vis="CyanVis">
         <shape type="BooleanShape" operation="Subtraction" material="Air" >
-          <shape type="Cone" rmin1="0.0" rmax1="Hcal_outer_radius + env_safety" rmin2="0.0" rmax2="Hcal_outer_radius + env_safety" z="Hcal_half_length + env_safety/2.0"/>
+          <shape type="Cone" rmin1="0.0" rmax1="Hcal_barrel_outer_radius + env_safety" rmin2="0.0" rmax2="Hcal_barrel_outer_radius + env_safety" z="Hcal_half_length + env_safety/2.0"/>
           <shape type="PolyhedraRegular"  numsides="Hcal_inner_symmetry" rmin="0.0"
                  rmax="Hcal_inner_radius - env_safety" dz="2*(Hcal_half_length + env_safety)"/>
         </shape>
diff --git a/Detector/DetCRD/compact/CRD_common_v01/SHcalGlass_Barrel_v05.xml b/Detector/DetCRD/compact/CRD_common_v01/SHcalGlass_Barrel_v05.xml
new file mode 100644
index 00000000..d0770876
--- /dev/null
+++ b/Detector/DetCRD/compact/CRD_common_v01/SHcalGlass_Barrel_v05.xml
@@ -0,0 +1,65 @@
+<!-- comment> Glass scintillator HCAL barrel modified from SHcalSc04_Barrel_v04_01.xml</comment -->
+
+<lccdd>
+  <define>
+    <constant name="Hcal_cell_size" value="40*mm"/>
+    <constant name="Hcal_cell_size_abnormal" value="50*mm"/>
+    <constant name="Hcal_inner_radius" value="Hcal_barrel_inner_radius"/>
+    <constant name="Hcal_outer_radius" value="Hcal_barrel_outer_radius"/>
+    <constant name="Hcal_half_length" value="Hcal_barrel_half_length"/>
+    <constant name="Hcal_inner_symmetry" value="Hcal_barrel_symmetry"/>
+    <constant name="Hcal_nlayers" value="48"/>
+    <constant name="Hcal_radiator_thickness" value="9.9*mm"/>
+    <constant name="Hcal_chamber_thickness" value="17.3*mm"/>
+    <constant name="Hcal_scintillator_ESR_thickness" value="0.065*mm"/>
+    <constant name="Hcal_scintillator_air_gap" value="0.17*mm"/>
+    <constant name="Hcal_scintillator_thickness" value="10*mm"/>
+    <constant name="Hcal_back_plate_thickness" value="15*mm"/>
+    <constant name="Hcal_lateral_structure_thickness" value="24*mm"/>
+    <constant name="Hcal_stave_gaps" value="0*mm"/>
+    <constant name="Hcal_middle_stave_gaps" value="0*mm"/>
+    <constant name="Hcal_modules_gap" value="2*mm"/>
+    <constant name="Hcal_layer_air_gap" value="0*mm"/>
+    <constant name="HcalSD_glass_anode_thickness" value="0.7*mm"/>
+    <constant name="HcalSD_sensitive_gas_gap" value="1.2*mm"/>
+    <constant name="HcalSD_glass_cathode_thickness" value="1.1*mm"/>
+    <constant name="Hcal_PCB_thickness" value="3.1*mm"/>
+    <constant name="Hcal_scintillator_ESR_air_thickness" value="10.2*mm"/>
+    <constant name="Ecal_outer_radius" value="Ecal_barrel_outer_radius"/>
+  </define>
+  <detectors>
+    <detector name="HcalBarrel" type="SHcalSc04_Barrel_v04" id="DetID_HCAL" readout="HcalBarrelCollection" insideTrackingVolume="false" vis="seeThrough">
+      <comment>Hadron Calorimeter Barrel</comment>
+
+      <envelope vis="SeeThrough">
+        <shape type="BooleanShape" operation="Subtraction" material="Air" >
+          <shape type="Cone" rmin1="0.0" rmax1="Hcal_barrel_outer_radius + env_safety" rmin2="0.0" rmax2="Hcal_barrel_outer_radius + env_safety" z="Hcal_half_length + env_safety/2.0"/>
+          <shape type="PolyhedraRegular"  numsides="Hcal_inner_symmetry" rmin="0.0" rmax="Hcal_inner_radius - env_safety" dz="2*(Hcal_half_length + env_safety)"/>
+        </shape>
+        <rotation x="0" y="0" z="90*deg-180*deg/Hcal_inner_symmetry"/>
+      </envelope>
+
+      <type_flags type=" DetType_CALORIMETER + DetType_BARREL + DetType_HADRONIC " />
+
+      <staves  material = "Steel235"  vis="SeeThrough"/>
+
+      <layer repeat="Hcal_nlayers" vis="seeThrough">
+        <slice material="Steel235" thickness = "2*mm"   vis="seeThrough"   />
+        <slice material="PCB" thickness = "Hcal_PCB_thickness"   vis="GreenVis"   />
+        <slice material="Air" thickness = "Hcal_scintillator_ESR_air_thickness" sensitive = "yes"   limits="cal_limits"  vis="SeeThrough" >
+          <sensitive material = "G4_GlassHCAL"  sensitive = "yes" limits="cal_limits"   vis="CyanVis"/>
+          <ladder material = "G4_ESR"  limits="cal_limits" vis="RedVis" />
+        </slice>
+        <slice material="Steel235" thickness = "2*mm"   vis="seeThrough"   />
+      </layer>
+    </detector>
+  </detectors>
+
+  <readouts>
+    <readout name="HcalBarrelCollection">
+      <id>system:5,stave:4,layer:6,tile:16,x:32:-16,y:-16</id>
+    </readout>
+  </readouts>
+
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/CRD_common_v01/SHcalGlass_Endcaps_v01.xml b/Detector/DetCRD/compact/CRD_common_v01/SHcalGlass_Endcaps_v01.xml
new file mode 100644
index 00000000..857363c2
--- /dev/null
+++ b/Detector/DetCRD/compact/CRD_common_v01/SHcalGlass_Endcaps_v01.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd>
+  <comment>Glass Scintillator tile HCAL endcap, modified from Jiyuan's SHcalSc04_Endcaps_v02.xml</comment>
+
+    <define>
+        <constant name="hcalendcap_inner_radius" value="Hcal_endcap_inner_radius"/>
+        <constant name="hcalendcap_outer_radius" value="Hcal_endcap_outer_radius"/>
+        <constant name="hcalendcap_thickness" value="Hcal_endcap_zmax-Hcal_endcap_zmin"/>
+        <constant name="hcalendcap_z" value="0.5*(Hcal_endcap_zmin+Hcal_endcap_zmax)"/>
+        <constant name="Nmodules" value="Hcal_endcap_symmetry"/>
+
+        <constant name="scintillator_xy" value="40.0*mm"/>
+        <constant name="scintillator_z" value="10.0*mm"/>
+        <constant name="wrapped_scintillator_xy" value="40.3*mm"/>
+        <constant name="wrapped_scintillator_z" value="10.2*mm"/>
+
+        <!-- Odd-shaped cells -->
+        <constant name="Nodd" value="5"/>
+        <constant name="short_elongation_1" value="0*mm"/>
+        <constant name="short_elongation_2" value="4.5*mm"/>
+        <constant name="short_elongation_3" value="9*mm"/>
+        <constant name="short_elongation_4" value="14*mm"/>
+        <constant name="short_elongation_5" value="17*mm"/>
+
+        <constant name="cassette_thickness" value="2.0*mm"/>
+        <constant name="esr_thickness" value="65.0*um"/>
+        <constant name="sipm_xy" value="3.0*mm"/>
+        <constant name="sipm_z" value="0.8*mm"/>
+        <constant name="pcb_thickness" value="3.1*mm"/>
+        <constant name="absorber_thickness" value="9.9*mm"/>
+
+        <constant name="inner_structure_thickness" value="50*mm"/>
+        <constant name="outer_structure_width" value="120*mm"/>
+        <constant name="outer_structure_thickness" value="50*mm"/>
+        <constant name="frame_thickness" value="3*mm"/>
+
+        <constant name="boundary_safety" value="1*nm"/>
+    </define>
+
+    <regions>
+        <region name="HcalEndcapsRegion">
+        </region>
+    </regions>
+
+    <detectors>
+        <detector id="DetID_HCAL_ENDCAP"
+                  name="HcalEndcaps"
+                  type="SHcalSc04_Endcaps_v02"
+                  readout="HcalEndcapsCollection"
+                  vis="Invisible"
+                  sensitive="true"
+                  region="HcalEndcapsRegion">
+            <!-- Use cm as unit if you want to use Pandora for reconstruction -->
+        <material name="G4_GlassHCAL"/>
+        </detector>
+    </detectors>
+
+    <readouts>
+        <readout name="HcalEndcapsCollection">
+            <segmentation type="NoSegmentation"/>
+            <!--segmentation type="CartesianGridXYZ"
+                          grid_size_x="1*cm"
+                          grid_size_y="1*cm"
+                          grid_size_z="1*cm"/-->
+            <id>system:5,stave:5,layer:6,row:7,phi:6</id>
+        </readout>
+    </readouts>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/CRD_common_v01/SHcalSc04_Barrel_v04_01.xml b/Detector/DetCRD/compact/CRD_common_v01/SHcalSc04_Barrel_v04_01.xml
index 32810ce1..424b3edb 100644
--- a/Detector/DetCRD/compact/CRD_common_v01/SHcalSc04_Barrel_v04_01.xml
+++ b/Detector/DetCRD/compact/CRD_common_v01/SHcalSc04_Barrel_v04_01.xml
@@ -1,16 +1,21 @@
-<!-- comment>Calorimeters</comment -->
+<!-- comment> Plastic scintillator HCAL barrel by Hongbin Diao (diaohb@mail.ustc.edu.cn)</comment -->
 
 <lccdd>
   <define>
-    <constant name="Hcal_cell_size" value="10*mm"/>
+    <constant name="Hcal_cell_size" value="40*mm"/>
+    <constant name="Hcal_cell_size_abnormal" value="50*mm"/>
     <constant name="Hcal_inner_radius" value="Hcal_barrel_inner_radius"/>
+    <constant name="Hcal_outer_radius" value="Hcal_barrel_outer_radius"/>
     <constant name="Hcal_half_length" value="Hcal_barrel_half_length"/>
     <constant name="Hcal_inner_symmetry" value="Hcal_barrel_symmetry"/>
-    <constant name="Hcal_nlayers" value="38"/>
-    <constant name="Hcal_radiator_thickness" value="20.0*mm"/>
-    <constant name="Hcal_chamber_thickness" value="6.5*mm"/>
+    <constant name="Hcal_nlayers" value="48"/>
+    <constant name="Hcal_radiator_thickness" value="16.8*mm"/>
+    <constant name="Hcal_chamber_thickness" value="10.4*mm"/>
+    <constant name="Hcal_scintillator_ESR_thickness" value="0.065*mm"/>
+    <constant name="Hcal_scintillator_air_gap" value="0.17*mm"/>
+    <constant name="Hcal_scintillator_thickness" value="3*mm"/>
     <constant name="Hcal_back_plate_thickness" value="15*mm"/>
-    <constant name="Hcal_lateral_structure_thickness" value="10*mm"/>
+    <constant name="Hcal_lateral_structure_thickness" value="24*mm"/>
     <constant name="Hcal_stave_gaps" value="0*mm"/>
     <constant name="Hcal_middle_stave_gaps" value="0*mm"/>
     <constant name="Hcal_modules_gap" value="2*mm"/>
@@ -18,52 +23,41 @@
     <constant name="HcalSD_glass_anode_thickness" value="0.7*mm"/>
     <constant name="HcalSD_sensitive_gas_gap" value="1.2*mm"/>
     <constant name="HcalSD_glass_cathode_thickness" value="1.1*mm"/>
-    <constant name="Hcal_scintillator_thickness" value="3.0*mm"/>
+    <constant name="Hcal_PCB_thickness" value="3.2*mm"/>
+    <constant name="Hcal_scintillator_ESR_air_thickness" value="3.2*mm"/>
     <constant name="Ecal_outer_radius" value="Ecal_barrel_outer_radius"/>
-    <constant name="Hcal_readout_segmentation_slice" value="3"/>
   </define>
   <detectors>
-    <detector name="HcalBarrel" type="SHcalSc04_Barrel_v04" id="DetID_HCAL" readout="HcalBarrelCollection" vis="GreenVis" insideTrackingVolume="false" >
+    <detector name="HcalBarrel" type="SHcalSc04_Barrel_v04" id="DetID_HCAL" readout="HcalBarrelCollection" insideTrackingVolume="false" vis="seeThrough">
       <comment>Hadron Calorimeter Barrel</comment>
 
-      <envelope vis="ILD_HCALVis">
+      <envelope vis="SeeThrough">
         <shape type="BooleanShape" operation="Subtraction" material="Air" >
-          <shape type="Cone" rmin1="0.0" rmax1="Hcal_outer_radius + env_safety" rmin2="0.0" rmax2="Hcal_outer_radius + env_safety" z="Hcal_half_length + env_safety/2.0"/>
-          <shape type="PolyhedraRegular"  numsides="Hcal_inner_symmetry" rmin="0.0"
-                 rmax="Hcal_inner_radius - env_safety" dz="2*(Hcal_half_length + env_safety)"/>
+          <shape type="Cone" rmin1="0.0" rmax1="Hcal_barrel_outer_radius + env_safety" rmin2="0.0" rmax2="Hcal_barrel_outer_radius + env_safety" z="Hcal_half_length + env_safety/2.0"/>
+          <shape type="PolyhedraRegular"  numsides="Hcal_inner_symmetry" rmin="0.0" rmax="Hcal_inner_radius - env_safety" dz="2*(Hcal_half_length + env_safety)"/>
         </shape>
         <rotation x="0" y="0" z="90*deg-180*deg/Hcal_inner_symmetry"/>
       </envelope>
 
       <type_flags type=" DetType_CALORIMETER + DetType_BARREL + DetType_HADRONIC " />
 
-      <staves  material = "Steel235"  vis="BlueVis"/>
+      <staves  material = "Steel235"  vis="SeeThrough"/>
 
-
-      <!--  select which subsegmentation will be used to fill the DDRec:LayeredCalorimeterData cell dimensions -->
-      <subsegmentation key="slice" value="Hcal_readout_segmentation_slice"/>
-
-      <layer repeat="Hcal_nlayers" vis="SeeThrough">
-        <slice material="FloatGlass" thickness="HcalSD_glass_anode_thickness" vis="Invisible"/>
-        <slice material="RPCGAS2"    thickness="HcalSD_sensitive_gas_gap" sensitive="yes" limits="cal_limits" vis="YellowVis"/>
-        <slice material="FloatGlass" thickness="HcalSD_glass_cathode_thickness" vis="Invisible"/>
-        <slice material="G4_POLYSTYRENE" thickness = "Hcal_scintillator_thickness" sensitive = "yes"   limits="cal_limits"  vis="CyanVis"   />
-        <slice material="Air"      thickness="Hcal_chamber_thickness - ( HcalSD_glass_anode_thickness + HcalSD_sensitive_gas_gap + HcalSD_glass_cathode_thickness + Hcal_scintillator_thickness)" vis="Invisible" />
+      <layer repeat="Hcal_nlayers" vis="seeThrough">
+        <slice material="Steel235" thickness = "2*mm"   vis="seeThrough"   />
+        <slice material="PCB" thickness = "Hcal_PCB_thickness"   vis="GreenVis"   />
+        <slice material="Air" thickness = "Hcal_scintillator_ESR_air_thickness" sensitive = "yes"   limits="cal_limits"  vis="SeeThrough" >
+          <sensitive material = "G4_POLYSTYRENE"  sensitive = "yes" limits="cal_limits"   vis="CyanVis"/>
+          <ladder material = "G4_ESR"  limits="cal_limits" vis="RedVis" />
+        </slice>
+        <slice material="Steel235" thickness = "2*mm"   vis="seeThrough"   />
       </layer>
     </detector>
   </detectors>
 
   <readouts>
     <readout name="HcalBarrelCollection">
-      <segmentation   type="MultiSegmentation"  key="slice">
-        <segmentation name="RPCgrid" type="CartesianGridXY"   key_value="1"  grid_size_x="Hcal_cell_size" grid_size_y="Hcal_cell_size" />
-        <segmentation name="Scigrid" type="TiledLayerGridXY"  key_value="3"  grid_size_x="3" grid_size_y="3.03248"/>
-      </segmentation>
-      <hits_collections>
-        <hits_collection name="HCalBarrelRPCHits"  key="slice" key_value="1"/>
-        <hits_collection name="HcalBarrelRegCollection"  key="slice" key_value="3"/>
-      </hits_collections>
-      <id>system:5,module:3,stave:4,tower:5,layer:6,slice:4,x:32:-16,y:-16</id>
+      <id>system:5,stave:4,layer:6,tile:16,x:32:-16,y:-16</id>
     </readout>
   </readouts>
 
diff --git a/Detector/DetCRD/compact/CRD_common_v01/SHcalSc04_Endcaps_v02.xml b/Detector/DetCRD/compact/CRD_common_v01/SHcalSc04_Endcaps_v02.xml
new file mode 100644
index 00000000..ccfb77b3
--- /dev/null
+++ b/Detector/DetCRD/compact/CRD_common_v01/SHcalSc04_Endcaps_v02.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd>
+  <comment>Plastic Scintillator tile HCAL endcap by Jiyuan Chen</comment>
+
+    <define>
+        <constant name="hcalendcap_inner_radius" value="Hcal_endcap_inner_radius"/>
+        <constant name="hcalendcap_outer_radius" value="Hcal_endcap_outer_radius"/>
+        <constant name="hcalendcap_thickness" value="Hcal_endcap_zmax-Hcal_endcap_zmin"/>
+        <constant name="hcalendcap_z" value="0.5*(Hcal_endcap_zmin+Hcal_endcap_zmax)"/>
+        <constant name="Nmodules" value="Hcal_endcap_symmetry"/>
+
+        <constant name="scintillator_xy" value="40.0*mm"/>
+        <constant name="scintillator_z" value="3.0*mm"/>
+        <constant name="wrapped_scintillator_xy" value="40.3*mm"/>
+        <constant name="wrapped_scintillator_z" value="3.2*mm"/>
+
+        <!-- Odd-shaped cells -->
+        <constant name="Nodd" value="5"/>
+        <constant name="short_elongation_1" value="0*mm"/>
+        <constant name="short_elongation_2" value="4.5*mm"/>
+        <constant name="short_elongation_3" value="9*mm"/>
+        <constant name="short_elongation_4" value="14*mm"/>
+        <constant name="short_elongation_5" value="17*mm"/>
+
+        <constant name="cassette_thickness" value="2.0*mm"/>
+        <constant name="esr_thickness" value="65.0*um"/>
+        <constant name="sipm_xy" value="3.0*mm"/>
+        <constant name="sipm_z" value="0.8*mm"/>
+        <constant name="pcb_thickness" value="3.2*mm"/>
+        <constant name="absorber_thickness" value="16.8*mm"/>
+
+        <constant name="inner_structure_thickness" value="50*mm"/>
+        <constant name="outer_structure_width" value="120*mm"/>
+        <constant name="outer_structure_thickness" value="50*mm"/>
+        <constant name="frame_thickness" value="3*mm"/>
+
+        <constant name="boundary_safety" value="1*nm"/>
+    </define>
+
+    <regions>
+        <region name="HcalEndcapsRegion">
+        </region>
+    </regions>
+
+    <detectors>
+        <detector id="DetID_HCAL_ENDCAP"
+                  name="HcalEndcaps"
+                  type="SHcalSc04_Endcaps_v02"
+                  readout="HcalEndcapsCollection"
+                  vis="Invisible"
+                  sensitive="true"
+                  region="HcalEndcapsRegion">
+            <!-- Use cm as unit if you want to use Pandora for reconstruction -->
+        <material name="G4_POLYSTYRENE"/>
+        </detector>
+    </detectors>
+
+    <readouts>
+        <readout name="HcalEndcapsCollection">
+            <segmentation type="NoSegmentation"/>
+            <!--segmentation type="CartesianGridXYZ"
+                          grid_size_x="1*cm"
+                          grid_size_y="1*cm"
+                          grid_size_z="1*cm"/-->
+            <id>system:5,stave:5,layer:6,row:7,phi:6</id>
+        </readout>
+    </readouts>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/CRD_o1_v01/CRD_o1_v01-onlyTracker.xml b/Detector/DetCRD/compact/CRD_o1_v01/CRD_o1_v01-onlyTracker.xml
index 738e55c4..528fa095 100644
--- a/Detector/DetCRD/compact/CRD_o1_v01/CRD_o1_v01-onlyTracker.xml
+++ b/Detector/DetCRD/compact/CRD_o1_v01/CRD_o1_v01-onlyTracker.xml
@@ -33,7 +33,7 @@
   <include ref="../CRD_common_v01/SIT_SimplePixel_v01_01.xml"/>
   <include ref="../CRD_common_v01/DC_Simple_v01_06.xml"/>
   <include ref="../CRD_common_v01/SET_SimplePixel_v01_01.xml"/>
-  
+
   <fields>
     <field name="InnerSolenoid" type="solenoid"
            inner_field="Field_nominal_value"
diff --git a/Detector/DetCRD/compact/TDR_CaloTest/TDR_Dimensions_v01_01.xml b/Detector/DetCRD/compact/TDR_CaloTest/TDR_Dimensions_v01_01.xml
new file mode 100644
index 00000000..7b37c14e
--- /dev/null
+++ b/Detector/DetCRD/compact/TDR_CaloTest/TDR_Dimensions_v01_01.xml
@@ -0,0 +1,369 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+
+  <info name="CRDDimensions"
+       title="master file with includes and world dimension"
+       author=""
+       url="no"
+       status="development"
+       version="1.0">
+    <comment>
+      undeterminded parameters
+    </comment>
+  </info>
+
+  <define>
+    <constant name="CrossingAngle" value="0.033*rad"/>  
+
+    <constant name="Global_endcap_costheta" value="0.99"/>
+
+    <constant name="GlobalTrackerReadoutID_DCH" type="string" value="system:8,chamber:1,layer:7,phi:16"/>
+    <constant name="GlobalTrackerReadoutID" type="string" value="system:5,side:-2,layer:9,module:8,sensor:8,barrelside:-2"/>
+
+    <constant name="Field_nominal_value" value="3*tesla"/>
+    <constant name="Field_outer_nominal_value" value="-1.3*tesla"/>
+
+    <constant name="env_safety" value="0.1*mm"/>
+
+    <constant name="DetID_NOTUSED"      value="  0"/>
+    <constant name="DetID_VXD"          value="  1"/>
+    <constant name="DetID_SIT"          value="  2"/>
+    <constant name="DetID_FTD"          value="  3"/>
+    <constant name="DetID_TPC"          value="  4"/>
+    <constant name="DetID_SET"          value="  5"/>
+    <constant name="DetID_ETD"          value="  6"/>
+    
+    <constant name="DetID_ECAL"         value=" 20"/>
+    <constant name="DetID_ECAL_PLUG"    value=" 21"/>
+    <constant name="DetID_HCAL"         value=" 22"/>
+    <constant name="DetID_HCAL_RING"    value=" 23"/>
+    <constant name="DetID_LCAL"         value=" 24"/>
+    <constant name="DetID_BCAL"         value=" 25"/>
+    <constant name="DetID_LHCAL"        value=" 26"/>
+    <constant name="DetID_YOKE"         value=" 27"/>
+    <constant name="DetID_COIL"         value=" 28"/>
+    <constant name="DetID_ECAL_ENDCAP"  value=" 29"/>
+    <constant name="DetID_HCAL_ENDCAP"  value=" 30"/>
+    <constant name="DetID_YOKE_ENDCAP"  value=" 31"/>
+    
+    <constant name="DetID_bwd"       value="-1"/>
+    <constant name="DetID_barrel"    value=" 0"/>
+    <constant name="DetID_fwd"       value="+1"/>
+
+    <!-- FIXME:need to check/-->
+    <constant name="BeamPipe_Be_inner_thickness"   value="0.2*mm"/>
+    <constant name="BeamPipe_Cooling_thickness"    value="0.3*mm"/>
+    <constant name="BeamPipe_Be_outer_thickness"   value="0.15*mm"/>
+    <constant name="BeamPipe_Be_total_thickness"   value="BeamPipe_Be_inner_thickness+BeamPipe_Cooling_thickness+BeamPipe_Be_outer_thickness"/>
+    <constant name="BeamPipe_Al_thickness"         value="BeamPipe_Be_total_thickness"/>
+    <constant name="BeamPipe_ThinCu_thickness"     value="2.0*mm"/>
+    <constant name="BeamPipe_Cu_thickness"         value="3.0*mm"/>
+
+    <constant name="BeamPipe_CentralBe_zmax"       value="85*mm"/>
+    <constant name="BeamPipe_CentralAl_zmax"       value="180*mm"/>
+    <constant name="BeamPipe_ExpandAl_zmax"        value="655*mm"/>
+    <constant name="BeamPipe_Linker_zmin"          value="700*mm"/>
+    <constant name="BeamPipe_Linker_zmax"          value="780*mm"/>
+    <constant name="BeamPipe_Waist_zmax"           value="805*mm"/>
+    <constant name="BeamPipe_Crotch_zmax"          value="855*mm"/>
+    <constant name="BeamPipe_FirstSeparated_zmax"  value="1110*mm"/>
+    <constant name="BeamPipe_Mask_zmin"            value="1210*mm"/>
+    <constant name="BeamPipe_Mask_zmax"            value="1230*mm"/>
+    <constant name="BeamPipe_Q1a_zmin"             value="1900*mm"/>
+    <constant name="BeamPipe_Q1a_zmax"             value="3110*mm"/>
+    <constant name="BeamPipe_Q1b_zmin"             value="3190*mm"/>
+    <constant name="BeamPipe_Q1b_zmax"             value="4400*mm"/>
+    <constant name="BeamPipe_QF1_zmin"             value="4700*mm"/>
+    <constant name="BeamPipe_QF1_zmax"             value="6200*mm"/>
+    <constant name="BeamPipe_end_z"                value="7050*mm"/>
+
+    <constant name="BeamPipe_Central_inner_radius"  value="10*mm"/>
+    <constant name="BeamPipe_Fork_inner_radius"     value="10*mm"/>
+    <constant name="BeamPipe_FirstExpand_width"     value="35*mm"/>
+    <constant name="BeamPipe_SecondExpand_width"    value="39*mm"/>
+    <constant name="BeamPipe_Mask_inner_radius"     value="6*mm"/>
+    <constant name="BeamPipe_Q1a_inner_radius"      value="BeamPipe_Fork_inner_radius"/>
+    <constant name="BeamPipe_Q1b_inner_radius"      value="11.5*mm"/>
+    <constant name="BeamPipe_QF1_inner_radius"      value="16*mm"/>
+
+    <constant name="BeamPipe_FrontLinker_rmax"      value="BeamPipe_FirstExpand_width/2+BeamPipe_Al_thickness"/>
+    <constant name="BeamPipe_ForwardRegion_rmax"    value="BeamPipe_SecondExpand_width/2+BeamPipe_Cu_thickness"/>
+
+    <constant name="Vertex_inner_radius" value="BeamPipe_Central_inner_radius+BeamPipe_Be_total_thickness"/>
+    <constant name="Vertex_outer_radius" value="72.5*mm"/>
+    <constant name="Vertex_half_length"  value="750*mm"/>
+
+    <!--constant name="BeamPipe_VertexRegion_rmax"     value="BeamPipe_Central_inner_radius+BeamPipe_Al_thickness
+	   +(BeamPipe_FirstExpand_width/2-BeamPipe_Central_inner_radius)/(BeamPipe_ExpandAl_zmax-BeamPipe_CentralAl_zmax)*(Vertex_half_length-BeamPipe_CentralAl_zmax)"/-->
+    <constant name="BeamPipe_VertexRegion_rmax"     value="BeamPipe_FirstExpand_width
+							   +(BeamPipe_SecondExpand_width-BeamPipe_FirstExpand_width)/(BeamPipe_Linker_zmax-BeamPipe_Linker_zmin)*(Vertex_half_length-BeamPipe_Linker_zmin)"/>
+    <constant name="Vertex_Side_rmin"    value="BeamPipe_VertexRegion_rmax"/>
+
+    <constant name="TPC_inner_radius" value="600*mm"/>
+    <constant name="TPC_outer_radius" value="1800*mm"/>
+    <constant name="TPC_half_length"  value="2900*mm"/>
+    <constant name="OuterTracker_half_length" value="TPC_half_length"/>
+
+    <constant name="SIT1_inner_radius"   value="150*mm"/>
+    <constant name="SIT2_inner_radius"   value="250*mm"/>
+    <constant name="SIT3_inner_radius"   value="500*mm"/>
+    <constant name="SIT1_half_length"    value="243*mm"/>
+    <constant name="SIT2_half_length"    value="405*mm"/>
+    <constant name="SIT3_half_length"    value="810*mm"/>
+
+    <constant name="SET_inner_radius"    value="1800*mm"/>
+
+    <constant name="SiTracker_region1_costheta"   value="0.85"/>
+    <constant name="SiTracker_region2_costheta"   value="0.9"/>
+    <constant name="SiTracker_region3_costheta"   value="0.95"/>
+    <constant name="SiTracker_endcap_barrel_zgap" value="5*mm"/>
+    <constant name="SiTracker_endcap_barrel_rgap" value="10*mm"/>
+    <constant name="SiTracker_endcap_gas_zgap"    value="3*mm"/>
+    <constant name="SiTracker_endcap_gas_rgap"    value="20*mm"/>
+    <constant name="SiTracker_endcap_z1" value="Vertex_half_length"/>
+    <constant name="SiTracker_endcap_z2" value="SIT3_half_length+SiTracker_endcap_barrel_zgap"/>
+    <constant name="SiTracker_endcap_z3" value="TPC_inner_radius/tan(acos(SiTracker_region2_costheta))"/>
+    <constant name="SiTracker_endcap_z4" value="TPC_inner_radius/tan(acos(SiTracker_region3_costheta))"/>
+    <constant name="SiTracker_endcap_z5" value="TPC_half_length+SiTracker_endcap_gas_zgap"/>
+    <constant name="SiTracker_endcap_outer_radius1" value="SiTracker_endcap_z1*tan(acos(SiTracker_region1_costheta))"/>
+    <constant name="SiTracker_endcap_outer_radius2" value="SIT3_inner_radius"/>
+    <constant name="SiTracker_endcap_outer_radius3" value="TPC_inner_radius-SiTracker_endcap_gas_rgap"/>
+    <constant name="SiTracker_endcap_outer_radius4" value="TPC_inner_radius-SiTracker_endcap_gas_rgap"/>
+    <constant name="SiTracker_endcap_outer_radius5" value="TPC_outer_radius+SiTracker_endcap_barrel_rgap"/>
+
+    <!--obseleted constance, used by old construct, should be removed while creating new constrcut-->
+    <constant name="TPC_Ecal_Hcal_barrel_halfZ"   value="TPC_half_length"/>
+
+    <constant name="Ecal_barrel_inner_radius" value="1830*mm"/>
+    <constant name="Ecal_barrel_thickness"    value="300*mm"/>
+    <constant name="Ecal_barrel_outer_radius" value="Ecal_barrel_inner_radius+Ecal_barrel_thickness"/>
+    <constant name="Ecal_barrel_half_length"  value="2900*mm"/>
+    <constant name="Ecal_barrel_symmetry"     value="32"/>
+    <constant name="Ecal_Tpc_gap"             value="Ecal_barrel_inner_radius-TPC_outer_radius"/>
+
+    <constant name="Ecal_endcap_inner_radius" value="350*mm"/>
+    <constant name="Ecal_endcap_outer_radius" value="Ecal_barrel_outer_radius"/>
+    <constant name="Ecal_endcap_zmin"         value="2930*mm"/>
+    <constant name="Ecal_endcap_thickness"    value="Ecal_barrel_thickness"/>
+    <constant name="Ecal_endcap_zmax"         value="Ecal_endcap_zmin+Ecal_endcap_thickness"/>
+    <constant name="Ecal_endcap_symmetry"     value="32"/>
+
+    <constant name="Hcal_barrel_inner_radius" value="2140*mm"/>
+    <constant name="Hcal_barrel_outer_radius" value="3455*mm"/>
+    <constant name="Hcal_barrel_half_length"  value="3230*mm"/>
+    <constant name="Hcal_barrel_symmetry"    value="16"/>
+    
+    <constant name="Hcal_endcap_inner_radius" value="450*mm"/>
+    <constant name="Hcal_endcap_outer_radius" value="Hcal_barrel_outer_radius"/>
+    <constant name="Hcal_endcap_zmin" value="3260*mm"/>
+    <constant name="Hcal_endcap_thickness" value="Hcal_barrel_outer_radius-Hcal_barrel_inner_radius"/>
+    <constant name="Hcal_endcap_zmax" value="Hcal_endcap_zmin+Hcal_endcap_thickness"/>
+    <constant name="Hcal_endcap_symmetry" value="16"/>
+
+    <constant name="Solenoid_inner_radius" value="3535*mm"/>
+    <constant name="Solenoid_outer_radius" value="4235*mm"/>
+    <constant name="Solenoid_half_length" value="4575*mm"/>
+    <constant name="SolenoidCoil_half_length" value="4075*mm"/>
+    <constant name="SolenoidCoil_radius" value="3650*mm"/>
+    <constant name="SolenoidCoil_center_radius" value="(Solenoid_inner_radius+Solenoid_outer_radius)/2"/>
+        
+    <constant name="Yoke_barrel_inner_radius" value="4235*mm"/>
+    <constant name="Yoke_barrel_outer_radius" value="5485*mm"/>
+    <constant name="Yoke_barrel_half_length" value="4525*mm"/>
+    <constant name="Yoke_barrel_symmetry" value="12"/>
+    
+    <constant name="Yoke_endcap_inner_radius" value="650*mm"/>
+    <constant name="Yoke_endcap_outer_radius" value="Yoke_barrel_outer_radius"/>
+    <constant name="Yoke_endcap_zmin" value="4635*mm"/>
+    <constant name="Yoke_endcap_zmax" value="5875*mm"/>
+    <constant name="Yoke_endcap_outer_symmetry" value="Yoke_barrel_symmetry"/>
+    <constant name="Yoke_endcap_inner_symmetry" value="0"/>
+
+    <!-- FIXME:need to check/--> 
+    <constant name="Lumical_inner_radius" value="BeamPipe_Fork_inner_radius+BeamPipe_ThinCu_thickness"/>
+    <constant name="Lumical_outer_radius" value="100.0*mm"/>
+    <constant name="Lumical_zmax" value="BeamPipe_FirstSeparated_zmax" />
+    <constant name="Lumical_zmin" value="700*mm"/>
+    <constant name="Lumical_thickness" value="(Lumical_zmax-Lumical_zmin)/2.0"/>
+        
+    <constant name="tracker_region_zmax" value="Ecal_endcap_zmin"/>
+    <constant name="tracker_region_rmax" value="Ecal_barrel_inner_radius"/>
+
+    <!--Muon Detector-->
+    <!--strip & fiber dimensions-->
+    <constant name="Muon_strip_x" value="4*cm"/>
+    <constant name="Muon_strip_y" value="1*cm"/>
+    <constant name="Muon_strip_z" value="4*m"/>
+    <constant name="Muon_strip_surf" value="1*mm"/>
+
+    <constant name="Muon_strip_surface_x" value="Muon_strip_x"/>
+    <constant name="Muon_strip_surface_y" value="Muon_strip_y"/>
+    <constant name="Muon_strip_surface_z" value="Muon_strip_z"/>
+
+    <constant name="Muon_strip_scintillator_x" value="Muon_strip_x-2*Muon_strip_surf"/>
+    <constant name="Muon_strip_scintillator_y" value="Muon_strip_y-2*Muon_strip_surf"/>
+    <constant name="Muon_strip_scintillator_z" value="Muon_strip_z"/>
+
+    <constant name="Muon_strip_SiPM_x" value="6*mm"/>
+    <constant name="Muon_strip_SiPM_y" value="6*mm"/>
+    <constant name="Muon_strip_SiPM_z" value="Muon_strip_surf"/>
+    <constant name="Muon_strip_SiPM_posx" value="0"/>
+    <constant name="Muon_strip_SiPM_posy" value="0"/>
+    <constant name="Muon_strip_SiPM_posz" value="0.5*Muon_strip_z+0.5*Muon_strip_SiPM_z"/>
+
+    <constant name="Muon_fiber_core_rmax" value="0.95*mm"/>
+    <constant name="Muon_fiber_core_z" value="Muon_strip_z"/>
+
+    <constant name="Muon_fiber_cladding_rmin" value="Muon_fiber_core_rmax"/>
+    <constant name="Muon_fiber_cladding_rmax" value="1*mm"/>
+    <constant name="Muon_fiber_cladding_z" value="Muon_strip_z"/>
+
+    <constant name="Muon_strip_cut_gap" value="0.1*mm"/>
+    <constant name="Muon_strip_cut3_rmax" value="Muon_fiber_cladding_rmax+Muon_strip_cut_gap"/>
+    <constant name="Muon_strip_cut3_z" value="Muon_strip_z"/>
+    <constant name="Muon_strip_cut3_posx" value="0"/>
+    <constant name="Muon_strip_cut3_posy" value="Muon_strip_cut3_rmax-Muon_fiber_cladding_rmax"/>
+    <constant name="Muon_strip_cut3_posz" value="0"/>
+
+    <constant name="Muon_strip_cut1_x" value="2*Muon_strip_cut3_rmax"/>
+    <constant name="Muon_strip_cut1_y" value="Muon_strip_surf"/>
+    <constant name="Muon_strip_cut1_z" value="Muon_strip_z"/>
+    <constant name="Muon_strip_cut1_posx" value="0"/>
+    <constant name="Muon_strip_cut1_posy" value="0.5*Muon_strip_y-0.5*Muon_strip_cut1_y"/>
+    <constant name="Muon_strip_cut1_posz" value="0"/>
+
+    <constant name="Muon_strip_cut2_x" value="Muon_strip_cut1_x"/>
+    <constant name="Muon_strip_cut2_y" value="0.5*Muon_strip_scintillator_y-Muon_strip_cut3_rmax+Muon_fiber_cladding_rmax"/>
+    <constant name="Muon_strip_cut2_z" value="Muon_strip_z"/>
+    <constant name="Muon_strip_cut2_posx" value="0"/>
+    <constant name="Muon_strip_cut2_posy" value="0.5*Muon_strip_scintillator_y-0.5*Muon_strip_cut2_y"/>
+    <constant name="Muon_strip_cut2_posz" value="0"/>
+
+    <!--standard scale-->
+    <constant name="Muon_standard_scale" value="105*cm"/>    
+    <constant name="Muon_Iron_gap_z" value="100*cm"/>
+
+    <!--Muon Barrel>
+    <constant name="Muon_barrel_barrel_num" value="2"/>
+    <constant name="Muon_barrel_iron_part_num" value="12"/>
+    <constant name="Muon_barrel_superlayer_num" value="6"/>
+    <constant name="Muon_barrel_strip_num_0" value="40"/>
+    <constant name="Muon_barrel_strip_num_1" value="48"/>
+    <constant name="Muon_barrel_strip_num_2" value="62"/>
+    <constant name="Muon_barrel_strip_num_3" value="74"/>
+    <constant name="Muon_barrel_strip_num_4" value="84"/>
+    <constant name="Muon_barrel_strip_num_5" value="96"/>
+    <constant name="Muon_barrel_strip_num" value="100"/>
+
+    <constant name="Muon_barrel_iron_x1" value="Muon_standard_scale"/>
+    <constant name="Muon_barrel_iron_y" value="Muon_strip_z+2*Muon_strip_surf+Muon_Iron_gap_z"/>
+    <constant name="Muon_barrel_iron_z" value="Muon_standard_scale"/>
+    <constant name="Muon_barrel_iron_posx" value="-1*Muon_standard_scale"/>
+
+    <constant name="Muon_barrel_barrel_y" value="Muon_barrel_iron_y"/>
+    <constant name="Muon_barrel_barrel_posy" value="0.5*Muon_barrel_barrel_y"/>
+
+    <constant name="Muon_barrel_superlayer_init" value="-21.5*cm"/>
+    <constant name="Muon_barrel_superlayer_gap" value="12.5*cm"/>
+    <constant name="Muon_barrel_superlayer_air_gap" value="0.1*cm"/>
+    <constant name="Muon_barrel_superlayer_aluminum_gap" value="0.5*Muon_barrel_superlayer_air_gap"/>
+
+    <constant name="Muon_barrel_superlayer_y" value="2*Muon_strip_y+2*Muon_barrel_superlayer_air_gap"/>
+    <constant name="Muon_barrel_superlayer_z" value="Muon_strip_z+2*Muon_strip_surf+2*Muon_barrel_superlayer_air_gap"/> 
+
+    <Muon Endcap>
+    <constant name="Muon_endcap_part_num" value="4"/>
+    <constant name="Muon_endcap_superlayer_num" value="6"/>
+    <constant name="Muon_endcap_iron_gap_num" value="Muon_endcap_superlayer_num+1"/>
+    <constant name="Muon_endcap_layer_num" value="2"/>
+    <constant name="Muon_endcap_strip_num" value="146"/>
+    <constant name="Muon_endcap_strip_num_cut" value="13"/>
+    <constant name="Muon_endcap_endcap_rmin" value="52*cm"/>
+    <constant name="Muon_endcap_magnification" value="1.02"/>
+
+    <constant name="Muon_endcap_iron_gap" value="12.5*cm"/>
+    <constant name="Muon_endcap_endcap_z" value="Muon_endcap_iron_gap_num*Muon_endcap_iron_gap+2*Muon_endcap_superlayer_num*Muon_strip_y"/>
+    <constant name="Muon_endcap_endcap_posy" value="Muon_strip_z+2*Muon_strip_surf+Muon_Iron_gap_z+0.5*Muon_endcap_endcap_z"/-->
+
+  </define>
+  
+  <limits>
+    <limitset name="cal_limits">
+      <limit name="step_length_max" particles="*" value="5.0" unit="mm" />
+    </limitset>
+    <limitset name="tpc_limits">
+      <limit name="step_length_max" particles="*" value="10.0" unit="mm" />
+    </limitset>
+    <limitset name="tracker_limits">
+      <limit name="step_length_max" particles="*" value="5.0" unit="mm" />
+    </limitset>
+    <limitset name="detail_limits">
+      <limit name="step_length_max" particles="*" value="1.0" unit="mm" />
+    </limitset>
+    <limitset name="support_limits">
+      <limit name="step_length_max" particles="*" value="10.0" unit="mm" />
+    </limitset>
+    <limitset name="yoke_limits">
+      <limit name="step_length_max" particles="*" value="10.0" unit="mm" />
+      <!--limit name="track_length_max" particles="*" value="5.0" unit="mm" /-->
+      <!--limit name="time_max" particles="*" value="5.0" unit="ns" /-->
+      <limit name="ekin_min" particles="*" value="10" unit="MeV" />
+      <limit name="range_min" particles="*" value="10.0" unit="mm" />
+    </limitset>
+  </limits>
+
+  <regions>
+    <region name="BeampipeRegion"/>
+    <region name="VertexRegion"/>
+    <region name="ForwardRegion"/>
+  </regions>
+
+  <display>
+    <vis name="VXDVis"           alpha="0.1" r="0.1"   g=".5"      b=".5"    showDaughters="true"  visible="true"/>
+    <vis name="VXDLayerVis"      alpha="1.0" r="0.1"   g=".5"      b=".5"    showDaughters="true"  visible="true"/>
+    <vis name="VXDSupportVis"    alpha="1.0" r="0.0"   g="1.0"     b="0.0"   showDaughters="true"  visible="true"/>
+    <vis name="FTDVis"           alpha="1.0" r="0.5"   g="0.87"    b="0.11"  showDaughters="true"  visible="true"/>
+    <vis name="FTDSupportVis"    alpha="1.0" r="0.3"   g="0.3"     b="1.0"   showDaughters="true"  visible="true"/>
+    <vis name="FTDSensitiveVis"  alpha="1.0" r="0.3"   g="0.5"     b="1.0"   showDaughters="true"  visible="true"/>
+    <vis name="DCVis"            alpha="1.0" r="0.96"  g="0.64"    b="0.90"  showDaughters="true"  visible="true"/>
+    <vis name="DCLayerVis"       alpha="1.0" r="0.96"  g="0.64"    b="0.90"  showDaughters="false" visible="true"/>
+    <vis name="TPCVis"           alpha="1.0" r="0.96"  g="0.64"    b="0.90"  showDaughters="true"  visible="true"/>
+    <vis name="TPCMotherVis"     alpha="1.0" r="0.96"  g="0.64"    b="0.90"  showDaughters="true"  visible="true"/>
+    <vis name="TPCGasVis"        alpha="1.0" r="0.96"  g="0.64"    b="0.90"  showDaughters="true"  visible="false"/>
+    <vis name="TPCCathodeVis"    alpha="1.0" r="0.6"   g="1.0"     b="0.80"  showDaughters="true"  visible="true"/>
+    <vis name="TPCCathodeGripVis" alpha="1." r="0.7"   g="0.7"     b="0.70"  showDaughters="true"  visible="true"/>
+    <vis name="TPCShellVis"      alpha="1.0" r="0.5"   g="0.5"     b="0.5"   showDaughters="true"  visible="true"/>
+    <vis name="SITVis"           alpha="0.0" r="0.54"  g="0.59"    b="0.93"  showDaughters="true"  visible="false"/>
+    <vis name="SITSupportVis"    alpha="1.0" r="0.0"   g="0.0"     b="1.0"   showDaughters="false" visible="true"/>
+    <vis name="SITSensitiveVis"  alpha="1.0" r="0.67"  g="0.99"    b="0.78"  showDaughters="false" visible="true"/>
+    <vis name="SETVis"           alpha="0.0" r="0.8"   g="0.8"     b="0.4"   showDaughters="true"  visible="false"/>
+    <vis name="SETSupportVis"    alpha="1.0" r="0.5"   g="0.3"     b="1.0"   showDaughters="true"  visible="true"/>
+    <vis name="SETSensitiveVis"  alpha="1.0" r="0.0"   g="0.60"    b="1.0"   showDaughters="true"  visible="true"/>
+    <vis name="ECALVis"          alpha="1.0" r="0.2"   g="0.6"     b="0"     showDaughters="true"  visible="true"/>
+    <vis name="HCALVis"          alpha="1.0" r="0.95"  g="0.78"    b="0.69"  showDaughters="true"  visible="true"/>
+    <vis name="SOLVis"           alpha="1.0" r="0.0"   g="0.0"     b="0.8"   showDaughters="true"  visible="true"/>
+    <vis name="YOKEVis"          alpha="1.0" r="0.64"  g="0.75"    b="0.99"  showDaughters="true" visible="true"/>
+    <vis name="LCALVis"          alpha="1.0" r="0.25"  g="0.88"    b="0.81"  showDaughters="true"  visible="true"/>
+    <vis name="SupportVis"       alpha="1.0" r="0.2"   g="0.2"     b="0.2"   showDaughters="true"  visible="true"/>
+    <vis name="ShellVis"         alpha="1.0" r="1.0"   g="1.0"     b="0.8"   showDaughters="false" visible="true"/>
+
+    <vis name="WhiteVis"         alpha="0.0" r=".96" g=".96"  b=".96"   showDaughters="true"  visible="true"/>
+    <vis name="LightGrayVis"     alpha="0.0" r=".75" g=".75"  b=".75"   showDaughters="true"  visible="true"/>
+    <vis name="Invisible"        alpha="0.0" r="0.0" g="0.0"  b="0.0"   showDaughters="false" visible="false"/>
+    <vis name="SeeThrough"       alpha="0.0" r="0.0" g="0.0"  b="0.0"   showDaughters="true"  visible="false"/>
+    <vis name="RedVis"           alpha="1.0" r="1.0" g="0.0"  b="0.0"   showDaughters="true"  visible="true"/>
+    <vis name="GreenVis"         alpha="1.0" r="0.0" g="1.0"  b="0.0"   showDaughters="true"  visible="true"/>
+    <vis name="BlueVis"          alpha="1.0" r="0.0" g="0.0"  b="1.0"   showDaughters="true"  visible="true"/>
+    <vis name="CyanVis"          alpha="1.0" r="0.0" g="1.0"  b="1.0"   showDaughters="true"  visible="true"/>
+    <vis name="MagentaVis"       alpha="1.0" r="1.0" g="0.0"  b="1.0"   showDaughters="true"  visible="true"/>
+    <vis name="VioletVis"        alpha="1.0" r=".83" g=".55"  b=".89"   showDaughters="true" visible="true"/>
+    <vis name="BlueVioletVis"    alpha="1.0" r=".55" g=".36"  b="1.0"   showDaughters="true"  visible="true"/>
+    <vis name="OrangeVis"        alpha="1.0" r="1.0" g="0.6"  b="0.0"   showDaughters="true"  visible="true"/>
+    <vis name="YellowVis"        alpha="1.0" r="1.0" g="1.0"  b="0.0"   showDaughters="true"  visible="true"/>
+    <vis name="BlackVis"         alpha="1.0" r="0.0" g="0.0"  b="0.0"   showDaughters="true"  visible="true"/>
+    <vis name="GrayVis"          alpha="1.0" r="0.5" g="0.5"  b="0.5"   showDaughters="true"  visible="true"/>
+  </display>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/TDR_CaloTest/TDR_longEcal_GSHcal.xml b/Detector/DetCRD/compact/TDR_CaloTest/TDR_longEcal_GSHcal.xml
new file mode 100644
index 00000000..99e88de6
--- /dev/null
+++ b/Detector/DetCRD/compact/TDR_CaloTest/TDR_longEcal_GSHcal.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+  <info name="TDR_o1_v01"
+        title="CepC reference detctor for TDR"
+        author=""
+        url="http://cepc.ihep.ac.cn"
+        status="developing"
+        version="v01">
+    <comment>CepC reference detector simulation models used for TDR </comment>
+  </info>
+  
+  <includes>
+    <gdmlFile  ref="${DD4hepINSTALL}/DDDetectors/compact/elements.xml"/>
+    <gdmlFile  ref="../CRD_common_v02/materials.xml"/>
+  </includes>
+  
+  <define>
+    <constant name="world_size" value="10*m"/>
+    <constant name="world_x" value="world_size"/>
+    <constant name="world_y" value="world_size"/>
+    <constant name="world_z" value="world_size"/>
+
+    <include ref="${DD4hepINSTALL}/DDDetectors/compact/detector_types.xml"/>
+  </define>
+
+  <include ref="./TDR_Dimensions_v01_01.xml"/>
+
+  <!--TODO: vertex cooling-->
+  <include ref="../CRD_common_v02/Beampipe_v01_03.xml"/>
+  <!--preliminary vertex and tracker, to update/-->
+  <include ref="../CRD_common_v02/VXD_StaggeredLadder_v02_01.xml"/>
+  <include ref="../CRD_common_v02/FTD_SkewRing_v01_05.xml"/>
+  <include ref="../CRD_common_v02/SIT_SimplePixel_v01_03.xml"/>
+  <!--include ref="../CRD_common_v01/TPC_Simple_v10_02.xml"/-->
+  <!--use 10 rows clustering version/-->
+  <include ref="../CRD_common_v02/TPC_ModularEndcap_o1_v02.xml"/>
+  <include ref="../CRD_common_v01/SET_SimplePixel_v01_01.xml"/>
+
+  <include ref="../CRD_common_v01/Ecal_Crystal_Barrel_v01_02.xml"/>
+  <include ref="../CRD_common_v01/Ecal_Crystal_Endcap_v01_01.xml"/>
+  <include ref="../CRD_common_v01/SHcalGlass_Barrel_v05.xml"/>
+  <include ref="../CRD_common_v01/SHcalGlass_Endcaps_v01.xml"/>
+
+  <!--Lumical to update-->
+  <include ref="../CRD_common_v01/Lumical_o1_v01.xml"/>
+  <!--preliminary Magnet, to update/-->
+  <include ref="../CRD_common_v02/Coil_Simple_v01_02.xml"/>
+  <!--preliminary Muon, obselete/-->
+  <!--include ref="../CRD_common_v02/Yoke_Polyhedra_Barrel_v01_01.xml"/>
+  <include ref="../CRD_common_v02/Yoke_Polyhedra_Endcaps_v01_01.xml"/-->
+ 
+  <!--muon detector-->
+  <include ref="../CRD_common_v01/Muon_Barrel_v01_01.xml"/>
+  <include ref="../CRD_common_v01/Muon_Endcap_v01_01.xml"/>
+ 
+  <fields>
+    <field name="InnerSolenoid" type="solenoid"
+           inner_field="Field_nominal_value"
+           outer_field="0"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="SolenoidCoil_center_radius"
+           outer_radius="Solenoid_outer_radius">
+    </field>
+    <field name="OuterSolenoid" type="solenoid"
+           inner_field="0"
+           outer_field="Field_outer_nominal_value"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="Solenoid_outer_radius"
+           outer_radius="Yoke_barrel_inner_radius">
+    </field>
+  </fields>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/TDR_CaloTest/TDR_onlyGSHcal.xml b/Detector/DetCRD/compact/TDR_CaloTest/TDR_onlyGSHcal.xml
new file mode 100644
index 00000000..ac7ada59
--- /dev/null
+++ b/Detector/DetCRD/compact/TDR_CaloTest/TDR_onlyGSHcal.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+  <info name="TDR_o1_v01"
+        title="CepC reference detctor for TDR"
+        author=""
+        url="http://cepc.ihep.ac.cn"
+        status="developing"
+        version="v01">
+    <comment>CepC reference detector simulation models used for TDR </comment>
+  </info>
+  
+  <includes>
+    <gdmlFile  ref="${DD4hepINSTALL}/DDDetectors/compact/elements.xml"/>
+    <gdmlFile  ref="../CRD_common_v02/materials.xml"/>
+  </includes>
+  
+  <define>
+    <constant name="world_size" value="10*m"/>
+    <constant name="world_x" value="world_size"/>
+    <constant name="world_y" value="world_size"/>
+    <constant name="world_z" value="world_size"/>
+
+    <include ref="${DD4hepINSTALL}/DDDetectors/compact/detector_types.xml"/>
+  </define>
+
+  <include ref="./TDR_Dimensions_v01_01.xml"/>
+
+  <!--TODO: vertex cooling-->
+  <!--include ref="../CRD_common_v02/Beampipe_v01_03.xml"/-->
+  <!--preliminary vertex and tracker, to update/-->
+  <!--include ref="../CRD_common_v02/VXD_StaggeredLadder_v02_01.xml"/>
+  <include ref="../CRD_common_v02/FTD_SkewRing_v01_05.xml"/>
+  <include ref="../CRD_common_v02/SIT_SimplePixel_v01_03.xml"/-->
+  <!--include ref="../CRD_common_v01/TPC_Simple_v10_02.xml"/-->
+  <!--use 10 rows clustering version/-->
+  <!--include ref="../CRD_common_v02/TPC_ModularEndcap_o1_v02.xml"/>
+  <include ref="../CRD_common_v01/SET_SimplePixel_v01_01.xml"/-->
+
+  <!--include ref="../CRD_common_v01/Ecal_Crystal_Barrel_Short_v02.xml"/-->
+  <!--include ref="../CRD_common_v01/Ecal_Crystal_Endcap_Short_v01.xml"/-->
+  <include ref="../CRD_common_v01/SHcalGlass_Barrel_v05.xml"/>
+  <include ref="../CRD_common_v01/SHcalGlass_Endcaps_v01.xml"/>
+
+  <!--Lumical to update-->
+  <!--include ref="../CRD_common_v01/Lumical_o1_v01.xml"/-->
+  <!--preliminary Magnet, to update/-->
+  <!--include ref="../CRD_common_v02/Coil_Simple_v01_02.xml"/-->
+  <!--preliminary Muon, obselete/-->
+  <!--include ref="../CRD_common_v02/Yoke_Polyhedra_Barrel_v01_01.xml"/>
+  <include ref="../CRD_common_v02/Yoke_Polyhedra_Endcaps_v01_01.xml"/-->
+ 
+  <!--muon detector-->
+  <!--include ref="../CRD_common_v01/Muon_Barrel_v01_01.xml"/-->
+  <!--include ref="../CRD_common_v01/Muon_Endcap_v01_01.xml"/-->
+ 
+  <fields>
+    <field name="InnerSolenoid" type="solenoid"
+           inner_field="Field_nominal_value"
+           outer_field="0"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="SolenoidCoil_center_radius"
+           outer_radius="Solenoid_outer_radius">
+    </field>
+    <field name="OuterSolenoid" type="solenoid"
+           inner_field="0"
+           outer_field="Field_outer_nominal_value"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="Solenoid_outer_radius"
+           outer_radius="Yoke_barrel_inner_radius">
+    </field>
+  </fields>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/TDR_CaloTest/TDR_onlyPSHcal.xml b/Detector/DetCRD/compact/TDR_CaloTest/TDR_onlyPSHcal.xml
new file mode 100644
index 00000000..3d56ee81
--- /dev/null
+++ b/Detector/DetCRD/compact/TDR_CaloTest/TDR_onlyPSHcal.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+  <info name="TDR_o1_v01"
+        title="CepC reference detctor for TDR"
+        author=""
+        url="http://cepc.ihep.ac.cn"
+        status="developing"
+        version="v01">
+    <comment>CepC reference detector simulation models used for TDR </comment>
+  </info>
+  
+  <includes>
+    <gdmlFile  ref="${DD4hepINSTALL}/DDDetectors/compact/elements.xml"/>
+    <gdmlFile  ref="../CRD_common_v02/materials.xml"/>
+  </includes>
+  
+  <define>
+    <constant name="world_size" value="10*m"/>
+    <constant name="world_x" value="world_size"/>
+    <constant name="world_y" value="world_size"/>
+    <constant name="world_z" value="world_size"/>
+
+    <include ref="${DD4hepINSTALL}/DDDetectors/compact/detector_types.xml"/>
+  </define>
+
+  <include ref="./TDR_Dimensions_v01_01.xml"/>
+
+  <!--TODO: vertex cooling-->
+  <!--include ref="../CRD_common_v02/Beampipe_v01_03.xml"/-->
+  <!--preliminary vertex and tracker, to update/-->
+  <!--include ref="../CRD_common_v02/VXD_StaggeredLadder_v02_01.xml"/>
+  <include ref="../CRD_common_v02/FTD_SkewRing_v01_05.xml"/>
+  <include ref="../CRD_common_v02/SIT_SimplePixel_v01_03.xml"/-->
+  <!--include ref="../CRD_common_v01/TPC_Simple_v10_02.xml"/-->
+  <!--use 10 rows clustering version/-->
+  <!--include ref="../CRD_common_v02/TPC_ModularEndcap_o1_v02.xml"/>
+  <include ref="../CRD_common_v01/SET_SimplePixel_v01_01.xml"/-->
+
+  <!--include ref="../CRD_common_v01/Ecal_Crystal_Barrel_Short_v02.xml"/-->
+  <!--include ref="../CRD_common_v01/Ecal_Crystal_Endcap_Short_v01.xml"/-->
+  <include ref="../CRD_common_v01/SHcalSc04_Barrel_v04_01.xml"/>
+  <include ref="../CRD_common_v01/SHcalSc04_Endcaps_v02.xml"/>
+
+  <!--Lumical to update-->
+  <!--include ref="../CRD_common_v01/Lumical_o1_v01.xml"/-->
+  <!--preliminary Magnet, to update/-->
+  <!--include ref="../CRD_common_v02/Coil_Simple_v01_02.xml"/-->
+  <!--preliminary Muon, obselete/-->
+  <!--include ref="../CRD_common_v02/Yoke_Polyhedra_Barrel_v01_01.xml"/>
+  <include ref="../CRD_common_v02/Yoke_Polyhedra_Endcaps_v01_01.xml"/-->
+ 
+  <!--muon detector-->
+  <!--include ref="../CRD_common_v01/Muon_Barrel_v01_01.xml"/-->
+  <!--include ref="../CRD_common_v01/Muon_Endcap_v01_01.xml"/-->
+ 
+  <fields>
+    <field name="InnerSolenoid" type="solenoid"
+           inner_field="Field_nominal_value"
+           outer_field="0"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="SolenoidCoil_center_radius"
+           outer_radius="Solenoid_outer_radius">
+    </field>
+    <field name="OuterSolenoid" type="solenoid"
+           inner_field="0"
+           outer_field="Field_outer_nominal_value"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="Solenoid_outer_radius"
+           outer_radius="Yoke_barrel_inner_radius">
+    </field>
+  </fields>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/TDR_CaloTest/TDR_onlyshortEcal.xml b/Detector/DetCRD/compact/TDR_CaloTest/TDR_onlyshortEcal.xml
new file mode 100644
index 00000000..48d39b44
--- /dev/null
+++ b/Detector/DetCRD/compact/TDR_CaloTest/TDR_onlyshortEcal.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+  <info name="TDR_o1_v01"
+        title="CepC reference detctor for TDR"
+        author=""
+        url="http://cepc.ihep.ac.cn"
+        status="developing"
+        version="v01">
+    <comment>CepC reference detector simulation models used for TDR </comment>
+  </info>
+  
+  <includes>
+    <gdmlFile  ref="${DD4hepINSTALL}/DDDetectors/compact/elements.xml"/>
+    <gdmlFile  ref="../CRD_common_v02/materials.xml"/>
+  </includes>
+  
+  <define>
+    <constant name="world_size" value="10*m"/>
+    <constant name="world_x" value="world_size"/>
+    <constant name="world_y" value="world_size"/>
+    <constant name="world_z" value="world_size"/>
+
+    <include ref="${DD4hepINSTALL}/DDDetectors/compact/detector_types.xml"/>
+  </define>
+
+  <include ref="./TDR_Dimensions_v01_01.xml"/>
+
+  <!--TODO: vertex cooling-->
+  <!--include ref="../CRD_common_v02/Beampipe_v01_03.xml"/-->
+  <!--preliminary vertex and tracker, to update/-->
+  <!--include ref="../CRD_common_v02/VXD_StaggeredLadder_v02_01.xml"/>
+  <include ref="../CRD_common_v02/FTD_SkewRing_v01_05.xml"/>
+  <include ref="../CRD_common_v02/SIT_SimplePixel_v01_03.xml"/-->
+  <!--include ref="../CRD_common_v01/TPC_Simple_v10_02.xml"/-->
+  <!--use 10 rows clustering version/-->
+  <!--include ref="../CRD_common_v02/TPC_ModularEndcap_o1_v02.xml"/>
+  <include ref="../CRD_common_v01/SET_SimplePixel_v01_01.xml"/-->
+
+  <include ref="../CRD_common_v01/Ecal_Crystal_Barrel_Short_v02.xml"/>
+  <include ref="../CRD_common_v01/Ecal_Crystal_Endcap_Short_v01.xml"/>
+  <!--include ref="../CRD_common_v01/SHcalSc04_Barrel_v04_01.xml"/-->
+  <!--include ref="../CRD_common_v01/SHcalSc04_Endcaps_v02.xml"/-->
+
+  <!--Lumical to update-->
+  <!--include ref="../CRD_common_v01/Lumical_o1_v01.xml"/-->
+  <!--preliminary Magnet, to update/-->
+  <!--include ref="../CRD_common_v02/Coil_Simple_v01_02.xml"/-->
+  <!--preliminary Muon, obselete/-->
+  <!--include ref="../CRD_common_v02/Yoke_Polyhedra_Barrel_v01_01.xml"/>
+  <include ref="../CRD_common_v02/Yoke_Polyhedra_Endcaps_v01_01.xml"/-->
+ 
+  <!--muon detector-->
+  <!--include ref="../CRD_common_v01/Muon_Barrel_v01_01.xml"/-->
+  <!--include ref="../CRD_common_v01/Muon_Endcap_v01_01.xml"/-->
+ 
+  <fields>
+    <field name="InnerSolenoid" type="solenoid"
+           inner_field="Field_nominal_value"
+           outer_field="0"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="SolenoidCoil_center_radius"
+           outer_radius="Solenoid_outer_radius">
+    </field>
+    <field name="OuterSolenoid" type="solenoid"
+           inner_field="0"
+           outer_field="Field_outer_nominal_value"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="Solenoid_outer_radius"
+           outer_radius="Yoke_barrel_inner_radius">
+    </field>
+  </fields>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/TDR_CaloTest/TDR_shortEcal_PSHcal.xml b/Detector/DetCRD/compact/TDR_CaloTest/TDR_shortEcal_PSHcal.xml
new file mode 100644
index 00000000..07ecdec0
--- /dev/null
+++ b/Detector/DetCRD/compact/TDR_CaloTest/TDR_shortEcal_PSHcal.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lccdd xmlns:compact="http://www.lcsim.org/schemas/compact/1.0"
+       xmlns:xs="http://www.w3.org/2001/XMLSchema"
+       xs:noNamespaceSchemaLocation="http://www.lcsim.org/schemas/compact/1.0/compact.xsd">
+  <info name="TDR_o1_v01"
+        title="CepC reference detctor for TDR"
+        author=""
+        url="http://cepc.ihep.ac.cn"
+        status="developing"
+        version="v01">
+    <comment>CepC reference detector simulation models used for TDR </comment>
+  </info>
+  
+  <includes>
+    <gdmlFile  ref="${DD4hepINSTALL}/DDDetectors/compact/elements.xml"/>
+    <gdmlFile  ref="../CRD_common_v02/materials.xml"/>
+  </includes>
+  
+  <define>
+    <constant name="world_size" value="10*m"/>
+    <constant name="world_x" value="world_size"/>
+    <constant name="world_y" value="world_size"/>
+    <constant name="world_z" value="world_size"/>
+
+    <include ref="${DD4hepINSTALL}/DDDetectors/compact/detector_types.xml"/>
+  </define>
+
+  <include ref="./TDR_Dimensions_v01_01.xml"/>
+
+  <!--TODO: vertex cooling-->
+  <include ref="../CRD_common_v02/Beampipe_v01_03.xml"/>
+  <!--preliminary vertex and tracker, to update/-->
+  <include ref="../CRD_common_v02/VXD_StaggeredLadder_v02_01.xml"/>
+  <include ref="../CRD_common_v02/FTD_SkewRing_v01_05.xml"/>
+  <include ref="../CRD_common_v02/SIT_SimplePixel_v01_03.xml"/>
+  <!--include ref="../CRD_common_v01/TPC_Simple_v10_02.xml"/-->
+  <!--use 10 rows clustering version/-->
+  <include ref="../CRD_common_v02/TPC_ModularEndcap_o1_v02.xml"/>
+  <include ref="../CRD_common_v01/SET_SimplePixel_v01_01.xml"/>
+
+  <include ref="../CRD_common_v01/Ecal_Crystal_Barrel_Short_v02.xml"/>
+  <include ref="../CRD_common_v01/Ecal_Crystal_Endcap_Short_v01.xml"/>
+  <include ref="../CRD_common_v01/SHcalSc04_Barrel_v04_01.xml"/>
+  <include ref="../CRD_common_v01/SHcalSc04_Endcaps_v02.xml"/>
+
+  <!--Lumical to update-->
+  <include ref="../CRD_common_v01/Lumical_o1_v01.xml"/>
+  <!--preliminary Magnet, to update/-->
+  <include ref="../CRD_common_v02/Coil_Simple_v01_02.xml"/>
+  <!--preliminary Muon, obselete/-->
+  <!--include ref="../CRD_common_v02/Yoke_Polyhedra_Barrel_v01_01.xml"/>
+  <include ref="../CRD_common_v02/Yoke_Polyhedra_Endcaps_v01_01.xml"/-->
+ 
+  <!--muon detector-->
+  <include ref="../CRD_common_v01/Muon_Barrel_v01_01.xml"/>
+  <include ref="../CRD_common_v01/Muon_Endcap_v01_01.xml"/>
+ 
+  <fields>
+    <field name="InnerSolenoid" type="solenoid"
+           inner_field="Field_nominal_value"
+           outer_field="0"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="SolenoidCoil_center_radius"
+           outer_radius="Solenoid_outer_radius">
+    </field>
+    <field name="OuterSolenoid" type="solenoid"
+           inner_field="0"
+           outer_field="Field_outer_nominal_value"
+           zmax="SolenoidCoil_half_length"
+           inner_radius="Solenoid_outer_radius"
+           outer_radius="Yoke_barrel_inner_radius">
+    </field>
+  </fields>
+
+</lccdd>
diff --git a/Detector/DetCRD/compact/TDR_o1_v01/TDR_Dimensions_v01_01.xml b/Detector/DetCRD/compact/TDR_o1_v01/TDR_Dimensions_v01_01.xml
index da96a43d..23f7c03a 100644
--- a/Detector/DetCRD/compact/TDR_o1_v01/TDR_Dimensions_v01_01.xml
+++ b/Detector/DetCRD/compact/TDR_o1_v01/TDR_Dimensions_v01_01.xml
@@ -34,6 +34,7 @@
     <constant name="DetID_TPC"          value="  4"/>
     <constant name="DetID_SET"          value="  5"/>
     <constant name="DetID_ETD"          value="  6"/>
+    <constant name="DetID_OTKBarrel"    value="  8"/>
     <constant name="DetID_OTKEndCap"    value="  9"/>
     
     <constant name="DetID_ECAL"         value=" 20"/>
diff --git a/Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01.xml b/Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01.xml
index 1782fa6d..48b9e456 100644
--- a/Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01.xml
+++ b/Detector/DetCRD/compact/TDR_o1_v01/TDR_o1_v01.xml
@@ -41,7 +41,7 @@
   <include ref="../CRD_common_v01/Ecal_Crystal_Barrel_v01_02.xml"/>
   <!--preliminary EcalEndcaps/-->
   <include ref="../CRD_common_v02/EcalEndcaps_Polyhedra_v01_01.xml"/>
-  <include ref="../CRD_common_v02/SHcalGlass_Barrel_v04_02.xml"/>
+  <include ref="../CRD_common_v01/SHcalGlass_Barrel_v05.xml"/>
   <!--preliminary HcalEndcaps/-->
   <include ref="../CRD_common_v02/HcalEndcaps_Polyhedra_v01_01.xml"/>
 
diff --git a/Detector/DetCRD/scripts/TDR_o1_v01/calodigi.py b/Detector/DetCRD/scripts/TDR_o1_v01/calodigi.py
new file mode 100644
index 00000000..707db432
--- /dev/null
+++ b/Detector/DetCRD/scripts/TDR_o1_v01/calodigi.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+import os
+from Gaudi.Configuration import *
+
+from Configurables import k4DataSvc
+dsvc = k4DataSvc("EventDataSvc", input="rec00.root")
+
+from Configurables import RndmGenSvc, HepRndm__Engine_CLHEP__RanluxEngine_
+seed = [12340]
+# rndmengine = HepRndm__Engine_CLHEP__RanluxEngine_() # The default engine in Gaudi                                                                                                                                            
+rndmengine = HepRndm__Engine_CLHEP__HepJamesRandom_("RndmGenSvc.Engine") # The default engine in Geant4                                                                                                                        
+rndmengine.SetSingleton = True
+rndmengine.Seeds = seed
+
+rndmgensvc = RndmGenSvc("RndmGenSvc")
+rndmgensvc.Engine = rndmengine.name()
+
+geometry_option = "TDR_o1_v01/TDR_o1_v01.xml"
+
+if not os.getenv("DETCRDROOT"):
+    print("Can't find the geometry. Please setup envvar DETCRDROOT." )
+    sys.exit(-1)
+
+geometry_path = os.path.join(os.getenv("DETCRDROOT"), "compact", geometry_option)
+if not os.path.exists(geometry_path):
+    print("Can't find the compact geometry file: %s"%geometry_path)
+    sys.exit(-1)
+
+from Configurables import GeomSvc
+geosvc = GeomSvc("GeomSvc")
+geosvc.compact = geometry_path
+
+from Configurables import MarlinEvtSeeder
+evtseeder = MarlinEvtSeeder("EventSeeder")
+
+from Configurables import GearSvc
+gearsvc = GearSvc("GearSvc")
+
+from Configurables import TrackSystemSvc
+tracksystemsvc = TrackSystemSvc("TrackSystemSvc")
+
+from Configurables import SimplePIDSvc
+pidsvc = SimplePIDSvc("SimplePIDSvc")
+cepcswdatatop = "/cvmfs/cepcsw.ihep.ac.cn/prototype/releases/data/latest"
+pidsvc.ParFile = os.path.join(cepcswdatatop, "CEPCSWData/offline-data/Service/SimplePIDSvc/data/dNdx_TPC.root")
+
+from Configurables import PodioInput
+podioinput = PodioInput("PodioReader", collections=[
+#    "EventHeader",
+    "MCParticle",
+    "EcalBarrelCollection", 
+    "EcalBarrelContributionCollection", 
+    "HcalBarrelCollection", 
+    "HcalBarrelContributionCollection", 
+    "CompleteTracks", 
+    "CompleteTracksParticleAssociation"
+    ])
+
+########## Digitalization ################
+
+##ECAL##
+from Configurables import EcalDigiAlg
+EcalDigi = EcalDigiAlg("EcalDigiAlg")
+EcalDigi.ReadOutName = "EcalBarrelCollection"
+EcalDigi.SimCaloHitCollection = "EcalBarrelCollection"
+EcalDigi.CaloHitCollection = "ECALBarrel"
+EcalDigi.CaloAssociationCollection = "ECALBarrelAssoCol"
+EcalDigi.CaloMCPAssociationCollection = "ECALBarrelParticleAssoCol"
+EcalDigi.SkipEvt = 0
+EcalDigi.Seed = 2079
+#Digitalization parameters
+EcalDigi.CalibrECAL = 1.
+EcalDigi.AttenuationLength = 7e10
+EcalDigi.TimeResolution = 0.5        #unit: ns
+EcalDigi.ChargeThresholdFrac = 0.05
+EcalDigi.Debug=1
+EcalDigi.WriteNtuple = 0
+EcalDigi.OutFileName = "Digi_ECAL.root"
+#########################################
+
+##HCAL##
+from Configurables import HcalDigiAlg
+HcalDigi = HcalDigiAlg("HcalDigiAlg")
+HcalDigi.ReadOutName = "HcalBarrelCollection"
+HcalDigi.SimCaloHitCollection = "HcalBarrelCollection"
+HcalDigi.CaloHitCollection = "HCALBarrel"
+HcalDigi.CaloAssociationCollection = "HCALBarrelAssoCol"
+HcalDigi.CaloMCPAssociationCollection = "HCALBarrelParticleAssoCol"
+HcalDigi.SkipEvt = 0
+HcalDigi.Seed = 2079
+#Digitalization parameters
+HcalDigi.MIPResponse = 0.01  # 0.5 MeV / MIP
+HcalDigi.MIPThreshold = 0.5    # Unit: MIP
+HcalDigi.CalibrHCAL = 1.
+HcalDigi.Debug=0
+HcalDigi.WriteNtuple = 0
+HcalDigi.OutFileName = "Digi_HCAL.root"
+
+
+# output
+from Configurables import PodioOutput
+out = PodioOutput("outputalg")
+out.filename = "CaloDigi_TDR_o1_v01_00.root"
+out.outputCommands = ["keep *"]
+
+# ApplicationMgr
+from Configurables import ApplicationMgr
+mgr = ApplicationMgr(
+    TopAlg = [podioinput, EcalDigi, HcalDigi, out],
+    EvtSel = 'NONE',
+    EvtMax = 5,
+    ExtSvc = [dsvc, rndmengine, rndmgensvc, geosvc],
+    HistogramPersistency = 'ROOT',
+    OutputLevel = ERROR
+)
diff --git a/Detector/DetCRD/scripts/TDR_o1_v01/tracking.py b/Detector/DetCRD/scripts/TDR_o1_v01/tracking.py
index cf78fb4e..b621a182 100644
--- a/Detector/DetCRD/scripts/TDR_o1_v01/tracking.py
+++ b/Detector/DetCRD/scripts/TDR_o1_v01/tracking.py
@@ -81,8 +81,8 @@ digiSIT.IsStrip = False
 digiSIT.SimTrackHitCollection = "SITCollection"
 digiSIT.TrackerHitCollection = sithitname
 digiSIT.TrackerHitAssociationCollection = "SITTrackerHitAssociation"
-digiSIT.ResolutionU = [0.0072]
-digiSIT.ResolutionV = [0.086]
+digiSIT.ResolutionU = [0.0098]
+digiSIT.ResolutionV = [0.0433]
 digiSIT.UsePlanarTag = True
 digiSIT.ParameterizeResolution = False
 digiSIT.ParametersU = [2.29655e-03, 0.965899e-03, 0.584699e-03, 17.0856, 84.566, 12.4695e-03, -0.0643059, 0.168662, 1.87998e-03, 0.514452]
@@ -94,8 +94,8 @@ digiSET.IsStrip = False
 digiSET.SimTrackHitCollection = "SETCollection"
 digiSET.TrackerHitCollection = sethitname
 digiSET.TrackerHitAssociationCollection = "SETTrackerHitAssociation"
-digiSET.ResolutionU = [0.0072]
-digiSET.ResolutionV = [0.086]
+digiSET.ResolutionU = [0.005]
+digiSET.ResolutionV = [0.021]
 digiSET.UsePlanarTag = True
 digiSET.ParameterizeResolution = False
 digiSET.ParametersU = [2.29655e-03, 0.965899e-03, 0.584699e-03, 17.0856, 84.566, 12.4695e-03, -0.0643059, 0.168662, 1.87998e-03, 0.514452]
@@ -120,15 +120,15 @@ digiTPC = TPCDigiAlg("TPCDigi")
 digiTPC.TPCCollection = "TPCCollection"
 digiTPC.TPCLowPtCollection = "TPCLowPtCollection"
 digiTPC.TPCTrackerHitsCol = gashitname
+#default value, modify them according to future Garfield simulation results
+#digiTPC.PixelClustering = True
+#digiTPC.PointResolutionRPhi = 0.144
+#digiTPC.DiffusionCoeffRPhi = 0.0323
+#digiTPC.PointResolutionZ = 0.4
+#digiTPC.DiffusionCoeffZ = 0.23
+#digiTPC.N_eff = 30
 #digiTPC.OutputLevel = DEBUG
 
-from Configurables import TPCPixelClusteringDigiAlg
-digiTPC2 = TPCPixelClusteringDigiAlg("TPCDigi2")
-digiTPC2.TPCCollection = "TPCCollection"
-digiTPC2.TPCLowPtCollection = "TPCLowPtCollection"
-digiTPC2.TPCTrackerHitsCol = gashitname
-#digiTPC2.OutputLevel = DEBUG
-
 # tracking
 from Configurables import KalTestTool
 # Close multiple scattering and smooth, used by clupatra
@@ -162,8 +162,10 @@ tracking.NDivisionsInTheta = 10
 tracking.NDivisionsInPhi = 60
 tracking.NDivisionsInPhiFTD = 16
 tracking.MinDistCutAttach = 50
+# for p=1GeV, theta=10degree, Chi2FitCut = 1500, HelixMaxChi2 = 1000000, Chi2WZ = 0.02
 tracking.Chi2FitCut = 200
 tracking.MaxChi2PerHit = 200
+tracking.HelixMaxChi2  = 50000
 tracking.Chi2WZTriplet = 0.1
 tracking.Chi2WZQuartet = 0.1
 tracking.Chi2WZSeptet  = 0.1
@@ -217,6 +219,7 @@ full.FTDHitToTrackDistance = 5.
 full.SITHitToTrackDistance = 3.
 full.SETHitToTrackDistance = 5.
 full.MinChi2ProbForSiliconTracks = 0
+full.MaxChi2PerHit = 500
 #full.OutputLevel = DEBUG
 
 from Configurables import TPCDndxAlg
@@ -248,7 +251,7 @@ out.outputCommands = ["keep *"]
 # ApplicationMgr
 from Configurables import ApplicationMgr
 mgr = ApplicationMgr(
-    TopAlg = [podioinput, digiVXD, digiSIT, digiSET, digiFTD, digiTPC2, tracking, forward, subset, clupatra, full, tpr, tpc_dndx, tmt, out],
+    TopAlg = [podioinput, digiVXD, digiSIT, digiSET, digiFTD, digiTPC, tracking, forward, subset, clupatra, full, tpr, tpc_dndx, tmt, out],
     EvtSel = 'NONE',
     EvtMax = 5,
     ExtSvc = [rndmengine, rndmgensvc, dsvc, evtseeder, geosvc, gearsvc, tracksystemsvc, pidsvc],
diff --git a/Detector/DetCRD/src/Calorimeter/CRDEcal_Endcap_Short_v01.cpp b/Detector/DetCRD/src/Calorimeter/CRDEcal_Endcap_Short_v01.cpp
new file mode 100644
index 00000000..ca728e5f
--- /dev/null
+++ b/Detector/DetCRD/src/Calorimeter/CRDEcal_Endcap_Short_v01.cpp
@@ -0,0 +1,520 @@
+//================================================================================
+// Description — Short-Crystal End-Cap ECAL
+//--------------------------------------------------------------------------------
+// Author: Ji-Yuan CHEN (SJTU; jy_chen@sjtu.edu.cn)
+//--------------------------------------------------------------------------------
+//
+// End-cap ECAL with short-bar crystals, 1×1×1 cm³ each.
+// Each block is of size 35×35×30 cm³.  Some blocks on the edges are truncated.
+// For placing more crystals, the gaps have been 'absorbed' in the blocks, and the actual size of each crystal is slightly smaller than (but still very close to) 1×1×1 cm³.
+//
+// The inner radius, number of modules in x or y direction, end-cap thickness, etc. are directly read from XML file.
+// Dead material: ESR (wrapper), SiPM, PCB, Cu (cooling material).
+//
+// Structure in a layer: ESR → crystal → ESR → SiPM → PCB → Cu.
+//
+// Default layout: 12 blocks in x and y directions; for filling up spaces, add some smaller blocks.  For a schematic diagram, see Page 1 of  <https://indico.ihep.ac.cn/event/22010/contributions/153187/attachments/77666/96430/2024_0324_Calorimeter_Endcaps.pdf>  (the structure on the left; the numbers and detailed block structures have been modified).
+//================================================================================
+
+#include "DD4hep/DetFactoryHelper.h"
+#include "DD4hep/DD4hepUnits.h"
+#include "DD4hep/Shapes.h"
+#include "DD4hep/DetType.h"
+#include "XML/Layering.h"
+#include "XML/Utilities.h"
+#include "DDRec/DetectorData.h"
+#include "DDSegmentation/Segmentation.h"
+#include <sstream>
+
+using std::cout;
+using std::endl;
+using std::string;
+using std::to_string;
+
+#define MYDEBUG(x) cout << __FILE__ << ":" << __LINE__ << ": " << x << endl;
+#define MYDEBUGVAL(x) cout << __FILE__ << ":" << __LINE__ << ": " << #x << ": " << x << endl;
+
+using dd4hep::Ref_t;
+using dd4hep::Detector;
+using dd4hep::SensitiveDetector;
+using dd4hep::pi;
+using dd4hep::degree;
+using dd4hep::DetElement;
+using dd4hep::Volume;
+using dd4hep::Tube;
+using dd4hep::Box;
+using dd4hep::SubtractionSolid;
+using dd4hep::UnionSolid;
+using dd4hep::Material;
+using dd4hep::PlacedVolume;
+using dd4hep::Position;
+using dd4hep::Transform3D;
+using dd4hep::RotationZ;
+using dd4hep::_toString;
+using dd4hep::RotationY;
+
+static Ref_t create_detector(Detector& theDetector, xml_h e, SensitiveDetector sens)
+{
+    xml_det_t x_det = e;
+
+    const string det_name = x_det.nameStr();
+    const string det_type = x_det.typeStr();
+    const int det_id = x_det.id();
+    MYDEBUGVAL(det_name)
+    MYDEBUGVAL(det_type)
+    MYDEBUGVAL(det_id)
+
+    // To prevent overlapping
+    const double boundary_safety = theDetector.constant<double>("boundary_safety");
+
+    // Global geometry
+    const double r_in = theDetector.constant<double>("ecalendcap_inner_radius");
+    const double r_out = theDetector.constant<double>("ecalendcap_outer_radius");
+    const double block_xy_out = theDetector.constant<double>("ecalendcap_block_xy");
+    const double block_z = theDetector.constant<double>("ecalendcap_thickness");
+    const double pos_z = theDetector.constant<double>("ecalendcap_z");
+    const int Nblock_xy = theDetector.constant<double>("Nblock_xy");    // In a sector
+    const int Nsectors = theDetector.constant<double>("Nsectors");
+    const double gap_narrow = theDetector.constant<double>("gap_narrow");
+    const double gap_wide = theDetector.constant<double>("gap_wide");
+    const double block_xy_in = block_xy_out - gap_narrow;
+    const double angle = 2 * pi / Nsectors;
+
+    // Filling the gaps
+    const double block_rect_short_out = theDetector.constant<double>("ecalendcap_block_fill_rect_short");
+    const double block_sq1_width = theDetector.constant<double>("ecalendcap_block_fill_sq1");
+    const double block_sq2_width = theDetector.constant<double>("ecalendcap_block_fill_sq2");
+
+    const double block_rect_short_in = block_rect_short_out - gap_narrow;
+    const double block_sq1_width_in = block_sq1_width - gap_narrow;
+    const double block_sq2_width_in = block_sq2_width - gap_narrow;
+
+    const int Ncell_rect_short = theDetector.constant<double>("Ncell_rect_short");
+    const int Ncell_sq1_xy = theDetector.constant<double>("Ncell_sq1_xy");
+    const int Ncell_sq2_xy = theDetector.constant<double>("Ncell_sq2_xy");
+
+    // Unit size
+    const int Ncell_xy = theDetector.constant<int>("Ncell_xy");
+    const double crystal_z = theDetector.constant<double>("crystal_z");
+    const double cell_xy = block_xy_in / Ncell_xy;
+
+    const double esr_thickness = theDetector.constant<double>("esr_thickness");    // Wrapper
+    const double sipm_x = theDetector.constant<double>("sipm_x");
+    const double sipm_y = theDetector.constant<double>("sipm_y");
+    const double sipm_z = theDetector.constant<double>("sipm_z");
+    const double pcb_thickness = theDetector.constant<double>("pcb_thickness");
+    const double cu_thickness = theDetector.constant<double>("cu_thickness");    // Cooling material: Cu
+    const double fibre_thickness = theDetector.constant<double>("fibre_thickness");    // Mechanical structure: carbon fibre
+
+    const double crystal_xy = cell_xy - 4 * boundary_safety - 2 * esr_thickness - fibre_thickness;
+    const double cell_z = crystal_z + 4 * boundary_safety + 2 * esr_thickness + fibre_thickness;
+
+    const double layer_thickness = cell_z + sipm_z + pcb_thickness + cu_thickness + 4 * boundary_safety;
+    const int Nlayers = (int) (block_z / layer_thickness);
+
+    MYDEBUGVAL(layer_thickness)
+    MYDEBUGVAL(Nlayers)
+
+    // Materials
+    Material mat_air(theDetector.material("Air"));
+    Material mat_sensitive(theDetector.material( x_det.materialStr() ));
+    Material mat_ESR(theDetector.material("G4_ESR"));
+    Material mat_SiPM(theDetector.material("G4_Si"));
+    Material mat_PCB(theDetector.material("PCB"));
+    Material mat_Cu(theDetector.material("G4_Cu"));
+    Material mat_CF(theDetector.material("CarbonFiber"));
+
+    // Define the detector and mother volumes (world)
+    DetElement ECAL(det_name, det_id);
+    Volume motherVol = theDetector.pickMotherVolume(ECAL);
+
+    // Create two tube-like envelopes to represent the end-cap volumes
+    Tube envelope_tube(0, r_out, 0.5 * block_z + boundary_safety);
+    Box envelope_box(r_in, r_in, block_z);
+    SubtractionSolid envelope_side(envelope_tube, envelope_box, Position(0, 0, 0));
+    UnionSolid envelope(envelope_side, envelope_side, Position(0, 0, 2 * pos_z));
+    Volume envelopeVol(det_name, envelope, mat_air);
+    PlacedVolume envelopePlv = motherVol.placeVolume(envelopeVol, Position(0, 0, -pos_z));
+    envelopePlv.addPhysVolID("system", det_id);
+    envelopeVol.setVisAttributes(theDetector, "SeeThrough");
+    ECAL.setPlacement(envelopePlv);
+    DetElement blockdet(ECAL, "box", det_id);
+
+    // Sector
+    Tube sector_tube(0, r_out, 0.5 * block_z + boundary_safety, 0, 0.5 * pi);
+    SubtractionSolid sector(sector_tube, envelope_box, Position(0, 0, 0));
+    Volume sector_vol("sector_vol", sector, mat_air);
+    sector_vol.setVisAttributes(theDetector, "GreenVis");
+
+    // Main block
+    Box block(0.5 * block_xy_out, 0.5 * block_xy_out, 0.5 * block_z + boundary_safety);
+    Volume block_vol("block_vol", block, mat_air);
+    block_vol.setVisAttributes(theDetector, "GreenVis");
+
+    Box block_cf_in(0.5 * block_xy_in, 0.5 * block_xy_in, 0.5 * block_z);
+    Volume block_cf("block_cf", SubtractionSolid(block, block_cf_in, Position(0, 0, 0)), mat_CF);
+    block_cf.setVisAttributes(theDetector, "GreenVis");
+
+    // Rectangle block for filling the space
+    Box block_rect(0.5 * block_xy_out, 0.5 * block_rect_short_out, 0.5 * block_z + boundary_safety);
+    Volume block_rect_vol("block_rect_vol", block_rect, mat_air);
+    block_rect_vol.setVisAttributes(theDetector, "GreenVis");
+
+    Box block_cf_rect_in(0.5 * block_xy_in, 0.5 * block_rect_short_out - gap_narrow, 0.5 * block_z);
+    Volume block_cf_rect("block_cf_rect", SubtractionSolid(block_rect, block_cf_rect_in, Position(0, 0, 0)), mat_CF);
+    block_cf_rect.setVisAttributes(theDetector, "GreenVis");
+
+    // Square blocks for filling the space
+    // Square 1
+    Box block_sq1(0.5 * block_sq1_width, 0.5 * block_sq1_width, 0.5 * block_z + boundary_safety);
+    Volume block_sq1_vol("block_sq1_vol", block_sq1, mat_air);
+    block_sq1_vol.setVisAttributes(theDetector, "GreenVis");
+
+    Box block_cf_sq1_in(0.5 * block_sq1_width_in, 0.5 * block_sq1_width_in, 0.5 * block_z);
+    Volume block_cf_sq1("block_cf_sq1", SubtractionSolid(block_sq1, block_cf_sq1_in, Position(0, 0, 0)), mat_CF);
+    block_cf_sq1.setVisAttributes(theDetector, "GreenVis");
+
+    // Square 2
+    Box block_sq2(0.5 * block_sq2_width, 0.5 * block_sq2_width, 0.5 * block_z + boundary_safety);
+    Volume block_sq2_vol("block_sq2_vol", block_sq2, mat_air);
+    block_sq2_vol.setVisAttributes(theDetector, "GreenVis");
+
+    Box block_cf_sq2_in(0.5 * block_sq2_width_in, 0.5 * block_sq2_width_in, 0.5 * block_z);
+    Volume block_cf_sq2("block_cf_sq2", SubtractionSolid(block_sq2, block_cf_sq2_in, Position(0, 0, 0)), mat_CF);
+    block_cf_sq2.setVisAttributes(theDetector, "GreenVis");
+
+    // Crystal, SiPM, ESR, and carbon fibre
+    Volume crystal("crystal", Box(0.5 * crystal_xy, 0.5 * crystal_xy, 0.5 * crystal_z), mat_sensitive);
+    crystal.setVisAttributes(theDetector, "SeeThrough");
+    crystal.setSensitiveDetector(sens);
+
+    Volume sipm("SiPM", Box(0.5 * sipm_x, 0.5 * sipm_y, 0.5 * sipm_z), mat_SiPM);
+    sipm.setVisAttributes(theDetector, "SeeThrough");
+
+    Box esr_out(0.5 * crystal_xy + esr_thickness + boundary_safety, 0.5 * crystal_xy + esr_thickness + boundary_safety, 0.5 * crystal_z + esr_thickness + boundary_safety);
+    Box esr_in(0.5 * crystal_xy + boundary_safety, 0.5 * crystal_xy + boundary_safety, 0.5 * crystal_z + boundary_safety);
+    Volume esr("esr", SubtractionSolid(esr_out, esr_in, Position(0, 0, 0)), mat_ESR);
+    esr.setVisAttributes(theDetector, "SeeThrough");
+
+    Box cf_out(0.5 * cell_xy, 0.5 * cell_xy, 0.5 * cell_z);
+    Box cf_in(0.5 * (cell_xy - fibre_thickness), 0.5 * (cell_xy - fibre_thickness), 0.5 * (cell_z - fibre_thickness));
+    Volume cf("cf", SubtractionSolid(cf_out, cf_in, Position(0, 0, 0)), mat_CF);
+    cf.setVisAttributes(theDetector, "SeeThrough");
+
+    // Positions
+    const double crystal_pos_z = -0.5 * layer_thickness + 0.5 * cell_z;
+    const double esr_pos_z = crystal_pos_z;
+    const double cf_pos_z = esr_pos_z;
+    const double sipm_pos_z = cf_pos_z + 0.5 * cell_z + boundary_safety + 0.5 * sipm_z;
+    const double pcb_pos_z = sipm_pos_z + 0.5 * sipm_z + boundary_safety + 0.5 * pcb_thickness;
+    const double cu_pos_z = pcb_pos_z + 0.5 * (pcb_thickness + cu_thickness);
+
+    // Loop for placing the units in a block
+    // Normal block
+    for (int ilayer = 1; ilayer <= Nlayers; ++ilayer)
+    {
+        Volume slice("slice", Box(0.5 * block_xy_in, 0.5 * block_xy_in, 0.5 * layer_thickness), mat_air);
+        slice.setVisAttributes(theDetector, "SeeThrough");
+        string slicename = "Slice_" + to_string(ilayer);
+        DetElement sd(blockdet, slicename, det_id);
+
+        Volume slice_pcb("slice_pcb", Box(0.5 * block_xy_in, 0.5 * block_xy_in, 0.5 * pcb_thickness), mat_PCB);
+        slice_pcb.setVisAttributes(theDetector, "SeeThrough");
+
+        Volume slice_cu("slice_cu", Box(0.5 * block_xy_in, 0.5 * block_xy_in, 0.5 * cu_thickness), mat_Cu);
+        slice_cu.setVisAttributes(theDetector, "SeeThrough");
+
+        PlacedVolume pcb_unit = slice.placeVolume(slice_pcb, Position(0, 0, pcb_pos_z));
+        pcb_unit.addPhysVolID("layer", ilayer);
+
+        PlacedVolume cu_unit = slice.placeVolume(slice_cu, Position(0, 0, cu_pos_z));
+        cu_unit.addPhysVolID("layer", ilayer);
+
+        for (int ix = 1; ix <= Ncell_xy; ++ix)
+            for (int iy = 1; iy <= Ncell_xy; ++iy)
+            {
+                PlacedVolume crystal_unit = slice.placeVolume(crystal,
+                        Position(-0.5 * block_xy_in + ix * cell_xy,
+                                 -0.5 * block_xy_in + iy * cell_xy,
+                                 crystal_pos_z));
+                PlacedVolume esr_unit = slice.placeVolume(esr,
+                        Position(-0.5 * block_xy_in + ix * cell_xy,
+                                 -0.5 * block_xy_in + iy * cell_xy,
+                                 esr_pos_z));
+                PlacedVolume cf_unit = slice.placeVolume(cf,
+                        Position(-0.5 * block_xy_in + ix * cell_xy,
+                                 -0.5 * block_xy_in + iy * cell_xy,
+                                 cf_pos_z));
+                PlacedVolume sipm_unit = slice.placeVolume(sipm,
+                        Position(-0.5 * block_xy_in + ix * cell_xy,
+                                 -0.5 * block_xy_in + iy * cell_xy,
+                                 sipm_pos_z));
+
+                crystal_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+                esr_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+                cf_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+                sipm_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+
+                string crystal_name = "Crystal_" + to_string(ilayer) + "_" + to_string(ix) + "_" + to_string(iy);
+                DetElement unit(sd, crystal_name, det_id);
+                unit.setPlacement(crystal_unit);
+            }
+
+        PlacedVolume plv = block_vol.placeVolume(slice, Position(0, 0, (ilayer - 0.5) * layer_thickness - 0.5 * block_z + boundary_safety));
+        plv.addPhysVolID("layer", ilayer);
+        sd.setPlacement(plv);
+    }
+
+    // Rectangle block
+    for (int ilayer = 1; ilayer <= Nlayers; ++ilayer)
+    {
+        Volume slice("slice", Box(0.5 * block_xy_in, 0.5 * block_rect_short_in, 0.5 * layer_thickness), mat_air);
+        slice.setVisAttributes(theDetector, "SeeThrough");
+        string slicename = "Rect_Slice_" + to_string(ilayer);
+        DetElement sd(blockdet, slicename, det_id);
+
+        Volume slice_pcb("slice_pcb", Box(0.5 * block_xy_in, 0.5 * block_rect_short_in, 0.5 * pcb_thickness), mat_PCB);
+        slice_pcb.setVisAttributes(theDetector, "SeeThrough");
+
+        Volume slice_cu("slice_cu", Box(0.5 * block_xy_in, 0.5 * block_rect_short_in, 0.5 * cu_thickness), mat_Cu);
+        slice_cu.setVisAttributes(theDetector, "SeeThrough");
+
+        PlacedVolume pcb_unit = slice.placeVolume(slice_pcb, Position(0, 0, pcb_pos_z));
+        pcb_unit.addPhysVolID("layer", ilayer);
+
+        PlacedVolume cu_unit = slice.placeVolume(slice_cu, Position(0, 0, cu_pos_z));
+        cu_unit.addPhysVolID("layer", ilayer);
+
+        for (int ix = 1; ix <= Ncell_xy; ++ix)
+            for (int iy = 1; iy <= Ncell_rect_short; ++iy)
+            {
+                PlacedVolume crystal_unit = slice.placeVolume(crystal,
+                        Position(-0.5 * block_xy_in + ix * cell_xy,
+                                 -0.5 * block_rect_short_in + iy * cell_xy,
+                                 crystal_pos_z));
+                PlacedVolume esr_unit = slice.placeVolume(esr,
+                        Position(-0.5 * block_xy_in + ix * cell_xy,
+                                 -0.5 * block_rect_short_in + iy * cell_xy,
+                                 esr_pos_z));
+                PlacedVolume cf_unit = slice.placeVolume(cf,
+                        Position(-0.5 * block_xy_in + ix * cell_xy,
+                                 -0.5 * block_rect_short_in + iy * cell_xy,
+                                 cf_pos_z));
+                PlacedVolume sipm_unit = slice.placeVolume(sipm,
+                        Position(-0.5 * block_xy_in + ix * cell_xy,
+                                 -0.5 * block_rect_short_in + iy * cell_xy,
+                                 sipm_pos_z));
+
+                crystal_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+                esr_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+                cf_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+                sipm_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+
+                string crystal_name = "Crystal_" + to_string(ilayer) + "_" + to_string(ix) + "_" + to_string(iy);
+                DetElement unit(sd, crystal_name, det_id);
+                unit.setPlacement(crystal_unit);
+            }
+
+        PlacedVolume plv = block_rect_vol.placeVolume(slice, Position(0, 0, (ilayer - 0.5) * layer_thickness - 0.5 * block_z + boundary_safety));
+        plv.addPhysVolID("layer", ilayer);
+        sd.setPlacement(plv);
+    }
+
+    // Square block 1
+    for (int ilayer = 1; ilayer <= Nlayers; ++ilayer)
+    {
+        Volume slice("slice", Box(0.5 * block_sq1_width, 0.5 * block_sq1_width, 0.5 * layer_thickness), mat_air);
+        slice.setVisAttributes(theDetector, "SeeThrough");
+        string slicename = "Sq1_Slice_" + to_string(ilayer);
+        DetElement sd(blockdet, slicename, det_id);
+
+        Volume slice_pcb("slice_pcb", Box(0.5 * block_sq1_width, 0.5 * block_sq1_width, 0.5 * pcb_thickness), mat_PCB);
+        slice_pcb.setVisAttributes(theDetector, "SeeThrough");
+
+        Volume slice_cu("slice_cu", Box(0.5 * block_sq1_width, 0.5 * block_sq1_width, 0.5 * cu_thickness), mat_Cu);
+        slice_cu.setVisAttributes(theDetector, "SeeThrough");
+
+        PlacedVolume pcb_unit = slice.placeVolume(slice_pcb, Position(0, 0, pcb_pos_z));
+        pcb_unit.addPhysVolID("layer", ilayer);
+
+        PlacedVolume cu_unit = slice.placeVolume(slice_cu, Position(0, 0, cu_pos_z));
+        cu_unit.addPhysVolID("layer", ilayer);
+
+        for (int ix = 1; ix <= Ncell_sq1_xy; ++ix)
+            for (int iy = 1; iy <= Ncell_sq1_xy; ++iy)
+            {
+                PlacedVolume crystal_unit = slice.placeVolume(crystal,
+                        Position(-0.5 * block_sq1_width + ix * cell_xy,
+                                 -0.5 * block_sq1_width + iy * cell_xy,
+                                 crystal_pos_z));
+                PlacedVolume esr_unit = slice.placeVolume(esr,
+                        Position(-0.5 * block_sq1_width + ix * cell_xy,
+                                 -0.5 * block_sq1_width + iy * cell_xy,
+                                 esr_pos_z));
+                PlacedVolume cf_unit = slice.placeVolume(cf,
+                        Position(-0.5 * block_sq1_width + ix * cell_xy,
+                                 -0.5 * block_sq1_width + iy * cell_xy,
+                                 cf_pos_z));
+                PlacedVolume sipm_unit = slice.placeVolume(sipm,
+                        Position(-0.5 * block_sq1_width + ix * cell_xy,
+                                 -0.5 * block_sq1_width + iy * cell_xy,
+                                 sipm_pos_z));
+
+                crystal_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+                esr_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+                cf_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+                sipm_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+
+                string crystal_name = "Crystal_" + to_string(ilayer) + "_" + to_string(ix) + "_" + to_string(iy);
+                DetElement unit(sd, crystal_name, det_id);
+                unit.setPlacement(crystal_unit);
+            }
+
+        PlacedVolume plv = block_sq1_vol.placeVolume(slice, Position(0, 0, (ilayer - 0.5) * layer_thickness - 0.5 * block_z + boundary_safety));
+        plv.addPhysVolID("layer", ilayer);
+        sd.setPlacement(plv);
+    }
+
+    // Square block 2
+    for (int ilayer = 1; ilayer <= Nlayers; ++ilayer)
+    {
+        Volume slice("slice", Box(0.5 * block_sq2_width, 0.5 * block_sq2_width, 0.5 * layer_thickness), mat_air);
+        slice.setVisAttributes(theDetector, "SeeThrough");
+        string slicename = "Sq2_Slice_" + to_string(ilayer);
+        DetElement sd(blockdet, slicename, det_id);
+
+        Volume slice_pcb("slice_pcb", Box(0.5 * block_sq2_width, 0.5 * block_sq2_width, 0.5 * pcb_thickness), mat_PCB);
+        slice_pcb.setVisAttributes(theDetector, "SeeThrough");
+
+        Volume slice_cu("slice_cu", Box(0.5 * block_sq2_width, 0.5 * block_sq2_width, 0.5 * cu_thickness), mat_Cu);
+        slice_cu.setVisAttributes(theDetector, "SeeThrough");
+
+        PlacedVolume pcb_unit = slice.placeVolume(slice_pcb, Position(0, 0, pcb_pos_z));
+        pcb_unit.addPhysVolID("layer", ilayer);
+
+        PlacedVolume cu_unit = slice.placeVolume(slice_cu, Position(0, 0, cu_pos_z));
+        cu_unit.addPhysVolID("layer", ilayer);
+
+        for (int ix = 1; ix <= Ncell_sq2_xy; ++ix)
+            for (int iy = 1; iy <= Ncell_sq2_xy; ++iy)
+            {
+                PlacedVolume crystal_unit = slice.placeVolume(crystal,
+                        Position(-0.5 * block_sq2_width + ix * cell_xy,
+                                 -0.5 * block_sq2_width + iy * cell_xy,
+                                 crystal_pos_z));
+                PlacedVolume esr_unit = slice.placeVolume(esr,
+                        Position(-0.5 * block_sq2_width + ix * cell_xy,
+                                 -0.5 * block_sq2_width + iy * cell_xy,
+                                 esr_pos_z));
+                PlacedVolume cf_unit = slice.placeVolume(cf,
+                        Position(-0.5 * block_sq2_width + ix * cell_xy,
+                                 -0.5 * block_sq2_width + iy * cell_xy,
+                                 cf_pos_z));
+                PlacedVolume sipm_unit = slice.placeVolume(sipm,
+                        Position(-0.5 * block_sq2_width + ix * cell_xy,
+                                 -0.5 * block_sq2_width + iy * cell_xy,
+                                 sipm_pos_z));
+
+                crystal_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+                esr_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+                cf_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+                sipm_unit.addPhysVolID("layer", ilayer).addPhysVolID("x", ix).addPhysVolID("y", iy);
+
+                string crystal_name = "Crystal_" + to_string(ilayer) + "_" + to_string(ix) + "_" + to_string(iy);
+                DetElement unit(sd, crystal_name, det_id);
+                unit.setPlacement(crystal_unit);
+            }
+
+        PlacedVolume plv = block_sq2_vol.placeVolume(slice, Position(0, 0, (ilayer - 0.5) * layer_thickness - 0.5 * block_z + boundary_safety));
+        plv.addPhysVolID("layer", ilayer);
+        sd.setPlacement(plv);
+    }
+
+    int istave = 0;
+
+    // Loop for placing the blocks in a module
+    // This session is not flexible enough...
+    for (int ix = 1; ix <= Nblock_xy; ++ix)
+        for (int iy = 1; iy <= Nblock_xy; ++iy)
+        {
+            const double distance = sqrt(pow((ix - 0.5) * block_xy_out, 2) + pow((iy - 0.5) * block_xy_out, 2));
+            if (distance < r_in || distance > r_out)
+                continue;
+
+            ++istave;
+
+            PlacedVolume plv_block_cf;
+            PlacedVolume plv_block;
+
+            if (ix == 2 && iy == Nblock_xy)
+            {
+                plv_block_cf = block_rect_vol.placeVolume(block_cf_rect, Position(0, 0, 0));
+                plv_block = sector_vol.placeVolume(block_rect_vol,
+                        Position(0.5 * gap_wide - gap_narrow + (ix - 0.5) * block_xy_out,
+                                 0.5 * gap_wide - gap_narrow + (iy - 1) * block_xy_out + 0.5 * block_rect_short_out,
+                                 0));
+            }
+            else if ((ix == 3 && iy == Nblock_xy) || (ix == Nblock_xy && iy == 3))
+            {
+                plv_block_cf = block_sq1_vol.placeVolume(block_cf_sq1, Position(0, 0, 0));
+                plv_block = sector_vol.placeVolume(block_sq1_vol,
+                        Position(0.5 * gap_wide - gap_narrow + (ix - 1) * block_xy_out + 0.5 * block_sq1_width,
+                                 0.5 * gap_wide - gap_narrow + (iy - 1) * block_xy_out + 0.5 * block_sq1_width,
+                                 0));
+            }
+            else if ((ix == 4 && iy == 5) || (ix == 5 && iy == 4))
+            {
+                plv_block_cf = block_sq2_vol.placeVolume(block_cf_sq2, Position(0, 0, 0));
+                plv_block = sector_vol.placeVolume(block_sq2_vol,
+                        Position(0.5 * gap_wide - gap_narrow + (ix - 1) * block_xy_out + 0.5 * block_sq2_width,
+                                 0.5 * gap_wide - gap_narrow + (iy - 1) * block_xy_out + 0.5 * block_sq2_width,
+                                 0));
+            }
+            else if (ix == Nblock_xy && iy == 2)
+            {
+                plv_block_cf = block_rect_vol.placeVolume(block_cf_rect, Position(0, 0, 0));
+                Transform3D transform(RotationZ(0.5 * pi),
+                        Position(0.5 * gap_wide - gap_narrow + (ix - 1) * block_xy_out + 0.5 * block_rect_short_out,
+                                 0.5 * gap_wide - gap_narrow + (iy - 0.5) * block_xy_out,
+                                 0));
+                plv_block = sector_vol.placeVolume(block_rect_vol, transform);
+            }
+            else
+            {
+                plv_block_cf = block_vol.placeVolume(block_cf, Position(0, 0, 0));
+                plv_block = sector_vol.placeVolume(block_vol,
+                        Position(0.5 * gap_wide - gap_narrow + (ix - 0.5) * block_xy_out,
+                                 0.5 * gap_wide - gap_narrow + (iy - 0.5) * block_xy_out,
+                                 0));
+            }
+            plv_block_cf.addPhysVolID("stave", istave);
+            plv_block.addPhysVolID("stave", istave);
+            DetElement sd(blockdet, _toString(istave, "block_%3d"), det_id);
+            sd.setPlacement(plv_block);
+        }
+
+    MYDEBUGVAL(istave)
+
+    // Loop for placing the modules
+    for (int i = 0; i < Nsectors; ++i)
+    {
+        const double s_rot = i * angle;
+        Transform3D transform_neg(RotationZ(s_rot) * RotationY(pi), Position(0, 0, 0));
+        Transform3D transform_pos(RotationZ(s_rot + angle), Position(0, 0, 2 * pos_z));
+        PlacedVolume plv_neg = envelopeVol.placeVolume(sector_vol, transform_neg);
+        PlacedVolume plv_pos = envelopeVol.placeVolume(sector_vol, transform_pos);
+        plv_neg.addPhysVolID("module", i);
+        plv_pos.addPhysVolID("module", i + Nsectors);
+        DetElement sd_neg(ECAL, _toString(i, "sector%3d"), det_id);
+        DetElement sd_pos(ECAL, _toString(i + Nsectors, "sector%3d"), det_id);
+        sd_neg.setPlacement(plv_neg);
+        sd_pos.setPlacement(plv_pos);
+    }
+
+    sens.setType("calorimeter");
+
+    MYDEBUG("create_detector FINISHED.")
+    return ECAL;
+}
+
+DECLARE_DETELEMENT(CRDEcalEndcap_Short_v01, create_detector)
diff --git a/Detector/DetCRD/src/Calorimeter/CRDEcal_Short_v02.cpp b/Detector/DetCRD/src/Calorimeter/CRDEcal_Short_v02.cpp
new file mode 100644
index 00000000..dd3ac9a5
--- /dev/null
+++ b/Detector/DetCRD/src/Calorimeter/CRDEcal_Short_v02.cpp
@@ -0,0 +1,374 @@
+//================================================================================
+// Description — 32-Sided Polygon Barrel ECAL
+//--------------------------------------------------------------------------------
+// Author: Ji-Yuan CHEN (SJTU; jy_chen@sjtu.edu.cn)
+//--------------------------------------------------------------------------------
+//
+// ECAL with short crystals.  Not crossed-bar structure.
+// 32 modules cover a phi range of 2pi.
+//
+// ‘Positive’ and ‘negative’ trapezia:
+//       ______________             __________
+//       \            /            /          \        ↑ R (pointing outwards)
+//        \__________/            /____________\       │
+//          Positive                 Negative        z ⊗ ——→ φ
+// (Inner base < outer base)     (Inner > outer)
+//
+// The inner radius, module thickness, barrel length, etc. are directly read from XML file.
+// Dead material: ESR (wrapper), carbon fibre (mechanical structure), SiPM, PCB, Cu (cooling material).
+//
+// Layout: 32 modules in phi direction
+//         → 15 blocks in z direction;
+//           → 24 (?) layers in R direction.
+// For a schematic diagram, see  <https://indico.ihep.ac.cn/event/22331/contributions/155518/attachments/77650/96399/Polygon32-CrossSection.pdf>  (the numbers have been modified).
+//================================================================================
+
+#include "DD4hep/DetFactoryHelper.h"
+#include "XML/Layering.h"
+#include "XML/Utilities.h"
+#include "DDRec/DetectorData.h"
+#include "DDSegmentation/Segmentation.h"
+
+using std::cout;
+using std::endl;
+using std::string;
+using std::to_string;
+
+#define MYDEBUG(x) cout << __FILE__ << ":" << __LINE__ << ": " << x << endl;
+#define MYDEBUGVAL(x) cout << __FILE__ << ":" << __LINE__ << ": " << #x << ": " << x << endl;
+
+using dd4hep::Ref_t;
+using dd4hep::Detector;
+using dd4hep::SensitiveDetector;
+using dd4hep::pi;
+using dd4hep::degree;
+using dd4hep::DetElement;
+using dd4hep::Volume;
+using dd4hep::PolyhedraRegular;
+using dd4hep::Material;
+using dd4hep::PlacedVolume;
+using dd4hep::Position;
+using dd4hep::Trapezoid;
+using dd4hep::Box;
+using dd4hep::SubtractionSolid;
+using dd4hep::Transform3D;
+using dd4hep::RotationZ;
+using dd4hep::RotationX;
+using dd4hep::_toString;
+
+static Ref_t create_detector(Detector& theDetector, xml_h e, SensitiveDetector sens)
+{
+    xml_det_t x_det = e;
+
+    string det_name = x_det.nameStr();
+    string det_type = x_det.typeStr();
+    const int detid = x_det.id();
+    MYDEBUGVAL(det_name)
+    MYDEBUGVAL(det_type)
+    MYDEBUGVAL(detid)
+
+    // To prevent overlapping
+    const double boundary_safety = theDetector.constant<double>("boundary_safety");
+
+    // Global geometry
+    const double r_in = theDetector.constant<double>("ecalbarrel_inner_radius");
+    const double r_out_max = theDetector.constant<double>("ecalbarrel_outer_radius");
+    const double h0 = theDetector.constant<double>("ecalbarrel_thickness");
+    const double Z0 = theDetector.constant<double>("ecalbarrel_zlength");
+    const int Nmodule = theDetector.constant<int>("Nmodule");    // 32 modules
+    const int Nblock_z = theDetector.constant<int>("Nblock_z");    // Block number in z direction
+    const double rotation_angle = theDetector.constant<double>("module_rotation");    // Angle between the leg and the radius through its mid-point.
+    const double angle = 2 * pi / Nmodule;
+
+    // Unit size
+    const double crystal_r = theDetector.constant<double>("crystal_r");
+    const double crystal_phi = theDetector.constant<double>("crystal_phi");
+    const double crystal_z = theDetector.constant<double>("crystal_z");
+    const double block_z = Z0 / Nblock_z;    // Length of a block in z direction
+
+    MYDEBUGVAL(block_z)
+
+    const double esr_thickness = theDetector.constant<double>("esr_thickness");    // Wrapper
+    const double sipm_r = theDetector.constant<double>("sipm_r");
+    const double sipm_phi = theDetector.constant<double>("sipm_phi");
+    const double sipm_z = theDetector.constant<double>("sipm_z");
+    const double pcb_thickness = theDetector.constant<double>("pcb_thickness");
+    const double cu_thickness = theDetector.constant<double>("cu_thickness");    // Cooling material: Cu
+    const double fibre_thickness = theDetector.constant<double>("fibre_thickness");    // Mechanical structure: carbon fibre
+    const double collection_width = theDetector.constant<double>("collection_width");
+    const double collection_thickness = theDetector.constant<double>("collection_thickness");
+
+    const double cell_r = crystal_r + 4 * boundary_safety + 2 * esr_thickness + fibre_thickness;
+    const double cell_phi = crystal_phi + 4 * boundary_safety + 2 * esr_thickness + fibre_thickness;
+    const double cell_z = crystal_z + 4 * boundary_safety + 2 * esr_thickness + fibre_thickness;
+
+    const double layer_thickness = cell_r + sipm_r + pcb_thickness + cu_thickness + 4 * boundary_safety;
+    MYDEBUGVAL(layer_thickness)
+
+    // Adjustments for the positive trapezia
+    const double height_leg_angle_pos = rotation_angle + 0.5 * angle;    // Angle between <height> and <leg> of the positive trapezia
+    const double height_leg_deviation_pos = 0.5 * h0 * tan(height_leg_angle_pos);    // Deviation because of the angle between <height> and <leg> (about the mid-point of the leg)
+    const double height_rad_deviation = 0.5 * h0 * tan(0.5 * angle);    // Deviation because of the angle between <height> and <radius through the mid-point of the leg> (about the mid-point of the leg)
+    const double leg_rad_deviation_pos = height_leg_deviation_pos - height_rad_deviation;    // Deviation because of the angle between <leg> and <radius through the mid-point of the leg> (about the mid-point of the leg). Or equivalently, deviation of the inner base due to rotation.
+
+    // Adjustments for the negative trapezia
+    const double height_leg_angle_neg = rotation_angle - 0.5 * angle;    // Angle between <height> and <leg> of the negative trapezia
+    const double height_leg_deviation_neg = 0.5 * h0 * tan(height_leg_angle_neg);    // Deviation because of the angle between <height> and <leg> (about the mid-point of the leg)
+    const double leg_rad_deviation_neg = height_leg_deviation_neg + height_rad_deviation;    // Deviation because of the angle between <leg> and <radius through the mid-point of the leg> (about the mid-point of the leg)
+
+    const double dim_in_pos = r_in * tan(0.5 * angle) - leg_rad_deviation_pos;
+    const double dim_out_pos = dim_in_pos + h0 * tan(height_leg_angle_pos);
+    const double dim_in_neg = r_in * tan(0.5 * angle) + leg_rad_deviation_neg;
+    const double dim_out_neg = dim_in_neg - h0 * tan(height_leg_angle_neg);
+    const double dim_y = 0.5 * Z0;
+    const double dim_z = 0.5 * h0;
+    const double r0 = r_in + 0.5 * h0;    // Rotation radius
+    const double r_out = sqrt(pow(r_in + h0, 2) + pow(dim_out_pos, 2));
+
+    if (r_out > r_out_max)
+        throw "Outer radius of barrel ECAL exceeds assigned maximum value!";
+
+    MYDEBUGVAL(dim_in_pos)
+    MYDEBUGVAL(dim_out_pos)
+    MYDEBUGVAL(dim_in_neg)
+    MYDEBUGVAL(dim_out_neg)
+
+    const int Nlayers = ((int) (h0 / layer_thickness) * layer_thickness + collection_thickness <= h0) ? (int) (h0 / layer_thickness) : (int) (h0 / layer_thickness) - 1;
+    const int Ncell_z = (int) (block_z / cell_z);    // Crystal number along z direction in each block
+    int Ncell_phi_pos;    // Crystal number along phi direction in each positive trapezium block
+    int Ncell_phi_neg;    // Crystal number along phi direction in each negative trapezium block
+
+    MYDEBUGVAL(Nlayers)
+    MYDEBUGVAL(Ncell_z)
+
+    // Materials
+    Material mat_air(theDetector.material("Air"));
+    Material mat_sensitive(theDetector.material( x_det.materialStr() ));
+    Material mat_ESR(theDetector.material("G4_ESR"));
+    Material mat_SiPM(theDetector.material("G4_Si"));
+    Material mat_PCB(theDetector.material("PCB"));
+    Material mat_Cu(theDetector.material("G4_Cu"));
+    Material mat_CF(theDetector.material("CarbonFiber"));
+
+    // Define the detector and mother volumes (world)
+    DetElement ECAL(det_name, detid);
+    Volume motherVol = theDetector.pickMotherVolume(ECAL);
+
+    // Create a tube-like envelope to represent the whole detector volume
+    PolyhedraRegular envelope(Nmodule, 0.5 * angle, r_in, r_out, Z0);
+    Volume envelopeVol(det_name, envelope, mat_air);
+    PlacedVolume envelopePlv = motherVol.placeVolume(envelopeVol, Position(0, 0, 0));
+    envelopePlv.addPhysVolID("system", detid);
+    envelopeVol.setVisAttributes(theDetector, "SeeThrough");
+    ECAL.setPlacement(envelopePlv);
+    DetElement blockdet(ECAL, "module", detid);
+
+    // Positive trapezium module
+    Trapezoid module_pos(dim_in_pos, dim_out_pos, dim_y, dim_y, dim_z);
+    Volume module_pos_vol("module_pos_vol", module_pos, mat_air);
+    module_pos_vol.setVisAttributes(theDetector, "CyanVis");
+
+    // Negative trapezium module
+    Trapezoid module_neg(dim_in_neg, dim_out_neg, dim_y, dim_y, dim_z);
+    Volume module_neg_vol("module_neg_vol", module_neg, mat_air);
+    module_neg_vol.setVisAttributes(theDetector, "CyanVis");
+
+    // Positive block
+    Trapezoid block_pos(dim_in_pos, dim_out_pos, 0.5 * block_z, 0.5 * block_z, dim_z);
+    Volume block_pos_vol("block_pos_vol", block_pos, mat_CF);
+    block_pos_vol.setVisAttributes(theDetector, "CyanVis");
+
+    // Negative block
+    Trapezoid block_neg(dim_in_neg, dim_out_neg, 0.5 * block_z, 0.5 * block_z, dim_z);
+    Volume block_neg_vol("block_neg_vol", block_neg, mat_CF);
+    block_neg_vol.setVisAttributes(theDetector, "CyanVis");
+
+    // Crystal, ESR, SiPM, carbon fibre and collection board
+    Volume crystal("crystal", Box(0.5 * crystal_phi, 0.5 * crystal_z, 0.5 * crystal_r), mat_sensitive);    // Order: phi → z → R.
+    crystal.setVisAttributes(theDetector, "SeeThrough");
+    crystal.setSensitiveDetector(sens);
+
+    Box esr_out(0.5 * crystal_phi + esr_thickness + boundary_safety, 0.5 * crystal_z + esr_thickness + boundary_safety, 0.5 * crystal_r + esr_thickness + boundary_safety);
+    Box esr_in(0.5 * crystal_phi + boundary_safety, 0.5 * crystal_z + boundary_safety, 0.5 * crystal_r + boundary_safety);
+    Volume esr("esr", SubtractionSolid(esr_out, esr_in, Position(0, 0, 0)), mat_ESR);
+    esr.setVisAttributes(theDetector, "SeeThrough");
+
+    Volume SiPM("SiPM", Box(0.5 * sipm_phi, 0.5 * sipm_z, 0.5 * sipm_r), mat_SiPM);
+    SiPM.setVisAttributes(theDetector, "SeeThrough");
+
+    Box cf_out(0.5 * cell_phi, 0.5 * cell_z, 0.5 * cell_r);
+    Box cf_in(0.5 * (cell_phi - fibre_thickness), 0.5 * (cell_z - fibre_thickness), 0.5 * (cell_r - fibre_thickness));
+    Volume cf("cf", SubtractionSolid(cf_out, cf_in, Position(0, 0, 0)), mat_CF);
+    cf.setVisAttributes(theDetector, "SeeThrough");
+
+    Volume collection("collection", Box(0.5 * collection_width, 0.5 * collection_thickness, 0.5 * collection_width), mat_PCB);
+    collection.setVisAttributes(theDetector, "SeeThrough");
+
+    // Positions
+    const double crystal_pos_r = -0.5 * layer_thickness + 0.5 * cell_r;
+    const double esr_pos_r = crystal_pos_r;
+    const double cf_pos_r = esr_pos_r;
+    const double sipm_pos_r = cf_pos_r + 0.5 * cell_r + boundary_safety + 0.5 * sipm_r;
+    const double pcb_pos_r = sipm_pos_r + 0.5 * sipm_r + boundary_safety + 0.5 * pcb_thickness;
+    const double cu_pos_r = pcb_pos_r + 0.5 * (pcb_thickness + cu_thickness);
+
+    // Loop for placing the crystals in one positive trapezium block
+    for (int ilayer = 1; ilayer <= Nlayers; ++ilayer)
+    {
+        const double layer_length_pos = dim_in_pos + (ilayer - 1) * layer_thickness * tan(height_leg_angle_pos);
+        Ncell_phi_pos = (int) (2 * layer_length_pos / cell_phi);
+        const double layer_phi_pos = Ncell_phi_pos * cell_phi;
+
+        Volume slice_pos("slice_pos", Box(0.5 * layer_phi_pos, 0.5 * block_z, 0.5 * layer_thickness), mat_air);
+        slice_pos.setVisAttributes(theDetector, "SeeThrough");
+        string slicename_pos = "Slice_pos_" + to_string(ilayer);
+        DetElement sd_pos(blockdet, slicename_pos, detid);
+
+        Volume slice_pcb_pos("slice_pcb_pos", Box(0.5 * layer_phi_pos, 0.5 * block_z, 0.5 * pcb_thickness), mat_PCB);
+        slice_pcb_pos.setVisAttributes(theDetector, "SeeThrough");
+
+        Volume slice_cu_pos("slice_cu_pos", Box(0.5 * layer_phi_pos, 0.5 * block_z, 0.5 * cu_thickness), mat_Cu);
+        slice_cu_pos.setVisAttributes(theDetector, "SeeThrough");
+
+        PlacedVolume pcb_unit_pos = slice_pos.placeVolume(slice_pcb_pos, Position(0, 0, pcb_pos_r));
+        pcb_unit_pos.addPhysVolID("layer", ilayer);
+
+        PlacedVolume cu_unit = slice_pos.placeVolume(slice_cu_pos, Position(0, 0, cu_pos_r));
+        cu_unit.addPhysVolID("layer", ilayer);
+
+        for (int iz = 1; iz <= Ncell_z; ++iz)
+            for (int iphi = 1; iphi <= Ncell_phi_pos; ++iphi)
+            {
+                PlacedVolume crystal_unit_pos = slice_pos.placeVolume(crystal,
+                        Position((-0.5 * (Ncell_phi_pos + 1) + iphi) * cell_phi,
+                                 (-0.5 * (Ncell_z + 1) + iz) * cell_z,
+                                 crystal_pos_r));
+                PlacedVolume esr_unit_pos = slice_pos.placeVolume(esr,
+                        Position((-0.5 * (Ncell_phi_pos + 1) + iphi) * cell_phi,
+                                 (-0.5 * (Ncell_z + 1) + iz) * cell_z,
+                                 esr_pos_r));
+                PlacedVolume cf_unit_pos = slice_pos.placeVolume(cf,
+                        Position((-0.5 * (Ncell_phi_pos + 1) + iphi) * cell_phi,
+                                 (-0.5 * (Ncell_z + 1) + iz) * cell_z,
+                                 cf_pos_r));
+                PlacedVolume sipm_unit_pos = slice_pos.placeVolume(SiPM,
+                        Position((-0.5 * (Ncell_phi_pos + 1) + iphi) * cell_phi,
+                                 (-0.5 * (Ncell_z + 1) + iz) * cell_z,
+                                 sipm_pos_r));
+
+                crystal_unit_pos.addPhysVolID("layer", ilayer).addPhysVolID("phi", iphi).addPhysVolID("z", iz);
+                esr_unit_pos.addPhysVolID("layer", ilayer).addPhysVolID("phi", iphi).addPhysVolID("z", iz);
+                cf_unit_pos.addPhysVolID("layer", ilayer).addPhysVolID("phi", iphi).addPhysVolID("z", iz);
+                sipm_unit_pos.addPhysVolID("layer", ilayer).addPhysVolID("phi", iphi).addPhysVolID("z", iz);
+
+                string crystal_name_pos = "Crystal_pos_" + to_string(ilayer) + "_" + to_string(iphi) + "_" + to_string(iz);
+                DetElement unit_pos(sd_pos, crystal_name_pos, detid);
+                unit_pos.setPlacement(crystal_unit_pos);
+            }
+
+        PlacedVolume plv = block_pos_vol.placeVolume(slice_pos, Position(0, 0, (ilayer - 0.5) * layer_thickness - dim_z));
+        plv.addPhysVolID("layer", ilayer);
+        sd_pos.setPlacement(plv);
+    }
+
+    // Loop for placing the crystals in one negative trapezium block
+    for (int ilayer = 1; ilayer <= Nlayers; ++ilayer)
+    {
+        const double layer_length_neg = dim_in_neg - ilayer * layer_thickness * tan(height_leg_angle_neg);
+        Ncell_phi_neg = (int) (2 * layer_length_neg / cell_phi);
+        const double layer_phi_neg = Ncell_phi_neg * cell_phi;
+
+        Volume slice_neg("slice_neg", Box(0.5 * layer_phi_neg, 0.5 * block_z, 0.5 * layer_thickness), mat_air);
+        slice_neg.setVisAttributes(theDetector, "SeeThrough");
+        string slicename_neg = "Slice_neg_" + to_string(ilayer);
+        DetElement sd_neg(blockdet, slicename_neg, detid);
+
+        Volume slice_pcb_neg("slice_pcb_neg", Box(0.5 * layer_phi_neg, 0.5 * block_z, 0.5 * pcb_thickness), mat_PCB);
+        slice_pcb_neg.setVisAttributes(theDetector, "SeeThrough");
+
+        Volume slice_cu_neg("slice_cu_neg", Box(0.5 * layer_phi_neg, 0.5 * block_z, 0.5 * cu_thickness), mat_Cu);
+        slice_cu_neg.setVisAttributes(theDetector, "SeeThrough");
+
+        PlacedVolume pcb_unit_neg = slice_neg.placeVolume(slice_pcb_neg, Position(0, 0, pcb_pos_r));
+        pcb_unit_neg.addPhysVolID("layer", ilayer);
+
+        PlacedVolume cu_unit = slice_neg.placeVolume(slice_cu_neg, Position(0, 0, cu_pos_r));
+        cu_unit.addPhysVolID("layer", ilayer);
+
+        for (int iz = 1; iz <= Ncell_z; ++iz)
+            for (int iphi = 1; iphi <= Ncell_phi_neg; ++iphi)
+            {
+                PlacedVolume crystal_unit_neg = slice_neg.placeVolume(crystal,
+                        Position((-0.5 * (Ncell_phi_neg + 1) + iphi) * cell_phi,
+                                 (-0.5 * (Ncell_z + 1) + iz) * cell_z,
+                                 crystal_pos_r));
+                PlacedVolume esr_unit_neg = slice_neg.placeVolume(esr,
+                        Position((-0.5 * (Ncell_phi_neg + 1) + iphi) * cell_phi,
+                                 (-0.5 * (Ncell_z + 1) + iz) * cell_z,
+                                 esr_pos_r));
+                PlacedVolume cf_unit_neg = slice_neg.placeVolume(cf,
+                        Position((-0.5 * (Ncell_phi_neg + 1) + iphi) * cell_phi,
+                                 (-0.5 * (Ncell_z + 1) + iz) * cell_z,
+                                 cf_pos_r));
+                PlacedVolume sipm_unit_neg = slice_neg.placeVolume(SiPM,
+                        Position((-0.5 * (Ncell_phi_neg + 1) + iphi) * cell_phi,
+                                 (-0.5 * (Ncell_z + 1) + iz) * cell_z,
+                                 sipm_pos_r));
+
+                crystal_unit_neg.addPhysVolID("layer", ilayer).addPhysVolID("phi", iphi).addPhysVolID("z", iz);
+                esr_unit_neg.addPhysVolID("layer", ilayer).addPhysVolID("phi", iphi).addPhysVolID("z", iz);
+                cf_unit_neg.addPhysVolID("layer", ilayer).addPhysVolID("phi", iphi).addPhysVolID("z", iz);
+                sipm_unit_neg.addPhysVolID("layer", ilayer).addPhysVolID("phi", iphi).addPhysVolID("z", iz);
+
+                string crystal_name_neg = "Crystal_neg_" + to_string(ilayer) + "_" + to_string(iphi) + "_" + to_string(iz);
+                DetElement unit_neg(sd_neg, crystal_name_neg, detid);
+                unit_neg.setPlacement(crystal_unit_neg);
+            }
+
+        PlacedVolume plv = block_neg_vol.placeVolume(slice_neg, Position(0, 0, (ilayer - 0.5) * layer_thickness - dim_z));
+        plv.addPhysVolID("layer", ilayer);
+        sd_neg.setPlacement(plv);
+    }
+
+    // Loop for placing the blocks in a module
+    for (int iz = 1; iz <= Nblock_z; ++iz)
+    {
+        PlacedVolume plv_collection_pos = block_pos_vol.placeVolume(collection, Position(0, 0, -0.5 * dim_z + Nlayers * layer_thickness + collection_thickness));
+        plv_collection_pos.addPhysVolID("stave", iz);
+
+        PlacedVolume plv_pos = module_pos_vol.placeVolume(block_pos_vol, Position(0, dim_y - (iz - 0.5) * block_z, 0));
+        plv_pos.addPhysVolID("stave", iz);
+        DetElement sd_pos(blockdet, _toString(iz, "block_pos_%3d"), detid);
+        sd_pos.setPlacement(plv_pos);
+
+        PlacedVolume plv_collection_neg = block_neg_vol.placeVolume(collection, Position(0, 0, -0.5 * dim_z + Nlayers * layer_thickness + collection_thickness));
+        plv_collection_neg.addPhysVolID("stave", iz);
+
+        PlacedVolume plv_neg = module_neg_vol.placeVolume(block_neg_vol, Position(0, dim_y - (iz - 0.5) * block_z, 0));
+        plv_neg.addPhysVolID("stave", iz);
+        DetElement sd_neg(blockdet, _toString(iz, "block_neg_%3d"), detid);
+        sd_neg.setPlacement(plv_neg);
+    }
+
+    // Loop for placing the modules
+    for (int i = 0; i < Nmodule; ++i)
+    {
+        const double m_rot = i * angle;
+        const double posx = -r0 * sin(m_rot);
+        const double posy = r0 * cos(m_rot);
+
+        Transform3D transform(RotationZ(m_rot) * RotationX(-0.5 * pi), Position(posx, posy, 0.0));
+        PlacedVolume plv = (i % 2 == 0) ? envelopeVol.placeVolume(module_pos_vol, transform) : envelopeVol.placeVolume(module_neg_vol, transform);
+        plv.addPhysVolID("module", i);
+
+        DetElement sd(ECAL, _toString(i, "module%3d"), detid);
+        sd.setPlacement(plv);
+    }
+
+    sens.setType("calorimeter");
+
+    MYDEBUG("create_detector FINISHED.");
+    return ECAL;
+}
+
+DECLARE_DETELEMENT(CRDEcalBarrel_Short_v02, create_detector)
diff --git a/Detector/DetCRD/src/Calorimeter/LongCrystalBarEndcapCalorimeter_v01.cpp b/Detector/DetCRD/src/Calorimeter/LongCrystalBarEndcapCalorimeter_v01.cpp
new file mode 100755
index 00000000..5fba0ab7
--- /dev/null
+++ b/Detector/DetCRD/src/Calorimeter/LongCrystalBarEndcapCalorimeter_v01.cpp
@@ -0,0 +1,464 @@
+//==========================================================================
+// LongCrystalBarEndcapCalorimeter_v01 implementation 
+//--------------------------------------------------------------------------
+// Author: Song Weizheng, IHEP
+//--------------------------------------------------------------------------
+// Data: 2024.6
+//==========================================================================
+
+#include "DD4hep/DetFactoryHelper.h" 
+#include "XML/Layering.h"
+#include "XML/Utilities.h"
+#include "DDRec/DetectorData.h"
+#include "DDSegmentation/Segmentation.h"
+
+#define MYDEBUG(x) std::cout << __FILE__ << ":" << __LINE__ << ": " << x << std::endl;
+#define MYDEBUGVAL(x) std::cout << __FILE__ << ":" << __LINE__ << ": " << #x << ": " << x << std::endl;
+
+using dd4hep::rec::LayeredCalorimeterData;
+using namespace dd4hep;
+using namespace std;
+
+static dd4hep::Ref_t create_detector(dd4hep::Detector& theDetector,
+                                     xml_h e,
+                                     dd4hep::SensitiveDetector sens) {
+
+    xml_det_t x_det = e;
+    std::string det_name = x_det.nameStr();
+    std::string det_type = x_det.typeStr();
+    MYDEBUGVAL(det_name);
+    MYDEBUGVAL(det_type);
+    int detid = x_det.id();
+
+    // ######################################
+    // ### detector description parameter ###
+    // ######################################
+
+    double radius_inner = theDetector.constant<double>("ecalendcap_inner_radius");
+    double radius_outer = theDetector.constant<double>("ecalendcap_outer_radius");
+    double z_min = theDetector.constant<double>("ecalendcap_zmin");
+    double z_depth = theDetector.constant<double>("ecalendcap_depth");
+
+    double x_width = theDetector.constant<double>("ecalendcap_x_width");
+    double y_width = theDetector.constant<double>("ecalendcap_y_width");
+    int Nlayers = theDetector.constant<double>("ecalendcap_layer");
+
+    double width_crystal = theDetector.constant<double>("ecalendcap_width_crystal");
+    double crystal_wrapping = theDetector.constant<double>("ecalendcap_crystal_wrapping");
+    double photoelectronic = theDetector.constant<double>("ecalendcap_length_photoelectronic");
+    double photoelectronic_width = theDetector.constant<double>("ecalendcap_width_photoelectronic");
+
+    double carbon = theDetector.constant<double>("ecalendcap_length_carbon");
+    double cable = theDetector.constant<double>("ecalendcap_length_cable");
+    double pcb = theDetector.constant<double>("ecalendcap_length_pcb");
+    double asic = theDetector.constant<double>("ecalendcap_length_asic");
+    double cooling = theDetector.constant<double>("ecalendcap_length_cooling");
+    double back_plate = theDetector.constant<double>("ecalendcap_length_back");
+    
+    // ###########################
+    // ### general measurement ###
+    // ###########################
+
+    double length_crystal = x_width - 2*carbon - 2*cable - 2*pcb - 2*asic - 2*cooling; // length of crystal wrap sipm
+    int Nbar = floor(length_crystal/width_crystal); // number of crystal
+
+    std::cout << "length_crystal" << " : " << length_crystal << std::endl;
+    std::cout << "Nbar" << " : " << Nbar << std::endl;
+
+    // // ####################
+    // // ### World Volume ###
+    // // ####################
+    dd4hep::Material air(theDetector.material("Air"));
+    dd4hep::Material vacuum(theDetector.material("Vacuum"));
+    dd4hep::Material mat_BGO(theDetector.material("G4_BGO")); 
+    dd4hep::Material mat_CF(theDetector.material("CarbonFiber"));
+    dd4hep::Material mat_Cu(theDetector.material("G4_Cu"));
+    dd4hep::Material mat_ESR(theDetector.material("G4_ESR"));
+    dd4hep::Material mat_Si(theDetector.material("G4_Si"));
+    dd4hep::Material mat_PCB(theDetector.material("PCB"));
+    
+    dd4hep::DetElement ECAL(det_name, detid);
+    dd4hep::Volume motherVol = theDetector.pickMotherVolume(ECAL);
+
+    dd4hep::Tube tube_shape(0, radius_outer, z_depth/2);
+    dd4hep::Box box_shape(x_width, y_width, z_depth);
+    dd4hep::SubtractionSolid booleanZplus(tube_shape, box_shape, Position(0.0, 0.0, 0.0));
+    dd4hep::SubtractionSolid booleanZminus(tube_shape, box_shape, Position(0.0, 0.0, 0.0));
+    dd4hep::UnionSolid unionean(booleanZplus, booleanZminus, Position(0.0, 0.0, 2*z_min+z_depth));
+    
+    // dd4hep::Volume envelopeVolZplus("envelopeVolZplus", boolean, vacuum); 
+    // dd4hep::Volume envelopeVolZminus("envelopeVolZminus", boolean, vacuum); 
+
+    
+    dd4hep::Volume envelopeVol("envelopeVol", unionean, vacuum); 
+    dd4hep::Transform3D transform(dd4hep::RotationZ(90*degree), dd4hep::Position(0.0, 0.0, -z_min-z_depth/2)); 
+    dd4hep::PlacedVolume envelopePlv = motherVol.placeVolume(envelopeVol, transform);
+
+    
+    // dd4hep::Transform3D transform2(dd4hep::RotationZ(90*degree),  dd4hep::Position(0,0,-z_min-z_depth/2));
+    // dd4hep::PlacedVolume	envelopePlvZplus = motherVol.placeVolume(envelopeVolZplus, transform1);
+    // dd4hep::PlacedVolume	envelopePlvZminus = motherVol.placeVolume(envelopeVolZminus, transform2);
+    // envelopePlvZplus.addPhysVolID("system",x_det.id()).addPhysVolID("module", 0);
+    envelopePlv.addPhysVolID("system",x_det.id());
+    // envelopeVolZplus.setVisAttributes(theDetector, "BlueVis");
+    envelopeVol.setVisAttributes(theDetector, "BlueVis");
+    ECAL.setPlacement(envelopePlv);
+    // moduleEle.setPlacement(envelopePlvZminus);
+
+    dd4hep::DetElement moduleEle(ECAL, "moduleEle", detid);
+    // dd4hep::DetElement towerEle(moduleEle, "towerEle", detid);
+    dd4hep::DetElement stavedet(moduleEle, "staveEle", detid);
+    
+    
+    // // ####################
+    // // ### Print Volume ###
+    // // ####################
+
+
+    // // ###############################
+    // // ### module inside placement ###
+    // // ###############################
+    
+    dd4hep::Box module_box(x_width/2, y_width/2, z_depth/2);
+    dd4hep::Volume module_volume("module_volume", module_box, air);
+    module_volume.setVisAttributes(theDetector, "GreenVis");
+
+    //Carbon fiber supporting
+
+    dd4hep::Volume CarbonFiber0("CarbonFiber0", dd4hep::Box(x_width/2, carbon/2, z_depth/2), mat_CF); 
+    CarbonFiber0.setVisAttributes(theDetector, "GrayVis");
+
+    dd4hep::Volume CarbonFiber1("CarbonFiber1", dd4hep::Box(carbon/2, (y_width-2*carbon)/2, z_depth/2), mat_CF); 
+    CarbonFiber1.setVisAttributes(theDetector, "GrayVis");
+
+    dd4hep::PlacedVolume plv_cf0 = module_volume.placeVolume(CarbonFiber0, Position(0, y_width/2-carbon/2, 0));
+    std::string cfname0 = "carbonfiber_s0";	
+    dd4hep::DetElement cfdet0(stavedet, cfname0, detid);
+    cfdet0.setPlacement(plv_cf0);
+
+    dd4hep::PlacedVolume plv_cf1 = module_volume.placeVolume(CarbonFiber0, Position(0, -y_width/2+carbon/2, 0));
+    std::string cfname1 = "carbonfiber_s1";	
+    dd4hep::DetElement cfdet1(stavedet, cfname1, detid);
+    cfdet1.setPlacement(plv_cf1);
+
+    dd4hep::PlacedVolume plv_bar2 = module_volume.placeVolume(CarbonFiber1, Position(x_width/2-carbon/2, 0, 0));
+    std::string barname2 = "carbonfiber_s2";
+    dd4hep::DetElement bardet2(stavedet, barname2, detid);
+    bardet2.setPlacement(plv_bar2);
+
+    dd4hep::PlacedVolume plv_bar3= module_volume.placeVolume(CarbonFiber1, Position(-x_width/2+carbon/2, 0, 0));
+    std::string barname3 = "carbonfiber_s3";
+    dd4hep::DetElement bardet3(stavedet, barname3, detid);
+    bardet3.setPlacement(plv_bar3);
+
+    //Cooling copper
+
+    dd4hep::Volume copper0("copper0", dd4hep::Box((x_width-2*carbon)/2, cooling/2, z_depth/2), mat_Cu); 
+    copper0.setVisAttributes(theDetector, "RedVis");
+
+    dd4hep::Volume copper1("copper1", dd4hep::Box(cooling/2, (x_width-2*carbon-2*cooling)/2, z_depth/2), mat_Cu); 
+    copper1.setVisAttributes(theDetector, "RedVis");
+
+    dd4hep::PlacedVolume plv_copper0 = module_volume.placeVolume(copper0, Position(0, y_width/2-carbon-cooling/2, 0));
+    std::string coppername0 = "copper_s0";
+    dd4hep::DetElement copperdet0(stavedet, coppername0, detid);
+    copperdet0.setPlacement(plv_copper0);
+
+    dd4hep::PlacedVolume plv_copper1 = module_volume.placeVolume(copper0, Position(0, -y_width/2+carbon+cooling/2, 0));
+    std::string coppername1 = "copper_s1";
+    dd4hep::DetElement copperdet1(stavedet, coppername1, detid);
+    copperdet1.setPlacement(plv_copper1);
+
+    dd4hep::PlacedVolume plv_copper2 = module_volume.placeVolume(copper1, Position(x_width/2-carbon-cooling/2, 0, 0));
+    std::string coppername2 = "copper_s2";
+    dd4hep::DetElement copperdet2(stavedet, coppername2, detid);
+    copperdet2.setPlacement(plv_copper2);
+
+    dd4hep::PlacedVolume plv_copper3 = module_volume.placeVolume(copper1, Position(-x_width/2+carbon+cooling/2, 0, 0));
+    std::string coppername3 = "copper_s3";
+    dd4hep::DetElement copperdet3(stavedet, coppername3, detid);
+    copperdet3.setPlacement(plv_copper3);
+
+    // Electronics
+
+    double electronics = cable + pcb + asic;
+
+    dd4hep::Volume electronics0("electronics0", dd4hep::Box((x_width-2*carbon-2*cooling)/2, electronics/2, z_depth/2), mat_PCB);
+    electronics0.setVisAttributes(theDetector, "YellowVis");
+
+    dd4hep::Volume electronics1("electronics1", dd4hep::Box(electronics/2, (y_width-2*carbon-2*cooling-2*electronics)/2, z_depth/2), mat_PCB);
+    electronics1.setVisAttributes(theDetector, "YellowVis");
+
+    dd4hep::PlacedVolume plv_elec0 = module_volume.placeVolume(electronics0, Position(0, y_width/2-carbon-cooling-electronics/2, 0));
+    std::string elecname0 = "electronics_s0";	
+    dd4hep::DetElement elecdet0(stavedet, elecname0, detid);
+    elecdet0.setPlacement(plv_elec0);
+
+    dd4hep::PlacedVolume plv_elec1 = module_volume.placeVolume(electronics0, Position(0, -y_width/2+carbon+cooling+electronics/2, 0));
+    std::string elecname1 = "electronics_s1";
+    dd4hep::DetElement elecdet1(stavedet, elecname1, detid);
+    elecdet1.setPlacement(plv_elec1);
+
+    dd4hep::PlacedVolume plv_elec2 = module_volume.placeVolume(electronics1, Position(x_width/2-carbon-cooling-electronics/2, 0, 0));
+    std::string elecname2 = "electronics_s2";
+    dd4hep::DetElement elecdet2(stavedet, elecname2, detid);
+    elecdet2.setPlacement(plv_elec2);
+
+    dd4hep::PlacedVolume plv_elec3 = module_volume.placeVolume(electronics1, Position(-x_width/2+carbon+cooling+electronics/2, 0, 0));
+    std::string elecname3 = "electronics_s3";
+    dd4hep::DetElement elecdet3(stavedet, elecname3, detid);
+    elecdet3.setPlacement(plv_elec3);
+
+    //Back Plate
+
+    // dd4hep::Volume backPlate("back_plate", dd4hep::Box(back_plate*15, back_plate*15, back_plate/2), mat_PCB);
+    // backPlate.setVisAttributes(theDetector, "GrayVis");
+    // std::string blockname = "back_plate_positive";
+    // dd4hep::PlacedVolume plv = subtrap_positive_vol.placeVolume(backPlate, Position(0, 0, dim_z_p-back_plate/2));
+    // sd.setPlacement(plv);
+
+    // single layer
+    dd4hep::Volume block("block", dd4hep::Box((x_width-2*carbon-2*cooling-2*electronics)/2, (y_width-2*carbon-2*cooling-2*electronics)/2, width_crystal/2), air);
+    block.setVisAttributes(theDetector, "SeeThrough");
+    std::string blockname = "Block";
+    dd4hep::DetElement sd(stavedet, blockname, detid);
+
+    dd4hep::Volume sipm_s0("sipm_s0", dd4hep::Box(photoelectronic/2, photoelectronic_width/2, photoelectronic_width/2), mat_Si); 
+    sipm_s0.setVisAttributes(theDetector, "BlueVis");
+
+    dd4hep::Volume sipm_s1("sipm_s1", dd4hep::Box(crystal_wrapping/2, photoelectronic_width/2, photoelectronic_width/2), mat_Si); 
+    sipm_s1.setVisAttributes(theDetector, "BlueVis");
+
+    double last_bar = length_crystal - (Nbar-1)*width_crystal;
+
+    dd4hep::Volume bar_s0("bar_s0", dd4hep::Box(length_crystal/2, width_crystal/2-crystal_wrapping, width_crystal/2-crystal_wrapping), mat_BGO); 
+    bar_s0.setVisAttributes(theDetector, "EcalBarrelVis");
+    bar_s0.setSensitiveDetector(sens);
+
+    dd4hep::Volume bar_s1("bar_s1", dd4hep::Box(length_crystal/2, last_bar/2-crystal_wrapping, width_crystal/2-crystal_wrapping), mat_BGO); 
+    bar_s1.setVisAttributes(theDetector, "EcalBarrelVis");
+    bar_s1.setSensitiveDetector(sens);
+
+    for(int ibar0=0;ibar0<Nbar;ibar0++){
+        if(ibar0 == Nbar-1){
+            dd4hep::Volume hardware_s1("hardware_s1", dd4hep::Box((x_width-2*carbon-2*cooling-2*electronics)/2, last_bar/2, width_crystal/2), air); 
+            hardware_s1.setVisAttributes(theDetector, "SeeThrough");
+
+            dd4hep::Volume crystal_s1("crystal_s1", dd4hep::Box((x_width-2*carbon-2*cooling-2*electronics-2*photoelectronic)/2, last_bar/2, width_crystal/2), mat_ESR); 
+            crystal_s1.setVisAttributes(theDetector, "EcalBarrelVis");
+
+            dd4hep::PlacedVolume plv_bar0 = crystal_s1.placeVolume(bar_s1, Position(0, 0, 0));
+            std::string barname0 = "CrystalBar_positive_s0_"+std::to_string(ibar0);	
+            dd4hep::DetElement bardet0(sd, barname0, detid);
+            bardet0.setPlacement(plv_bar0);
+
+            dd4hep::PlacedVolume plv_sipm4 = crystal_s1.placeVolume(sipm_s1, Position(-(x_width-2*carbon-2*cooling-2*electronics)/2+photoelectronic+crystal_wrapping/2, 0, -photoelectronic_width/2));
+            std::string sipmname4 = "SiPM_positive_s8_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet4(sd, sipmname4, detid);
+            sipmdet4.setPlacement(plv_sipm4);
+
+            dd4hep::PlacedVolume plv_sipm5 = crystal_s1.placeVolume(sipm_s1, Position((x_width-2*carbon-2*cooling-2*electronics)/2-photoelectronic-crystal_wrapping/2, 0, photoelectronic_width/2));
+            std::string sipmname5 = "SiPM_positive_s9_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet5(sd, sipmname5, detid);
+            sipmdet5.setPlacement(plv_sipm5);
+
+            dd4hep::PlacedVolume plv_sipm6 = crystal_s1.placeVolume(sipm_s1, Position(-(x_width-2*carbon-2*cooling-2*electronics)/2+photoelectronic+crystal_wrapping/2, 0, photoelectronic_width/2));
+            std::string sipmname6 = "SiPM_positive_s10_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet6(sd, sipmname6, detid);
+            sipmdet6.setPlacement(plv_sipm6);
+
+            dd4hep::PlacedVolume plv_sipm7 = crystal_s1.placeVolume(sipm_s1, Position((x_width-2*carbon-2*cooling-2*electronics)/2-photoelectronic-crystal_wrapping/2, 0, -photoelectronic_width/2));
+            std::string sipmname7 = "SiPM_positive_s11_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet7(sd, sipmname7, detid);
+            sipmdet7.setPlacement(plv_sipm7);
+
+            dd4hep::PlacedVolume plv_sipm0 = hardware_s1.placeVolume(sipm_s0, Position(-(x_width-2*carbon-2*cooling-2*electronics)/2+photoelectronic/2, 0, -photoelectronic_width/2));
+            std::string sipmname0 = "SiPM_positive_s0_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet0(sd, sipmname0, detid);
+            sipmdet0.setPlacement(plv_sipm0);
+
+            dd4hep::PlacedVolume plv_sipm1 = hardware_s1.placeVolume(sipm_s0, Position((x_width-2*carbon-2*cooling-2*electronics)/2-photoelectronic/2, 0, photoelectronic_width/2));
+            std::string sipmname1 = "SiPM_positive_s1_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet1(sd, sipmname1, detid);
+            sipmdet1.setPlacement(plv_sipm1);
+
+            dd4hep::PlacedVolume plv_sipm2 = hardware_s1.placeVolume(sipm_s0, Position(-(x_width-2*carbon-2*cooling-2*electronics)/2+photoelectronic/2, 0, photoelectronic_width/2));
+            std::string sipmname2 = "SiPM_positive_s2_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet2(sd, sipmname2, detid);
+            sipmdet2.setPlacement(plv_sipm2);
+
+            dd4hep::PlacedVolume plv_sipm3 = hardware_s1.placeVolume(sipm_s0, Position((x_width-2*carbon-2*cooling-2*electronics)/2-photoelectronic/2, 0, -photoelectronic_width/2));
+            std::string sipmname3 = "SiPM_positive_s3_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet3(sd, sipmname3, detid);
+            sipmdet3.setPlacement(plv_sipm3);
+
+            dd4hep::PlacedVolume plv_cry0 = hardware_s1.placeVolume(crystal_s1, Position(0, 0, 0));
+            std::string cryname0 = "Crystal_positive_s0_"+std::to_string(ibar0);
+            dd4hep::DetElement crydet0(sd, cryname0, detid);
+            crydet0.setPlacement(plv_cry0);
+
+            dd4hep::PlacedVolume plv_hard0 = block.placeVolume(hardware_s1, Position(0, -length_crystal/2+last_bar/2, 0));
+            plv_hard0.addPhysVolID("bar",ibar0);
+            std::string hardname0 = "Hardware_positive_s0_"+std::to_string(ibar0);
+            dd4hep::DetElement harddet0(sd, hardname0, detid);
+            harddet0.setPlacement(plv_hard0);
+        }
+        else{
+
+            dd4hep::Volume hardware_s0("hardware_s0", dd4hep::Box((x_width-2*carbon-2*cooling-2*electronics)/2, width_crystal/2, width_crystal/2), air); 
+            hardware_s0.setVisAttributes(theDetector, "SeeThrough");
+
+            dd4hep::Volume crystal_s0("crystal_s0", dd4hep::Box((x_width-2*carbon-2*cooling-2*electronics-2*photoelectronic)/2, width_crystal/2, width_crystal/2), mat_ESR); 
+            crystal_s0.setVisAttributes(theDetector, "EcalBarrelVis");
+
+            dd4hep::PlacedVolume plv_bar0 = crystal_s0.placeVolume(bar_s0, Position(0, 0, 0));
+            std::string barname0 = "CrystalBar_s0_"+std::to_string(ibar0);	
+            dd4hep::DetElement bardet0(sd, barname0, detid);
+            bardet0.setPlacement(plv_bar0);
+
+            dd4hep::PlacedVolume plv_sipm4 = crystal_s0.placeVolume(sipm_s1, Position(-(x_width-2*carbon-2*cooling-2*electronics-2*photoelectronic)/2+crystal_wrapping/2, 0, -photoelectronic_width/2));
+            std::string sipmname4 = "SiPM_positive_s8_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet4(sd, sipmname4, detid);
+            sipmdet4.setPlacement(plv_sipm4);
+
+            dd4hep::PlacedVolume plv_sipm5 = crystal_s0.placeVolume(sipm_s1, Position((x_width-2*carbon-2*cooling-2*electronics-2*photoelectronic)/2-crystal_wrapping/2, 0, photoelectronic_width/2));
+            std::string sipmname5 = "SiPM_positive_s9_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet5(sd, sipmname5, detid);
+            sipmdet5.setPlacement(plv_sipm5);
+
+            dd4hep::PlacedVolume plv_sipm6 = crystal_s0.placeVolume(sipm_s1, Position(-(x_width-2*carbon-2*cooling-2*electronics-2*photoelectronic)/2+crystal_wrapping/2, 0, photoelectronic_width/2));
+            std::string sipmname6 = "SiPM_positive_s10_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet6(sd, sipmname6, detid);
+            sipmdet6.setPlacement(plv_sipm6);
+
+            dd4hep::PlacedVolume plv_sipm7 = crystal_s0.placeVolume(sipm_s1, Position((x_width-2*carbon-2*cooling-2*electronics-2*photoelectronic)/2-crystal_wrapping/2, 0, -photoelectronic_width/2));
+            std::string sipmname7 = "SiPM_positive_s11_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet7(sd, sipmname7, detid);
+            sipmdet7.setPlacement(plv_sipm7);
+
+            dd4hep::PlacedVolume plv_sipm0 = hardware_s0.placeVolume(sipm_s0, Position(-(x_width-2*carbon-2*cooling-2*electronics)/2+photoelectronic/2, 0, -photoelectronic_width/2));
+            std::string sipmname0 = "SiPM_positive_s0_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet0(sd, sipmname0, detid);
+            sipmdet0.setPlacement(plv_sipm0);
+
+            dd4hep::PlacedVolume plv_sipm1 = hardware_s0.placeVolume(sipm_s0, Position((x_width-2*carbon-2*cooling-2*electronics)/2-photoelectronic/2, 0, photoelectronic_width/2));
+            std::string sipmname1 = "SiPM_positive_s1_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet1(sd, sipmname1, detid);
+            sipmdet1.setPlacement(plv_sipm1);
+
+            dd4hep::PlacedVolume plv_sipm2 = hardware_s0.placeVolume(sipm_s0, Position(-(x_width-2*carbon-2*cooling-2*electronics)/2+photoelectronic/2, 0, photoelectronic_width/2));
+            std::string sipmname2 = "SiPM_positive_s2_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet2(sd, sipmname2, detid);
+            sipmdet2.setPlacement(plv_sipm2);
+
+            dd4hep::PlacedVolume plv_sipm3 = hardware_s0.placeVolume(sipm_s0, Position((x_width-2*carbon-2*cooling-2*electronics)/2-photoelectronic/2, 0, -photoelectronic_width/2));
+            std::string sipmname3 = "SiPM_positive_s3_"+std::to_string(ibar0);
+            dd4hep::DetElement sipmdet3(sd, sipmname3, detid);
+            sipmdet3.setPlacement(plv_sipm3);
+
+            dd4hep::PlacedVolume plv_cry0 = hardware_s0.placeVolume(crystal_s0, Position(0, 0, 0));
+            std::string cryname0 = "Crystal_positive_s0_"+std::to_string(ibar0);
+            dd4hep::DetElement crydet0(sd, cryname0, detid);
+            crydet0.setPlacement(plv_cry0);
+
+            dd4hep::PlacedVolume plv_hard0 = block.placeVolume(hardware_s0, Position(0, length_crystal/2-(2*ibar0+1)*width_crystal/2, 0));
+            std::string hardname0 = "Hardware_positive_s0_"+std::to_string(ibar0);
+            dd4hep::DetElement harddet0(sd, hardname0, detid);
+            plv_hard0.addPhysVolID("bar",ibar0);
+            harddet0.setPlacement(plv_hard0);
+        }
+    }
+
+    // // #########################
+    // // ### modules placement ###
+    // // #########################
+
+    for(int ilayer=0; ilayer<Nlayers; ilayer=ilayer+1){
+        if(ilayer%2==0){
+            dd4hep::PlacedVolume plv = module_volume.placeVolume(block, Position(0, 0, 0.5*width_crystal+ilayer*width_crystal-z_depth/2));
+            plv.addPhysVolID("slayer", 0).addPhysVolID("dlayer", floor(ilayer/2+1));
+            stavedet.setPlacement(plv); 
+        }
+        else{
+            dd4hep::Transform3D transform(dd4hep::RotationZ(90*degree),  dd4hep::Position(0, 0, 0.5*width_crystal+ilayer*width_crystal-z_depth/2)); 
+            dd4hep::PlacedVolume plv = module_volume.placeVolume(block, transform);
+            plv.addPhysVolID("slayer", 1).addPhysVolID("dlayer", floor(ilayer/2+1));
+            stavedet.setPlacement(plv); 
+            
+        }             
+    }
+
+    // several smaller module
+    // dd4hep::Box module_box1(x_width/2, 260/2, z_depth/2);
+    // dd4hep::Volume module_volume1("module_volume1", module_box1, air);
+    // module_volume1.setVisAttributes(theDetector, "GreenVis");
+
+    // dd4hep::Volume block1("block1", dd4hep::Box(x_width/2, 260/2, width_crystal/2), air);
+    // block1.setVisAttributes(theDetector, "SeeThrough");
+    // std::string blockname = "Block1";
+    // dd4hep::DetElement sd1(stavedet, blockname1, detid);
+
+    // dd4hep::Volume bar_s0("bar_s0", dd4hep::Box(length_crystal/2, width_crystal/2-crystal_wrapping, width_crystal/2-crystal_wrapping), mat_BGO); 
+    // bar_s0.setVisAttributes(theDetector, "EcalBarrelVis");
+    // bar_s0.setSensitiveDetector(sens);
+
+    // // #########################
+    // // ### modules placement ###
+    // // #########################
+
+    int number = 0;
+    for(int i=-6; i<7; i=i+1){
+        for(int j=-6; j<7; j=j+1){
+            // if((i==2 && j==2) || (i==2 && j==3) || (i==3 && j==2) || (i==3 && j==3)){
+                if(i==0 || j==0) continue; 
+                if(sqrt(abs(i)*abs(i) + abs(j)*abs(j))<2 || sqrt(abs(i)*abs(i) + abs(j)*abs(j))>6.1) continue;
+                else{
+                    dd4hep::PlacedVolume plvPlus, plvMinus;
+                    // cout<<"i: "<<i<<" j: "<<j<<endl;
+                    if(i>0 && j>0){
+                        plvPlus = envelopeVol.placeVolume(module_volume, Position(x_width*i - x_width/2, y_width*j - y_width/2, 2*z_min+z_depth));
+                        dd4hep::Transform3D transform(dd4hep::RotationY(-180*degree),  Position(x_width*i - x_width/2, y_width*j - y_width/2, 0)); 
+                        plvMinus = envelopeVol.placeVolume(module_volume, transform);
+
+                        
+                    } 
+                    else if (i>0 && j<0){
+                        plvPlus = envelopeVol.placeVolume(module_volume, Position(x_width*i - x_width/2, y_width*j + y_width/2, 2*z_min+z_depth));
+                        dd4hep::Transform3D transform(dd4hep::RotationY(-180*degree),  Position(x_width*i - x_width/2, y_width*j + y_width/2, 0));
+                        plvMinus = envelopeVol.placeVolume(module_volume, transform);
+                    }  
+
+                    else if (i<0 && j>0) {
+                        plvPlus = envelopeVol.placeVolume(module_volume, Position(x_width*i + x_width/2, y_width*j - y_width/2, 2*z_min+z_depth));
+                        dd4hep::Transform3D transform(dd4hep::RotationY(-180*degree),  Position(x_width*i + x_width/2, y_width*j - y_width/2, 0));
+                        plvMinus = envelopeVol.placeVolume(module_volume, transform);
+                    } 
+                    else  {
+                        plvPlus = envelopeVol.placeVolume(module_volume, Position(x_width*i + x_width/2, y_width*j + y_width/2, 2*z_min+z_depth));
+                        dd4hep::Transform3D transform(dd4hep::RotationY(-180*degree),  Position(x_width*i + x_width/2, y_width*j + y_width/2, 0));
+                        plvMinus = envelopeVol.placeVolume(module_volume, transform);
+                    }
+
+                    plvPlus.addPhysVolID("module", 0).addPhysVolID("stave", 100*(i+6)+(j+6));
+                    plvMinus.addPhysVolID("module", 1).addPhysVolID("stave", 100*(i+6)+(j+6));
+                    // DetElement sd1(towerEle, "stave1_"+std::to_string(100*(i+6)+(j+6)), detid);
+                    // DetElement sd2(towerEle, "stave2_"+std::to_string(100*(i+6)+(j+6)), detid);
+                    moduleEle.setPlacement(plvPlus);
+                    moduleEle.setPlacement(plvMinus);
+                    number++; 
+                }
+            // }
+        }   
+    }
+
+    cout<<"number: "<<number<<endl;
+
+  
+     
+
+    sens.setType("calorimeter");
+    MYDEBUG("create_detector DONE. ");
+    return ECAL;
+} 
+
+DECLARE_DETELEMENT(LongCrystalBarEndcapCalorimeter_v01, create_detector)
diff --git a/Detector/DetCRD/src/Muon/Muon_Barrel_v01_01.cpp b/Detector/DetCRD/src/Muon/Muon_Barrel_v01_01.cpp
index 9e6abc73..6515e976 100644
--- a/Detector/DetCRD/src/Muon/Muon_Barrel_v01_01.cpp
+++ b/Detector/DetCRD/src/Muon/Muon_Barrel_v01_01.cpp
@@ -320,6 +320,7 @@ static dd4hep::Ref_t create_detector(dd4hep::Detector& theDetector,
     }
     dd4hep::Transform3D pv(dd4hep::Rotation3D(dd4hep::RotationX(90*dd4hep::degree)),dd4hep::Position(0,0,0));
     dd4hep::PlacedVolume phv = motherVol.placeVolume(envelope,pv);
+    phv.addPhysVolID("system",x_det.id());
     sdet.setPlacement(phv);
 
     MYDEBUG("create_detector DONE. ");
diff --git a/Detector/DetCRD/src/Muon/Muon_Endcap_v01_01.cpp b/Detector/DetCRD/src/Muon/Muon_Endcap_v01_01.cpp
index acb54e5b..7631b537 100644
--- a/Detector/DetCRD/src/Muon/Muon_Endcap_v01_01.cpp
+++ b/Detector/DetCRD/src/Muon/Muon_Endcap_v01_01.cpp
@@ -294,7 +294,7 @@ static dd4hep::Ref_t create_detector(dd4hep::Detector& theDetector,
     sdetB.setPlacement(pv);
 
     pv = motherVol.placeVolume(assembly);
-//    pv.addPhysVolID("system",x_det.id());
+    pv.addPhysVolID("system",x_det.id());
     both_endcaps.setPlacement(pv);
     both_endcaps.add(sdetA);
     both_endcaps.add(sdetB);
diff --git a/Detector/DetCRD/src/Tracker/SiTrackerStaggeredLadder_v01_geo.cpp b/Detector/DetCRD/src/Tracker/SiTrackerStaggeredLadder_v01_geo.cpp
index b8245d66..64fe00bb 100644
--- a/Detector/DetCRD/src/Tracker/SiTrackerStaggeredLadder_v01_geo.cpp
+++ b/Detector/DetCRD/src/Tracker/SiTrackerStaggeredLadder_v01_geo.cpp
@@ -101,7 +101,7 @@ static dd4hep::Ref_t create_element(dd4hep::Detector& theDetector, xml_h e, dd4h
   std::string deadwireVis    = x_display.attr<string>(_Unicode(deadwire));
 
   //fetch the shell parameters
-  if (x_det.hasAttr(_Unicode(shell))) {
+  if (x_det.hasChild(_Unicode(shell))) {
     xml_comp_t x_shell(x_det.child(_Unicode(shell)));
     double rmin_shell = x_shell.rmin();
     double rmax_shell = x_shell.rmax();
diff --git a/Detector/DetCRD/src/Tracker/SiTracker_otkbarrel_v01_geo.cpp b/Detector/DetCRD/src/Tracker/SiTracker_otkbarrel_v01_geo.cpp
new file mode 100644
index 00000000..0f55e8ef
--- /dev/null
+++ b/Detector/DetCRD/src/Tracker/SiTracker_otkbarrel_v01_geo.cpp
@@ -0,0 +1,374 @@
+#include "DD4hep/DetFactoryHelper.h"
+#include "DD4hep/DD4hepUnits.h"
+#include "DD4hep/DetType.h"
+#include "DDRec/Surface.h"
+#include "DDRec/DetectorData.h"
+#include "XML/Utilities.h"
+#include <cmath>
+
+using namespace std;
+
+using dd4hep::Box;
+using dd4hep::DetElement;
+using dd4hep::Material;
+using dd4hep::Position;
+using dd4hep::RotationY;
+using dd4hep::RotationZYX;
+using dd4hep::Transform3D;
+using dd4hep::Rotation3D;
+using dd4hep::Volume;
+using dd4hep::_toString;
+using dd4hep::rec::volSurfaceList;
+using dd4hep::rec::ZPlanarData;
+using dd4hep::mm;
+
+
+static dd4hep::Ref_t create_element(dd4hep::Detector& theDetector, xml_h e, dd4hep::SensitiveDetector sens)  {
+
+    xml_det_t  x_det    = e;
+    Material   air      = theDetector.air();
+    int        det_id   = x_det.id();
+    string     name     = x_det.nameStr();
+    DetElement otkbarrel(name, det_id);
+
+    Volume envelope = dd4hep::xml::createPlacedEnvelope(theDetector, e, otkbarrel);
+    dd4hep::xml::setDetectorTypeFlag(e, otkbarrel) ;
+    if(theDetector.buildType()==dd4hep::BUILD_ENVELOPE) return otkbarrel;
+    envelope.setVisAttributes(theDetector.visAttributes("SeeThrough"));
+
+    if (x_det.hasAttr(_U(sensitive))) {
+        xml_dim_t sd_typ = x_det.child(_U(sensitive));
+        sens.setType(sd_typ.typeStr());
+    }
+    else {
+        sens.setType("tracker");
+    }
+    std::cout << " ** building SiTracker_otkbarrel ..." << std::endl ;
+
+    dd4hep::rec::ZPlanarData* zPlanarData = new dd4hep::rec::ZPlanarData;
+    //*****************************************************************//
+
+    // Reading parameters from the .xml file
+
+    //*****************************************************************//
+
+    //fetch the display parameters
+    xml_comp_t x_display(x_det.child(_Unicode(display)));
+    std::string ladderVis      = x_display.attr<string>(_Unicode(ladder));
+    std::string supportVis     = x_display.attr<string>(_Unicode(support));
+    std::string flexVis        = x_display.attr<string>(_Unicode(flex));
+    std::string sensEnvVis     = x_display.attr<string>(_Unicode(sens_env));
+    std::string sensVis        = x_display.attr<string>(_Unicode(sens));
+    std::string deadsensVis    = x_display.attr<string>(_Unicode(deadsensor));
+    std::string pcbVis         = x_display.attr<string>(_Unicode(pcb));
+    std::string asicVis        = x_display.attr<string>(_Unicode(asic));
+
+
+    for(xml_coll_t layer_i(x_det,_U(layer)); layer_i; ++layer_i){
+        //fetch the ladder overall parameters of this layer
+        xml_comp_t x_layer(layer_i);
+
+        dd4hep::PlacedVolume pv;
+        int layer_id                 = x_layer.attr<int>(_Unicode(layer_id));
+
+        std::cout << "layer_id: " << layer_id << endl;
+
+        double sensitive_radius      = x_layer.attr<double>(_Unicode(ladder_radius));
+        int n_ladders                = x_layer.attr<int>(_Unicode(n_ladders)) ;
+        double ladder_offset         = x_layer.attr<double>(_Unicode(ladder_offset));
+        double ladder_radius         = sqrt(ladder_offset*ladder_offset + sensitive_radius*sensitive_radius);
+        double ladder_phi0           = -atan(ladder_offset/sensitive_radius);
+        double ladder_total_thickness         = x_layer.attr<double>(_Unicode(ladder_thickness));
+        double ladder_total_width         = x_layer.attr<double>(_Unicode(ladder_width));
+        double ladder_total_length         = x_layer.attr<double>(_Unicode(ladder_length));
+        std::cout << "ladder_radius: " << ladder_radius/mm <<" mm" << endl;
+        std::cout << "sensitive_radius: " << sensitive_radius/mm << " mm" << endl;
+        std::cout << "ladder_thickness: " << ladder_total_thickness/mm << " mm" << endl;
+        std::cout << "ladder_width: " << ladder_total_width/mm << " mm" << endl;
+        std::cout << "ladder_length: " << ladder_total_length/mm << " mm" << endl;
+
+        std::string layerName = dd4hep::_toString( layer_id , "layer_%d"  );
+        dd4hep::Assembly layer_assembly( layerName ) ;
+        pv = envelope.placeVolume( layer_assembly ) ;
+        dd4hep::DetElement layerDE( otkbarrel , layerName  , x_det.id() );
+        layerDE.setPlacement( pv ) ;
+
+        const double ladder_dphi = ( dd4hep::twopi / n_ladders ) ;
+        std::cout << "ladder_dphi: " << ladder_dphi << endl;
+
+
+        //fetch the ladder parameters
+        xml_comp_t x_ladder(x_layer.child(_Unicode(ladder)));
+
+        //fetch the ladder support parameters
+        xml_comp_t x_ladder_support(x_ladder.child(_Unicode(ladderSupport)));
+        double support_height        = x_ladder_support.attr<double>(_Unicode(height));
+        double support_length        = x_ladder_support.attr<double>(_Unicode(length));
+        double support_thickness     = x_ladder_support.attr<double>(_Unicode(thickness));
+        double support_width         = x_ladder_support.attr<double>(_Unicode(width));
+        Material support_mat;
+        if(x_ladder_support.hasAttr(_Unicode(mat)))
+        {
+            support_mat = theDetector.material(x_ladder_support.attr<string>(_Unicode(mat)));
+        }
+        else
+        {
+            support_mat = theDetector.material(x_ladder_support.materialStr());
+        }
+        std::cout << "support_length: " << support_length/mm << " mm" << endl;
+        std::cout << "support_thickness: " << support_thickness/mm << " mm" << endl;
+        std::cout << "support_width: " << support_width/mm << " mm" << endl;
+
+
+        //fetch the flex parameters
+        double flex_thickness(0);
+        double flex_width(0);
+        double flex_length(0);
+        xml_comp_t x_flex(x_ladder.child(_Unicode(flex)));
+        for(xml_coll_t flex_i(x_flex,_U(slice)); flex_i; ++flex_i){
+            xml_comp_t x_flex_slice(flex_i);
+            double x_flex_slice_thickness = x_flex_slice.attr<double>(_Unicode(thickness));
+            double x_flex_slice_width = x_flex_slice.attr<double>(_Unicode(width));
+            double x_flex_slice_length = x_flex_slice.attr<double>(_Unicode(length));
+            flex_thickness += x_flex_slice_thickness;
+            if (x_flex_slice_width > flex_width) flex_width = x_flex_slice_width;
+            if (x_flex_slice_length > flex_length) flex_length = x_flex_slice_length;
+            std::cout << "x_flex_slice_thickness: " << x_flex_slice_thickness/mm << " mm" << endl;
+        }
+        std::cout << "flex_thickness: " << flex_thickness/mm << " mm" << endl;
+        std::cout << "flex_width: " << flex_width/mm << " mm" << endl;
+        std::cout << "flex_length: " << flex_length/mm << " mm" << endl;
+
+
+        //fetch the sensor parameters
+        xml_comp_t x_sensor(x_ladder.child(_Unicode(sensor)));
+        int n_sensors_per_side                  = x_sensor.attr<int>(_Unicode(n_sensors));
+        double dead_gap                         = x_sensor.attr<double>(_Unicode(gap));
+        double sensor_thickness                 = x_sensor.attr<double>(_Unicode(thickness));
+        double sensor_length                    = x_sensor.attr<double>(_Unicode(length));
+        double sensor_active_width              = x_sensor.attr<double>(_Unicode(active_width));
+        double sensor_dead_width                = x_sensor.attr<double>(_Unicode(dead_width));
+        double sensor_total_width               = sensor_active_width + sensor_dead_width;
+        Material sensor_mat                     = theDetector.material(x_sensor.attr<string>(_Unicode(sensor_mat)));
+
+        std::cout << "n_sensors_per_side: " << n_sensors_per_side << endl;
+        std::cout << "dead_gap: " << dead_gap/mm << " mm" << endl;
+        std::cout << "sensor_thickness: " << sensor_thickness/mm << " mm" << endl;
+        std::cout << "sensor_length: " << sensor_length/mm << " mm" << endl;
+        std::cout << "sensor_active_width: " << sensor_active_width/mm << " mm" << endl;
+        std::cout << "sensor_dead_width: " << sensor_dead_width/mm << " mm" << endl;
+        std::cout << "sensor_total_width: " << sensor_total_width/mm << " mm" << endl;
+
+        //fetch parameters for the pcb and asic parts
+        xml_comp_t x_other(x_ladder.child(_Unicode(other)));
+        double pcb_thickness               = x_other.attr<double>(_Unicode(pcb_thickness));
+        double pcb_width                   = x_other.attr<double>(_Unicode(pcb_width));
+        double pcb_length                  = x_other.attr<double>(_Unicode(pcb_length));
+        double pcb_zgap                    = x_other.attr<double>(_Unicode(pcb_zgap));
+        double asic_thickness              = x_other.attr<double>(_Unicode(asic_thickness));
+        double asic_width                  = x_other.attr<double>(_Unicode(asic_width));
+        double asic_length                 = x_other.attr<double>(_Unicode(asic_length));
+        double asic_zgap                   = x_other.attr<double>(_Unicode(asic_zgap));
+        Material pcb_mat                   = theDetector.material(x_other.attr<string>(_Unicode(pcb_mat)));
+        Material asic_mat                  = theDetector.material(x_other.attr<string>(_Unicode(asic_mat)));
+
+        std::cout << "pcb_thickness: " << pcb_thickness/mm << " mm" << endl;
+        std::cout << "pcb_width: " << pcb_width/mm << " mm" << endl;
+        std::cout << "pcb_length: " << pcb_length/mm << " mm" << endl;
+        std::cout << "pcb_zgap: " << pcb_zgap/mm << " mm" << endl;
+        std::cout << "asic_thickness: " << asic_thickness/mm << " mm" << endl;    std::cout << "asic_width: " << asic_width/mm << " mm" << endl;
+        std::cout << "asic_length: " << asic_length/mm << " mm" << endl;
+        std::cout << "asic_zgap: " << asic_zgap/mm << " mm" << endl;
+
+        //*****************************************************************//
+
+        // Creating objects
+
+        //*****************************************************************//
+
+
+        //create ladder logical volume
+        Box LadderSolid(ladder_total_thickness / 2.0,
+                        ladder_total_width / 2.0,
+                        ladder_total_length / 2.0);
+        Volume LadderLogical(name + dd4hep::_toString( layer_id, "_LadderLogical_%02d"),
+                             LadderSolid, air);
+        // create flex envelope logical volume
+        Box FlexEnvelopeSolid(flex_thickness / 2.0, flex_width / 2.0, flex_length / 2.0);
+        Volume FlexEnvelopeLogical(name + dd4hep::_toString( layer_id, "_FlexEnvelopeLogical_%02d"), FlexEnvelopeSolid, air);
+        FlexEnvelopeLogical.setVisAttributes(theDetector.visAttributes("SeeThrough"));
+        //otkbarrel.setVisAttributes(theDetector, flexVis, FlexEnvelopeLogical);
+
+        //create the flex layers inside the flex envelope
+        double flex_start_height(-flex_thickness/2.);
+        int index = 0;
+        for(xml_coll_t flex_i(x_flex,_U(slice)); flex_i; ++flex_i){
+            xml_comp_t x_flex_slice(flex_i);
+            double x_flex_slice_thickness = x_flex_slice.attr<double>(_Unicode(thickness));
+            double x_flex_slice_width = x_flex_slice.attr<double>(_Unicode(width));
+            double x_flex_slice_length = x_flex_slice.attr<double>(_Unicode(length));
+            Material x_flex_slice_mat;
+            if(x_flex_slice.hasAttr(_Unicode(mat)))
+            {
+                x_flex_slice_mat = theDetector.material(x_flex_slice.attr<string>(_Unicode(mat)));
+            }
+            else
+            {
+                x_flex_slice_mat = theDetector.material(x_flex_slice.materialStr());
+            }
+            // Material x_flex_slice_mat = theDetector.material(x_flex_slice.attr<string>(_Unicode(mat)));
+            Box FlexLayerSolid(x_flex_slice_thickness/2.0, x_flex_slice_width/2.0, x_flex_slice_length/2.0);
+            Volume FlexLayerLogical(name + dd4hep::_toString( layer_id, "_FlexLayerLogical_%02d") + dd4hep::_toString( index, "index_%02d"), FlexLayerSolid, x_flex_slice_mat);
+            FlexLayerLogical.setVisAttributes(theDetector.visAttributes(flexVis));
+            double flex_slice_height = flex_start_height + x_flex_slice_thickness/2.;
+            pv = FlexEnvelopeLogical.placeVolume(FlexLayerLogical, Position(flex_slice_height, 0., 0.));
+            std::cout << "flex thickness = " << x_flex_slice_thickness << std::endl;
+            std::cout << "flex width = " << x_flex_slice_width << std::endl;
+            std::cout << "flex length = " << x_flex_slice_length << std::endl;
+            // std::cout << "flex material: " << x_flex_slice_mat << std::endl;
+            flex_start_height += x_flex_slice_thickness;
+            index++;
+        }
+
+        //place the flex envelope inside the ladder envelope
+        pv = LadderLogical.placeVolume(FlexEnvelopeLogical, Position((support_height+flex_thickness)/2.0+sensor_thickness, (-support_width+flex_width)/2.0, 0.));
+
+        //create sensor envelope logical volume
+        Box SensorEnvelopeSolid((sensor_thickness+pcb_thickness+asic_thickness) / 2.0, sensor_total_width / 2.0, support_length / 2.0);
+        Volume SensorEnvelopeLogical(name + dd4hep::_toString( layer_id, "_SensorEnvelopeLogical_%02d"), SensorEnvelopeSolid, air);
+        SensorEnvelopeLogical.setVisAttributes(theDetector.visAttributes(sensEnvVis));
+
+        //create sensor logical volume
+        Box SensorSolid(sensor_thickness / 2.0, sensor_active_width / 2.0, sensor_length / 2.0);
+        Volume SensorLogical(name + dd4hep::_toString( layer_id, "_SensorLogical_%02d"), SensorSolid, sensor_mat);
+        SensorLogical.setSensitiveDetector(sens);
+        if (x_det.hasAttr(_U(limits))) SensorLogical.setLimitSet(theDetector, x_det.limitsStr());
+        //otkbarrel.setVisAttributes(theDetector, deadsensVis, SensorDeadLogical);
+        SensorLogical.setVisAttributes(theDetector.visAttributes(sensVis));
+
+        //create dead sensor logical volume
+        Box SensorDeadSolid(sensor_thickness / 2.0, sensor_dead_width / 2.0, sensor_length / 2.0);
+        Volume SensorDeadLogical(name + dd4hep::_toString( layer_id, "_SensorDeadLogical_%02d"), SensorDeadSolid, sensor_mat);
+        SensorDeadLogical.setVisAttributes(theDetector.visAttributes(deadsensVis));
+
+        //create pcb sensor logical volume
+        Box PcbSolid(pcb_thickness / 2.0, pcb_width / 2.0, pcb_length / 2.0);
+        Volume PcbLogical(name + dd4hep::_toString( layer_id, "_PcbLogical_%02d"), PcbSolid, pcb_mat);
+        PcbLogical.setVisAttributes(theDetector.visAttributes(pcbVis));
+
+        //create asic sensor logical volume
+        Box AsicSolid(asic_thickness / 2.0, asic_width / 2.0, asic_length / 2.0);
+        Volume AsicLogical(name + dd4hep::_toString( layer_id, "_AsicLogical_%02d"), AsicSolid, asic_mat);
+        AsicLogical.setVisAttributes(theDetector.visAttributes(asicVis));
+
+        // place the active sensor, dead sensor, pcb and asic inside the sensor envelope
+        std::vector<dd4hep::PlacedVolume> Sensor_pv;
+        for(int isensor=0;isensor<n_sensors_per_side;++isensor){
+            double sensor_total_z = n_sensors_per_side*sensor_length + dead_gap*n_sensors_per_side;
+            double sensor_xpos = -(pcb_thickness+asic_thickness)/2.0;
+            double sensor_ypos_active = (sensor_total_width-sensor_active_width)/2.0;
+            double sensor_ypos_dead = (-sensor_total_width+sensor_dead_width)/ 2.0;
+            double sensor_zpos = -sensor_total_z/2.0 + (sensor_length + dead_gap)/2.0 + isensor*(sensor_length + dead_gap);
+            double pcb_xpos = (sensor_thickness-asic_thickness)/2.0;
+            double pcb_ypos = sensor_ypos_active;
+            double pcb_zpos_0 = sensor_zpos-(sensor_length/2.0-pcb_zgap-pcb_length/2.0);
+            double pcb_zpos_1 = sensor_zpos+(sensor_length/2.0-pcb_zgap-pcb_length/2.0);
+            double asic_xpos = (sensor_thickness+pcb_thickness)/2.0;
+            double asic_ypos = pcb_ypos;
+            double asic_zpos_0 = sensor_zpos-(sensor_length/2.0-asic_zgap-asic_length/2.0);
+            double asic_zpos_1 = sensor_zpos+(sensor_length/2.0-asic_zgap-asic_length/2.0);
+            pv = SensorEnvelopeLogical.placeVolume(SensorLogical, Position(sensor_xpos,sensor_ypos_active,sensor_zpos));
+            pv.addPhysVolID("layer", layer_id).addPhysVolID("active", 0).addPhysVolID("sensor", isensor) ;
+            Sensor_pv.push_back(pv);
+            pv = SensorEnvelopeLogical.placeVolume(SensorDeadLogical, Position(sensor_xpos,sensor_ypos_dead,sensor_zpos));
+            pv = SensorEnvelopeLogical.placeVolume(PcbLogical, Position(pcb_xpos,pcb_ypos,pcb_zpos_0));
+            pv = SensorEnvelopeLogical.placeVolume(PcbLogical, Position(pcb_xpos,pcb_ypos,pcb_zpos_1));
+            pv = SensorEnvelopeLogical.placeVolume(AsicLogical, Position(asic_xpos,asic_ypos,asic_zpos_0));
+            pv = SensorEnvelopeLogical.placeVolume(AsicLogical, Position(asic_xpos,asic_ypos,asic_zpos_1));
+        }
+
+        //place the sensor envelope inside the ladder envelope
+        pv = LadderLogical.placeVolume(SensorEnvelopeLogical,
+                                       Position(support_height/2.0+(sensor_thickness+pcb_thickness+asic_thickness)/2.0,0.,0.));
+
+        //create the ladder support envelope
+        Box LadderSupportEnvelopeSolid(support_height/2.0, support_width/2.0, support_length/2.0);
+        Volume LadderSupportEnvelopeLogical(name + _toString( layer_id,"_SupEnvLogical_%02d"), LadderSupportEnvelopeSolid, air);
+        otkbarrel.setVisAttributes(theDetector, "seeThrough", LadderSupportEnvelopeLogical);
+
+        //create ladder support volume
+        Box LadderSupportSolid(support_thickness / 2.0 , support_width / 2.0 , support_length / 2.0);
+        Volume LadderSupportLogical(name + _toString( layer_id,"_SupLogical_%02d"), LadderSupportSolid, support_mat);
+        LadderSupportLogical.setVisAttributes(theDetector.visAttributes(supportVis));
+
+        pv = LadderSupportEnvelopeLogical.placeVolume(LadderSupportLogical);
+        pv = LadderLogical.placeVolume(LadderSupportEnvelopeLogical);
+
+        for(int i = 0; i < n_ladders; i++){
+            float rot = layer_id*0.5;
+            std::stringstream ladder_enum;
+            ladder_enum << "otkbarrel_ladder_" << layer_id << "_" << i;
+            DetElement ladderDE(layerDE, ladder_enum.str(), x_det.id());
+            // std::cout << "start building " << ladder_enum.str() << ":" << endl;
+
+
+
+            //====== create the meassurement surface ===================
+            dd4hep::rec::Vector3D o(0,0,0);
+            dd4hep::rec::Vector3D u( 0., 0., 1.);
+            dd4hep::rec::Vector3D v( 0., 1., 0.);
+            dd4hep::rec::Vector3D n( 1., 0., 0.);
+            double inner_thick = sensor_thickness/2.0;
+            double outer_thick = (support_height + sensor_thickness)/2.0;
+            dd4hep::rec::VolPlane surfSens( SensorLogical ,
+                                            dd4hep::rec::SurfaceType(dd4hep::rec::SurfaceType::Sensitive),
+                                            inner_thick, outer_thick , u,v,n,o ) ;
+
+
+            for(int isensor=0; isensor < n_sensors_per_side; ++isensor){
+                std::stringstream sensor_str;
+                sensor_str << ladder_enum.str() << "_" << isensor;
+                // std::cout << "\tstart building " << sensor_str.str() << ":" << endl;
+                DetElement sensorDE(ladderDE, sensor_str.str(), x_det.id());
+                sensorDE.setPlacement(Sensor_pv[isensor]);
+                volSurfaceList(sensorDE)->push_back(surfSens);
+                // std::cout << "\t" << sensor_str.str() << " done." << endl;
+            }
+            Transform3D tr (RotationZYX(ladder_dphi*(i+rot),0.,0.),Position(ladder_radius*cos(ladder_phi0+ladder_dphi*(i+rot)), ladder_radius*sin(ladder_phi0+ladder_dphi*(i+rot)), 0.));
+            pv = layer_assembly.placeVolume(LadderLogical,tr);
+            pv.addPhysVolID("module", i ) ;
+            ladderDE.setPlacement(pv);
+            std::cout << ladder_enum.str() << " done." << endl;
+            if(i==0) std::cout << "xy=" << ladder_radius*cos(ladder_phi0) << " " << ladder_radius*sin(ladder_phi0) << std::endl;
+        }
+
+        // package the reconstruction data
+        dd4hep::rec::ZPlanarData::LayerLayout otkbarrelLayer;
+
+        otkbarrelLayer.ladderNumber         = n_ladders;
+        otkbarrelLayer.phi0                 = ladder_phi0;
+        otkbarrelLayer.sensorsPerLadder     = n_sensors_per_side;
+        otkbarrelLayer.lengthSensor         = sensor_length;
+        otkbarrelLayer.distanceSupport      = sensitive_radius;
+        otkbarrelLayer.thicknessSupport     = support_thickness / 2.0;
+        otkbarrelLayer.offsetSupport        = -ladder_offset;
+        otkbarrelLayer.widthSupport         = support_width;
+        otkbarrelLayer.zHalfSupport         = support_length / 2.0;
+        otkbarrelLayer.distanceSensitive    = sensitive_radius + support_height / 2.0 + flex_thickness;
+        otkbarrelLayer.thicknessSensitive   = sensor_thickness;
+        otkbarrelLayer.offsetSensitive      = -ladder_offset + (support_width/2.0 - sensor_active_width/2.0);
+        otkbarrelLayer.widthSensitive       = sensor_active_width;
+        otkbarrelLayer.zHalfSensitive       = (n_sensors_per_side*(sensor_length + dead_gap) - dead_gap) / 2.0;
+
+        zPlanarData->layers.push_back(otkbarrelLayer);
+    }
+    std::cout << (*zPlanarData) << endl;
+    otkbarrel.addExtension< ZPlanarData >(zPlanarData);
+    if ( x_det.hasAttr(_U(combineHits)) ) {
+        otkbarrel.setCombineHits(x_det.attr<bool>(_U(combineHits)),sens);
+    }
+    std::cout << "otkbarrel done." << endl;
+    return otkbarrel;
+}
+DECLARE_DETELEMENT(SiTracker_otkbarrel_v01,create_element)
+
diff --git a/Detector/DetCRD/src/Tracker/TPC_ModularEndcap_o1_v01.cpp b/Detector/DetCRD/src/Tracker/TPC_ModularEndcap_o1_v01.cpp
index edd4bddb..49df905f 100644
--- a/Detector/DetCRD/src/Tracker/TPC_ModularEndcap_o1_v01.cpp
+++ b/Detector/DetCRD/src/Tracker/TPC_ModularEndcap_o1_v01.cpp
@@ -90,6 +90,7 @@ static Ref_t create_element(Detector& theDetector, xml_h e, SensitiveDetector se
     const double rOuter             = theDetector.constant<double>("TPC_outer_radius") ;
     const double drInnerWall        = theDetector.constant<double>("TPC_dr_InnerWall");
     const double drOuterWall        = theDetector.constant<double>("TPC_dr_OuterWall");
+    const double drInnerServiceArea = theDetector.constant<double>("TPC_dr_InnerServiceArea");
     const double dz_Cathode         = theDetector.constant<double>("TPC_dz_Cathode");
     const double dz_Endplate        = theDetector.constant<double>("TPC_dz_Endplate");
     const double dz_Readout         = theDetector.constant<double>("TPC_dz_Readout");
@@ -510,8 +511,8 @@ static Ref_t create_element(Detector& theDetector, xml_h e, SensitiveDetector se
     tpcData->rMax  = rOuter;
     tpcData->innerWallThickness = drInnerWall;
     tpcData->outerWallThickness = drOuterWall;
-    tpcData->rMinReadout = rInner + drInnerWall;
-    tpcData->rMaxReadout = rInner + drInnerWall + tpcnumberOfPadRows*tpcpadheight;
+    tpcData->rMinReadout = rInner + drInnerWall + drInnerServiceArea;
+    tpcData->rMaxReadout = rInner + drInnerWall + drInnerServiceArea + tpcnumberOfPadRows*tpcpadheight;
     tpcData->maxRow = tpcnumberOfPadRows;
     tpcData->padHeight = tpcpadheight;
     tpcData->padWidth =  tpcpadwidth;
diff --git a/Detector/DetCRD/src/Tracker/TPC_Simple_o1_v01.cpp b/Detector/DetCRD/src/Tracker/TPC_Simple_o1_v01.cpp
index a137e03d..a8bb12ef 100644
--- a/Detector/DetCRD/src/Tracker/TPC_Simple_o1_v01.cpp
+++ b/Detector/DetCRD/src/Tracker/TPC_Simple_o1_v01.cpp
@@ -90,6 +90,7 @@ static Ref_t create_element(Detector& theDetector, xml_h e, SensitiveDetector se
     const double rOuter             = theDetector.constant<double>("TPC_outer_radius") ;
     const double drInnerWall        = theDetector.constant<double>("TPC_dr_InnerWall");
     const double drOuterWall        = theDetector.constant<double>("TPC_dr_OuterWall");
+    const double drInnerServiceArea = theDetector.constant<double>("TPC_dr_InnerServiceArea");
     const double dz_Cathode         = theDetector.constant<double>("TPC_dz_Cathode");
     const double dz_Endplate        = theDetector.constant<double>("TPC_dz_Endplate");
     const double dz_Readout         = theDetector.constant<double>("TPC_dz_Readout");
@@ -454,8 +455,8 @@ static Ref_t create_element(Detector& theDetector, xml_h e, SensitiveDetector se
     tpcData->rMax  = rOuter;
     tpcData->innerWallThickness = drInnerWall;
     tpcData->outerWallThickness = drOuterWall;
-    tpcData->rMinReadout = rInner + drInnerWall;
-    tpcData->rMaxReadout = rInner + drInnerWall + tpcnumberOfPadRows*tpcpadheight;
+    tpcData->rMinReadout = rInner + drInnerWall + drInnerServiceArea;
+    tpcData->rMaxReadout = rInner + drInnerWall + drInnerServiceArea + tpcnumberOfPadRows*tpcpadheight;
     tpcData->maxRow = tpcnumberOfPadRows;
     tpcData->padHeight = tpcpadheight;
     tpcData->padWidth =  tpcpadwidth;
diff --git a/Digitisers/CMakeLists.txt b/Digitisers/CMakeLists.txt
index 6f6f2e40..27da8e2e 100644
--- a/Digitisers/CMakeLists.txt
+++ b/Digitisers/CMakeLists.txt
@@ -2,3 +2,4 @@ add_subdirectory(DCHDigi)
 add_subdirectory(G2CDArbor)
 add_subdirectory(SimHitMerge)
 add_subdirectory(SimpleDigi)
+add_subdirectory(CaloDigi)
diff --git a/Digitisers/CaloDigi/CMakeLists.txt b/Digitisers/CaloDigi/CMakeLists.txt
new file mode 100644
index 00000000..c3b4182c
--- /dev/null
+++ b/Digitisers/CaloDigi/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Modules
+gaudi_add_module(CaloDigi
+                 SOURCES src/EcalDigiAlg.cpp
+                 SOURCES src/HcalDigiAlg.cpp
+                 LINK k4FWCore::k4FWCore
+                      GearSvc
+                      DetInterface
+                      Gaudi::GaudiKernel
+                      Gaudi::GaudiAlgLib 
+                      ${CLHEP_LIBRARIES}
+                      ${GEAR_LIBRARIES} 
+                      ${GSL_LIBRARIES} 
+                      ${LCIO_LIBRARIES}
+                      ${ROOT_LIBRARIES}
+                      EDM4HEP::edm4hep EDM4HEP::edm4hepDict
+)
+install(TARGETS CaloDigi
+  EXPORT CEPCSWTargets
+  RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin
+  LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shlib
+  COMPONENT dev)
+
diff --git a/Digitisers/CaloDigi/src/CaloBar.h b/Digitisers/CaloDigi/src/CaloBar.h
new file mode 100644
index 00000000..57d226bb
--- /dev/null
+++ b/Digitisers/CaloDigi/src/CaloBar.h
@@ -0,0 +1,54 @@
+#ifndef _CALOBAR
+#define _CALOBAR
+
+#include <DD4hep/Objects.h>
+#include "TVector3.h"
+
+class CaloBar{
+
+public:
+  CaloBar(unsigned long long _cellID, int _system, int _module, int _slayer, int _dlayer, int _stave, int _bar, TVector3 _pos, double _Q1, double _Q2, double _T1, double _T2)
+  : cellID(_cellID), system(_system), module(_module), stave(_stave), dlayer(_dlayer), slayer(_slayer), bar(_bar), position(_pos), Q1(_Q1), Q2(_Q2), T1(_T1), T2(_T2) {}; 
+  CaloBar() {};
+
+  inline bool operator == (const CaloBar &x) const{
+    return ( (cellID == x.cellID) && getEnergy()==x.getEnergy() );
+  }
+  unsigned long long getcellID() const { return cellID; }
+  int getSystem() const { return system; }
+  int getModule() const { return module; }
+  int getStave()  const { return stave;  }
+  int getDlayer() const { return dlayer; }
+  int getSlayer() const { return slayer; }
+  int getBar()    const { return bar;    }
+  double getQ1()  const { return Q1;     }
+  double getQ2()  const { return Q2;     }
+  double getT1()  const { return T1;     }
+  double getT2()  const { return T2;     }
+
+  TVector3 getPosition() const { return position; }
+  double getEnergy() const { return (Q1+Q2)/2.; }
+
+  void setcellID(unsigned long long _cellid) { cellID = _cellid; }
+  void setcellID(int _system, int _module, int _stave, int _dlayer, int _slayer, int _bar) { system=_system; module=_module; stave=_stave; dlayer=_dlayer; slayer=_slayer; bar=_bar; }
+  void setPosition( TVector3 posv3) { position.SetXYZ( posv3.x(), posv3.y(), posv3.z() ); }
+  void setQ(double _q1, double _q2) { Q1=_q1; Q2=_q2; }
+  void setT(double _t1, double _t2) { T1=_t1; T2=_t2; }
+
+private:
+	unsigned long long cellID;
+	int system;
+	int module;
+	int stave;
+	int dlayer;
+	int slayer;
+	int bar;
+	TVector3 position;
+	double Q1;      // Q in left readout
+	double Q2;      // Q in right readout;
+	double T1;    // T in left readout;
+	double T2;    // T in right readout;
+
+};
+  
+#endif
diff --git a/Digitisers/CaloDigi/src/CaloHit.h b/Digitisers/CaloDigi/src/CaloHit.h
new file mode 100644
index 00000000..2c6017de
--- /dev/null
+++ b/Digitisers/CaloDigi/src/CaloHit.h
@@ -0,0 +1,50 @@
+#ifndef _CALOHIT
+#define _CALOHIT
+
+#include <DD4hep/Objects.h>
+#include "TVector3.h"
+
+class CaloHit{
+
+public:
+  CaloHit(unsigned long long _cellID, int _system, int _module, int _layer, int _stave, int _tower, int _slice, TVector3 _pos, double _E, int _step_numbers)
+  : cellID(_cellID), system(_system), module(_module), layer(_layer), stave(_stave), tower(_tower), slice(_slice), position(_pos), E(_E), step_numbers(_step_numbers) {}; 
+  CaloHit() {};
+
+  inline bool operator == (const CaloHit &x) const{
+    return ( (cellID == x.cellID) && getEnergy()==x.getEnergy() );
+  }
+  unsigned long long getcellID() const { return cellID; }
+  int getSystem() const { return system; }
+  int getModule() const { return module; }
+  int getStave()  const { return stave;  }
+  int getLayer()  const { return layer;  }
+  int getTower()  const { return tower;  }
+  int getSlice()  const { return slice;  }
+
+  TVector3 getPosition() const { return position; }
+  double getEnergy() const { return E; }
+  int getStep_numbers() const { return step_numbers; }
+
+  void setcellID(unsigned long long _cellid) { cellID = _cellid; }
+  void setcellID(int _system, int _module, int _layer, int _stave, int _tower, int _slice) { system=_system; module=_module; stave=_stave; layer=_layer; tower=_tower; slice=_slice;}
+  void setPosition( TVector3 posv3) { position.SetXYZ( posv3.x(), posv3.y(), posv3.z() ); }
+  void setE(double _E) { E=_E; }
+  void setStep_numbers(int _step_numbers) { step_numbers=_step_numbers; }
+
+
+private:
+	unsigned long long cellID;
+	int system;
+	int module;
+	int stave;
+	int layer;
+	int tower;
+	int slice;
+	TVector3 position;
+	double E;
+  int step_numbers;
+
+};
+  
+#endif
diff --git a/Digitisers/CaloDigi/src/EcalDigiAlg.cpp b/Digitisers/CaloDigi/src/EcalDigiAlg.cpp
new file mode 100644
index 00000000..bba34045
--- /dev/null
+++ b/Digitisers/CaloDigi/src/EcalDigiAlg.cpp
@@ -0,0 +1,625 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// Unit in code: mm, ns. 
+// NOTE: This digitialization highly matches detector geometry CRDEcalBarrel_v01/LongCrystalBarBarrelCalorimeter32Polygon_v01.
+// TODO: read geometry info automatically.  
+#include "EcalDigiAlg.h"
+
+#include "edm4hep/SimCalorimeterHit.h"
+#include "edm4hep/CalorimeterHit.h"
+#include "edm4hep/Vector3f.h"
+#include "edm4hep/Cluster.h"
+
+#include "DD4hep/Detector.h"
+#include <DD4hep/Objects.h>
+#include <DDRec/CellIDPositionConverter.h>
+
+#include "TVector3.h"
+#include "TRandom3.h"
+#include <math.h>
+#include <cmath>
+#include <iostream>
+#include <algorithm>
+#include <map>
+#include <random>
+
+// #include <fstream>
+// #include <ctime>
+
+#define C 299.79  // unit: mm/ns
+#define PI 3.141592653
+using namespace std;
+using namespace dd4hep;
+using dd4hep::rec::LayeredCalorimeterData;
+using dd4hep::rec::LayeredCalorimeterStruct;
+
+DECLARE_COMPONENT( EcalDigiAlg )
+
+EcalDigiAlg::EcalDigiAlg(const std::string& name, ISvcLocator* svcLoc)
+  : GaudiAlgorithm(name, svcLoc),
+    _nEvt(0)
+{
+  
+	// Input collections
+	declareProperty("SimCaloHitCollection", r_SimCaloCol, "Handle of the Input SimCaloHit collection");
+  
+	// Output collections
+	declareProperty("CaloHitCollection", w_DigiCaloCol, "Handle of Digi CaloHit collection");
+	declareProperty("CaloAssociationCollection", w_CaloAssociationCol, "Handle of CaloAssociation collection");
+	declareProperty("CaloMCPAssociationCollection", w_MCPCaloAssociationCol, "Handle of CaloAssociation collection");
+   
+}
+
+StatusCode EcalDigiAlg::initialize()
+{
+	if(_writeNtuple){
+		std::string s_outfile = _filename;
+		m_wfile = new TFile(s_outfile.c_str(), "recreate");
+		t_SimCont = new TTree("SimStep", "SimStep");
+		t_SimBar = new TTree("SimBarHit", "SimBarHit");
+		t_SimCont->Branch("step_x", &m_step_x);
+		t_SimCont->Branch("step_y", &m_step_y);
+		t_SimCont->Branch("step_z", &m_step_z);
+		t_SimCont->Branch("step_t", &m_step_t);			// yyy: time of each step
+		t_SimCont->Branch("stepBar_x", &m_stepBar_x);
+		t_SimCont->Branch("stepBar_y", &m_stepBar_y);
+		t_SimCont->Branch("stepBar_z", &m_stepBar_z);
+		t_SimCont->Branch("step_E", &m_step_E);
+		t_SimCont->Branch("step_T1", &m_step_T1);
+		t_SimCont->Branch("step_T2", &m_step_T2);
+		t_SimBar->Branch("totE_Truth", &totE_Truth);
+		t_SimBar->Branch("totE_Digi", &totE_Digi);
+		t_SimBar->Branch("simBar_x", &m_simBar_x);
+		t_SimBar->Branch("simBar_y", &m_simBar_y);
+		t_SimBar->Branch("simBar_z", &m_simBar_z);
+		t_SimBar->Branch("simBar_T1", &m_simBar_T1);
+		t_SimBar->Branch("simBar_T2", &m_simBar_T2);
+		t_SimBar->Branch("simBar_Q1_Truth", &m_simBar_Q1_Truth);
+		t_SimBar->Branch("simBar_Q2_Truth", &m_simBar_Q2_Truth);
+		t_SimBar->Branch("simBar_Q1_Digi", &m_simBar_Q1_Digi);
+		t_SimBar->Branch("simBar_Q2_Digi", &m_simBar_Q2_Digi);
+		t_SimBar->Branch("simBar_module", &m_simBar_module);
+		t_SimBar->Branch("simBar_stave", &m_simBar_stave);
+		t_SimBar->Branch("simBar_dlayer", &m_simBar_dlayer);
+		t_SimBar->Branch("simBar_slayer", &m_simBar_slayer);
+		t_SimBar->Branch("simBar_cellID", &m_simBar_cellID);
+	}
+
+	std::cout<<"EcalDigiAlg::m_scale="<<m_scale<<std::endl;
+	m_geosvc = service<IGeomSvc>("GeomSvc");
+	if ( !m_geosvc )  throw "EcalDigiAlg :Failed to find GeomSvc ...";
+	m_dd4hep = m_geosvc->lcdd();
+	if ( !m_dd4hep )  throw "EcalDigiAlg :Failed to get dd4hep::Detector ...";
+	m_cellIDConverter = new dd4hep::rec::CellIDPositionConverter(*m_dd4hep);
+   	m_decoder = m_geosvc->getDecoder(_readoutName);
+	if (!m_decoder) {
+		error() << "Failed to get the decoder. " << endmsg;
+		return StatusCode::FAILURE;
+	}
+
+	rndm.SetSeed(_seed);
+	std::cout<<"EcalDigiAlg::initialize"<<std::endl;
+	return GaudiAlgorithm::initialize();
+}
+
+StatusCode EcalDigiAlg::execute()
+{
+	// clock_t yyy_start, yyy_enddigi;
+	// yyy_start = clock(); // 记录开始时间
+
+	if(_nEvt==0) std::cout<<"EcalDigiAlg::execute Start"<<std::endl;
+	std::cout<<"Processing event: "<<_nEvt<<std::endl;
+   	if(_nEvt<_Nskip){ _nEvt++; return StatusCode::SUCCESS; }
+
+	Clear();
+
+ 	const edm4hep::SimCalorimeterHitCollection* SimHitCol =  r_SimCaloCol.get();
+	edm4hep::CalorimeterHitCollection* caloVec = w_DigiCaloCol.createAndPut();
+	edm4hep::MCRecoCaloAssociationCollection* caloAssoVec = w_CaloAssociationCol.createAndPut();
+  	edm4hep::MCRecoCaloParticleAssociationCollection* caloMCPAssoVec = w_MCPCaloAssociationCol.createAndPut();
+ 	std::vector<edm4hep::SimCalorimeterHit> m_simhitCol; m_simhitCol.clear();
+  	std::vector<CaloBar> m_barCol; m_barCol.clear(); 
+
+	if(SimHitCol == 0){
+		std::cout<<"not found SimCalorimeterHitCollection"<< std::endl;
+		return StatusCode::SUCCESS;
+	}
+
+  	if(_Debug>=1) std::cout<<"digi, input sim hit size="<< SimHitCol->size() <<std::endl;
+
+	totE_Truth=0;
+	totE_Digi=0;
+
+	//Merge input simhit(steps) to real simhit(bar).
+	MergeHits(*SimHitCol, m_simhitCol);
+	if(_Debug>=1) std::cout<<"Finish Hit Merge, with Nhit: "<<m_simhitCol.size()<<std::endl;
+
+	// DetElement beamcal = m_dd4hep->detector("CaloDetector");
+	// LayeredCalorimeterData* bcExtension = beamcal.extension<LayeredCalorimeterData>();
+	// std::vector<LayeredCalorimeterStruct::Layer> layers = bcExtension->layers;
+
+	//Loop in SimHit, digitalize SimHit to DigiBar
+	for(int i=0;i<m_simhitCol.size();i++){
+
+		auto SimHit = m_simhitCol.at(i);
+
+		unsigned long long id = SimHit.getCellID();
+		CaloBar hitbar;
+		hitbar.setcellID( id);
+		hitbar.setcellID(	m_decoder->get(id, "system"), 
+											m_decoder->get(id, "module"), 
+											m_decoder->get(id, "stave"), 
+											m_decoder->get(id, "dlayer"), 
+											m_decoder->get(id, "slayer"),
+											m_decoder->get(id, "bar"));
+
+		double Lbar = GetBarLength(hitbar);  //NOTE: Is fixed with geometry LongCrystalBarBarrelCalorimeter32Polygon_v01.
+
+		dd4hep::Position hitpos = m_cellIDConverter->position(id);
+    TVector3 barpos(10*hitpos.x(), 10*hitpos.y(), 10*hitpos.z()); //cm to mm.
+		hitbar.setPosition(barpos);
+
+    //printf("in bar #%d: cellID [%d, %d, %d, %d], position (%.3f, %.3f, %.3f), energy %.3f \n", hitbar.getModule(), hitbar.getStave(), hitbar.getDlayer(), hitbar.getSlayer(), hitbar.getBar(), 
+    //       10*hitpos.x(), 10*hitpos.y(), 10*hitpos.z(), SimHit.getEnergy() );
+
+    MCParticleToEnergyWeightMap MCPEnMap; MCPEnMap.clear();
+		std::vector<HitStep> DigiLvec; DigiLvec.clear();
+		std::vector<HitStep> DigiRvec; DigiRvec.clear();
+		double totQ1_Truth = 0;
+		double totQ2_Truth = 0;
+		double totQ1_Digi = 0;
+		double totQ2_Digi = 0;
+		double totQ1 = 0;
+		double totQ2 = 0;
+
+		//Loop in all SimHitContribution(G4Step). 
+		//if(_Debug>=2) std::cout<<"SimHit contribution size: "<<SimHit.contributions_size()<<std::endl;
+		for(int iCont=0; iCont < SimHit.contributions_size(); ++iCont){
+			auto conb = SimHit.getContributions(iCont);
+			if( !conb.isAvailable() ) { std::cout<<"EcalDigiAlg  Can not get SimHitContribution: "<<iCont<<std::endl; continue;}
+
+			double en = conb.getEnergy();
+			if(en == 0) continue;
+
+			auto mcp = conb.getParticle();
+			MCPEnMap[mcp] += en;
+			TVector3 steppos(conb.getStepPosition().x, conb.getStepPosition().y, conb.getStepPosition().z);
+			TVector3 rpos = steppos-hitbar.getPosition();
+			float step_time = conb.getTime();		// yyy: step time
+
+			m_step_x.push_back(steppos.x());
+			m_step_y.push_back(steppos.y());
+			m_step_z.push_back(steppos.z());
+			m_step_t.push_back(step_time);			// yyy: push back step time
+			m_step_E.push_back(en);
+			m_stepBar_x.push_back(hitbar.getPosition().x());
+			m_stepBar_y.push_back(hitbar.getPosition().y());
+			m_stepBar_z.push_back(hitbar.getPosition().z());
+
+			if(_Debug>=3){
+				cout<<"Cell Pos: "<<hitbar.getPosition().x()<<'\t'<<hitbar.getPosition().y()<<'\t'<<hitbar.getPosition().z()<<endl;
+				cout<<"step pos: "<<steppos.x()<<'\t'<<steppos.y()<<'\t'<<steppos.z()<<endl;
+				cout<<"Relative pos: "<<rpos.x()<<'\t'<<rpos.y()<<'\t'<<rpos.z()<<endl;
+				cout<<"Cell: "<<hitbar.getModule()<<"  "<<hitbar.getDlayer()<<"  "<<hitbar.getSlayer()<<endl;
+			}
+
+			//Get digitalized signal(Q1, Q2, T1, T2) from step
+			//Define: 1 is left, 2 is right, clockwise direction in phi. 
+
+			int sign=-999;
+			if(hitbar.getSlayer()==1) sign = rpos.z()==0 ? 1 : rpos.z()/fabs(rpos.z());
+			else{
+        int _module = hitbar.getModule(); 
+        if(_module<=7 || _module>=25) sign = rpos.x()==0 ?  1: rpos.x()/fabs(rpos.x());
+        if(_module>=9 && _module<=23) sign = rpos.x()==0 ? -1:-rpos.x()/fabs(rpos.x());
+				else if(_module==8)  sign = rpos.y()==0 ?  1: rpos.y()/fabs(rpos.y());
+				else if(_module==24) sign = rpos.y()==0 ? -1:-rpos.y()/fabs(rpos.y());
+
+			}
+			if(!fabs(sign)) {std::cout<<"ERROR: Wrong bar direction/position!"<<std::endl; continue;}
+			
+			// ####### For Charge Digitization #######
+			double Ratio_left = exp(-(Lbar/2 + sign*sqrt(rpos.Mag2()))/Latt) / (exp(-(Lbar/2 + sign*sqrt(rpos.Mag2()))/Latt) + exp(-(Lbar/2 - sign*sqrt(rpos.Mag2()))/Latt));
+			double Ratio_right = 1 - Ratio_left;
+			totQ1_Truth += en*Ratio_left;
+			totQ2_Truth += en*Ratio_right;
+
+			// ####### For Time Digitization #######
+			double Qi_left = en*exp(-(Lbar/2 + sign*sqrt(rpos.Mag2()))/Latt);	
+			double Qi_right = en*exp(-(Lbar/2 - sign*sqrt(rpos.Mag2()))/Latt);
+
+			if(_Debug>=3){
+				cout<<Qi_left<<'\t'<<Qi_right<<endl;
+				cout<<Lbar<<'\t'<<sign*sqrt(rpos.Mag2())<<endl;
+			}
+
+			double Ti_left = -1; int looptime=0;
+			while(Ti_left<0){ 
+				// Ti_left = Tinit + rndm.Gaus(nMat*(Lbar/2 + sign*sqrt(rpos.Mag2()))/C, Tres); 
+				Ti_left = Tinit + rndm.Gaus(nMat*(Lbar/2 + sign*sqrt(rpos.Mag2()))/C, Tres) + step_time;  // yyy: add step time 
+				looptime++;
+				if(looptime>500){ std::cout<<"ERROR: Step "<<iCont<<" can not get a positive left-side time!"<<std::endl; break;}
+			}
+			if(looptime>500) continue;		
+			double Ti_right = -1; looptime=0;
+			while(Ti_right<0){ 
+				// Ti_right = Tinit + rndm.Gaus(nMat*(Lbar/2 - sign*sqrt(rpos.Mag2()))/C, Tres); 
+				Ti_right = Tinit + rndm.Gaus(nMat*(Lbar/2 - sign*sqrt(rpos.Mag2()))/C, Tres) + step_time;  // yyy: add step time 
+				looptime++;
+        if(looptime>500){ 
+          std::cout<<"ERROR: Step "<<iCont<<" can not get a positive right-side time!"<<std::endl; 
+          std::cout<<"  Initial time "<<Tinit<<", transport time central value "<<nMat*(Lbar/2 - sign*sqrt(rpos.Mag2()))/C<<std::endl;
+          break;
+        }
+			}
+			if(looptime>500) continue;		
+
+			m_step_T1.push_back(Ti_left);
+			m_step_T2.push_back(Ti_right);
+			totQ1 += Qi_left;
+			totQ2 += Qi_right;
+
+			HitStep stepoutL, stepoutR;
+			stepoutL.setQ(Qi_left); stepoutL.setT(Ti_left);
+			stepoutR.setQ(Qi_right); stepoutR.setT(Ti_right);
+			DigiLvec.push_back(stepoutL);
+			DigiRvec.push_back(stepoutR);
+		}
+
+		// #######################################
+		// ####### Ideal Time Digitization #######
+		// #######################################
+
+		//if(_Debug>=2) std::cout<<"Time Digitalize: time at Q >"<<_Qthfrac<<"*totQ"<<std::endl;
+		std::sort(DigiLvec.begin(), DigiLvec.end());
+		std::sort(DigiRvec.begin(), DigiRvec.end());
+		double thQ1=0;
+		double thQ2=0;
+		double thT1, thT2; 
+		for(int iCont=0;iCont<DigiLvec.size();iCont++){
+			thQ1 += DigiLvec[iCont].getQ();
+			if(thQ1>totQ1*_Qthfrac){ 
+				thT1 = DigiLvec[iCont].getT(); 
+				if(_Debug>=3) std::cout<<"Get T1 at index: "<<iCont<<std::endl;
+				break;
+			}
+		}
+		for(int iCont=0;iCont<DigiRvec.size();iCont++){
+			thQ2 += DigiRvec[iCont].getQ();
+			if(thQ2>totQ2*_Qthfrac){ 
+				thT2 = DigiRvec[iCont].getT(); 
+				if(_Debug>=3) std::cout<<"Get T2 at index: "<<iCont<<std::endl;
+				break;
+			}
+		}
+
+    if(_UseRelDigi){		
+		// #############################################
+		// ####### Realistic Charge Digitization #######
+		// #############################################
+
+		// double sEcalCryMipLY = gRandom->Gaus(fEcalCryMipLY, 0.1 * fEcalCryMipLY);
+		double sEcalCryMipLY = fEcalCryMipLY;
+
+    //TODO: fEcalMIPEnergy should depends on crystal size. 
+		int ScinGen = std::round(gRandom->Poisson((totQ1_Truth+totQ2_Truth)*1000 / fEcalMIPEnergy * sEcalCryMipLY));
+
+		totQ1_Digi = EnergyDigi(ScinGen*totQ1_Truth/(totQ1_Truth+totQ2_Truth), sEcalCryMipLY)/1000;
+		totQ2_Digi = EnergyDigi(ScinGen*totQ2_Truth/(totQ1_Truth+totQ2_Truth), sEcalCryMipLY)/1000;
+    }
+    else{
+      double totQ1_Digi_tmp = totQ1_Truth;
+      double totQ2_Digi_tmp = totQ2_Truth;
+      if( (totQ1_Digi_tmp+totQ2_Digi_tmp)!=0 ){
+        totQ1_Digi = totQ1_Digi_tmp/(totQ1_Digi_tmp+totQ2_Digi_tmp);
+        totQ2_Digi = totQ2_Digi_tmp/(totQ1_Digi_tmp+totQ2_Digi_tmp);
+      }
+      if( totQ1_Digi/fEcalMIPEnergy < fEcalMIP_Thre ) totQ1_Digi = 0;
+      if( totQ2_Digi/fEcalMIPEnergy < fEcalMIP_Thre ) totQ2_Digi = 0;
+    }
+    if(totQ1_Digi==0 && totQ2_Digi==0) continue;
+
+		hitbar.setQ(totQ1_Digi, totQ2_Digi);
+		hitbar.setT(thT1, thT2);
+
+		// ##################################
+		// ####### Some associations  #######
+		// ##################################
+
+		//2 hits with double-readout time. 
+		edm4hep::Vector3f m_pos(hitbar.getPosition().X(), hitbar.getPosition().Y(), hitbar.getPosition().Z());
+		auto digiHit1 = caloVec->create();
+		digiHit1.setCellID(hitbar.getcellID());
+		digiHit1.setEnergy(hitbar.getQ1());
+		digiHit1.setTime(hitbar.getT1());
+		digiHit1.setPosition(m_pos);
+		auto digiHit2 = caloVec->create();
+		digiHit2.setCellID(hitbar.getcellID());
+		digiHit2.setEnergy(hitbar.getQ2());
+		digiHit2.setTime(hitbar.getT2());
+		digiHit2.setPosition(m_pos);
+
+		//SimHit - CaloHit association
+		auto rel1 = caloAssoVec->create();
+		rel1.setRec(digiHit1);
+		rel1.setSim(SimHit);
+		rel1.setWeight( hitbar.getQ1()/(hitbar.getQ1()+hitbar.getQ2()) );
+		auto rel2 = caloAssoVec->create();
+		rel2.setRec(digiHit2);
+		rel2.setSim(SimHit);
+		rel2.setWeight( hitbar.getQ2()/(hitbar.getQ1()+hitbar.getQ2()) );
+
+		//MCParticle - CaloHit association
+		//float maxMCE = -99.;
+		//edm4hep::MCParticle selMCP; 
+		for(auto iter : MCPEnMap){
+		//if(iter.second>maxMCE){
+		//  maxMCE = iter.second;
+		//  selMCP = iter.first;
+		//}
+		auto rel_MCP1 = caloMCPAssoVec->create();
+		rel_MCP1.setRec(digiHit1);
+		rel_MCP1.setSim(iter.first);
+		rel_MCP1.setWeight(iter.second/SimHit.getEnergy());
+		auto rel_MCP2 = caloMCPAssoVec->create();
+		rel_MCP2.setRec(digiHit2);
+		rel_MCP2.setSim(iter.first);
+		rel_MCP2.setWeight(iter.second/SimHit.getEnergy());      
+		}
+
+		//if(selMCP.isAvailable()){
+		//  auto rel_MCP1 = caloMCPAssoVec->create();
+		//  rel_MCP1.setRec(digiHit1);
+		//  rel_MCP1.setSim(selMCP);
+		//  rel_MCP1.setWeight(1.);
+		//  auto rel_MCP2 = caloMCPAssoVec->create();
+		//  rel_MCP2.setRec(digiHit2);
+		//  rel_MCP2.setSim(selMCP);
+		//  rel_MCP2.setWeight(1.);
+		//}
+
+		// ########################################
+		// ####### Temp: write into trees.  #######
+		// ########################################
+		
+    	m_barCol.push_back(hitbar);
+		if(hitbar.getQ1()>0 && hitbar.getQ2()>0) totE_Digi+=(hitbar.getQ1()+hitbar.getQ2());
+		if(totQ1_Truth>(fEcalMIPEnergy*fEcalMIP_Thre/1000.) && totQ2_Truth>(fEcalMIPEnergy*fEcalMIP_Thre/1000.)){
+			// cout<<"Truth Energy:"<<totQ1_Truth<<"   "<<totQ2_Truth<<endl;
+			totE_Truth+=(totQ1_Truth+totQ2_Truth);
+		}
+
+		if(_writeNtuple){
+			m_simBar_x.push_back(hitbar.getPosition().x());
+			m_simBar_y.push_back(hitbar.getPosition().y());
+			m_simBar_z.push_back(hitbar.getPosition().z());
+			m_simBar_Q1_Truth.push_back(totQ1_Truth);
+			m_simBar_Q2_Truth.push_back(totQ2_Truth);
+			m_simBar_Q1_Digi.push_back(hitbar.getQ1());
+			m_simBar_Q2_Digi.push_back(hitbar.getQ2());
+			m_simBar_T1.push_back(hitbar.getT1());
+      m_simBar_T2.push_back(hitbar.getT2());
+			m_simBar_module.push_back(hitbar.getModule());
+			m_simBar_stave.push_back(hitbar.getStave());
+			m_simBar_dlayer.push_back(hitbar.getDlayer());
+			m_simBar_slayer.push_back(hitbar.getSlayer());
+			m_simBar_cellID.push_back(hitbar.getcellID());
+		}
+	}
+
+	if(_writeNtuple){
+		t_SimCont->Fill();
+		t_SimBar->Fill();
+	}
+
+	if(_Debug>=1) std::cout<<"End Loop: Bar Digitalization!"<<std::endl;
+	std::cout<<"Total Truth Energy: "<<totE_Truth<<std::endl;
+	std::cout<<"Total Digi Energy: "<<totE_Digi<<std::endl;
+
+	// yyy_enddigi = clock();
+	// double duration_digi = double(yyy_enddigi - yyy_start) / CLOCKS_PER_SEC;
+	// // 将时间输出到txt文件中
+	// std::ofstream outfile("runtime_ecaldigi.txt", std::ios::app);
+	// outfile << _nEvt << "    " << duration_digi << std::endl;
+	// outfile.close();
+
+  	_nEvt ++ ;
+  	//delete SimHitCol, caloVec, caloAssoVec; 
+  	m_simhitCol.clear();
+	return StatusCode::SUCCESS;
+}
+
+StatusCode EcalDigiAlg::finalize()
+{
+	if(_writeNtuple){
+  		m_wfile->cd();
+	  	t_SimCont->Write();
+  		t_SimBar->Write();
+	  	m_wfile->Close();
+    	delete m_wfile, t_SimCont, t_SimBar; 
+  	}
+	info() << "Processed " << _nEvt << " events " << endmsg;
+	delete m_cellIDConverter, m_decoder, m_geosvc;
+	return GaudiAlgorithm::finalize();
+}
+
+double EcalDigiAlg::EnergyDigi(double ScinGen, double sEcalCryMipLY){
+    Int_t sPix = int(ScinGen);
+
+	// if(sPix/sEcalCryMipLY < fEcalMIP_Thre) return 0;
+	// return sPix/sEcalCryMipLY*fEcalMIPEnergy;
+
+	// return sPix/sEcalCryMipLY*fEcalMIPEnergy;
+
+	// ####### SiPM Saturation  #######
+    // sPix = std::round(fEcalSiPMPixels * (1 - TMath::Exp(-sPix / fEcalSiPMPixels)));
+	
+	// ################################
+	// ####### ADC Digitization #######
+	// ################################
+
+    Bool_t Use_G1 = kFALSE;
+    Bool_t Use_G2 = kFALSE;
+    Bool_t Use_G3 = kFALSE;
+
+    Double_t sADCMean = sPix * fEcalChargeADCMean;
+    Double_t sADCSigma = std::sqrt(sPix * fEcalChargeADCSigma * fEcalChargeADCSigma + fEcalNoiseADCSigma * fEcalNoiseADCSigma);
+    Int_t sADC = -1;
+    sADC = std::round(gRandom->Gaus(sADCMean, sADCSigma));
+
+    if(sADC <= fADCSwitch){
+        Use_G1 = kTRUE;
+        sADC = std::round(gRandom->Gaus(sADC, fEcalADCError * sADC));
+        Double_t sMIP = sADC / fEcalChargeADCMean / sEcalCryMipLY;
+        if(sMIP < fEcalMIP_Thre) return 0;
+        return sMIP * fEcalMIPEnergy;
+    }
+    else if(sADC > fADCSwitch && int(sADC/fGainRatio_12) <= fADCSwitch){
+        Use_G2 = kTRUE;
+        sADCMean = sPix * fEcalChargeADCMean / fGainRatio_12;
+        sADCSigma = std::sqrt(sPix * fEcalChargeADCSigma / fGainRatio_12 * fEcalChargeADCSigma / fGainRatio_12 + fEcalNoiseADCSigma * fEcalNoiseADCSigma);
+        sADC = std::round(gRandom->Gaus(sADCMean, sADCSigma));
+        sADC = std::round(gRandom->Gaus(sADC, fEcalADCError * sADC));
+        Double_t sMIP = sADC / fEcalChargeADCMean * fGainRatio_12 / sEcalCryMipLY;
+        if(sMIP < fEcalMIP_Thre) return 0;
+        return sMIP * fEcalMIPEnergy;
+    }
+    else if(int(sADC/fGainRatio_12) > fADCSwitch){
+        Use_G3 = kTRUE;
+        sADCMean = sPix * fEcalChargeADCMean / fGainRatio_12 / fGainRatio_23;
+        sADCSigma = std::sqrt(sPix * fEcalChargeADCSigma / fGainRatio_12 / fGainRatio_23 * fEcalChargeADCSigma / fGainRatio_12 / fGainRatio_23 + fEcalNoiseADCSigma * fEcalNoiseADCSigma);
+        sADC = std::round(gRandom->Gaus(sADCMean, sADCSigma));
+        sADC = std::round(gRandom->Gaus(sADC, fEcalADCError * sADC));
+		if (sADC > fADC-1) sADC = fADC-1;
+        Double_t sMIP = sADC / fEcalChargeADCMean * fGainRatio_12 * fGainRatio_23 / sEcalCryMipLY;
+		if(sMIP < fEcalMIP_Thre) return 0;
+        return sMIP * fEcalMIPEnergy;
+    }
+}
+
+StatusCode EcalDigiAlg::MergeHits( const edm4hep::SimCalorimeterHitCollection& m_col, std::vector<edm4hep::SimCalorimeterHit>& m_hits ){
+
+  m_hits.clear(); 
+	std::vector<edm4hep::MutableSimCalorimeterHit> m_mergedhit;
+	m_mergedhit.clear();
+
+	for(int iter=0; iter<m_col.size(); iter++){
+		edm4hep::SimCalorimeterHit m_step = m_col[iter];
+		if(!m_step.isAvailable()){ cout<<"ERROR HIT!"<<endl; continue;}
+		if(m_step.getEnergy()==0 || m_step.contributions_size()<1) continue;
+		unsigned long long cellid = m_step.getCellID();
+		dd4hep::Position hitpos = m_cellIDConverter->position(cellid);
+		edm4hep::Vector3f pos(hitpos.x()*10, hitpos.y()*10, hitpos.z()*10);
+
+		edm4hep::MutableCaloHitContribution conb;
+		conb.setEnergy(m_step.getEnergy());
+		conb.setStepPosition(m_step.getPosition());
+    conb.setParticle( m_step.getContributions(0).getParticle() );
+		conb.setTime(m_step.getContributions(0).getTime());
+
+		edm4hep::MutableSimCalorimeterHit m_hit = find(m_mergedhit, cellid);
+		if(m_hit.getCellID()==0){
+			m_hit.setCellID(cellid);
+			m_hit.setPosition(pos);
+			m_mergedhit.push_back(m_hit);
+		}
+		m_hit.addToContributions(conb);
+		m_hit.setEnergy(m_hit.getEnergy()+m_step.getEnergy());
+	}
+
+  for(auto iter = m_mergedhit.begin(); iter!=m_mergedhit.end(); iter++){
+    edm4hep::SimCalorimeterHit constsimhit = *iter; 
+    m_hits.push_back( constsimhit );  
+  }
+  return StatusCode::SUCCESS; 
+}
+
+double EcalDigiAlg::GetBarLength(CaloBar& bar){
+    //TODO: reading bar length from geosvc. 
+    if(bar.getSlayer()==1) return 375.133;
+    else{
+        if(bar.getModule()%2 == 0){
+            return 295.905 + (bar.getDlayer()-1)* 6.13231;
+        }
+        else{
+            return 416.843 - (bar.getDlayer()+1)* 2.25221;
+        }
+        
+    }
+}
+
+
+/*
+dd4hep::Position EcalDigiAlg::GetCellPos(dd4hep::Position& pos, CaloBar& bar){
+	dd4hep::Position rpos = pos-bar.getPosition();
+	TVector3 vec(0,0,0); 
+	if(bar.getSlayer()==1) vec.SetXYZ(0, 0, floor(rpos.z()/10)*10+5 );
+	else if(bar.getSlayer()==0){
+		if((bar.getModule()==0||bar.getModule()==4) && bar.getDlayer()%2==1) vec.SetXYZ(floor(rpos.x()/10)*10+5,0,0);
+		if((bar.getModule()==0||bar.getModule()==4) && bar.getDlayer()%2==0) vec.SetXYZ(floor((rpos.x()-5)/10)*10+10,0,0);
+		if((bar.getModule()==2||bar.getModule()==6) && bar.getDlayer()%2==1) vec.SetXYZ(0, floor(rpos.y()/10)*10+5,0);
+		if((bar.getModule()==2||bar.getModule()==6) && bar.getDlayer()%2==0) vec.SetXYZ(0, floor((rpos.y()-5)/10)*10+10,0);
+		if(bar.getModule()==1 || bar.getModule()==5){
+			TVector3 unitv(1./sqrt(2), -1./sqrt(2), 0);
+			if(bar.getDlayer()%2==1) vec = (floor(rpos.Dot(unitv)/10)*10+5)*unitv;
+			if(bar.getDlayer()%2==0) vec = (floor((rpos.Dot(unitv)-5)/10)*10+10)*unitv;
+		}
+		if(bar.getModule()==3 || bar.getModule()==7){
+			TVector3 unitv(1./sqrt(2), 1./sqrt(2), 0);
+			if(bar.getDlayer()%2==1) vec = (floor(rpos.Dot(unitv)/10)*10+5)*unitv;
+			if(bar.getDlayer()%2==0) vec = (floor((rpos.Dot(unitv)-5)/10)*10+10)*unitv;
+		}
+	}
+	dd4hep::Position relv(vec.x(), vec.y(), vec.z());
+	return relv+bar.getPosition();
+}
+
+
+edm4hep::MutableSimCalorimeterHit EcalDigiAlg::find(edm4hep::SimCalorimeterHitCollection& m_col, dd4hep::Position& pos){
+   for(int i=0;i<m_col.size();i++){
+    edm4hep::MutableSimCalorimeterHit hit = m_col[i];
+		dd4hep::Position ipos(hit.getPosition().x, hit.getPosition().y, hit.getPosition().z);
+		if(ipos==pos) return hit;
+	}
+   edm4hep::MutableSimCalorimeterHit hit;
+   hit.setCellID(0);
+   return hit;
+}
+*/
+edm4hep::MutableSimCalorimeterHit EcalDigiAlg::find(const std::vector<edm4hep::MutableSimCalorimeterHit>& m_col, unsigned long long& cellid) const{
+  for(int i=0;i<m_col.size();i++){
+		edm4hep::MutableSimCalorimeterHit hit=m_col.at(i);
+		if(hit.getCellID() == cellid) return hit;
+	}
+	edm4hep::MutableSimCalorimeterHit hit ;
+	hit.setCellID(0);
+	return hit;
+}
+
+void EcalDigiAlg::Clear(){
+  	totE_Truth = -99;
+	totE_Digi = -99;
+	m_step_x.clear();
+	m_step_y.clear();
+	m_step_z.clear();
+	m_step_t.clear();   // yyy: clear
+	m_step_E.clear();
+	m_stepBar_x.clear();
+	m_stepBar_y.clear();
+	m_stepBar_z.clear();
+	m_step_T1.clear();
+	m_step_T2.clear();
+	m_simBar_x.clear();
+	m_simBar_y.clear();
+	m_simBar_z.clear();
+	m_simBar_T1.clear();
+	m_simBar_T2.clear();
+	m_simBar_Q1_Truth.clear();
+	m_simBar_Q2_Truth.clear();
+	m_simBar_Q1_Digi.clear();
+	m_simBar_Q2_Digi.clear();
+	m_simBar_module.clear();
+	m_simBar_stave.clear();
+	m_simBar_dlayer.clear();
+	m_simBar_slayer.clear();
+  	m_simBar_cellID.clear();
+}
diff --git a/Digitisers/CaloDigi/src/EcalDigiAlg.h b/Digitisers/CaloDigi/src/EcalDigiAlg.h
new file mode 100644
index 00000000..058a06bd
--- /dev/null
+++ b/Digitisers/CaloDigi/src/EcalDigiAlg.h
@@ -0,0 +1,132 @@
+#ifndef _ECAL_DIGI_ALG_H
+#define _ECAL_DIGI_ALG_H
+
+#include "k4FWCore/DataHandle.h"
+#include "GaudiAlg/GaudiAlgorithm.h"
+#include "edm4hep/MutableCaloHitContribution.h"
+#include "edm4hep/MutableSimCalorimeterHit.h"
+#include "edm4hep/SimCalorimeterHit.h"
+#include "edm4hep/CalorimeterHit.h"
+#include "edm4hep/CalorimeterHitCollection.h"
+#include "edm4hep/SimCalorimeterHitCollection.h"
+#include "edm4hep/MCRecoCaloAssociationCollection.h"
+#include "edm4hep/MCRecoCaloParticleAssociationCollection.h"
+#include "CaloBar.h"
+#include "HitStep.h"
+
+#include <DDRec/DetectorData.h>
+#include <DDRec/CellIDPositionConverter.h>
+#include <DD4hep/Segmentations.h> 
+#include "DetInterface/IGeomSvc.h"
+#include "TVector3.h"
+#include "TRandom3.h"
+#include "TFile.h"
+#include "TString.h"
+#include "TH3.h"
+#include "TH1.h"
+
+#include <cstdlib>
+#include "time.h"
+#include <TTimeStamp.h> 
+#include <ctime>
+
+#define C 299.79  // unit: mm/ns
+#define PI 3.141592653
+
+class EcalDigiAlg : public GaudiAlgorithm
+{
+ 
+public:
+ 
+  EcalDigiAlg(const std::string& name, ISvcLocator* svcLoc);
+ 
+  /** Called at the begin of the job before anything is read.
+   * Use to initialize the processor, e.g. book histograms.
+   */
+  virtual StatusCode initialize() ;
+ 
+  /** Called for every event - the working horse.
+   */
+  virtual StatusCode execute() ; 
+ 
+  /** Called after data processing for clean up.
+   */
+  virtual StatusCode finalize() ;
+
+
+  StatusCode MergeHits(const edm4hep::SimCalorimeterHitCollection& m_col, std::vector<edm4hep::SimCalorimeterHit>& m_hits);  
+
+	double GetBarLength(CaloBar& bar); //TODO: should read from geom file! 
+	edm4hep::MutableSimCalorimeterHit find(const std::vector<edm4hep::MutableSimCalorimeterHit>& m_col, unsigned long long& cellid) const;
+  // double Digitization(double edepCry, double fCrosstalkChannel);
+  double EnergyDigi(double ScinGen, double sEcalCryMipLY);
+
+	void Clear();
+
+protected:
+
+  SmartIF<IGeomSvc> m_geosvc;
+  typedef std::vector<float> FloatVec;
+  typedef std::map<const edm4hep::MCParticle, float> MCParticleToEnergyWeightMap;
+
+	int _nEvt ;
+	float m_length;
+	TRandom3 rndm;
+	TFile* m_wfile;
+	TTree* t_SimCont;
+	TTree* t_SimBar;
+	
+  double totE_Truth, totE_Digi;
+  FloatVec m_step_t;  // yyy: time of each step
+	FloatVec m_step_x, m_step_y, m_step_z, m_step_E, m_step_T1, m_step_T2, m_stepBar_x, m_stepBar_y, m_stepBar_z;
+	FloatVec m_simBar_x, m_simBar_y, m_simBar_z, m_simBar_T1, m_simBar_T2, m_simBar_Q1_Truth, m_simBar_Q2_Truth, m_simBar_Q1_Digi, m_simBar_Q2_Digi, m_simBar_dlayer, m_simBar_stave, m_simBar_slayer, m_simBar_module;
+  std::vector<unsigned long long> m_simBar_cellID;
+
+
+	dd4hep::rec::CellIDPositionConverter* m_cellIDConverter;
+	dd4hep::DDSegmentation::BitFieldCoder* m_decoder;
+  dd4hep::Detector* m_dd4hep;
+
+	Gaudi::Property<float> m_scale{ this, "Scale", 1 };
+
+  // Input collections
+  DataHandle<edm4hep::SimCalorimeterHitCollection> r_SimCaloCol{"SimCaloCol", Gaudi::DataHandle::Reader, this};
+  mutable Gaudi::Property<std::string> _readoutName{this, "ReadOutName", "CaloHitsCollection", "Readout name"};
+  mutable Gaudi::Property<std::string> _filename{this, "OutFileName", "testout.root", "Output file name"};
+  mutable Gaudi::Property<int>   _UseRelDigi{this,   "UseRealisticDigi",  1, "If use the realistic model"};
+
+  //Input parameters
+  mutable Gaudi::Property<int>   _writeNtuple{this,  "WriteNtuple", 1, "Write ntuple"};
+  mutable Gaudi::Property<int>   _Nskip{this,  "SkipEvt", 0, "Skip event"};
+  mutable Gaudi::Property<float> _seed{this,   "Seed", 2131, "Random Seed"};
+  mutable Gaudi::Property<int>  _Debug{this,   "Debug", 0, "Debug level"};
+  mutable Gaudi::Property<float> _Eth {this,   "EnergyThreshold", 0.001, "Energy Threshold (/GeV)"};
+  mutable Gaudi::Property<float> r_cali{this,  "CalibrECAL", 1, "Calibration coefficients for ECAL"};
+  mutable Gaudi::Property<float> Latt{this, 	"AttenuationLength", 7000, "Crystal Attenuation Length(mm)"};
+  mutable Gaudi::Property<float> Tres{this, 	"TimeResolution", 0.1, "Crystal time resolution in one side (ns)"};
+  mutable Gaudi::Property<float> nMat{this, 	"MatRefractive", 2.15, "Material refractive index of crystal"};
+  mutable Gaudi::Property<float> Tinit{this, 	"InitalTime", 2, "Start time (ns)"}; 
+  
+  mutable Gaudi::Property<float> _Qthfrac  {this, 	"ChargeThresholdFrac", 0.05, "Charge threshold fraction"};
+
+  mutable Gaudi::Property<int> fADC{this, 	"ADC", 4096, "Total ADC conuts"};
+  mutable Gaudi::Property<int> fNofGain{this, 	"NofGain", 3, "Number of gain modes"};
+  mutable Gaudi::Property<int> fADCSwitch{this, 	"ADCSwitch", 4000, "switching point of different gain mode"};
+  mutable Gaudi::Property<float> fGainRatio_12{this, 	"GainRatio_12", 15, "Gain-1 over Gain-2"};
+  mutable Gaudi::Property<float> fGainRatio_23{this, 	"GainRatio_23", 10, "Gain-2 over Gain-3"};
+  mutable Gaudi::Property<float> fEcalCryMipLY{this, 	"EcalCryMipLY", 100, "Crystal light yield (p.e./MIP)"};
+  mutable Gaudi::Property<float> fEcalMIPEnergy{this, 	"EcalMIPEnergy", 8.9, "MIP Energy deposit in 1cm BGO (MeV/MIP)"};
+  mutable Gaudi::Property<int> fEcalSiPMPixels{this, 	"EcalSiPMPixels", 250000, "Pixels number of SiPM"};
+  mutable Gaudi::Property<float> fEcalChargeADCMean{this, 	"EcalChargeADCMean", 5, "ADC per p.e. for Gain-1 (ADC)"};
+  mutable Gaudi::Property<float> fEcalChargeADCSigma{this, 	"EcalChargeADCSigma", 2.5, "Sigma of ADC per p.e. for Gain-1 (ADC)"};
+  mutable Gaudi::Property<float> fEcalADCError{this, 	"EcalADCError", 0.002, "ADC precision"};
+  mutable Gaudi::Property<float> fEcalNoiseADCSigma{this, 	"EcalNoiseADCSigma", 3, "Sigma of electronic noise (ADC)"};
+  mutable Gaudi::Property<float> fEcalMIP_Thre{this, 	"EcalMIP_Thre", 0.1, "Energy threshold for single readout end (MIP)"};
+
+  // Output collections
+  DataHandle<edm4hep::CalorimeterHitCollection>    w_DigiCaloCol{"DigiCaloCol", Gaudi::DataHandle::Writer, this};
+  DataHandle<edm4hep::MCRecoCaloAssociationCollection>    w_CaloAssociationCol{"ECALBarrelAssoCol", Gaudi::DataHandle::Writer, this};
+  DataHandle<edm4hep::MCRecoCaloParticleAssociationCollection>    w_MCPCaloAssociationCol{"ECALBarrelParticleAssoCol", Gaudi::DataHandle::Writer, this};
+};
+
+#endif
diff --git a/Digitisers/CaloDigi/src/HcalDigiAlg.cpp b/Digitisers/CaloDigi/src/HcalDigiAlg.cpp
new file mode 100644
index 00000000..a72a30cb
--- /dev/null
+++ b/Digitisers/CaloDigi/src/HcalDigiAlg.cpp
@@ -0,0 +1,252 @@
+// /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// // Unit in code: mm, ns. 
+
+#include "HcalDigiAlg.h" 
+
+#include "edm4hep/SimCalorimeterHit.h"
+#include "edm4hep/CalorimeterHit.h"
+#include "edm4hep/Vector3f.h"
+#include "edm4hep/Cluster.h"
+
+#include "DD4hep/Detector.h"
+#include <DD4hep/Objects.h>
+#include <DDRec/CellIDPositionConverter.h>
+
+#include "TVector3.h"
+#include <math.h>
+#include <cmath>
+#include <iostream>
+#include <algorithm>
+#include <map>
+
+// #include <fstream>
+// #include <ctime>
+
+#define C 299.79  // unit: mm/ns
+#define PI 3.141592653
+using namespace std;
+using namespace dd4hep;
+
+DECLARE_COMPONENT( HcalDigiAlg )
+
+HcalDigiAlg::HcalDigiAlg(const std::string& name, ISvcLocator* svcLoc)
+  : GaudiAlgorithm(name, svcLoc),
+    _nEvt(0)
+{
+  
+	// Input collections
+	declareProperty("SimCaloHitCollection", r_SimCaloCol, "Handle of the Input SimCaloHit collection");
+  
+	// Output collections
+	declareProperty("CaloHitCollection", w_DigiCaloCol, "Handle of Digi CaloHit collection");
+	declareProperty("CaloAssociationCollection", w_CaloAssociationCol, "Handle of CaloAssociation collection");
+  declareProperty("CaloMCPAssociationCollection", w_MCPCaloAssociationCol, "Handle of CaloAssociation collection"); 
+}
+
+StatusCode HcalDigiAlg::initialize()
+{
+  if(_writeNtuple){
+    std::string s_outfile = _filename;
+    m_wfile = new TFile(s_outfile.c_str(), "recreate");
+    t_simHit = new TTree("simHit", "simHit");
+    
+    t_simHit->Branch("totE", &m_totE);
+    t_simHit->Branch("simHit_x", &m_simHit_x);
+    t_simHit->Branch("simHit_y", &m_simHit_y);
+    t_simHit->Branch("simHit_z", &m_simHit_z);
+    t_simHit->Branch("simHit_E", &m_simHit_E);
+    t_simHit->Branch("simHit_HG", &m_simHit_HG);
+    t_simHit->Branch("simHit_LG", &m_simHit_LG);
+    t_simHit->Branch("simHit_steps", &m_simHit_steps);
+    
+    t_simHit->Branch("simHit_module", &m_simHit_module);
+    t_simHit->Branch("simHit_stave", &m_simHit_stave);
+    t_simHit->Branch("simHit_layer", &m_simHit_layer);
+    t_simHit->Branch("simHit_tower", &m_simHit_tower);
+    t_simHit->Branch("simHit_slice", &m_simHit_slice);
+    t_simHit->Branch("simHit_cellID", &m_simHit_cellID);
+  }
+	std::cout<<"HcalDigiAlg::m_scale="<<m_scale<<std::endl;
+	m_geosvc = service<IGeomSvc>("GeomSvc");
+	if ( !m_geosvc )  throw "HcalDigiAlg :Failed to find GeomSvc ...";
+	dd4hep::Detector* m_dd4hep = m_geosvc->lcdd();
+	if ( !m_dd4hep )  throw "HcalDigiAlg :Failed to get dd4hep::Detector ...";
+	m_cellIDConverter = new dd4hep::rec::CellIDPositionConverter(*m_dd4hep);
+   	m_decoder = m_geosvc->getDecoder(_readoutName);
+	if (!m_decoder) {
+		error() << "Failed to get the decoder. " << endmsg;
+		return StatusCode::FAILURE;
+	}
+
+
+	rndm.SetSeed(_seed);
+	std::cout<<"HcalDigiAlg::initialize"<<std::endl;
+	return GaudiAlgorithm::initialize();
+}
+
+StatusCode HcalDigiAlg::execute()
+{
+// clock_t yyy_start, yyy_enddigi;
+// yyy_start = clock(); // 记录开始时间
+
+	if(_nEvt==0) std::cout<<"HcalDigiAlg::execute Start"<<std::endl;
+	std::cout<<"Processing event: "<<_nEvt<<std::endl;
+   	if(_nEvt<_Nskip){ _nEvt++; return StatusCode::SUCCESS; }
+
+	Clear();
+  m_totE = 0.;
+ 	const edm4hep::SimCalorimeterHitCollection* SimHitCol =  r_SimCaloCol.get();
+
+	edm4hep::CalorimeterHitCollection* caloVec = w_DigiCaloCol.createAndPut();
+	edm4hep::MCRecoCaloAssociationCollection* caloAssoVec = w_CaloAssociationCol.createAndPut();
+  edm4hep::MCRecoCaloParticleAssociationCollection* caloMCPAssoVec = w_MCPCaloAssociationCol.createAndPut(); 
+
+	if(SimHitCol == 0) 
+	{
+		std::cout<<"not found SimCalorimeterHitCollection"<< std::endl;
+		return StatusCode::SUCCESS;
+	}
+  if(_Debug>=1) std::cout<<"digi, input sim hit size="<< SimHitCol->size() <<std::endl;
+
+
+  for(int isim=0; isim<SimHitCol->size(); isim++){
+
+    auto simhit = SimHitCol->at(isim);
+    if(!simhit.isAvailable()) continue;
+    if(simhit.getEnergy()==0) continue;
+
+    unsigned long long id = simhit.getCellID();
+    double Ehit = simhit.getEnergy();
+
+    double sChargeOutHG = 0;
+    double sChargeOutLG = 0;
+    //Digitization
+    if(_UseRelDigi){
+      // -- Scintillation (Energy -> MIP -> Np.e.)
+      int sPix = gRandom->Poisson(Ehit / _MIPCali * (_MIPADC / _PeADCMean));
+      // -- Tile non-uniformity 
+      sPix = sPix * (1.0 + gRandom->Uniform(-_TileRes, _TileRes));
+      // -- SiPM Saturation (Np.e. -> Npixel)
+      sPix = std::round(_Pixel * (1.0 - TMath::Exp(-sPix * 1.0 / _Pixel)));
+      // -- ADC response (Npixel -> ADC)
+      double sChargeMean = sPix * _PeADCMean;
+      double sChargeSigma = sqrt(sPix * _PeADCSigma * _PeADCSigma);
+      double sChargeOut = gRandom->Gaus(sChargeMean, sChargeSigma);
+      // -- (ADC->MIP)
+      sChargeOutHG = sChargeOut + gRandom->Gaus(_BaselineHG, _BaselineSigmaHG);
+      sChargeOutLG = sChargeOut / _HLRatio + gRandom->Gaus(_BaselineLG, _BaselineSigmaLG);
+      sChargeOutHG = std::round(gRandom->Gaus(sChargeOutHG, sChargeOutHG * _ADCError));
+      sChargeOutLG = std::round(gRandom->Gaus(sChargeOutLG, sChargeOutLG * _ADCError));
+      if (sChargeOutLG > _ADCLimit)
+          sChargeOutLG = _ADCLimit;
+      Double_t sMIP = 0;
+      if (sChargeOutHG > _ADCSwitch)
+      {
+          sMIP = (sChargeOutLG - _BaselineLG) * _HLRatio / _MIPADC;
+          sChargeOutHG = _ADCSwitch;
+      }
+      else
+      {
+          sMIP = (sChargeOutHG - _BaselineHG) / _MIPADC;
+      }
+      Ehit = sMIP * _MIPCali;
+    }
+    if(Ehit<_MIPCali*_Eth_Mip) continue;
+
+    //Global calibration. 
+    //TODO: add more digitization terms here. 
+    double Ehit_cali = Ehit*r_cali;
+
+    //Loop contributions to get hit time and MCParticle. 
+    double Thit_ave = 0.;
+    double Ehit_raw = 0.;
+    MCParticleToEnergyWeightMap MCPEnMap; MCPEnMap.clear();
+    for(int iConb=0; iConb<simhit.contributions_size(); ++iConb){
+      auto conb = simhit.getContributions(iConb);
+      if(!conb.isAvailable()) continue;
+      if(conb.getEnergy()==0) continue;
+
+      Thit_ave += conb.getTime();
+      
+      auto mcp = conb.getParticle();
+      MCPEnMap[mcp] += conb.getEnergy();
+      Ehit_raw += conb.getEnergy();
+    }
+    Thit_ave = Thit_ave/simhit.contributions_size();
+    //Create DigiHit
+    auto digiHit = caloVec->create();
+    digiHit.setCellID(id);
+    digiHit.setEnergy(Ehit_cali);
+    digiHit.setTime(Thit_ave);
+    digiHit.setPosition(simhit.getPosition());
+
+    //Create SimHit-DigiHit association. 
+    auto rel = caloAssoVec->create();
+    rel.setRec(digiHit);
+    rel.setSim(simhit);
+    rel.setWeight(1.);
+
+    //Create DigiHit-MCParticle association.
+    for(auto iter : MCPEnMap){
+      auto rel_MC = caloMCPAssoVec->create();
+      rel_MC.setRec(digiHit);
+      rel_MC.setSim(iter.first);
+      rel_MC.setWeight(iter.second/Ehit_raw);
+    }
+
+    if(_writeNtuple){
+      m_totE += digiHit.getEnergy();
+      m_simHit_x.push_back(digiHit.getPosition().x);
+      m_simHit_y.push_back(digiHit.getPosition().y);
+      m_simHit_z.push_back(digiHit.getPosition().z);
+      m_simHit_E.push_back(digiHit.getEnergy());
+      m_simHit_HG.push_back(sChargeOutHG);
+      m_simHit_LG.push_back(sChargeOutLG);
+      m_simHit_steps.push_back(simhit.contributions_size());
+      m_simHit_module.push_back(m_decoder->get(id, "module"));
+      m_simHit_stave.push_back(m_decoder->get(id, "stave"));
+      m_simHit_layer.push_back(m_decoder->get(id, "layer"));
+      m_simHit_slice.push_back(m_decoder->get(id, "slice"));
+      m_simHit_tower.push_back(m_decoder->get(id, "tower"));
+      m_simHit_cellID.push_back(id);
+    }
+  }
+
+	if(_writeNtuple) t_simHit->Fill();
+
+	_nEvt ++ ;
+	return StatusCode::SUCCESS;
+}
+
+StatusCode HcalDigiAlg::finalize()
+{
+  if(_writeNtuple){
+	  m_wfile->cd();
+	  t_simHit->Write();
+    m_wfile->Close();
+	  delete m_wfile, t_simHit; 
+  }
+
+	info() << "Processed " << _nEvt << " events " << endmsg;
+	delete m_cellIDConverter, m_decoder, m_geosvc;
+	return GaudiAlgorithm::finalize();
+}
+
+
+void HcalDigiAlg::Clear(){
+  m_totE = -99;
+	m_simHit_x.clear();
+	m_simHit_y.clear();
+	m_simHit_z.clear();
+	m_simHit_E.clear();
+	m_simHit_HG.clear();
+	m_simHit_LG.clear();
+	m_simHit_steps.clear();
+	m_simHit_module.clear();
+	m_simHit_stave.clear();
+	m_simHit_layer.clear();
+	m_simHit_slice.clear();
+	m_simHit_tower.clear();
+  m_simHit_cellID.clear();
+}
+
diff --git a/Digitisers/CaloDigi/src/HcalDigiAlg.h b/Digitisers/CaloDigi/src/HcalDigiAlg.h
new file mode 100644
index 00000000..c65f3d5a
--- /dev/null
+++ b/Digitisers/CaloDigi/src/HcalDigiAlg.h
@@ -0,0 +1,115 @@
+#ifndef HCAL_DIGI_ALG_H 
+#define HCAL_DIGI_ALG_H
+
+#include "k4FWCore/DataHandle.h"
+#include "GaudiAlg/GaudiAlgorithm.h"
+#include "edm4hep/MutableCaloHitContribution.h"
+#include "edm4hep/MutableSimCalorimeterHit.h"
+#include "edm4hep/SimCalorimeterHit.h"
+#include "edm4hep/CalorimeterHit.h"
+#include "edm4hep/CalorimeterHitCollection.h"
+#include "edm4hep/SimCalorimeterHitCollection.h"
+#include "edm4hep/MCRecoCaloAssociationCollection.h"
+#include "edm4hep/MCRecoCaloParticleAssociationCollection.h"
+#include "edm4hep/MCParticle.h"
+
+#include <DDRec/DetectorData.h>
+#include <DDRec/CellIDPositionConverter.h>
+#include <DD4hep/Segmentations.h> 
+#include "DetInterface/IGeomSvc.h"
+#include "TVector3.h"
+#include "TRandom3.h"
+#include "TFile.h"
+#include "TString.h"
+#include "TH3.h"
+#include "TH1.h"
+
+#include <cstdlib>
+#include "time.h"
+#include <TTimeStamp.h> 
+#include <ctime>
+
+#define C 299.79  // unit: mm/ns
+#define PI 3.141592653
+
+class HcalDigiAlg : public GaudiAlgorithm
+{
+ 
+public:
+ 
+  HcalDigiAlg(const std::string& name, ISvcLocator* svcLoc);
+ 
+  /** Called at the begin of the job before anything is read.
+   * Use to initialize the processor, e.g. book histograms.
+   */
+  virtual StatusCode initialize() ;
+ 
+  /** Called for every event - the working horse.
+   */
+  virtual StatusCode execute() ; 
+ 
+  /** Called after data processing for clean up.
+   */
+  virtual StatusCode finalize() ;
+
+	void Clear();
+
+protected:
+
+  SmartIF<IGeomSvc> m_geosvc;
+  typedef std::vector<float> FloatVec;
+  typedef std::map<const edm4hep::MCParticle, float> MCParticleToEnergyWeightMap;
+
+	int _nEvt ;
+	TRandom3 rndm;
+	TFile* m_wfile;
+	TTree* t_simHit;
+
+  double m_totE;
+	FloatVec m_simHit_x, m_simHit_y, m_simHit_z, m_simHit_E, m_simHit_slice, m_simHit_layer, m_simHit_tower, m_simHit_stave, m_simHit_module, m_simHit_HG, m_simHit_LG;
+  std::vector<unsigned long long> m_simHit_cellID;
+  std::vector<int> m_simHit_steps;
+
+
+	dd4hep::rec::CellIDPositionConverter* m_cellIDConverter;
+	dd4hep::DDSegmentation::BitFieldCoder* m_decoder;
+
+	Gaudi::Property<float> m_scale{ this, "Scale", 1 };
+
+  // Input collections
+  DataHandle<edm4hep::SimCalorimeterHitCollection> r_SimCaloCol{"SimCaloCol", Gaudi::DataHandle::Reader, this};
+  mutable Gaudi::Property<std::string> _readoutName{this, "ReadOutName", "CaloHitsCollection", "Readout name"};
+  mutable Gaudi::Property<std::string> _filename{this, "OutFileName", "testout.root", "Output file name"};
+
+  //Input parameters
+  mutable Gaudi::Property<int>   _writeNtuple{this,  "WriteNtuple", 1, "Write ntuple"};
+  mutable Gaudi::Property<int>   _Nskip{this,  "SkipEvt", 0, "Skip event"};
+  mutable Gaudi::Property<float> _seed{this,   "Seed", 2131, "Random Seed"};
+  mutable Gaudi::Property<int>  _Debug{this,   "Debug", 0, "Debug level"};
+  mutable Gaudi::Property<float> r_cali{this,  "CalibrHCAL", 1, "Global calibration coefficients for HCAL"};
+  mutable Gaudi::Property<int>   _UseRelDigi{this,   "UseRealisticDigi",  1, "If use the realistic model"};
+
+  //add digitization parameters from AHCAL prototype
+  mutable Gaudi::Property<float> _MIPCali{this,   "MIPResponse",  0.000461, "MIP response (/GeV)"};
+  mutable Gaudi::Property<float> _Eth_Mip{this,   "MIPThreshold", 0.5, "Energy Threshold (/MIP)"};
+  mutable Gaudi::Property<int>   _Pixel{this,   "SiPMPixel",  7284, "number of SiPM pixels"};
+  mutable Gaudi::Property<float> _ADCError{this,   "ADCError",  0.0002, "ADC Error (/ADC)"};
+  mutable Gaudi::Property<float> _MIPADC{this,   "MIPADCMean",  345.7, "Mean value of MIP response adc (/ADC)"};
+  mutable Gaudi::Property<float> _TileRes{this,   "TileNonUniformity",  0.05, "Non-uniformity level of one tile response"};
+  mutable Gaudi::Property<float> _PeADCMean{this,   "PeADCMean",  30.0, "Mean value of single photons adc (/ADC)"};
+  mutable Gaudi::Property<float> _PeADCSigma{this,   "PeADCSigma",  3.5, "Sigma of single photons adc (/ADC)"};
+  mutable Gaudi::Property<float> _BaselineHG{this,   "ADCBaselineHG",  377.4, "Mean value of HG baseline adc (/ADC)"};
+  mutable Gaudi::Property<float> _BaselineSigmaHG{this,   "ADCBaselineSigmaHG",  3.3, "Sigma of HG baseline adc (/ADC)"};
+  mutable Gaudi::Property<float> _BaselineLG{this,   "ADCBaselineLG",  373.9, "Mean value of LG baseline adc (/ADC)"};
+  mutable Gaudi::Property<float> _BaselineSigmaLG{this,   "ADCBaselineSigmaLG",  2.2, "Sigma of LG baseline adc (/ADC)"};
+  mutable Gaudi::Property<float> _HLRatio{this,   "ADCHLRatio",  29.9, "The ratio of HG to LG"};
+  mutable Gaudi::Property<float> _ADCSwitch{this,   "ADCSwitch",  2930, "transition point from HG to LG (/ADC)"};
+  mutable Gaudi::Property<float> _ADCLimit{this,   "ADCLimit",  3000, "ADC saturation of LG (/ADC)"};
+
+  // Output collections
+  DataHandle<edm4hep::CalorimeterHitCollection>    w_DigiCaloCol{"DigiCaloCol", Gaudi::DataHandle::Writer, this};
+  DataHandle<edm4hep::MCRecoCaloAssociationCollection>    w_CaloAssociationCol{"HCALBarrelAssoCol", Gaudi::DataHandle::Writer, this};
+  DataHandle<edm4hep::MCRecoCaloParticleAssociationCollection>    w_MCPCaloAssociationCol{"HCALBarrelParticleAssoCol", Gaudi::DataHandle::Writer, this};
+};
+
+#endif
diff --git a/Digitisers/CaloDigi/src/HitStep.h b/Digitisers/CaloDigi/src/HitStep.h
new file mode 100644
index 00000000..2ae0715b
--- /dev/null
+++ b/Digitisers/CaloDigi/src/HitStep.h
@@ -0,0 +1,26 @@
+#ifndef HIT_STEP_H
+#define HIT_STEP_H
+
+class HitStep{
+
+public: 
+  HitStep (double _Q, double _T): Q(_Q), T(_T) {};
+  //HitStep (double _Q, double _T) { Q=_Q; T=_T; };
+  HitStep() {};
+
+  void setQ(double _Q) { Q =_Q; }
+  void setT(double _T) { T =_T; }
+
+  double getQ() const { return Q; }
+  double getT() const { return T; }
+  inline bool operator < (const HitStep &x) const { 
+    return T <x.T ;
+  }
+
+private: 
+  double Q;
+  double T;
+
+};
+
+#endif
diff --git a/Digitisers/SimpleDigi/CMakeLists.txt b/Digitisers/SimpleDigi/CMakeLists.txt
index 3e335bf0..28c8ea89 100644
--- a/Digitisers/SimpleDigi/CMakeLists.txt
+++ b/Digitisers/SimpleDigi/CMakeLists.txt
@@ -2,7 +2,6 @@
 gaudi_add_module(SimpleDigi 
                  SOURCES src/PlanarDigiAlg.cpp
                          src/TPCDigiAlg.cpp
-                         src/TPCPixelClusteringDigiAlg.cpp
                          src/voxel.cpp
                          src/CylinderDigiAlg.cpp
                  LINK GearSvc
diff --git a/Digitisers/SimpleDigi/src/PlanarDigiAlg.cpp b/Digitisers/SimpleDigi/src/PlanarDigiAlg.cpp
index 9c073a70..23e6b9a5 100644
--- a/Digitisers/SimpleDigi/src/PlanarDigiAlg.cpp
+++ b/Digitisers/SimpleDigi/src/PlanarDigiAlg.cpp
@@ -174,6 +174,7 @@ StatusCode PlanarDigiAlg::execute()
   int i = 0;
   for( auto SimTHit : *STHcol ) {
     if (SimTHit.getEDep()<=_eThreshold) continue;
+    if (gsl_ran_flat(_rng, 0, 1)>_efficiency) continue;
     debug() << "MCParticle id " << SimTHit.getMCParticle().id() << endmsg;
 
     const int celId = SimTHit.getCellID() ;
diff --git a/Digitisers/SimpleDigi/src/PlanarDigiAlg.h b/Digitisers/SimpleDigi/src/PlanarDigiAlg.h
index 981a171c..3f9dafe5 100644
--- a/Digitisers/SimpleDigi/src/PlanarDigiAlg.h
+++ b/Digitisers/SimpleDigi/src/PlanarDigiAlg.h
@@ -83,6 +83,7 @@ protected:
   // cov[0]=thetaU, cov[1]=phiU, cov[2]=resU, cov[0]=thetaV, cov[1]=phiV, cov[2]=resV
   Gaudi::Property<bool> _usePlanarTag{ this, "UsePlanarTag", true };
   Gaudi::Property<float> _eThreshold{ this, "EnergyThreshold", 0 };
+  Gaudi::Property<float> _efficiency{ this, "Efficiency", 1 };
   Gaudi::Property<float> _maxPull{ this, "PullCutToRetry", 1000. };
   Gaudi::Property<bool> _parameterize{ this, "ParameterizeResolution", false};
   Gaudi::Property<FloatVec> _parU{ this, "ParametersU", {0} };
diff --git a/Digitisers/SimpleDigi/src/TPCDigiAlg.cpp b/Digitisers/SimpleDigi/src/TPCDigiAlg.cpp
index 091ccff2..6b0d042a 100644
--- a/Digitisers/SimpleDigi/src/TPCDigiAlg.cpp
+++ b/Digitisers/SimpleDigi/src/TPCDigiAlg.cpp
@@ -110,8 +110,8 @@ TPCDigiAlg::TPCDigiAlg(const std::string& name, ISvcLocator* svcLoc)
   //                           "Store the pointer to the SimTrackerHits in RawHits (deprecated) ",
   //                           _use_raw_hits_to_store_simhit_pointer,
   //                           bool(false));
-
-  declareProperty("PointResolutionPadPhi",_pointResoPadPhi=0.900,
+  // not used in pixel clustering mode
+  declareProperty("PointResolutionPadPhi",_pointResoPadPhi=0.9,
                   "Pad Phi Resolution constant in TPC");
   //registerProcessorParameter( "PointResolutionPadPhi" ,
   //                           "Pad Phi Resolution constant in TPC"  ,
@@ -125,21 +125,21 @@ TPCDigiAlg::TPCDigiAlg(const std::string& name, ISvcLocator* svcLoc)
   //                           _rejectCellID0 ,
   //                           (int)1) ;
 
-  declareProperty("PointResolutionRPhi",_pointResoRPhi0=0.050,
+  declareProperty("PointResolutionRPhi",_pointResoRPhi0=0.144,
                   "R-Phi Resolution constant in TPC");
   //registerProcessorParameter( "PointResolutionRPhi" ,
   //                           "R-Phi Resolution constant in TPC"  ,
   //                           _pointResoRPhi0 ,
   //                           (float)0.050) ;
 
-  declareProperty("DiffusionCoeffRPhi",_diffRPhi=0.025,
+  declareProperty("DiffusionCoeffRPhi",_diffRPhi=0.0323,
                   "R-Phi Diffusion Coefficent in TPC");
   //registerProcessorParameter( "DiffusionCoeffRPhi" ,
   //                           "R-Phi Diffusion Coefficent in TPC"  ,
   //                           _diffRPhi ,
   //                           (float)0.025) ;
-
-  declareProperty("N_eff",_nEff=22,
+  // CDR: 22, relative with gas
+  declareProperty("N_eff",_nEff=30,
                   "Number of Effective electrons per pad in TPC");
   //registerProcessorParameter( "N_eff" ,
   //                           "Number of Effective electrons per pad in TPC"  ,
@@ -153,7 +153,7 @@ TPCDigiAlg::TPCDigiAlg(const std::string& name, ISvcLocator* svcLoc)
   //                           _pointResoZ0 ,
   //                           (float)0.4) ;
 
-  declareProperty("DiffusionCoeffZ",_diffZ=0.08,
+  declareProperty("DiffusionCoeffZ",_diffZ=0.23,
                   "Z Diffusion Coefficent in TPC");
   //registerProcessorParameter( "DiffusionCoeffZ" ,
   //                           "Z Diffusion Coefficent in TPC"  ,
@@ -195,6 +195,8 @@ TPCDigiAlg::TPCDigiAlg(const std::string& name, ISvcLocator* svcLoc)
   //                           "Defines the maximum number of adjacent hits which can be merged"  ,
   //                           _maxMerge ,
   //                           (int)3) ;
+
+  declareProperty("PixelClustering", _pixelClustering=bool(true), "pixel clustering mode or pad readout mode");
 }
 
 
@@ -764,20 +766,7 @@ StatusCode TPCDigiAlg::execute()
 
       edep = SimTHit.getEDep();
 
-      // Calculate Point Resolutions according to Ron's Formula
-
-      // sigma_{RPhi}^2 = sigma_0^2 + Cd^2/N_{eff} * L_{drift}
-
-      // sigma_0^2 = (50micron)^2 + (900micron*sin(phi))^2
-      // Cd^2/N_{eff}} = 25^2/(22/sin(theta)*h/6mm)
-      // Cd = 25 ( microns / cm^(1/2) )
-      // (this is for B=4T, h is the pad height = pad-row pitch in mm,
-      // theta is the polar angle)
-
-      // sigma_{z}^2 = (400microns)^2 + L_{drift}cm * (80micron/sqrt(cm))^2
-
-      double aReso =_pointResoRPhi0*_pointResoRPhi0 + (_pointResoPadPhi*_pointResoPadPhi * sin(padPhi)*sin(padPhi)) ;
-      double driftLength = gearTPC.getMaxDriftLength() - (fabs(thisPoint.z()));
+      double driftLength = gearTPC.getMaxDriftLength() - (fabs(thisPoint.z())); // mm unit
 
       if (driftLength <0) {
         debug() << " TPCDigiAlg : Warning! driftLength < 0 " << driftLength << " --> Check out your GEAR file!!!!" << endmsg;
@@ -785,16 +774,45 @@ StatusCode TPCDigiAlg::execute()
         debug() << "gearTPC.getMaxDriftLength() = " << gearTPC.getMaxDriftLength() << endmsg;
         driftLength = 0.10;
       }
+      driftLength /= 10; // to cm unit
 
       padheight = padLayout.getPadHeight(padLayout.getNearestPad(thisPoint.perp(),thisPoint.phi()));
+      double ne = _nEff * (padheight/6.0);
 
-      double bReso = ( (_diffRPhi * _diffRPhi) / _nEff ) * sin(padTheta) * ( 6.0 / (padheight) )  * ( 4.0 / bField  ) ;
+      double tpcRPhiRes, tpcZRes;
+      if (_pixelClustering) {
+        // Calculate Point Resolutions according to Chang Yue and ZHAO Guang's work
 
-      double tpcRPhiRes = sqrt( aReso + bReso * (driftLength / 10.0) ); // driftLength in cm
+        // sigma_{RPhi}^2 = (sigma_0^2 + Cd^2 * L_{drift})/N_{eff}
 
-      double tpcZRes  = sqrt(( _pointResoZ0 * _pointResoZ0 )
-                             +
-                             ( _diffZ * _diffZ ) * (driftLength / 10.0) ); // driftLength in cm
+        // sigma_0 = 0.5mm/sqrt(12)
+        // N_{eff} = 30*h/6mm
+        // Cd = 32.3 ( microns / cm^(1/2) )
+        // (this is for B=3T, h is the pad height = pad-row pitch in mm)
+
+        // sigma_{z}^2 = (400microns)^2 + L_{drift}cm * (80micron/sqrt(cm))^2
+        double aReso = _pointResoRPhi0*_pointResoRPhi0 ;
+        double bReso = _diffRPhi * _diffRPhi ;
+        tpcRPhiRes   = sqrt(aReso + bReso * driftLength) / sqrt(ne); // driftLength in cm
+        tpcZRes      = sqrt( _pointResoZ0 * _pointResoZ0 + _diffZ * _diffZ * driftLength ) / sqrt(ne); // driftLength in cm
+      }
+      else {
+        // Calculate Point Resolutions according to Ron's Formula
+
+        // sigma_{RPhi}^2 = sigma_0^2 + Cd^2/N_{eff} * L_{drift}
+
+        // sigma_0^2 = (50micron)^2 + (900micron*sin(phi))^2
+        // Cd^2/N_{eff}} = 25^2/(22/sin(theta)*h/6mm)
+        // Cd = 25 ( microns / cm^(1/2) )
+        // (this is for B=4T, h is the pad height = pad-row pitch in mm,
+        // theta is the polar angle)
+
+        // sigma_{z}^2 = (400microns)^2 + L_{drift}cm * (80micron/sqrt(cm))^2
+        double aReso = _pointResoRPhi0*_pointResoRPhi0 + (_pointResoPadPhi*_pointResoPadPhi * sin(padPhi)*sin(padPhi)) ;
+        double bReso = ( (_diffRPhi * _diffRPhi) / ne ) * sin(padTheta) * ( 4.0 / bField  ) ;
+        tpcRPhiRes   = sqrt( aReso + bReso * driftLength ); // driftLength in cm
+        tpcZRes      = sqrt( _pointResoZ0 * _pointResoZ0 + _diffZ * _diffZ * driftLength ); // driftLength in cm
+      }
 
       int padIndex = padLayout.getNearestPad(thisPoint.perp(),thisPoint.phi());
 
@@ -832,7 +850,8 @@ StatusCode TPCDigiAlg::execute()
 
       // create a tpc voxel hit and store it for this row
       Voxel_tpc * atpcVoxel = new Voxel_tpc(iRowHit,iPhiHit,iZHit, thisPoint, edep, tpcRPhiRes, tpcZRes);
-
+      debug() << "to seed hit position: " << atpcVoxel->getX() << "," << atpcVoxel->getY() << "," << atpcVoxel->getZ()
+              << " iRow=" << iRowHit << " iPhi=" << iPhiHit << " padIndex=" << padIndex << endmsg;
       _tpcRowHits.at(iRowHit).push_back(atpcVoxel);
       ++numberOfVoxelsCreated;
 
@@ -989,7 +1008,8 @@ StatusCode TPCDigiAlg::execute()
             if( momentum_set ){
 
               const edm4hep::Vector3f Momentum1 = Hit1.getMomentum() ;
-              const edm4hep::Vector3f Momentum2 = Hit1.getMomentum() ;
+              //fucd: Hit1.getMomentum() -> Hit2.getMomentum()
+              const edm4hep::Vector3f Momentum2 = Hit2.getMomentum() ;
 
               CLHEP::Hep3Vector mom1(Momentum1[0],Momentum1[1],Momentum1[2]);
               CLHEP::Hep3Vector mom2(Momentum2[0],Momentum2[1],Momentum2[2]);
@@ -1167,7 +1187,7 @@ void TPCDigiAlg::writeVoxelToHit( Voxel_tpc* aVoxel){
   Voxel_tpc* seed_hit  = aVoxel;
 
   //  if( seed_hit->getRowIndex() > 5 ) return ;
-  debug() << "==============" << endmsg;
+
   //store hit variables
   edm4hep::MutableTrackerHit trkHit;// = _trkhitVec->create();
   //now the hit pos has to be smeared
@@ -1187,7 +1207,7 @@ void TPCDigiAlg::writeVoxelToHit( Voxel_tpc* aVoxel){
 
   // make sure the hit is not smeared beyond the TPC Max DriftLength
   if( fabs(point.z()) > gearTPC.getMaxDriftLength() ) point.setZ( (fabs(point.z()) / point.z() ) * gearTPC.getMaxDriftLength() );
-  debug() << "==============" << endmsg;
+  debug() << seed_hit->getX() << "," << seed_hit->getY() << "," << seed_hit->getZ() << " -> " << point << endmsg;
   edm4hep::Vector3d pos(point.x(),point.y(),point.z());
   trkHit.setPosition(pos);
   trkHit.setEDep(seed_hit->getEDep());
@@ -1238,7 +1258,7 @@ void TPCDigiAlg::writeVoxelToHit( Voxel_tpc* aVoxel){
     << "\n" ;
     throw errorMsg.str();
   }
-  debug() << "==============" << endmsg;
+
   // For no error in R
   std::array<float,TRKHITNCOVMATRIX> covMat={sin(unsmearedPhi)*sin(unsmearedPhi)*tpcRPhiRes*tpcRPhiRes,
     -cos(unsmearedPhi)*sin(unsmearedPhi)*tpcRPhiRes*tpcRPhiRes,
@@ -1256,7 +1276,7 @@ void TPCDigiAlg::writeVoxelToHit( Voxel_tpc* aVoxel){
     << "\n" ;
     throw errorMsg.str();
   }
-  debug() << "==============" << endmsg;
+
   if(pos[0]*pos[0]+pos[1]*pos[1]>0.0){
     //    push back the SimTHit for this TrackerHit
 
@@ -1273,7 +1293,7 @@ void TPCDigiAlg::writeVoxelToHit( Voxel_tpc* aVoxel){
     _NRecTPCHits++;
   }
 
-  debug() << "==============" << endmsg;
+
 #ifdef DIGIPLOTS
 //  edm4hep::SimTrackerHit* theSimHit = _tpcHitMap[seed_hit];
 //  double rSimSqrd = theSimHit->getPosition()[0]*theSimHit->getPosition()[0] + theSimHit->getPosition()[1]*theSimHit->getPosition()[1];
@@ -1315,7 +1335,7 @@ void TPCDigiAlg::writeMergedVoxelsToHit( vector <Voxel_tpc*>* hitsToMerge){
 
   unsigned number_of_hits_to_merge = hitsToMerge->size();
 
-
+  debug() << "number_of_hits_to_merge = " << number_of_hits_to_merge << endmsg;
   for(unsigned int ihitCluster = 0; ihitCluster < number_of_hits_to_merge; ++ihitCluster){
 
     sumZ += hitsToMerge->at(ihitCluster)->getZ();
@@ -1327,6 +1347,7 @@ void TPCDigiAlg::writeMergedVoxelsToHit( vector <Voxel_tpc*>* hitsToMerge){
     if (_use_raw_hits_to_store_simhit_pointer) {
       trkHit.addToRawHits(_tpcHitMap[hitsToMerge->at(ihitCluster)].getObjectID());
     }
+    debug() << "raw hit: " << _tpcHitMap[hitsToMerge->at(ihitCluster)].getPosition() << endmsg;
 
     auto rel = _relCol->create();
     rel.setRec (trkHit);
@@ -1366,7 +1387,7 @@ void TPCDigiAlg::writeMergedVoxelsToHit( vector <Voxel_tpc*>* hitsToMerge){
   if( fabs(point.z()) > gearTPC.getMaxDriftLength() ) point.setZ( (fabs(point.z()) / point.z() ) * gearTPC.getMaxDriftLength() );
 
   double pos[3] = {point.x(),point.y(),point.z()};
-
+  debug() << "to hit: " << pos[0] << "," << pos[1] << "," << pos[2] << endmsg;
   //---------------------------------------------------------------------------------
   trkHit.setPosition(pos);
   trkHit.setEDep(sumEDep);
diff --git a/Digitisers/SimpleDigi/src/TPCDigiAlg.h b/Digitisers/SimpleDigi/src/TPCDigiAlg.h
index 0e59c553..ff5d8dc8 100644
--- a/Digitisers/SimpleDigi/src/TPCDigiAlg.h
+++ b/Digitisers/SimpleDigi/src/TPCDigiAlg.h
@@ -169,6 +169,8 @@ protected:
   edm4hep::TrackerHitCollection* _trkhitVec;
   edm4hep::MCRecoTrackerAssociationCollection* _relCol;
 
+  bool _pixelClustering;
+
   bool _use_raw_hits_to_store_simhit_pointer;
 
   int _rejectCellID0;
diff --git a/Digitisers/SimpleDigi/src/TPCPixelClusteringDigiAlg.cpp b/Digitisers/SimpleDigi/src/TPCPixelClusteringDigiAlg.cpp
deleted file mode 100644
index 09b3ab15..00000000
--- a/Digitisers/SimpleDigi/src/TPCPixelClusteringDigiAlg.cpp
+++ /dev/null
@@ -1,1578 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-
-#include "TPCPixelClusteringDigiAlg.h"
-#include <iostream>
-#include <iomanip>
-#include <vector>
-#include <map>
-#include <cmath>
-#include <algorithm>
-#include <array>
-
-#include <gsl/gsl_randist.h>
-
-#include "DataHelper/Circle.h"
-#include "DataHelper/SimpleHelix.h"
-#include "DataHelper/LCCylinder.h"
-
-#include "DetInterface/IGeomSvc.h"
-
-#include "EventSeeder/IEventSeeder.h"
-//#include <IMPL/LCFlagImpl.h>
-//#include <IMPL/LCRelationImpl.h>
-//#include <IMPL/TrackerHitImpl.h>
-
-//stl exception handler
-#include <stdexcept>
-#include "CLHEP/Units/SystemOfUnits.h"
-#include "voxel.h"
-
-#include "GearSvc/IGearSvc.h"
-// STUFF needed for GEAR
-#include <gear/GEAR.h>
-#include <gear/TPCParameters.h>
-#include <gear/PadRowLayout2D.h>
-#include <gear/BField.h>
-//
-#include "UTIL/ILDConf.h"
-#include "Identifier/CEPCConf.h"
-
-#define TRKHITNCOVMATRIX 6
-
-//using namespace lcio ;
-using namespace std ;
-
-DECLARE_COMPONENT(TPCPixelClusteringDigiAlg)
-
-//[](Voxel_tpc* a, Voxel_tpc* b) {return a->getPhiIndex() < b->getPhiIndex();}
-
-TPCPixelClusteringDigiAlg::TPCPixelClusteringDigiAlg(const std::string& name, ISvcLocator* svcLoc)
-  : GaudiAlgorithm(name, svcLoc),
-    _nEvt(0)
-{
-
-  declareProperty("HeaderCol", _headerCol);
-  // modify processor description
-  //_description = "Produces TPC TrackerHit collection from SimTrackerHit collection, smeared in RPhi and Z. A search is made for adjacent hits on a pad row, if they are closer in z and r-phi than the steering parameters _doubleHitResRPhi (default value 2.0 mm) and _doubleHitResZ (default value 5.0 mm) they are considered to overlap. Clusters of hits smaller than _maxMerge (default value 3) are merged into a single tracker hit, with the position given as the average poision of the hits in phi and in z. Clusters which have _maxMerge hits or more are determined to be identifiable as multiple hits, and are not added to the tracker hit collection. This of course means that good hits caught up in a cluster of background hits will be lossed." ;
-
-  // register steering parameters: name, description, class-variable, default value
-  declareProperty("TPCCollection",_padRowHitColHdl, "The default pad-row based SimTrackerHit collection");
-
-  declareProperty("TPCSpacePointCollection",_spacePointColHdl,
-                  "Additional space point collection which provides additional guide hits between pad row centers.");
-
-  declareProperty("TPCLowPtCollection",_lowPtHitsColHdl,
-                  "The LowPt SimTrackerHit collection Produced by Mokka TPC Driver TPC0X");
-
-  declareProperty("TPCTrackerHitsCol",_TPCTrackerHitsColHdl,
-                  "The output TrackerHit collection");
-
-  declareProperty("TPCTrackerHitAssCol", _TPCAssColHdl, "Handle of TrackerHit SimTrackHit relation collection");
-
-  declareProperty("UseRawHitsToStoreSimhitPointer",
-                  _use_raw_hits_to_store_simhit_pointer=bool(false),
-                  "Store the pointer to the SimTrackerHits in RawHits (deprecated) ");
-
-  declareProperty("PointResolutionPadPhi",_pointResoPadPhi=0.5/sqrt(12),
-                  "Pad Phi Resolution constant in TPC");
-
-  declareProperty("RejectCellID0",_rejectCellID0=1,
-                  "whether or not to use hits without proper cell ID (pad row)");
-
-  declareProperty("PointResolutionRPhi",_pointResoRPhi0=0.5/sqrt(12),
-                  "R-Phi Resolution constant in TPC");
-
-  declareProperty("DiffusionCoeffRPhi",_diffRPhi=0.0323,
-                  "R-Phi Diffusion Coefficent in TPC");
-
-  declareProperty("N_eff",_nEff=30,
-                  "Number of Effective electrons per 6 mm in TPC");
-
-  declareProperty("PointResolutionZ",_pointResoZ0=0.4,
-                  "TPC Z Resolution Coefficent independent of diffusion");
-
-  declareProperty("DiffusionCoeffZ",_diffZ=0.23,
-                  "Z Diffusion Coefficent in TPC");
-
-  declareProperty("HitSortingBinningZ",_binningZ=5.0,
-                  "Defines spatial slice in Z");
-
-  declareProperty("HitSortingBinningRPhi",_binningRPhi=2.0,
-                  "Defines spatial slice in RP");
-
-  declareProperty("DoubleHitResolutionZ",_doubleHitResZ=5.0,
-                  "Defines the minimum distance for two seperable hits in Z");
-
-  declareProperty("DoubleHitResolutionRPhi",_doubleHitResRPhi=2.0,
-                  "Defines the minimum distance for two seperable hits in RPhi");
-
-  declareProperty("MaxClusterSizeForMerge",_maxMerge=3,
-                  "Defines the maximum number of adjacent hits which can be merged");
-}
-
-
-StatusCode TPCPixelClusteringDigiAlg::initialize()
-{
-  debug() << "in TPCPixelClusteringDigiAlg initialize()" <<endmsg;
-
-//  // From GNU documentation:
-//  // A replacement for the standard terminate_handler which prints
-//  // more information about the terminating exception (if any) on stderr. Call ...
-//  std::set_terminate (__gnu_cxx::__verbose_terminate_handler);
-//
-//#ifdef DIGIPLOTS
-//  /// Hook an AIDA implementation -----------------------------------------------
-//
-//  // First create a pointer to the "IAnalysisFactory" of a specific AIDA
-//  // implementation. This factory can then be used to produce all other
-//  // factories.
-//  _AF = AIDA_createAnalysisFactory();
-//
-//  // Create a ITreeFactory. -----------------------------------------------------
-//  // A ITree can be used to store AIDA objects in memory or on disk.
-//
-//  _TRF = _AF->createTreeFactory();
-//
-//  /// Create a ITree object which is bound to a file. ---------------------------
-//  // You must always create a "ITree" object to create any other factory.
-//  /*
-//   * Creates a new tree and associates it with a store.
-//   * The store is assumed to be read/write.
-//   * The store will be created if it does not exist.
-//   * @param storeName The name of the store, if empty (""), the tree is
-//   *                  created in memory and therefore will not be associated
-//   *                  with a file.
-//   * @param storeType Implementation specific string, may control store type
-//   * @param readOnly If true the store is opened readonly, an exception if it
-//   *                 does not exist
-//   * @param createNew If false the file must exist, if true the file will be
-//   *                  created
-//   * @param options Other options, currently are not specified
-//   */
-//  // ITree * ITreeFactory::create(const std::string & storeName,
-//  //                              const std::string & storeType = "",
-//  //                              bool readOnly = false,
-//  //                              bool createNew = false,
-//  //                              const std::string & options = "") ;
-//
-//  _TREE = _TRF->create("TPCDigi.root",
-//                       "root",
-//                       false,
-//                       true);
-//
-//  /// Create an IHistogramFactory which is bound to the tree "*_TREE". -----------
-//
-//  /*
-//   * Create an IHistogramFactory.
-//   * @param tree The ITree which created histograms will be associated to.
-//   * @return     The IHistogramFactory.
-//   */
-//  // IHistogramFactory * IAnalysisFactory::createHistogramFactory(ITree & tree);
-//
-//  _HF = _AF->createHistogramFactory(*_TREE);
-//
-//  _TREE->mkdir("Histograms");
-//
-//  /*
-//   * Create a IHistogram1D.
-//   * @param path      The path of the created IHistogram. The path can either
-//   *                  be a relative or full path.
-//   *                  ("/folder1/folder2/dataName" and
-//   *                  "../folder/dataName" are valid paths).
-//   *                  All the directories in the path must exist. The
-//   *                  characther `/` cannot be used in names; it is only
-//   *                  used to delimit directories within paths.
-//   * @param title     The title of the IHistogram1D.
-//   * @param nBins     The number of bins of the x axis.
-//   * @param lowerEdge The lower edge of the x axis.
-//   * @param upperEdge The upper edge of the x axis.
-//   * @param options   The options for the IHistogram1D. The default is "".
-//   *                  "type=efficiency" for an efficiency IHistogram1D.
-//   * @return          The newly created IHistogram1D.
-//   */
-//
-//
-//
-//  _phiDiffHisto = _HF->createHistogram1D("Histograms/phi_diff",
-//                                         "Calculated Phi - Track Phi",
-//                                         201, -0.05, 0.05);
-//
-//  _thetaDiffHisto = _HF->createHistogram1D("Histograms/theta_diff",
-//                                           "Calculated Theta - Track Theta",
-//                                           201, -0.05, 0.05);
-//
-//  _phiRelHisto = _HF->createHistogram1D("Histograms/padPhi",
-//                                        "Phi Relative to the Pad",
-//                                        201, 0.0, 6.3);
-//
-//  _thetaRelHisto = _HF->createHistogram1D("Histograms/padtheta",
-//                                          "Theta Relative to the pad",
-//                                          201, 0.0, 6.3);
-//
-//  _rPhiDiffHisto = _HF->createHistogram1D("Histograms/rPhiDiff",
-//                                          "rPhi_rec - rPhi_sim",
-//                                          201, -1.0, 1.0);
-//
-//  _zDiffHisto = _HF->createHistogram1D("Histograms/zDiff",
-//                                       "Z_rec - Z_sim",
-//                                       201, -1.0, 1.0);
-//
-//  _zPullHisto = _HF->createHistogram1D("Histograms/zPull",
-//                                       "(z_rec - z_sim) / Sigma_z",
-//                                       201, -10.0, 10.0);
-//
-//  _phiDistHisto = _HF->createHistogram1D("Histograms/phiDist",
-//                                         "phi_rec - Phi_sim",
-//                                         201, -1.0, 1.0);
-//
-//  _rPhiPullHisto = _HF->createHistogram1D("Histograms/rPhiPull",
-//                                          "(rPhi_rec - rPhi_sim) / Sigma_rPhi",
-//                                          201, -10.0, 10.0);
-//
-//  _zSigmaVsZHisto = _HF->createHistogram2D("Histograms/zSigmaVsZ",
-//                                           "z Sigma vs Z ",
-//                                           3000, 0.0, 3000.0,
-//                                           201, -0.20, 5.20);
-//
-//  _zSigmaHisto = _HF->createHistogram1D("Histograms/zSigma",
-//                                        "z Sigma ",
-//                                        201, -0.20, 5.20);
-//
-//  _rPhiSigmaHisto = _HF->createHistogram1D("Histograms/rPhiSigma",
-//                                           "rPhi Sigma",
-//                                           201, -0.20, 0.20);
-//
-//  _radiusCheckHisto = _HF->createHistogram1D("Histograms/radiusCheck",
-//                                             "R_hit - TPC Rmin - ((RowIndex + 0.5 )* padheight)",
-//                                             201, -0.20, 0.20);
-//
-//  _ResidualsRPhiHisto = _HF->createHistogram1D("Histograms/ResidualsRPhi",
-//                                               "MC Track Phi - Hit Phi",
-//                                               50, -0.001, 0.001);
-//
-//  _NSimTPCHitsHisto = _HF->createHistogram1D("Histograms/SimTPCHits",
-//                                             "Number of SimTPC Hits",
-//                                             100, 0.0, 1000000.0);
-//
-//  _NBackgroundSimTPCHitsHisto = _HF->createHistogram1D("Histograms/NBackgroundSimTPCHits",
-//                                                       "Number of Background SimTPC Hits",
-//                                                       100, 0.0, 1000000.0);
-//
-//  _NPhysicsSimTPCHitsHisto = _HF->createHistogram1D("Histograms/NPhysicsSimTPCHits",
-//                                                    "Number of PhysicsSimTPC Hits",
-//                                                    100, 0.0, 100000.0);
-//
-//  _NPhysicsAbove02GeVSimTPCHitsHisto = _HF->createHistogram1D("Histograms/NPhysicsAbove02GeVTPCHits",
-//                                                              "Number of PhysicsSimTPC Hits above 0.2GeV pt",
-//                                                              100, 0.0, 100000.0);
-//
-//  _NPhysicsAbove1GeVSimTPCHitsHisto = _HF->createHistogram1D("Histograms/NPhysicsAbove1GeVPtTPCHits",
-//                                                             "Number of PhysicsSimTPC Hits above 1.0 GeV pt",
-//                                                             100, 0.0, 100000.0);
-//
-//  _NRecTPCHitsHisto = _HF->createHistogram1D("Histograms/NRecTPCHits",
-//                                             "Number of Rec TPC Hits",
-//                                             50, 0.0, 100000.0);
-//
-//  _NLostPhysicsTPCHitsHisto = _HF->createHistogram1D("Histograms/NLostPhysicsTPCHits",
-//                                                     "Number of PhysicsSimTPC Hits Lost",
-//                                                     100, 0.0, 5000.0);
-//
-//  _NLostPhysicsAbove02GeVPtTPCHitsHisto = _HF->createHistogram1D("Histograms/NLostPhysicsAbove02GeVPtTPCHits",
-//                                                                 "Number of PhysicsSimTPC Hits Lost above 0.2 GeV pt",
-//                                                                 100, 0.0, 5000.0);
-//
-//  _NLostPhysicsAbove1GeVPtTPCHitsHisto = _HF->createHistogram1D("Histograms/NLostPhysicsAbove1GeVPtTPCHits",
-//                                                                "Number of PhysicsSimTPC Hits Lost above 1.0 GeV pt",
-//                                                                100, 0.0, 1000.0);
-//
-//  _NRevomedHitsHisto = _HF->createHistogram1D("Histograms/NRevomedHits",
-//                                              "Number of Removed TPC hits",
-//                                              100, 0.0, 1000000.0);
-//
-//
-//  _NKeptPhysicsTPCHitsHistoPercent = _HF->createHistogram1D("Histograms/NKeptPhysicsTPCHitsPercent",
-//                                                            "Number of PhysicsSimTPC Hits Kept",
-//                                                            303, 0.0, 1.01);
-//
-//  _NKeptPhysicsAbove02GeVPtTPCHitsHistoPercent = _HF->createHistogram1D("Histograms/NKeptPhysicsAbove02GeVPtTPCHitsPercent",
-//                                                                        "Number of PhysicsSimTPC Hits Kept above 0.2 GeV pt",
-//                                                                        303, 0.0, 1.01);
-//
-//  _NKeptPhysicsAbove1GeVPtTPCHitsHistoPercent = _HF->createHistogram1D("Histograms/NKeptPhysicsAbove1GeVPtTPCHitsPercent",
-//                                                                       "Number of PhysicsSimTPC Hits Kept above 1.0 GeV pt",
-//                                                                       303, 0.0, 1.01);
-//
-//#endif
-//
-//
-//  printParameters() ;
-
-  // get the GEAR manager
-  auto _gear = service<IGearSvc>("GearSvc");
-  if ( !_gear ) {
-    error() << "Failed to find GearSvc ..." << endmsg;
-    return StatusCode::FAILURE;
-  }
-  _GEAR = _gear->getGearMgr();
-  
-
-  // initialize gsl random generator
-  _random = gsl_rng_alloc(gsl_rng_ranlxs2);
-  _SEEDER = service<IEventSeeder>("EventSeeder");
-  _SEEDER->registerAlg(this);
-
-  _cellid_encoder = 0 ;
-  _nRun = 0 ;
-  _nEvt = 0 ;
-
-  debug()<<" __LINE__"<<endmsg;
-  //_cellid_encoder = new BitField64( lcio::ILDCellID0::encoder_string ) ;
-  debug()<<" __LINE__"<<endmsg;
-  return GaudiAlgorithm::initialize();
-}
-
-StatusCode TPCPixelClusteringDigiAlg::execute()
-{
-  debug() << "in TPCPixelClusteringDigiAlg execute()" <<endmsg;
-
-  //auto header = _headerCol.get()->at(0);
-  //int evtNo = header.getEventNumber();
-  //int runNo = header.getRunNumber();
-
-  unsigned int thisSeed = _SEEDER->getSeed(this, _nEvt, 0);
-  gsl_rng_set( _random, thisSeed);
-
-  //debug() << "seed set to " << thisSeed << " for event number "<< evtNo << endmsg;
-
-   int numberOfVoxelsCreated(0);
-
-  _NSimTPCHits = 0;
-  _NBackgroundSimTPCHits = 0;
-  _NPhysicsSimTPCHits = 0;
-  _NPhysicsAbove02GeVSimTPCHits = 0;
-  _NPhysicsAbove1GeVSimTPCHits = 0;
-  _NRecTPCHits = 0;
-
-  _NLostPhysicsTPCHits = 0;
-  _NLostPhysicsAbove02GeVPtTPCHits = 0;
-  _NLostPhysicsAbove1GeVPtTPCHits = 0;
-  _NRevomedHits = 0;
-
-  static bool firstEvent = true;
-  _tpcRowHits.clear();
-
-  // fg: make sure we have one message in the log file with run and event number for the DBD production ....
-  info() << "  =========  processing event "
-         << std::setw(9) << _nEvt/*evtNo*/ << " run "
-         << std::setw(9) << 0/*runNo*/
-         << "  ========= " << endmsg;
-
-
-  if(firstEvent==true) {
-    if (! _use_raw_hits_to_store_simhit_pointer ) {
-
-      debug() << "The relations to SimTrackerHits are now stored in relation collection TPCTrackerHitRelations\n SimTrackerHits are no longer stored in RawTrackerHits. Enable this deprecated feature by setting UseRawHitsToStoreSimhitPointer to true in steering file." << endmsg;
-
-    }
-    else{
-
-        debug() << "SimTrackerHits will be stored in RawTrackerHits. This is a deprecated please use the relations stored in TPCTrackerHitRelations" << endmsg;
-
-    }
-
-
-  }
-
-  firstEvent = false ;
-
-  const gear::TPCParameters& gearTPC = _GEAR->getTPCParameters() ;
-  const gear::PadRowLayout2D& padLayout = gearTPC.getPadLayout() ;
-  // this gets the center of the first pad in the pad layout
-  const gear::Vector2D padCoord = padLayout.getPadCenter(1) ;
-
-  // this assumes that the pad width in r*phi is the same for all pads
-  _padWidth = padLayout.getPadWidth(0)*padCoord[0];
-  // set size of row_hits to hold (n_rows) vectors
-  _tpcRowHits.resize(padLayout.getNRows());
-
-  //// created the collection which will be written out
-  _trkhitVec = _TPCTrackerHitsColHdl.createAndPut();
-  _relCol = _TPCAssColHdl.createAndPut();//LCRELATION
-
-  //zhang TODO
-  // to store the weights
-  //LCFlagImpl lcFlag(0) ;
-  //lcFlag.setBit( LCIO::LCREL_WEIGHTED ) ;
-  //_relCol->setFlag( lcFlag.getFlag()  ) ;
-  //
-
-  //auto mcCol = _mcColHdl.get();
-  //auto mcp = mcCol->at(0);
-  //debug() << "First MCParticle " << mcp << endmsg;
-
-  // first deal with the pad-row based hits from Mokka
-  const edm4hep::SimTrackerHitCollection* STHcol = nullptr;
-  try {
-    STHcol = _padRowHitColHdl.get();
-  }
-  catch ( GaudiException &e ) {
-    debug() << "Collection " << _padRowHitColHdl.fullKey() << " is unavailable in event " << _nEvt << endmsg;
-    return StatusCode::SUCCESS;
-  }
-
-  // _cellid_encoder = new UTIL::BitField64( lcio::ILDCellID0::encoder_string ) ;
-  // _cellid_encoder = new UTIL::BitField64( "system:5,side:-2,layer:13,module:6,sensor:6" ) ;
-  _cellid_encoder = new UTIL::BitField64( CEPCConf::DetEncoderString::getStringRepresentation() ) ;
-  debug() << "encoder_string: " << CEPCConf::DetEncoderString::getStringRepresentation() << endmsg;
-  //int det_id = 0 ;
-  //if ( (STHcol != nullptr) && (STHcol->size()>0) ) {
-  //  auto SimTHit = STHcol->at( 0 ) ;
-  //  _cellid_encoder->setValue(SimTHit.getCellID()) ;
-  //  if ( (*_cellid_encoder)[lcio::ILDCellID0::subdet]!= ILDDetID::TPC ){
-  //    //fatal() << "unsupported detector ID NOT TPC. det_id = " << det_id << endmsg;
-  //    return StatusCode::FAILURE;
-  //  }
-  //}else{
-  //  debug() << "Collection " << _padRowHitCol.fullKey() << " is unavailable in event " << _nEvt << endmsg;
-  //  return StatusCode::SUCCESS;
-  //}
-
-  if (!STHcol) {
-    return StatusCode::SUCCESS;
-  }
-  // auto SimTHit0 = STHcol->at(0);
-
-  // std::map< Voxel_tpc *, decltype(SimTHit0) > _tpcHitMap;
-  _tpcHitMap.clear();
-
-  auto geosvc = service<IGeomSvc>("GeomSvc");
-  if(!geosvc){
-    error() << "Failed to get the GeomSvc" << endmsg;
-    return StatusCode::FAILURE;
-  }
-  auto detector = geosvc->lcdd();
-  if(!detector){
-    error() << "Failed to get the Detector from GeomSvc" << endmsg;
-    return StatusCode::FAILURE;
-  }
-  std::string name = "TPCCollection";//_padRowHitColHdl.objKey();
-  debug() << "Readout name: " << name << endmsg;
-  auto decoder = geosvc->getDecoder(name);
-  if(!decoder){
-    error() << "Failed to get the decoder. " << endmsg;
-    return StatusCode::FAILURE;
-  }
-  
-  if( STHcol != nullptr ){
-
-    int n_sim_hits = STHcol->size()  ;
-
-    //TODO
-    //LCFlagImpl colFlag( STHcol->getFlag() ) ;
-
-    _NSimTPCHits = n_sim_hits;
-
-    debug() << "number of Pad-Row based SimHits = " << n_sim_hits << endmsg;
-    
-    edm4hep::MCParticle nMinus2MCP;
-    edm4hep::MCParticle previousMCP;
-    edm4hep::SimTrackerHit nMinus2SimHit;
-    edm4hep::SimTrackerHit previousSimTHit;
-
-    debug() << decoder->fieldDescription() << endmsg;
-    debug() << "processing nhit=" << n_sim_hits << endmsg;
-    // loop over all the pad row based sim hits
-    for(unsigned int i = 0; i<n_sim_hits; i++){
-      auto SimTHit = STHcol->at(i);
-
-      // this will used for nominaml smearing for very low pt rubish, so set it to zero initially
-      double ptSqrdMC = 0;
-
-      float edep;
-      double padPhi(0.0);
-      double padTheta (0.0);
-
-      auto cellId = SimTHit.getCellID();
-      int system  = decoder->get(cellId, "system");
-      int layer   = decoder->get(cellId, "layer"  );
-
-      auto& pos = SimTHit.getPosition();
-      debug() << "processing hit id = " << SimTHit.id() << endmsg;
-      debug() << " SimTHit= "
-              << " x = "  << pos[0]
-              << " y = "  << pos[1]
-              << " z = "  << pos[2]
-              << " cellID = " << cellId
-              << " system = " << system
-              << " layer = " << layer
-              << endmsg;
-
-      CLHEP::Hep3Vector thisPoint(pos[0],pos[1],pos[2]);
-      double padheight = padLayout.getPadHeight(padLayout.getNearestPad(thisPoint.perp(),thisPoint.phi()));
-
-      const double bField = _GEAR->getBField().at( gear::Vector3D( 0., 0., 0.) ).z() ;
-      // conversion constant. r = pt / (FCT*bField)
-      const double FCT = 2.99792458E-4;
-      bool found_mc = false;
-      edm4hep::MCParticle mcp;
-      try{ // protect crash while MCParticle unavailable
-        mcp = SimTHit.getMCParticle() ;
-      }
-      catch(...){
-        debug() << "catch throw MCParticle not available" << endmsg;
-      }
-      // increase the counters for the different classification of simhits
-      //yzhang FIXME mcp!=NULL
-      if(mcp.isAvailable()){
-
-        // get the pt of the MCParticle, this will used later to uses nominal smearing for low momentum rubish
-        const auto& momentumMC= mcp.getMomentum() ;
-        ptSqrdMC = momentumMC[0]*momentumMC[0]+momentumMC[1]*momentumMC[1] ;
-        
-        debug() << " mcp id = " << mcp.id() 
-                << " px = "  << momentumMC[0]
-                << " py = "  << momentumMC[1]
-                << " pz = "  << momentumMC[2]
-                << endmsg;
-        
-        // SJA:FIXME: the fact that it is a physics hit relies on the fact that for overlay
-        // the pointer to the mcp is set to NULL. This distinction may not always be true ...
-        ++_NPhysicsSimTPCHits ;
-        if( ptSqrdMC > (0.2*0.2) ) ++_NPhysicsAbove02GeVSimTPCHits ;
-        if( ptSqrdMC > 1.0 )  ++_NPhysicsAbove1GeVSimTPCHits ;
-        
-#ifdef DIGIPLOTS
-//        if(mcp) plotHelixHitResidual(mcp, &thisPoint);
-#endif
-      } else {
-        debug() << "MCParticle not available" << endmsg;
-        ++_NBackgroundSimTPCHits;
-      }
-
-      // if the hits contain the momentum of the particle use this to calculate the angles relative to the pad
-      //yzhang TODO
-      //colFlag.bitSet(LCIO::THBIT_MOMENTUM) == true
-      //if(colFlag.bitSet(LCIO::THBIT_MOMENTUM))
-      
-      bool colFlag_bitSet = true;
-      if(colFlag_bitSet){
-        // FIXME: fucd, since momentum from SimTrackerHit available, change MCParticle's to SimTrackerHit's, which better?
-        //const edm4hep::Vector3f mcpMomentum = mcp.getMomentum() ;
-        const edm4hep::Vector3f mcpMomentum = SimTHit.getMomentum();
-
-        CLHEP::Hep3Vector mom(mcpMomentum[0],mcpMomentum[1],mcpMomentum[2]);
-        // const double pt = mom.perp();
-        // const double radius = pt / (FCT*bField);
-        
-        // const double tanLambda = mom.z()/pt;
-        padPhi = fabs(thisPoint.deltaPhi(mom));
-        padTheta = mom.theta();
-      }
-      else { // LCIO::THBIT_MOMENTUM not set
-
-        // as the momentum vector is not available from the hits use triplets of
-        // hits to fit a circle and calculate theta and phi relative to the pad
-
-        if ((!mcp.isAvailable()) || (sqrt(ptSqrdMC) / (FCT*bField)) < ( padheight / (0.1 * CLHEP::twopi))) {
-          // if the hit has no record of it MCParticle then there is no way to know if this hit has consecutive hits from the same MCParticle
-          // so just set nominal values theta=phi=90
-          // here make a cut for particles which will suffer more than a 10 percent change in phi over the distance of the pad
-          // R > padheight/(0.1*2PI)
-          // in both cases set the angles to 90 degrees
-          padTheta = CLHEP::twopi/4.0 ;
-          padPhi = CLHEP::twopi/4.0 ;
-        }
-        else{
-          edm4hep::SimTrackerHit nextSimTHit;
-          edm4hep::SimTrackerHit nPlus2SimHit;
-          edm4hep::MCParticle nextMCP;
-          edm4hep::MCParticle nPlus2MCP;
-          // if there is at least one more hit after this one, set the pointer to the MCParticle for the next hit
-          if (i < (n_sim_hits-1) ) {
-            nextSimTHit = STHcol->at( i+1 ) ;
-            nextMCP     = nextSimTHit.getMCParticle() ;
-          }
-          else{ // set make sure that the pointers are set back to NULL so that the comparisons later hold
-            //nextSimTHit = edm4hep::SimTrackerHit;
-            //nextMCP     = edm4hep::MCParticle;
-          }
-          // if there is at least two more hits after this one, set the pointer to the MCParticle for the next but one hit
-          if (i < (n_sim_hits-2) ) {
-            nPlus2SimHit = STHcol->at( i+2 );
-            nPlus2MCP    = nPlus2SimHit.getMCParticle() ;
-          }
-          else{ // set make sure that the pointers are set back to NULL so that the comparisons later hold
-            //_nPlus2SimHit = edm4hep::SimTrackerHit;
-            //_nPlus2MCP    = edm4hep::MCParticle;
-          }
-
-          if      ( mcp==previousMCP && mcp==nextMCP )    { // middle hit of 3 from the same MCParticle
-
-            CLHEP::Hep3Vector precedingPoint(previousSimTHit.getPosition()[0],previousSimTHit.getPosition()[1],previousSimTHit.getPosition()[2]) ;
-            CLHEP::Hep3Vector followingPoint(nextSimTHit.getPosition()[0],nextSimTHit.getPosition()[1],nextSimTHit.getPosition()[2]) ;
-
-            debug() << "address of _previousSimTHit = " << previousSimTHit
-                    << " x = "  << previousSimTHit.getPosition()[0]
-                    << " y = "  << previousSimTHit.getPosition()[1]
-                    << " z = "  << previousSimTHit.getPosition()[2]
-                    << std::endl ;
-
-            debug() << "address of _nextSimTHit = " << nextSimTHit
-                    << " x = "  << nextSimTHit.getPosition()[0]
-                    << " y = "  << nextSimTHit.getPosition()[1]
-                    << " z = "  << nextSimTHit.getPosition()[2]
-                    << std::endl ;
-
-            // get phi and theta using functions defined below
-            padPhi = getPadPhi( &thisPoint, &precedingPoint, &thisPoint, &followingPoint);
-            padTheta = getPadTheta(&precedingPoint, &thisPoint, &followingPoint);
-
-          }
-          else if ( mcp==nextMCP     && mcp==nPlus2MCP )  { // first  hit of 3 from the same MCParticle
-
-            CLHEP::Hep3Vector followingPoint(nextSimTHit.getPosition()[0],nextSimTHit.getPosition()[1],nextSimTHit.getPosition()[2]) ;
-            CLHEP::Hep3Vector nPlus2Point(nPlus2SimHit.getPosition()[0],nPlus2SimHit.getPosition()[1],nPlus2SimHit.getPosition()[2]) ;
-
-            // get phi and theta using functions defined below
-            padPhi = getPadPhi( &thisPoint, &thisPoint, &followingPoint, &nPlus2Point);
-            padTheta = getPadTheta(&thisPoint, &followingPoint, &nPlus2Point);
-
-          }
-          else if ( mcp==previousMCP && mcp==nMinus2MCP ) { // last   hit of 3 from the same MCParticle
-            auto& posMinus = nMinus2SimHit.getPosition();
-            auto& posPrevious = previousSimTHit.getPosition();
-            
-            CLHEP::Hep3Vector nMinus2Point(posMinus[0],posMinus[1],posMinus[2]);
-            CLHEP::Hep3Vector precedingPoint(posPrevious[0],posPrevious[1],posPrevious[2]);
-
-            // get phi and theta using functions defined below
-            padPhi = getPadPhi( &thisPoint, &nMinus2Point, &precedingPoint, &thisPoint);
-            padTheta = getPadTheta(&nMinus2Point, &precedingPoint, &thisPoint);
-
-          }
-          else{ // the hit is isolated as either a single hit, or a pair of hits, from a single MCParticle
-            padTheta = CLHEP::twopi/4.0 ;
-            padPhi = CLHEP::twopi/4.0 ;
-          }
-        }
-
-//#ifdef DIGIPLOTS
-//        if(colFlag.bitSet(LCIO::THBIT_MOMENTUM)) {
-//
-//          const float * mcpMomentum = SimTHit.getMomentum() ;
-//
-//          CLHEP::Hep3Vector mom(mcpMomentum[0],mcpMomentum[1],mcpMomentum[2]);
-//
-//          double trackPhi = mom.phi();
-//
-//          if(trackPhi<0.0) trackPhi=trackPhi+twopi;
-//          if(trackPhi>twopi) trackPhi=trackPhi-twopi;
-//          if(trackPhi>twopi/2.0) trackPhi = trackPhi - twopi/2.0 ;
-//
-//          double localPhi = thisPoint.phi() - padPhi;
-//
-//          _phiRelHisto->fill(padPhi);
-//          _phiDiffHisto->fill((fabs(localPhi - trackPhi))/trackPhi);
-//          _thetaRelHisto->fill(padTheta);
-//          _thetaDiffHisto->fill( (sin(padTheta) - sin(mom.theta()))/sin(mom.theta()) );
-//
-//          debug() << "track Phi = " << trackPhi * (360.0 / twopi) << endmsg;
-//          debug() << "localPhi = " << localPhi * (360.0 / twopi) << endmsg;
-//          debug() << "pad Phi = " << padPhi * (360.0 / twopi) << endmsg;
-//          debug() << "pad Phi from track mom = " << ( thisPoint.phi() - trackPhi ) * (360.0 / twopi) << endmsg;
-//          debug() << "padTheta = " << padTheta * (360.0 / twopi) << endmsg;
-//          debug() << "padTheta from track mom = " << mom.theta() * (360.0 / twopi) << endmsg;
-//
-//        }
-//#endif
-
-      }
-      debug() << "padPhi = " << padPhi << " padTheta = " << padTheta << endmsg;
-      //      int pad = padLayout.getNearestPad(thisPoint.perp(),thisPoint.phi());
-      int layerNumber = SimTHit.getCellID();
-
-      if(_rejectCellID0 && (layerNumber<1)) {
-        continue;
-      }
-
-      edep = SimTHit.getEDep();
-
-      // Calculate Point Resolutions according to Chang Yue and ZHAO Guang's work
-
-      // sigma_{RPhi}^2 = (sigma_0^2 + Cd^2 * L_{drift})/N_{eff}
-
-      // sigma_0 = 0.5mm/sqrt(12)
-      // N_{eff} = 30*h/6mm
-      // Cd = 32.3 ( microns / cm^(1/2) )
-      // (this is for B=3T, h is the pad height = pad-row pitch in mm)
-
-      // sigma_{z}^2 = (400microns)^2 + L_{drift}cm * (80micron/sqrt(cm))^2
-
-      double aReso =_pointResoRPhi0*_pointResoRPhi0;
-      double driftLength = gearTPC.getMaxDriftLength() - (fabs(thisPoint.z()));
-
-      if (driftLength <0) {
-        debug() << " TPCPixelClusteringDigiAlg : Warning! driftLength < 0 " << driftLength << " --> Check out your GEAR file!!!!" << endmsg;
-        debug() << "Setting driftLength to 0.1" << endmsg;
-        debug() << "gearTPC.getMaxDriftLength() = " << gearTPC.getMaxDriftLength() << endmsg;
-        driftLength = 0.10;
-      }
-
-      padheight = padLayout.getPadHeight(padLayout.getNearestPad(thisPoint.perp(),thisPoint.phi()));
-
-      double bReso = _diffRPhi * _diffRPhi;
-      double ne    = _nEff * (padheight/6.0);
-      double tpcRPhiRes = sqrt( aReso + bReso * (driftLength / 10.0) ) / sqrt(ne); // driftLength in cm
-      double tpcZRes  = sqrt(( _pointResoZ0 * _pointResoZ0 ) + ( _diffZ * _diffZ ) * (driftLength / 10.0) ) / sqrt(ne); // driftLength in cm
-
-      int padIndex = padLayout.getNearestPad(thisPoint.perp(),thisPoint.phi());
-
-      const gear::DoubleVec & planeExt = padLayout.getPlaneExtent() ;
-      double TPCPadPlaneRMin = planeExt[0] ;
-      double TPCPadPlaneRMax = planeExt[1] ;
-
-      int iRowHit = padLayout.getRowNumber(padIndex);
-      int iPhiHit = padLayout.getPadNumber(padIndex);
-      int NBinsZ =  (int) ((2.0 * gearTPC.getMaxDriftLength()) / _binningZ);
-      int iZHit = (int) ( (float) NBinsZ * ( gearTPC.getMaxDriftLength() + thisPoint.z() ) / ( 2.0 * gearTPC.getMaxDriftLength() ) ) ;
-
-      debug() << "iRowHit = " << iRowHit << " iPhiHit = " << iPhiHit << " iZHit = " << " driftLength = " << driftLength
-              << " aReso = " << aReso << " bReso = " << bReso << " tpcRPhiRes = " << tpcRPhiRes << " tpcZRes = " << tpcZRes << endmsg;
-
-      if(iZHit<0) iZHit=0;
-      if(iZHit>NBinsZ) iZHit=NBinsZ;
-
-      // make sure that the hit lies at the middle of the pad ring
-      thisPoint.setPerp(padLayout.getPadCenter(padIndex)[0]);
-
-      if( (thisPoint.perp() < TPCPadPlaneRMin) || (thisPoint.perp() > TPCPadPlaneRMax) ) {
-        debug() << "Hit R not in TPC " << endmsg;
-        debug() << "R = " << thisPoint.perp() << endmsg;
-        debug() << "the tpc InnerRadius = " << TPCPadPlaneRMin << endmsg;
-        debug() << "the tpc OuterRadius = " << TPCPadPlaneRMax << endmsg;
-        debug() << "Hit Dropped " << endmsg;
-        continue;
-      }
-
-      if( (fabs(thisPoint.z()) > gearTPC.getMaxDriftLength()) ) {
-        debug() << "Hit Z not in TPC " << endmsg;
-        debug() << "Z = " << thisPoint.z() << endmsg;
-        debug() << "the tpc Max Z = " << gearTPC.getMaxDriftLength() << endmsg;
-        debug() << "Hit Dropped " << endmsg;
-        continue;
-      }
-
-      // create a tpc voxel hit and store it for this row
-      Voxel_tpc * atpcVoxel = new Voxel_tpc(iRowHit,iPhiHit,iZHit, thisPoint, edep, tpcRPhiRes, tpcZRes);
-
-      _tpcRowHits.at(iRowHit).push_back(atpcVoxel);
-      ++numberOfVoxelsCreated;
-
-      // store the simhit pointer for this tpcvoxel hit in the hit map
-      _tpcHitMap[atpcVoxel] = SimTHit;
-
-      // move the pointers on
-      nMinus2MCP = previousMCP;
-      previousMCP = mcp ;
-      nMinus2SimHit = previousSimTHit;
-      previousSimTHit = SimTHit;
-
-    }
-  }
-
-  // now process the LowPt collection
-  try {
-    auto STHcolLowPt = _lowPtHitsColHdl.get();
-
-    if(nullptr!=STHcolLowPt){
-
-      int n_sim_hitsLowPt = STHcolLowPt->size()  ;
-
-      _NBackgroundSimTPCHits += n_sim_hitsLowPt;
-      _NSimTPCHits += n_sim_hitsLowPt;
-
-      _padWidth = padLayout.getPadWidth(0)*padCoord[0];
-
-      debug() << "number of LowPt hits:" << n_sim_hitsLowPt << endmsg;
-
-      // loop over the LowPt hit collection
-      for(auto SimTHit : *STHcolLowPt){
-
-        CLHEP::Hep3Vector thisPoint(SimTHit.getPosition()[0],SimTHit.getPosition()[1],SimTHit.getPosition()[2]);
-
-        const gear::DoubleVec & planeExt = padLayout.getPlaneExtent() ;
-        double TPCPadPlaneRMin = planeExt[0] ;
-        double TPCPadPlaneRMax = planeExt[1] ;
-
-        int NBinsZ =  (int) ((2.0 * gearTPC.getMaxDriftLength()) / _binningZ);
-
-        if( (thisPoint.perp() < TPCPadPlaneRMin) || (thisPoint.perp() > TPCPadPlaneRMax) ) {
-          debug() << "Hit R not in TPC " << endmsg;
-          debug() << "R = " << thisPoint.perp() << endmsg;
-          debug() << "the tpc InnerRadius = " << TPCPadPlaneRMin << endmsg;
-          debug() << "the tpc OuterRadius = " << TPCPadPlaneRMax << endmsg;
-          debug() << "Hit Dropped " << endmsg;
-          continue;
-        }
-
-        if( (fabs(thisPoint.z()) > gearTPC.getMaxDriftLength()) ) {
-          debug() << "Hit Z not in TPC " << endmsg;
-          debug() << "Z = " << thisPoint.z() << endmsg;
-          debug() << "the tpc Max Z = " << gearTPC.getMaxDriftLength() << endmsg;
-          debug() << "Hit Dropped " << endmsg;
-          continue;
-        }
-
-        int padIndex = padLayout.getNearestPad(thisPoint.perp(),thisPoint.phi());
-        //double padheight = padLayout.getPadHeight(padIndex);
-
-        int iRowHit = padLayout.getRowNumber(padIndex);
-        int iPhiHit = padLayout.getPadNumber(padIndex);
-        int iZHit = (int) ( (float) NBinsZ *
-            ( gearTPC.getMaxDriftLength() + thisPoint.z() ) / ( 2.0 * gearTPC.getMaxDriftLength() ) ) ;
-
-        // shift the hit in r-phi to the nearest pad-row centre
-        thisPoint.setPerp(padLayout.getPadCenter(padIndex)[0]);
-
-        // set the resolutions to the pads to digital like values
-        double tpcRPhiRes = _padWidth;
-        double tpcZRes = _binningZ;
-
-        // create a tpc voxel hit for this simhit and store it for this tpc pad row
-        Voxel_tpc * atpcVoxel = new Voxel_tpc(iRowHit,iPhiHit,iZHit, thisPoint, SimTHit.getEDep(), tpcRPhiRes, tpcZRes);
-
-        _tpcRowHits.at(iRowHit).push_back(atpcVoxel);
-        ++numberOfVoxelsCreated;
-
-        // store the simhit pointer for this voxel hit in a map
-        _tpcHitMap[atpcVoxel] = SimTHit;
-
-      }
-    }
-  }catch(GaudiException e){
-    warning() << "Catch exception when read LowPtHitsCol" << endmsg;
-  }
-
-  int number_of_adjacent_hits(0);
-
-  debug() << "finished looping over simhits, number of voxels = " << numberOfVoxelsCreated << endmsg;
-
-  int numberOfhitsTreated(0);
-
-  vector <Voxel_tpc *> row_hits;
-
-  // loop over the tpc rows containing hits and check for merged hits
-  for (unsigned int i = 0; i<_tpcRowHits.size(); ++i){
-
-    row_hits = _tpcRowHits.at(i);
-    std::sort(row_hits.begin(), row_hits.end(), [](Voxel_tpc* a, Voxel_tpc* b) {return a->getPhiIndex() < b->getPhiIndex();});
-
-    // double loop over the hits in this row
-    for (unsigned int j = 0; j<row_hits.size(); ++j){
-
-      ++numberOfhitsTreated;
-
-      for (unsigned int k = j+1; k<row_hits.size(); ++k){
-
-        if(row_hits[k]->getPhiIndex() > (row_hits[j]->getPhiIndex())+2){ // SJA:FIXME: here we need an OR to catch the wrap around
-          break; // only compare hits in adjacent phi bins
-        }
-
-        // look to see if the two hit occupy the same pad in phi or if not whether they are within the r-phi double hit resolution
-        else if( row_hits[k]->getPhiIndex()==row_hits[j]->getPhiIndex()
-                ||
-                ( (fabs(row_hits[k]->getHep3Vector().deltaPhi(row_hits[j]->getHep3Vector()))) * row_hits[j]->getR()) < _doubleHitResRPhi ) {
-
-          // if neighboring in phi then compare z
-
-          decltype(_tpcHitMap)::mapped_type Hit1, Hit2;
-
-          // search of the simhit pointers in the tpchit map
-          auto it = _tpcHitMap.find(row_hits[j]);;
-          if(it!= _tpcHitMap.end()) {
-            Hit1 = it->second ; // hit found
-          }
-
-          it=_tpcHitMap.find(row_hits[k]);
-          if(it!= _tpcHitMap.end()) {
-            Hit2 = it->second ; // hit found
-          }
-
-          double pathlengthZ1(0.0);
-          double pathlengthZ2(0.0);
-
-          if( Hit1.isAvailable() && Hit2.isAvailable() ){ // if both sim hits were found
-
-            // check if the track momentum has been stored for the hits
-            bool momentum_set = true;
-
-            if( STHcol != NULL ){
-              //yzhang TODO
-              //LCFlagImpl colFlag( STHcol->getFlag() ) ;
-              //momentum_set = momentum_set && colFlag.bitSet(LCIO::THBIT_MOMENTUM) ;
-            }
-
-            //if( STHcolLowPt != NULL ){
-              //yzhang TODO
-              //LCFlagImpl colFlag( STHcolLowPt->getFlag() ) ;
-              //momentum_set =  momentum_set && colFlag.bitSet(LCIO::THBIT_MOMENTUM) ;
-            //}
-
-            if( momentum_set ){
-
-              const edm4hep::Vector3f Momentum1 = Hit1.getMomentum() ;
-              const edm4hep::Vector3f Momentum2 = Hit1.getMomentum() ;
-
-              CLHEP::Hep3Vector mom1(Momentum1[0],Momentum1[1],Momentum1[2]);
-              CLHEP::Hep3Vector mom2(Momentum2[0],Momentum2[1],Momentum2[2]);
-
-              pathlengthZ1 = fabs( Hit1.getPathLength() * mom1.cosTheta() );
-              pathlengthZ2 = fabs( Hit2.getPathLength() * mom2.cosTheta() );
-            }
-            else {
-              pathlengthZ1 = _doubleHitResZ ; // assume the worst i.e. that the track is moving in z
-              pathlengthZ2 = _doubleHitResZ ; // assume the worst i.e. that the track is moving in z
-            }
-
-            double dZ = fabs(row_hits[j]->getZ() - row_hits[k]->getZ());
-
-            double spacial_coverage = 0.5*(pathlengthZ1 + pathlengthZ2) + _binningZ;
-
-            if( (dZ - spacial_coverage) < _doubleHitResZ ){
-
-              row_hits[j]->setAdjacent(row_hits[k]);
-              row_hits[k]->setAdjacent(row_hits[j]);
-              ++number_of_adjacent_hits;
-
-            }
-          } else {
-            debug() << "Hit1=" << Hit1 << "Hit2=" << Hit2 << endmsg;
-          }
-        }
-      }
-    }
-
-
-    // now all hits have been checked for adjacent hits, go throught and write out the hits or merge
-
-    for (unsigned int j = 0; j<row_hits.size(); ++j){
-
-      Voxel_tpc* seed_hit = row_hits[j];
-
-      if(seed_hit->IsMerged() || seed_hit->IsClusterHit()) {
-        continue;
-      }
-
-      if(seed_hit->getNumberOfAdjacent()==0){ // no adjacent hits so smear and write to hit collection
-        writeVoxelToHit(seed_hit);
-      } else if(seed_hit->getNumberOfAdjacent() < (_maxMerge)){ // potential 3-hit cluster, can use simple average merge.
-
-        vector <Voxel_tpc*>* hitsToMerge = new vector <Voxel_tpc*>;
-
-        int clusterSize = seed_hit->clusterFind(hitsToMerge);
-
-        if( clusterSize <= _maxMerge ){ // merge cluster
-          seed_hit->setIsMerged();
-          writeMergedVoxelsToHit(hitsToMerge);
-        }
-        delete hitsToMerge;
-      }
-    }
-  }
-
-  int numberOfHits(0);
-  // count up the number of hits merged or lost
-  for (unsigned int i = 0; i<_tpcRowHits.size(); ++i){
-    row_hits = _tpcRowHits.at(i);
-    for (unsigned int j = 0; j<row_hits.size(); ++j){
-      numberOfHits++;
-      Voxel_tpc* seed_hit = row_hits[j];
-      if(seed_hit->IsMerged() || seed_hit->IsClusterHit() || seed_hit->getNumberOfAdjacent() > _maxMerge ) {
-        ++_NRevomedHits;
-        auto mcp = (_tpcHitMap[ seed_hit ]).getMCParticle() ;
-        if(mcp.isAvailable()) {
-          ++_NLostPhysicsTPCHits;
-          const auto& mom= mcp.getMomentum() ;
-          double ptSQRD = mom[0]*mom[0]+mom[1]*mom[1] ;
-          if( ptSQRD > (0.2*0.2) ) ++_NLostPhysicsAbove02GeVPtTPCHits ;
-          if( ptSQRD > 1.0 )  ++_NLostPhysicsAbove1GeVPtTPCHits ;
-        }
-      }
-    }
-  }
-
-  debug() << "the number of adjacent hits is " <<  number_of_adjacent_hits << "  _doubleHitResZ " << _doubleHitResZ << endmsg;
-  debug() << "number of rec_hits = "  << _NRecTPCHits << endmsg ;
-  debug() << "finished row hits " << numberOfHits << " " << numberOfhitsTreated << endmsg;
-
-  // set the parameters to decode the type information in the collection
-  // for the time being this has to be done manually
-  // in the future we should provide a more convenient mechanism to
-  // decode this sort of meta information
-
-  //StringVec typeNames ;
-  //IntVec typeValues ;
-  //typeNames.push_back( LCIO::TPCHIT ) ;
-  //typeValues.push_back( 1 ) ;
-  //yzhang TODO
-  //_trkhitVec->parameters().setValues("TrackerHitTypeNames" , typeNames ) ;
-  //_trkhitVec->parameters().setValues("TrackerHitTypeValues" , typeValues ) ;
-
-  //// add the collection to the event
-  //evt->addCollection( _trkhitVec , _TPCTrackerHitsCol ) ;
-  //evt->addCollection( _relCol , _outRelColName ) ;
-
-  // delete voxels
-  for (unsigned int i = 0; i<_tpcRowHits.size(); ++i){
-    vector <Voxel_tpc *>* current_row = &_tpcRowHits.at(i);
-    for (unsigned int j = 0; j<current_row->size(); ++j){
-      delete current_row->at(j);
-    }
-  }
-
-//#ifdef DIGIPLOTS
-  //_NSimTPCHitsHisto->fill(_NSimTPCHits);
-  //_NBackgroundSimTPCHitsHisto->fill(_NBackgroundSimTPCHits);
-  //_NPhysicsSimTPCHitsHisto->fill(_NPhysicsSimTPCHits);
-  //_NPhysicsAbove02GeVSimTPCHitsHisto->fill(_NPhysicsAbove02GeVSimTPCHits);
-  //_NPhysicsAbove1GeVSimTPCHitsHisto->fill(_NPhysicsAbove1GeVSimTPCHits);
-  //_NRecTPCHitsHisto->fill(_NRecTPCHits);
-
-  //_NLostPhysicsTPCHitsHisto->fill(_NLostPhysicsTPCHits);
-  //_NLostPhysicsAbove02GeVPtTPCHitsHisto->fill(_NLostPhysicsAbove02GeVPtTPCHits);
-  //_NLostPhysicsAbove1GeVPtTPCHitsHisto->fill(_NLostPhysicsAbove1GeVPtTPCHits);
-  //_NRevomedHitsHisto->fill(_NRevomedHits);
-
-  //_NKeptPhysicsTPCHitsHistoPercent->fill( (float)(_NPhysicsSimTPCHits-_NLostPhysicsTPCHits) / (float)_NPhysicsSimTPCHits );
-  //_NKeptPhysicsAbove02GeVPtTPCHitsHistoPercent->fill( (float)(_NPhysicsAbove02GeVSimTPCHits-_NLostPhysicsAbove02GeVPtTPCHits) / (float)_NPhysicsAbove02GeVSimTPCHits );
-  //_NKeptPhysicsAbove1GeVPtTPCHitsHistoPercent->fill( (float)(_NPhysicsAbove1GeVSimTPCHits-_NLostPhysicsAbove1GeVPtTPCHits) / (float)_NPhysicsAbove1GeVSimTPCHits );
-//#endif
-
-  debug() << "_NSimTPCHits = " << _NSimTPCHits << endmsg;
-  debug() << "_NBackgroundSimTPCHits = " << _NBackgroundSimTPCHits << endmsg;
-  debug() << "_NPhysicsSimTPCHits = " << _NPhysicsSimTPCHits << endmsg;
-  debug() << "_NPhysicsAbove02GeVSimTPCHits = " << _NPhysicsAbove02GeVSimTPCHits << endmsg;
-  debug() << "_NPhysicsAbove1GeVSimTPCHits = " << _NPhysicsAbove1GeVSimTPCHits << endmsg;
-  debug() << "_NRecTPCHits = " << _NRecTPCHits<< endmsg;
-  debug() << "_NLostPhysicsTPCHits = " << _NLostPhysicsTPCHits << endmsg;
-  debug() << "_NLostPhysicsAbove02GeVPtTPCHits = " << _NLostPhysicsAbove02GeVPtTPCHits << endmsg;
-  debug() << "_NLostPhysicsAbove1GeVPtTPCHits = " << _NLostPhysicsAbove1GeVPtTPCHits << endmsg;
-  debug() << "_NRevomedHits = " << _NRevomedHits << endmsg;
-
-  _nEvt++;
-  //Clear the maps and the end of the event.
-  _tpcHitMap.clear();
-  _tpcRowHits.clear();
-
-  delete _cellid_encoder ;
-  return StatusCode::SUCCESS;
-}
-
-
-
-StatusCode TPCPixelClusteringDigiAlg::finalize()
-{
-
-#ifdef DIGIPLOTS
-  //_TREE->commit();
-  //_TREE->cd("/Histograms");
-  //_TREE->ls("..");
-
-  //_TREE->close();
-  //info() << "DIGICHECKPLOTS Finished" << endmsg;
-#endif
-
-  gsl_rng_free(_random);
-  info() << "TPCPixelClusteringDigiAlg::end()  " << name()
-  << " processed " << _nEvt << " events in " << _nRun << " runs "
-  << endl ;
-
-  return StatusCode::SUCCESS;
-}
-
-void TPCPixelClusteringDigiAlg::writeVoxelToHit( Voxel_tpc* aVoxel){
-
-  const gear::TPCParameters& gearTPC = _GEAR->getTPCParameters() ;
-  const gear::PadRowLayout2D& padLayout = gearTPC.getPadLayout() ;
-  const gear::Vector2D padCoord = padLayout.getPadCenter(1) ;
-
-  Voxel_tpc* seed_hit  = aVoxel;
-
-  //  if( seed_hit->getRowIndex() > 5 ) return ;
-
-  //store hit variables
-  edm4hep::MutableTrackerHit trkHit;// = _trkhitVec->create();
-  //now the hit pos has to be smeared
-
-  double tpcRPhiRes = seed_hit->getRPhiRes();
-  double tpcZRes = seed_hit->getZRes();
-
-  CLHEP::Hep3Vector point(seed_hit->getX(),seed_hit->getY(),seed_hit->getZ());
-
-  double unsmearedPhi = point.phi();
-
-  double randrp = gsl_ran_gaussian(_random,tpcRPhiRes);
-  double randz =  gsl_ran_gaussian(_random,tpcZRes);
-
-  point.setPhi( point.phi() + randrp/ point.perp() );
-  point.setZ( point.z() + randz );
-
-  // make sure the hit is not smeared beyond the TPC Max DriftLength
-  if( fabs(point.z()) > gearTPC.getMaxDriftLength() ) point.setZ( (fabs(point.z()) / point.z() ) * gearTPC.getMaxDriftLength() );
-
-  edm4hep::Vector3d pos(point.x(),point.y(),point.z());
-  trkHit.setPosition(pos);
-  trkHit.setEDep(seed_hit->getEDep());
-  //  trkHit->setType( 500 );
-
-  //  int side = lcio::ILDDetID::barrel ;
-  //
-  //  if( pos[2] < 0.0 ) side = 1 ;
-  //change to Marlin's, fucd 
-  //map<Voxel_tpc*,edm4hep::SimTrackerHit>::iterator it=_tpcHitMap.find(seed_hit);
-  //assert(_tpcHitMap.end() != it);
-
-  //const int celId = it->second.getCellID();
-  //_cellid_encoder->setValue( celId );
-  //trkHit.setCellID( _cellid_encoder->lowWord() );
-
-  //int side   = (*_cellid_encoder)[lcio::ILDCellID0::side];
-  //int layer  = (*_cellid_encoder)[lcio::ILDCellID0::layer];
-  //int module = (*_cellid_encoder)[lcio::ILDCellID0::module];
-  //int sensor = (*_cellid_encoder)[lcio::ILDCellID0::sensor];
-
-  //debug() << "Hit = " << " has celId " << celId << endmsg;
-  //debug() << "side = " << side << endmsg;
-  //debug() << "layerNumber = " <<  layer << endmsg;
-  //debug() << "moduleNumber = " << module << endmsg;
-  //debug() << "sensorNumber = " << sensor << endmsg;
-
-
-  (*_cellid_encoder)[ lcio::ILDCellID0::subdet ] = lcio::ILDDetID::TPC ;
-  (*_cellid_encoder)[ lcio::ILDCellID0::layer  ] = seed_hit->getRowIndex() ;
-  (*_cellid_encoder)[ lcio::ILDCellID0::module ] = 0 ;
-
-  //SJA:FIXME: for now don't use side
-  //  (*_cellid_encoder)[ lcio::ILDCellID0::side   ] = side ;
-  (*_cellid_encoder)[ lcio::ILDCellID0::side   ] = lcio::ILDDetID::barrel ;
-  debug() << "cellID = " << _cellid_encoder->lowWord() << " " << lcio::ILDCellID0::subdet << " " << lcio::ILDCellID0::layer << " " << lcio::ILDCellID0::module << endmsg;
-  trkHit.setCellID(_cellid_encoder->lowWord());
-  //_cellid_encoder->setCellID( &trkHit ) ;
-
-
-  // check values for inf and nan
-  if( std::isnan(unsmearedPhi) || std::isinf(unsmearedPhi) || std::isnan(tpcRPhiRes) || std::isinf(tpcRPhiRes) ) {
-    std::stringstream errorMsg;
-    errorMsg << "\nProcessor: TPCPixelClusteringDigiAlg \n"
-    << "unsmearedPhi = "
-    <<  unsmearedPhi
-    << " tpcRPhiRes = "
-    <<  tpcRPhiRes
-    << "\n" ;
-    throw errorMsg.str();
-  }
-
-  // For no error in R
-  std::array<float,TRKHITNCOVMATRIX> covMat={sin(unsmearedPhi)*sin(unsmearedPhi)*tpcRPhiRes*tpcRPhiRes,
-    -cos(unsmearedPhi)*sin(unsmearedPhi)*tpcRPhiRes*tpcRPhiRes,
-    cos(unsmearedPhi)*cos(unsmearedPhi)*tpcRPhiRes*tpcRPhiRes,
-    0.,
-    0.,
-    float(tpcZRes*tpcZRes)};
-
-  trkHit.setCovMatrix(covMat);
-
-  if( !_tpcHitMap[seed_hit].isAvailable()){
-    std::stringstream errorMsg;
-    errorMsg << "\nProcessor: TPCPixelClusteringDigiAlg \n"
-    << "SimTracker Pointer is NULL throwing exception\n"
-    << "\n" ;
-    throw errorMsg.str();
-  }
-
-  if(pos[0]*pos[0]+pos[1]*pos[1]>0.0){
-    //    push back the SimTHit for this TrackerHit
-
-    if (_use_raw_hits_to_store_simhit_pointer) {
-      trkHit.addToRawHits(_tpcHitMap[seed_hit].getObjectID());
-    }
-
-    auto rel = _relCol->create();
-    rel.setRec (trkHit);
-    rel.setSim (_tpcHitMap[seed_hit]);
-    rel.setWeight( 1.0 );
-
-    _trkhitVec->push_back( trkHit );
-    _NRecTPCHits++;
-  }
-
-#ifdef DIGIPLOTS
-//  edm4hep::SimTrackerHit* theSimHit = _tpcHitMap[seed_hit];
-//  double rSimSqrd = theSimHit->getPosition()[0]*theSimHit->getPosition()[0] + theSimHit->getPosition()[1]*theSimHit->getPosition()[1];
-//
-//  double phiSim = atan2(theSimHit->getPosition()[1],theSimHit->getPosition()[0]);
-//
-//  double rPhiDiff = (point.phi() - phiSim)*sqrt(rSimSqrd);
-//  double rPhiPull = ((point.phi() - phiSim)*sqrt(rSimSqrd))/(sqrt((covMat[2])/(cos(point.phi())*cos(point.phi()))));
-//
-//  double zDiff = point.getZ() - theSimHit->getPosition()[2];
-//  double zPull = zDiff/sqrt(covMat[5]);
-//
-//
-  //_rPhiDiffHisto->fill(rPhiDiff);
-  //_rPhiPullHisto->fill(rPhiPull);
-  //_phiDistHisto->fill(point.phi() - phiSim);
-  //_zDiffHisto->fill(zDiff);
-  //_zPullHisto->fill(zPull);
-
-  //_zSigmaVsZHisto->fill(seed_hit->getZ(),sqrt(covMat[5]));
-  //_rPhiSigmaHisto->fill(sqrt((covMat[2])/(cos(point.phi())*cos(point.phi()))));
-  //_zSigmaHisto->fill(sqrt(covMat[5]));
-#endif
-}
-
-void TPCPixelClusteringDigiAlg::writeMergedVoxelsToHit( vector <Voxel_tpc*>* hitsToMerge){
-
-  const gear::TPCParameters& gearTPC = _GEAR->getTPCParameters() ;
-  const gear::PadRowLayout2D& padLayout = gearTPC.getPadLayout() ;
-  const gear::Vector2D padCoord = padLayout.getPadCenter(1) ;
-
-  edm4hep::MutableTrackerHit trkHit;// = _trkhitVec->create();
-
-  double sumZ = 0;
-  double sumPhi = 0;
-  double sumEDep = 0;
-  //  double R = 0;
-  double lastR = 0;
-
-  unsigned number_of_hits_to_merge = hitsToMerge->size();
-
-
-  for(unsigned int ihitCluster = 0; ihitCluster < number_of_hits_to_merge; ++ihitCluster){
-
-    sumZ += hitsToMerge->at(ihitCluster)->getZ();
-    sumPhi += hitsToMerge->at(ihitCluster)->getPhi();
-    sumEDep += hitsToMerge->at(ihitCluster)->getEDep();
-    hitsToMerge->at(ihitCluster)->setIsMerged();
-    lastR = hitsToMerge->at(ihitCluster)->getR();
-
-    if (_use_raw_hits_to_store_simhit_pointer) {
-      trkHit.addToRawHits(_tpcHitMap[hitsToMerge->at(ihitCluster)].getObjectID());
-    }
-
-    auto rel = _relCol->create();
-    rel.setRec (trkHit);
-    rel.setSim (_tpcHitMap[ hitsToMerge->at(ihitCluster) ]);
-    rel.setWeight( float(1.0/number_of_hits_to_merge) );
-
-  }
-
-  double avgZ = sumZ/(hitsToMerge->size());
-  double avgPhi = sumPhi/(hitsToMerge->size());
-
-  CLHEP::Hep3Vector* mergedPoint = new CLHEP::Hep3Vector(1.0,1.0,1.0);
-  mergedPoint->setPerp(lastR);
-  mergedPoint->setPhi(avgPhi);
-  mergedPoint->setZ(avgZ);
-
-  //store hit variables
-
-  // first the hit pos has to be smeared------------------------------------------------
-
-  //FIXME: which errors should we use for smearing the merged hits ?
-  //       this might be a bit large ....
-  double tpcRPhiRes = _padWidth;
-  double tpcZRes = _binningZ;
-
-  CLHEP::Hep3Vector point( mergedPoint->getX(), mergedPoint->getY(), mergedPoint->getZ()  ) ;
-
-//  double unsmearedPhi = point.phi();
-
-  double randrp = gsl_ran_gaussian(_random,tpcRPhiRes);
-  double randz =  gsl_ran_gaussian(_random,tpcZRes);
-
-  point.setPhi( point.phi() + randrp/ point.perp() );
-  point.setZ( point.z() + randz );
-
-  // make sure the hit is not smeared beyond the TPC Max DriftLength
-  if( fabs(point.z()) > gearTPC.getMaxDriftLength() ) point.setZ( (fabs(point.z()) / point.z() ) * gearTPC.getMaxDriftLength() );
-
-  double pos[3] = {point.x(),point.y(),point.z()};
-
-  //---------------------------------------------------------------------------------
-  trkHit.setPosition(pos);
-  trkHit.setEDep(sumEDep);
-  //  trkHit->setType( 500 );
-
-  // SJA:FIXME: here you can use the value 2 but not 3 which is odd as the width of the field is 1, only 0 and 1 should be allowed?
-  int side = 1 ;
-  int padIndex = padLayout.getNearestPad(mergedPoint->perp(),mergedPoint->phi());
-  int row = padLayout.getRowNumber(padIndex);
-
-  if( pos[2] < 0.0 ) side = 1 ;
-
-  (*_cellid_encoder)[ lcio::ILDCellID0::subdet ] = lcio::ILDDetID::TPC ;
-  (*_cellid_encoder)[ lcio::ILDCellID0::layer  ] = row ;
-  (*_cellid_encoder)[ lcio::ILDCellID0::module ] = 0 ;
-  ////SJA:FIXME: for now don't use side
-  ////  (*_cellid_encoder)[ lcio::ILDCellID0::side   ] = side ;
-  (*_cellid_encoder)[ lcio::ILDCellID0::side   ] = 0 ;
-
-  debug() << "cellID = " << _cellid_encoder->lowWord() << " " << lcio::ILDCellID0::subdet << " " << lcio::ILDCellID0::layer << " " << lcio::ILDCellID0::module << endmsg;
-  //yzhang FIXME?
-  //_cellid_encoder->setCellID( &trkHit ) ;
-  trkHit.setCellID( _cellid_encoder->lowWord() );
-
-  double phi = mergedPoint->getPhi();
-
-  // check values for inf and nan
-  if( std::isnan(phi) || std::isinf(phi) || std::isnan(tpcRPhiRes) || std::isinf(tpcRPhiRes) ) {
-    std::stringstream errorMsg;
-    errorMsg << "\nProcessor: TPCPixelClusteringDigiAlg \n"
-    << "phi = "
-    <<  phi
-    << " tpcRPhiRes = "
-    <<  tpcRPhiRes
-    << "\n" ;
-    throw errorMsg.str();
-  }
-
-  // For no error in R
-  std::array<float,TRKHITNCOVMATRIX> covMat={sin(phi)*sin(phi)*tpcRPhiRes*tpcRPhiRes,
-    -cos(phi)*sin(phi)*tpcRPhiRes*tpcRPhiRes,
-    cos(phi)*cos(phi)*tpcRPhiRes*tpcRPhiRes,
-    0.,
-    0.,
-    float(tpcZRes*tpcZRes)};
-
-  trkHit.setCovMatrix(covMat);
-
-  if(pos[0]*pos[0]+pos[1]*pos[1]>0.0){
-    //yzhang TODO
-    _trkhitVec->push_back( trkHit );
-    ++_nRechits;
-  } else {
-    //???yzhang TODO
-    //delete trkHit;
-  }
-
-  delete mergedPoint;
-
-}
-
-
-#ifdef DIGIPLOTS
-//void TPCPixelClusteringDigiAlg::plotHelixHitResidual( edm4hep::MCParticle *mcp, CLHEP::Hep3Vector *thisPoint){
-//
-//  const double bField = _GEAR->getBField().at( gear::Vector3D( 0., 0., 0.) ).z() ;
-//  const double FCT = 2.99792458E-4;
-//  double charge = mcp->getCharge();
-//  const double *mom = mcp->getMomentum();
-//  double pt = sqrt(mom[0]*mom[0]+mom[1]*mom[1]);
-//  double radius = pt / (FCT*bField);
-//  double tanLambda = mom[2]/pt;
-//  double omega = charge / radius;
-//
-//  if(pt>1.0) {
-//
-//    //FIXME SJA: this is only valid for tracks from the IP and should be done correctly for non prompt tracks
-//    double Z0 = 0.;
-//    double D0  = 0.;
-//
-//    LCVector3D refPoint(0.,0.,0);
-//
-//    SimpleHelix* helix = new SimpleHelix(D0,
-//                                         atan2(mom[1],mom[0]),
-//                                         omega,
-//                                         Z0,
-//                                         tanLambda,
-//                                         refPoint);
-//
-//
-//    // an almost "infinite" cylinder in z
-//    LCVector3D startCylinder(0.,0.,-1000000.0);
-//    LCVector3D endCylinder(0.,0.,1000000.0);
-//    bool endplane=true;
-//
-//    LCCylinder cylinder(startCylinder,endCylinder,thisPoint->perp(),endplane);
-//
-//    bool pointExists = false;
-//
-//    double pathlength = helix->getIntersectionWithCylinder( cylinder, pointExists);
-//
-//    LCErrorMatrix* errors = new LCErrorMatrix();
-//
-//    if(pointExists){
-//
-//      LCVector3D intersection = helix->getPosition(pathlength, errors);
-//
-//      double intersectionPhi = atan2(intersection[1],intersection[0]);
-//      double residualRPhi = ((intersectionPhi-thisPoint->phi())) ;
-//      _ResidualsRPhiHisto->fill(residualRPhi);
-//
-//    }
-//
-//    delete errors;
-//    delete helix;
-//
-//    const gear::TPCParameters& gearTPC = _GEAR->getTPCParameters() ;
-//    const gear::PadRowLayout2D& padLayout = gearTPC.getPadLayout() ;
-//
-//    int row = padLayout.getRowNumber(padLayout.getNearestPad(thisPoint->perp(),thisPoint->phi()));
-//    int pad = padLayout.getNearestPad(thisPoint->perp(),thisPoint->phi());
-//
-//    double rHit_diff = thisPoint->perp()
-//    - padLayout.getPlaneExtent()[0]
-//    - (( row + 0.5 )
-//       * padLayout.getPadHeight(pad)) ;
-//
-//    _radiusCheckHisto->fill(rHit_diff);
-//
-//    //      info() << "$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$#$" << endmsg;
-//    //      info() << "thisPoint->perp() = " << thisPoint->perp() << endmsg;
-//    //      info() << "TPC Sensitive rMin = " << padLayout.getPlaneExtent()[0] << endmsg;
-//    //      info() << "Row number + 0.5 = " <<  row + 0.5 << endmsg;
-//    //      info() << "Pad Height = " <<  padLayout.getPadHeight(pad) << endmsg;
-//    //      info() << "Row Height = " <<   padLayout.getRowHeight(row) << endmsg;
-//    //      info() << "R_hit - TPC Rmin - ((RowIndex + 0.5 )* padheight) = " << rHit_diff << endmsg;
-//
-//  }
-//  return;
-//}
-#endif
-
-
-double TPCPixelClusteringDigiAlg::getPadPhi( CLHEP::Hep3Vector *thisPoint, CLHEP::Hep3Vector* firstPoint, CLHEP::Hep3Vector* middlePoint, CLHEP::Hep3Vector* lastPoint){
-
-  CLHEP::Hep2Vector firstPointRPhi(firstPoint->x(),firstPoint->y());
-  CLHEP::Hep2Vector middlePointRPhi(middlePoint->x(),middlePoint->y());
-  CLHEP::Hep2Vector lastPointRPhi(lastPoint->x(),lastPoint->y());
-
-  // check that the points are not the same, at least at the level of a tenth of a micron
-  if( (fabs( firstPointRPhi.x() - middlePointRPhi.x() ) < 1.e-05  && fabs( firstPointRPhi.y() - middlePointRPhi.y() ) < 1.e-05)
-     ||
-     (fabs( middlePointRPhi.x() - lastPointRPhi.x() ) < 1.e-05  && fabs( middlePointRPhi.y() - lastPointRPhi.y() ) < 1.e-05)
-     ||
-     (fabs( firstPointRPhi.x() - lastPointRPhi.x() ) < 1.e-05  && fabs( firstPointRPhi.y() - lastPointRPhi.y() ) < 1.e-05)
-     ) {
-
-    warning() << " TPCPixelClusteringDigiAlg::getPadPhi "
-    << "2 of the 3 SimTracker hits passed to Circle Fit are the same hit taking pad phi as PI/2\n"
-    << " firstPoint->x() "  << firstPoint->x()
-    << " firstPoint->y() "  << firstPoint->y()
-    << " firstPoint->z() "  << firstPoint->z()
-    << " middlePoint->x() "  << middlePoint->x()
-    << " middlePoint->y() "  << middlePoint->y()
-    << " middlePoint->z() "  << middlePoint->z()
-    << " lastPoint->x() "  << lastPoint->x()
-    << " lastPoint->y() "  << lastPoint->y()
-    << " lastPoint.z() "  << lastPoint->z()
-    << std::endl ;
-
-    return CLHEP::twopi/4.0 ;
-
-  }
-
-
-
-  Circle theCircle(&firstPointRPhi, &middlePointRPhi, &lastPointRPhi);
-
-  double localPhi = atan2((thisPoint->y() - theCircle.GetCenter()->y()) ,(thisPoint->x() - theCircle.GetCenter()->x())) + (CLHEP::twopi/4.0) ;
-
-  if(localPhi>CLHEP::twopi) localPhi=localPhi - CLHEP::twopi;
-  if(localPhi<0.0) localPhi=localPhi + CLHEP::twopi;
-  if(localPhi>CLHEP::pi) localPhi = localPhi - CLHEP::pi ;
-
-  double pointPhi = thisPoint->phi();
-
-  if(pointPhi>CLHEP::twopi) pointPhi=pointPhi - CLHEP::twopi;
-  if(pointPhi<0.0) pointPhi=pointPhi + CLHEP::twopi;
-  if(pointPhi>CLHEP::pi) pointPhi = pointPhi - CLHEP::pi;
-
-  double padPhi = fabs(pointPhi - localPhi);
-
-  // check that the value returned is reasonable
-  if( std::isnan(padPhi) || std::isinf(padPhi) ) {
-    std::stringstream errorMsg;
-    errorMsg << "\nProcessor: TPCPixelClusteringDigiAlg \n"
-    << "padPhi = "
-    <<  padPhi
-    << "\n" ;
-    throw errorMsg.str();
-  }
-
-  return padPhi;
-
-}
-
-double TPCPixelClusteringDigiAlg::getPadTheta(CLHEP::Hep3Vector* firstPoint, CLHEP::Hep3Vector* middlePoint, CLHEP::Hep3Vector* lastPoint){
-
-  // Calculate thetaPad for current hit
-  CLHEP::Hep2Vector firstPointRPhi(firstPoint->x(),firstPoint->y()) ;
-  CLHEP::Hep2Vector middlePointRPhi(middlePoint->x(),middlePoint->y());
-  CLHEP::Hep2Vector lastPointRPhi(lastPoint->x(),lastPoint->y());
-
-  // check that the points are not the same, at least at the level of a tenth of a micron
-  if( (fabs( firstPointRPhi.x() - middlePointRPhi.x() ) < 1.e-05  && fabs( firstPointRPhi.y() - middlePointRPhi.y() ) < 1.e-05)
-     ||
-     (fabs( middlePointRPhi.x() - lastPointRPhi.x() ) < 1.e-05  && fabs( middlePointRPhi.y() - lastPointRPhi.y() ) < 1.e-05)
-     ||
-     (fabs( firstPointRPhi.x() - lastPointRPhi.x() ) < 1.e-05  && fabs( firstPointRPhi.y() - lastPointRPhi.y() ) < 1.e-05)
-     ) {
-
-    warning() << " TPCPixelClusteringDigiAlg::getPadTheta "
-    << "2 of the 3 SimTracker hits passed to Circle Fit are the same hit taking pad phi as PI/2\n"
-    << " firstPoint->x() "  << firstPoint->x()
-    << " firstPoint->y() "  << firstPoint->y()
-    << " firstPoint->z() "  << firstPoint->z()
-    << " middlePoint->x() "  << middlePoint->x()
-    << " middlePoint->y() "  << middlePoint->y()
-    << " middlePoint->z() "  << middlePoint->z()
-    << " lastPoint->x() "  << lastPoint->x()
-    << " lastPoint->y() "  << lastPoint->y()
-    << " lastPoint.z() "  << lastPoint->z()
-    << endmsg;
-
-    return CLHEP::twopi/4.0 ;
-
-  }
-
-
-  Circle theCircle(&firstPointRPhi, &middlePointRPhi, &lastPointRPhi);
-
-  double deltaPhi = firstPoint->deltaPhi(*lastPoint);
-
-  double pathlength = fabs(deltaPhi) *  theCircle.GetRadius();
-
-  double padTheta = atan ( pathlength / fabs(lastPoint->z() - firstPoint->z()) ) ;
-
-  double pathlength1 = 2.0 * asin( ( sqrt (
-                                           (middlePointRPhi.x() - firstPointRPhi.x()) * (middlePointRPhi.x()-firstPointRPhi.x())
-                                           +
-                                           (middlePointRPhi.y()-firstPointRPhi.y()) * (middlePointRPhi.y()-firstPointRPhi.y())
-                                           ) / 2.0 ) / theCircle.GetRadius() ) * theCircle.GetRadius()  ;
-
-
-  double pathlength2 = 2.0 * asin( ( sqrt (
-                                           (lastPointRPhi.x()-middlePointRPhi.x()) * (lastPointRPhi.x()-middlePointRPhi.x())
-                                           +
-                                           (lastPointRPhi.y()-middlePointRPhi.y()) * (lastPointRPhi.y()-middlePointRPhi.y())
-                                           ) / 2.0 ) / theCircle.GetRadius() ) * theCircle.GetRadius()  ;
-
-
-  padTheta = atan ((fabs(pathlength1 + pathlength2)) / (fabs(lastPoint->z() - firstPoint->z())) ) ;
-
-  // check that the value returned is reasonable
-  if( std::isnan(padTheta) || std::isinf(padTheta) ) {
-    std::stringstream errorMsg;
-    errorMsg << "\nProcessor: TPCPixelClusteringDigiAlg \n"
-    << "padTheta = "
-    <<  padTheta
-    << "\n" ;
-    throw errorMsg.str();
-  }
-
-  return padTheta;
-
-}
diff --git a/Digitisers/SimpleDigi/src/TPCPixelClusteringDigiAlg.h b/Digitisers/SimpleDigi/src/TPCPixelClusteringDigiAlg.h
deleted file mode 100644
index ac552f20..00000000
--- a/Digitisers/SimpleDigi/src/TPCPixelClusteringDigiAlg.h
+++ /dev/null
@@ -1,274 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-
-/*
-Evolved version of TPCDigi that provides additional functionality to deal with background. Couple to the Mokka Sensitive Detector Driver TPCSD03.cc
-
-SJA:FIXME: Still needs to be tidied up for production release.
-
-Three cases can be consider in the treatment of SimTrackerHits
-i)   A clean isolated hit; this will be smeared according to the parametric point resolution and converted to a TrackerHit
-ii)  Two or Three hits which are considered to be closer than the double hit resolution and which therefore cannot be viewed as seperable hits. These will be merged and be assigned a large associated measurement error.
-iii) A continuous set of hits within one pad row which cannot be resolved as single hits, these are condidered to be charaterisable as background hits created by extremely low pt charged particles (pt < 10MeV) and therefore are removed from the hit collection.
-
-The Driver has been modified to take an additional collection of SimTrackerHits which are produced by the Mokka TPC Sensitive Driver TPCSD03.cc. These hits are produced for particles which have very low pt and often do not move outside of the dimensions of a single pad row. These hits need to be treated differently as they do not cross any geometric boundaries in a Padrow based TPC Geometry. This negates the need to voxalise the TPC in Geant4 which has proved in the past to be prohibitive in terms of processing time due to the vastly increased number of geometric volumes.
-
-Steve Aplin 26 June 2009 (DESY)
-
-*/
-
-#ifndef TPCPixelClusteringDigiAlg_h
-#define TPCPixelClusteringDigiAlg_h 1
-
-#include "GaudiAlg/GaudiAlgorithm.h"
-#include "k4FWCore/DataHandle.h"
-#include "edm4hep/EventHeaderCollection.h"
-#include "edm4hep/SimTrackerHitCollection.h"
-#include "edm4hep/TrackerHitCollection.h"
-#include "edm4hep/MCParticleCollection.h"
-#include "edm4hep/MCRecoTrackerAssociationCollection.h"
-
-#include <gsl/gsl_rng.h>
-
-#include <type_traits>
-
-//#ifdef MARLIN_USE_AIDA
-
-//#include <marlin/AIDAProcessor.h>
-//#include <AIDA/IHistogramFactory.h>
-//#include <AIDA/ICloud1D.h>
-//#include <AIDA/IHistogram1D.h>
-
-
-
-//#define DIGIPLOTS
-
-
-
-//#ifdef DIGIPLOTS
-// includes all AIDA header files
-//#include <AIDA/AIDA.h>
-//#endif
-
-//#endif
-
-#include <string>
-#include <vector>
-#include <map>
-
-//#include <EVENT/LCCollection.h>
-//#include <EVENT/SimTrackerHit.h>
-//#include <EVENT/MCParticle.h>
-//#include <IMPL/LCCollectionVec.h>
-//#include <IMPL/TrackerHitImpl.h>
-#include <UTIL/BitField64.h>
-#include <edm4hep/MCParticle.h>
-#include "edm4hep/SimTrackerHit.h"
-
-#include "CLHEP/Vector/TwoVector.h"
-class Voxel_tpc;
-
-
-
-
-//using namespace lcio ;
-//using namespace marlin ;
-//#ifdef MARLIN_USE_AIDA
-//using namespace AIDA ;
-//#endif
-
-
-namespace gear { class GearMgr; }
-class IEventSeeder;
-
-/** ====== TPCPixelClusteringDigiAlg ====== <br>
- *
- * This algorithm depends on Circle.h from MarlinUtil
- *
- * Caution: This digitiser presently does not process space-point like SimTrackerHits which have been flagged with CellIDs set to the negetive row number. This must be implemented in future.
- *Produces TPC TrackerHit collection from SimTrackerHit collection, smeared in r-phi and z.
- * Double hits are identified but are currently not added to the collection. This may be change
- * at a later date when criteria for their seperation is defined. The resolutions are defined in
- * the GEAR stearing file.
- *
- * Resolution in r-phi is calculated according to the formular <br>
- * sigma_{point}^2 = sigma_0^2 + Cd^2/N_{eff} * L_{drift}
- * Cd^2/N_{eff}} = 25^2/(22/sin(theta)*h/6mm)
- * Cd = 25 ( microns / cm^(1/2) )
- * (this is for B=4T, h is the pad height = pad-row pitch in mm,
- * theta is the polar angle)
- *
- * At the moment resolution in z assumed to be independent of drift length. <br>
- *
- * The type of TPC TrackerHit is set to 500 via method TrackerHitImpl::setType(int type) <br>
- * <h4>Input collections and prerequisites</h4>
- * Algorithm requires collections of SimTrackerHits in TPC <br>
- * <h4>Output</h4>
- * Processor produces collection of digitized TrackerHits in TPC <br>
- * @param CollectionName The name of input SimTrackerHit collection <br>
- * (default name STpc01_TPC)
- * @param RejectCellID0 Whether or not to reject SimTrackerHits with Cell ID 0. Mokka drivers
- * TPC00-TPC03 encoded the pad row number in the cell ID, which should always be non-zero anyway.
- * Drivers TPC04 and TPC05 do not simulate pad rows and thus have the cell ID set to zero for all hits.
- * You will need to set RejectCellID0 to 0 in order to use this processor with these drivers, but note
- * that the implications for track reconstruction are not strictly defined. Mokka driver TPC06 uses
- * a mixed approach with one hit per pad row having non-zero cell ID, extra hits having 0 cell ID.
- * Typically, unless you use TPC04 or TPC05, you should not touch this parameter. <br>
- * (default value 1)
- * @param TPCTrackerHitsCol The name of output collection of TrackerHits <br>
- * (default name TPCTrackerHits) <br>
- * <br>
- * @authors S. Aplin, DESY and A.Raspereza, MPI
- *
- * Changed 7/9/07 so that the const and diffusion resolution terms are taken as processor parameters rather than the gear file.
- * The parameters _pixZ and pixRP were also changed from gear parameters to processor parameters
- * clare.lynch@bristol.ac.uk
- *
- */
-class TPCPixelClusteringDigiAlg: public GaudiAlgorithm{
-
-public:
-
-  TPCPixelClusteringDigiAlg(const std::string& name, ISvcLocator* svcLoc);
-
-  /** Called at the begin of the job before anything is read.
-   * Use to initialize the processor, e.g. book histograms.
-   */
-  virtual StatusCode initialize();
-
-  /** Called for every event - the working horse.
-   */
-  virtual StatusCode execute();
-
-  /** Called after data processing for clean up.
-   */
-  virtual StatusCode finalize();
-
-  void writeVoxelToHit( Voxel_tpc* aVoxel) ;
-  void writeMergedVoxelsToHit( std::vector <Voxel_tpc*>* hitList ) ;
-  void plotHelixHitResidual(edm4hep::MCParticle *mcp, CLHEP::Hep3Vector *thisPointRPhi);
-  double getPadPhi( CLHEP::Hep3Vector* thisPointRPhi, CLHEP::Hep3Vector* firstPointRPhi, CLHEP::Hep3Vector* middlePointRPhi, CLHEP::Hep3Vector* lastPointRPhi);
-  double getPadTheta( CLHEP::Hep3Vector* firstPointRPhi, CLHEP::Hep3Vector* middlePointRPhi, CLHEP::Hep3Vector* lastPointRPhi );
-
-protected:
-
-  DataHandle<edm4hep::EventHeaderCollection> _headerCol{"EventHeader", Gaudi::DataHandle::Reader, this};
-  /** Input collection name.
-   */
-  //std::string _padRowHitColName ;
-  //std::string _spacePointColName ;
-  //std::string _lowPtHitscolName ;
-  DataHandle<edm4hep::SimTrackerHitCollection> _padRowHitColHdl{"TPCCollection", Gaudi::DataHandle::Reader, this};
-  DataHandle<edm4hep::SimTrackerHitCollection> _spacePointColHdl{"TPCSpacePointCollection", Gaudi::DataHandle::Reader, this};
-  DataHandle<edm4hep::SimTrackerHitCollection> _lowPtHitsColHdl{"TPCLowPtCollection", Gaudi::DataHandle::Reader, this};
-  //DataHandle<edm4hep::MCParticleCollection> _mcColHdl{"MCParticle", Gaudi::DataHandle::Reader, this};
-
-  /** Output collection name.
-   */
-  DataHandle<edm4hep::TrackerHitCollection> _TPCTrackerHitsColHdl{"TPCTrackerHits", Gaudi::DataHandle::Writer, this};
-  DataHandle<edm4hep::MCRecoTrackerAssociationCollection> _TPCAssColHdl{"TPCTrackerHitAss", Gaudi::DataHandle::Writer, this};
-  edm4hep::TrackerHitCollection* _trkhitVec;
-  edm4hep::MCRecoTrackerAssociationCollection* _relCol;
-
-  bool _use_raw_hits_to_store_simhit_pointer;
-
-  int _rejectCellID0;
-  float _padWidth;
-
-  int _nRun ;
-  int _nEvt ;
-
-  //edm4hep::MCParticle _mcp;
-  //edm4hep::MCParticle _previousMCP;
-  //edm4hep::MCParticle _nextMCP;
-  //edm4hep::MCParticle _nMinus2MCP;
-  //edm4hep::MCParticle _nPlus2MCP;
-
-  //edm4hep::SimTrackerHit _SimTHit;
-  //edm4hep::SimTrackerHit _previousSimTHit;
-  //edm4hep::SimTrackerHit _nextSimTHit;
-  //edm4hep::SimTrackerHit _nPlus2SimHit;
-  //edm4hep::SimTrackerHit _nMinus2SimHit;
-
-  gear::GearMgr* _GEAR;
-  IEventSeeder* _SEEDER;
-  // gsl random number generator
-  gsl_rng * _random ;
-
-  float _pointResoRPhi0; // Coefficient for RPhi point res independant of drift length
-  float _pointResoPadPhi; // Coefficient for the point res dependance on relative phi angle to the pad verticle
-  float _diffRPhi; // Coefficient for the rphi point res dependance on diffusion
-  int   _nEff; // number of effective electrons
-
-
-  float _pointResoZ0; // Coefficient Z point res independant of drift length
-  float _diffZ; // Coefficient for the Z point res dependance on diffusion
-
-  float _binningZ;
-  float _binningRPhi;
-  float _doubleHitResZ;
-  float _doubleHitResRPhi;
-  int _maxMerge;
-
-  int _nRechits;
-
-  std::vector< std::vector <Voxel_tpc *> > _tpcRowHits;
-  // std::map< Voxel_tpc *,edm4hep::SimTrackerHit > _tpcHitMap;
-  std::map< Voxel_tpc *, edm4hep::SimTrackerHit > _tpcHitMap;
-
-  UTIL::BitField64* _cellid_encoder;
-
-  int  _NSimTPCHits;
-  int  _NBackgroundSimTPCHits;
-  int  _NPhysicsSimTPCHits;
-  int  _NPhysicsAbove02GeVSimTPCHits;
-  int  _NPhysicsAbove1GeVSimTPCHits;
-  int  _NRecTPCHits;
-
-  int  _NLostPhysicsTPCHits;
-  int  _NLostPhysicsAbove02GeVPtTPCHits;
-  int  _NLostPhysicsAbove1GeVPtTPCHits;
-  int  _NRevomedHits;
-
-
-//#ifdef DIGIPLOTS
-//  IAnalysisFactory * _AF;
-//  ITreeFactory * _TRF;
-//  ITree * _TREE;
-//  IHistogramFactory * _HF;
-//  IHistogram1D * _phiDiffHisto;
-//  IHistogram1D * _thetaDiffHisto;
-//  IHistogram1D * _phiRelHisto;
-//  IHistogram1D * _thetaRelHisto;
-//
-//  IHistogram1D * _phiDistHisto;
-//  IHistogram1D * _rPhiPullHisto;
-//  IHistogram1D * _rPhiDiffHisto;
-//  IHistogram1D * _zDiffHisto;
-//  IHistogram1D * _zPullHisto;
-//  IHistogram2D * _zSigmaVsZHisto;
-//  IHistogram1D * _zSigmaHisto;
-//  IHistogram1D * _rPhiSigmaHisto;
-//  IHistogram1D * _radiusCheckHisto;
-//  IHistogram1D * _ResidualsRPhiHisto;
-//
-//  IHistogram1D * _NSimTPCHitsHisto;
-//  IHistogram1D * _NBackgroundSimTPCHitsHisto;
-//  IHistogram1D * _NPhysicsSimTPCHitsHisto;
-//  IHistogram1D * _NPhysicsAbove02GeVSimTPCHitsHisto;
-//  IHistogram1D * _NPhysicsAbove1GeVSimTPCHitsHisto;
-//  IHistogram1D * _NRecTPCHitsHisto;
-//
-//  IHistogram1D * _NLostPhysicsTPCHitsHisto;
-//  IHistogram1D * _NLostPhysicsAbove02GeVPtTPCHitsHisto;
-//  IHistogram1D * _NLostPhysicsAbove1GeVPtTPCHitsHisto;
-//  IHistogram1D * _NRevomedHitsHisto;
-//
-//  IHistogram1D * _NKeptPhysicsTPCHitsHistoPercent;
-//  IHistogram1D * _NKeptPhysicsAbove02GeVPtTPCHitsHistoPercent;
-//  IHistogram1D * _NKeptPhysicsAbove1GeVPtTPCHitsHistoPercent;
-//
-//#endif
-
-
-} ;
-#endif
diff --git a/Examples/options/beambkgsim.py b/Examples/options/beambkgsim.py
deleted file mode 100644
index a786b3c7..00000000
--- a/Examples/options/beambkgsim.py
+++ /dev/null
@@ -1,127 +0,0 @@
-#!/usr/bin/env python
-#Author: Zhan Li <lizhan@ihep.ac.cn>
-#Created [2024-03-07 Thu 14:53]
-
-import os
-import sys
-
-import Gaudi.Configuration
-from Configurables import RndmGenSvc, HepRndm__Engine_CLHEP__RanluxEngine_, k4DataSvc, GeomSvc
-from Configurables import TimeProjectionChamberSensDetTool
-from Configurables import GenAlgo
-from Configurables import GtGunTool
-from Configurables import StdHepRdr
-from Configurables import SLCIORdr
-from Configurables import HepMCRdr
-from Configurables import GenPrinter
-from Configurables import GtBeamBackgroundTool
-from Configurables import DetSimSvc
-from Configurables import DetSimAlg
-from Configurables import AnExampleDetElemTool
-from Configurables import PodioOutput
-from Configurables import ApplicationMgr
-
-seed = [42]
-
-rndmengine = Gaudi.Configuration.HepRndm__Engine_CLHEP__HepJamesRandom_("RndmGenSvc.Engine")
-rndmengine.SetSingleton = True
-rndmengine.Seeds = seed
-
-rndmgensvc = RndmGenSvc("RndmGenSvc")
-rndmgensvc.Engine = rndmengine.name()
-
-
-dsvc = k4DataSvc("EventDataSvc")
-#geometry_option = "CepC_v4-onlyVXD.xml"
-#geometry_option = "CepC_v4_onlyTracker.xml"
-geometry_option = "CepC_v4.xml"
-
-geometry_path = os.path.join(os.getenv("DETCEPCV4ROOT"), "compact", geometry_option)
-geosvc = GeomSvc("GeomSvc")
-geosvc.compact = geometry_path
-
-#Previously I do not have these 2 lines
-tpc_sensdettool = TimeProjectionChamberSensDetTool("TimeProjectionChamberSensDetTool")
-tpc_sensdettool.TypeOption = 1
-
-
-# Physics Generator
-# ------------------ WARNING ------------------
-# Files for code test only. 
-# Contact @Haoyu Shi (shihy@ihep.ac.cn) for reliable simulation samples and rates. 
-# ---------------------------------------------
-bg = GtBeamBackgroundTool("GtBeamBackgroundTool")
-bg.InputFileMap = {
-  "BGB":"/cefs/higgs/guofy/CEPCSW_BeamBkgSim/run/BGB_Higgs.root",
-  "BGC":"/cefs/higgs/guofy/CEPCSW_BeamBkgSim/run/BGC_Higgs.root",
-  "BTH":"/cefs/higgs/guofy/CEPCSW_BeamBkgSim/run/BTH_Higgs.root",
-#  "TSC":"",
-#  "Pair":"",
-  "SR":"/cefs/higgs/guofy/CEPCSW_BeamBkgSim/run/SRBkg/"
-}
-bg.InputFormatMap = {
-  "BGB":"BeamBackgroundFileParserV1",
-  "BGC":"BeamBackgroundFileParserV1",
-  "BTH":"BeamBackgroundFileParserV1",
-#  "TSC":"BeamBackgroundFileParserV1",
-#  "Pair":"BeamBackgroundFileParserV1",
-  "SR":"BeamBackgroundFileParserV2"
-}
-bg.InputRateMap = {  # in Hz
-  "BGB":1e7, 
-  "BGC":5e6,
-  "BTH":5e5,
-#  "TSC":"5e5",
-#  "Pair":"",
-  "SR":1
-}
-bg.TimeWindow = 1e-6
-bg.InputBeamEnergy = 120.
-bg.RotationAlongYMap = {
-  "BGB":16.5e-3,
-  "BGC":16.5e-3,
-  "BTH":16.5e-3,
-#  "TSC":"16.5e-3",
-#  "Pair":"",
-  "SR":0
-}
-
-gun = GtGunTool("GtGunTool")
-gun.Particles = ["gamma"]
-gun.EnergyMins = [10]
-gun.EnergyMaxs = [10]
-gun.ThetaMins = [90]
-gun.ThetaMaxs = [90]
-gun.PhiMins = [0]
-gun.PhiMaxs = [0]
-
-genprinter = GenPrinter("GenPrinter")
-
-genalg = GenAlgo("GenAlgo")
-genalg.GenTools = ["GtBeamBackgroundTool", "GtGunTool"]
-
-detsimsvc = DetSimSvc("DetSimSvc")
-
-detsimalg = DetSimAlg("DetSimAlg")
-detsimalg.RandomSeeds = seed
-
-
-detsimalg.RunCmds = []
-detsimalg.AnaElems = [
-    "Edm4hepWriterAnaElemTool"
-]
-detsimalg.RootDetElem = "WorldDetElemTool"
-
-example_dettool = AnExampleDetElemTool("AnExampleDetElemTool")
-
-
-# POD I/O
-out = PodioOutput("outputalg")
-out.filename = "beam-SETv0.root"
-out.outputCommands = ["keep *"]
-
-ApplicationMgr( TopAlg = [genalg, detsimalg, out],
-                EvtSel = 'NONE',
-                EvtMax = 3,
-                ExtSvc = [rndmengine, rndmgensvc, dsvc, geosvc],
-)
diff --git a/Examples/options/sim_beambkg.py b/Examples/options/sim_beambkg.py
new file mode 100755
index 00000000..e53127f8
--- /dev/null
+++ b/Examples/options/sim_beambkg.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python
+import os
+from Gaudi.Configuration import * 
+
+from Configurables import k4DataSvc
+dsvc = k4DataSvc("EventDataSvc")
+
+from Configurables import RndmGenSvc, HepRndm__Engine_CLHEP__RanluxEngine_
+
+seed = [556]
+Nevt = 1
+yyy_input = "/cefs/higgs/songwz/summer24/bkgGenerator/pairHiggs/pair4Higgs/"
+yyy_output = '../simdir/sim_bkg_556.root'
+
+# rndmengine = HepRndm__Engine_CLHEP__RanluxEngine_() # The default engine in Gaudi
+rndmengine = HepRndm__Engine_CLHEP__HepJamesRandom_("RndmGenSvc.Engine") # The default engine in Geant4
+rndmengine.SetSingleton = True
+rndmengine.Seeds = seed
+
+rndmgensvc = RndmGenSvc("RndmGenSvc")
+rndmgensvc.Engine = rndmengine.name()
+
+geometry_option = "TDR_o1_v01/TDR_o1_v01.xml"
+
+if not os.getenv("DETCRDROOT"):
+    print("Can't find the geometry. Please setup envvar DETCRDROOT." )
+    sys.exit(-1)
+
+geometry_path = os.path.join(os.getenv("DETCRDROOT"), "compact", geometry_option)
+if not os.path.exists(geometry_path):
+    print("Can't find the compact geometry file: %s"%geometry_path)
+    sys.exit(-1)
+
+from Configurables import GeomSvc
+geosvc = GeomSvc("GeomSvc")
+geosvc.compact = geometry_path
+
+from Configurables import NTupleSvc
+ntsvc = NTupleSvc("NTupleSvc")
+#ntsvc.Output = ["MyTuples DATAFILE='result.root' OPT='NEW' TYP='ROOT'"]
+
+##############################################################################
+# Physics Generator
+##############################################################################
+from Configurables import GenAlgo
+from Configurables import GtGunTool
+from Configurables import StdHepRdr
+from Configurables import SLCIORdr
+from Configurables import HepMCRdr
+from Configurables import GenPrinter
+from Configurables import GtBeamBackgroundTool
+
+# gun = GtGunTool("GtGunTool")
+# gun.Particles =  ["mu-"]
+# gun.PositionXs = [0.]
+# gun.PositionYs = [0.]
+# gun.PositionZs = [0.]
+# gun.EnergyMins = [5.0] # GeV
+# gun.EnergyMaxs = [5.0] # GeV
+# gun.ThetaMins  = [90]   # deg
+# gun.ThetaMaxs  = [90]   # deg
+# gun.PhiMins    = [0]   # deg
+# gun.PhiMaxs    = [0]   # deg
+# gun.Times      = [0.75e3]  # ns
+
+bg = GtBeamBackgroundTool("GtBeamBackgroundTool") 
+Nbunch = 10
+TbunchSpacing = 355. # 50MW, higgs mode, and in ns
+bg.TimeWindow = Nbunch*TbunchSpacing
+bg.InputFileMap = {
+  "BGB":"/cefs/higgs/songwz/summer24/bkgGenerator/singleBeamHiggs/BGB.root",
+  "BGC":"/cefs/higgs/songwz/summer24/bkgGenerator/singleBeamHiggs/BGC.root",
+  "BTH":"/cefs/higgs/songwz/summer24/bkgGenerator/singleBeamHiggs/BTH.root",
+}
+bg.InputFileMap.update(dict([(f'pair{i}', yyy_input) for i in range(Nbunch)]))
+bg.InputFormatMap = {
+  "BGB":"BeamBackgroundFileParserV1",
+  "BGC":"BeamBackgroundFileParserV1",
+  "BTH":"BeamBackgroundFileParserV1",
+}
+bg.InputFormatMap.update(dict([(f'pair{i}', "BeamBackgroundFileParserV2") for i in range(Nbunch)]))
+bg.InputRateMap = {  # in Hz
+  "BGB":83280.65, 
+  "BGC":884002.12,
+  "BTH":623520.09,
+}
+bg.InputBeamEnergy = 120.
+bg.RotationAlongYMap = {
+  "BGB":16.5e-3,
+  "BGC":16.5e-3,
+  "BTH":16.5e-3
+}
+bg.RotationAlongYMap.update(dict([(f'pair{i}', 0) for i in range(Nbunch)]))
+bg.TimeBkgMap = dict([(f'pair{i}', TbunchSpacing*i) for i in range(Nbunch)]) #ns
+bg.NumberMcParticle = { #-1: pair use one file and single beam use rate*time; >=0: fixed number
+  "BGB":-1,
+  "BGC":-1,
+  "BTH":-1
+}
+bg.NumberMcParticle.update(dict([(f'pair{i}', -1) for i in range(Nbunch)]))
+
+# stdheprdr = StdHepRdr("StdHepRdr")
+# stdheprdr.Input = "/cefs/data/stdhep/CEPC240/higgs/Higgs_10M/data/E240.Pnnh_gg.e0.p0.whizard195/nnh_gg.e0.p0.00100.stdhep"
+# stdheprdr.StartTime  = 2.4e3 #ns
+
+# # lciordr = SLCIORdr("SLCIORdr")
+# # lciordr.Input = "/cefs/data/stdhep/lcio250/signal/Higgs/E250.Pbbh.whizard195/E250.Pbbh_X.e0.p0.whizard195/Pbbh_X.e0.p0.00001.slcio"
+# # hepmcrdr = HepMCRdr("HepMCRdr")
+# # hepmcrdr.Input = "example_UsingIterators.txt"
+
+genprinter = GenPrinter("GenPrinter")
+
+genalg = GenAlgo("GenAlgo")
+genalg.GenTools = ["GtBeamBackgroundTool"]
+# genalg.GenTools = ["GtGunTool"]
+# genalg.GenTools = ["StdHepRdr"]
+# genalg.GenTools = ["StdHepRdr", "GenPrinter"]
+# genalg.GenTools = ["SLCIORdr", "GenPrinter"]
+# genalg.GenTools = ["HepMCRdr", "GenPrinter"]
+
+##############################################################################
+# Detector Simulation
+##############################################################################
+from Configurables import DetSimSvc
+detsimsvc = DetSimSvc("DetSimSvc")
+
+from Configurables import DetSimAlg
+detsimalg = DetSimAlg("DetSimAlg")
+detsimalg.RandomSeeds = seed
+# detsimalg.VisMacs = ["vis.mac"]
+detsimalg.RunCmds = [
+#    "/tracking/verbose 2",
+]
+detsimalg.AnaElems = [
+    # example_anatool.name()
+  # "ExampleAnaDoseElemTool",
+    "Edm4hepWriterAnaElemTool"
+]
+detsimalg.RootDetElem = "WorldDetElemTool" 
+detsimalg.PhysicsList = "QGSP_BERT_EMV"
+
+# from Configurables import ExampleAnaDoseElemTool
+# dosesimtool = ExampleAnaDoseElemTool("ExampleAnaDoseElemTool")
+# dosesimtool.Gridnbins = [220, 220, 60]
+# dosesimtool.Coormin = [0, 0, 0]
+# dosesimtool.Coormax = [2200, 2200, 600]
+# # dosesimtool.Gridnbins = [2200,1,4400]
+# # dosesimtool.Coormin = [0,-0.5,-2200]
+# # dosesimtool.Coormax = [2200,0.5,2200]
+# dosesimtool.Regionhist = [20,1.e-12,10]
+# dosesimtool.filename = "output/testdose_yyy_n_"
+# dosesimtool.Dosecofffilename = "dosecoffe/"
+# dosesimtool.HEHadroncut = 0.02
+
+from Configurables import MarlinEvtSeeder
+evtseeder = MarlinEvtSeeder("EventSeeder")
+
+from Configurables import GearSvc
+gearsvc = GearSvc("GearSvc")
+#gearsvc.GearXMLFile = "../../Detector/DetCEPCv4/compact/FullDetGear.xml"
+
+from Configurables import TrackSystemSvc
+tracksystemsvc = TrackSystemSvc("TrackSystemSvc")
+
+from Configurables import AnExampleDetElemTool
+example_dettool = AnExampleDetElemTool("AnExampleDetElemTool")
+
+from Configurables import TimeProjectionChamberSensDetTool
+tpc_sensdettool = TimeProjectionChamberSensDetTool("TimeProjectionChamberSensDetTool")
+tpc_sensdettool.TypeOption = 1
+
+from Configurables import MarlinEvtSeeder
+evtseeder = MarlinEvtSeeder("EventSeeder")
+
+from Configurables import CalorimeterSensDetTool
+from Configurables import DriftChamberSensDetTool
+cal_sensdettool = CalorimeterSensDetTool("CalorimeterSensDetTool")
+cal_sensdettool.CalNamesMergeDisable = ["CaloDetector"]
+# cal_sensdettool.CalNamesApplyBirks = ["HcalBarrel"]
+
+
+# output
+from Configurables import PodioOutput
+out = PodioOutput("outputalg")
+out.filename = yyy_output
+out.outputCommands = ["keep *"]
+
+# ApplicationMgr
+from Configurables import ApplicationMgr
+ApplicationMgr(
+    TopAlg = [genalg, detsimalg, out], #digiVXD, digiSIT, digiSET, digiFTD, spSET, digiTPC, tracking, forward, subset, full, 
+    EvtSel = 'NONE',
+    EvtMax = Nevt,
+    ExtSvc = [rndmengine, rndmgensvc, dsvc, evtseeder, geosvc, gearsvc, tracksystemsvc],
+    #ExtSvc = [rndmengine, rndmgensvc, dsvc, geosvc],
+    OutputLevel=INFO
+)
diff --git a/Generator/src/BeamBackgroundFileParserV1.cpp b/Generator/src/BeamBackgroundFileParserV1.cpp
index 478aca94..a39d2cd3 100644
--- a/Generator/src/BeamBackgroundFileParserV1.cpp
+++ b/Generator/src/BeamBackgroundFileParserV1.cpp
@@ -10,7 +10,8 @@ BeamBackgroundFileParserV1::BeamBackgroundFileParserV1(const std::string& filena
                                                        const std::string& treename,
                                                        double beam_energy, 
                                                        double rate,
-                                                       double timewindow ) {
+                                                       double timewindow,
+                                                       int Nmcp) {
 
 
     m_inputFile.reset(TFile::Open(filename.c_str(), "read"));
@@ -28,12 +29,12 @@ BeamBackgroundFileParserV1::BeamBackgroundFileParserV1(const std::string& filena
     m_readTree->SetBranchAddress("dp", &dp);
     m_readTree->SetBranchAddress("dz", &dz);
     m_readTree->SetBranchAddress("pid", &pid);
-    if(m_readTree->GetBranch("cosz")){
-      m_readTree->SetBranchAddress("cosz", &cosz);
-    }
+    m_readTree->SetBranchAddress("charge", &charge);
+    m_readTree->SetBranchAddress("cosz", &cosz);
 
     m_rate = rate;
     m_timewindow = timewindow;
+    m_Nmcp = Nmcp;
 }
 
 bool BeamBackgroundFileParserV1::load(IBeamBackgroundFileParser::BeamBackgroundData& result, int iEntry) {
@@ -47,23 +48,17 @@ bool BeamBackgroundFileParserV1::load(IBeamBackgroundFileParser::BeamBackgroundD
         m_readTree->GetEntry(iEntry);
 
         double p = m_beam_energy*(1+dp);
-
-        // Now, we get a almost valid data
         const double m2mm = 1e3; // convert from m to mm
-        result.pdgid = pid;
         result.x     = x * m2mm;
         result.y     = y * m2mm;
         result.z     = (z+dz) * m2mm;
-
         result.px    = p * cosx;
         result.py    = p * cosy;
-        if(m_readTree->GetBranch("cosz")){
-        result.pz    = p * cosz;}
-        else{
-        result.pz    = p * std::sqrt(1-cosx*cosx-cosy*cosy);}
-
+        result.pz    = p * cosz;
         result.mass  = 0.000511; // assume e-/e+, mass is 0.511 MeV
-
+        result.t = CLHEP::RandFlat::shoot(0., m_timewindow); // uniformly distributed within a time window, and ns
+        result.pdgid = pid;
+        result.charge = charge;
         return true;
 
     }
@@ -73,8 +68,11 @@ bool BeamBackgroundFileParserV1::load(IBeamBackgroundFileParser::BeamBackgroundD
 
 bool BeamBackgroundFileParserV1::SampleParticleNum(int& npart, int& start ){
 
-  npart = int(m_rate * m_timewindow);
-  npart = CLHEP::RandPoisson::shoot(npart);
+  if(m_Nmcp==-1){
+    npart = int(m_rate * m_timewindow);
+    npart = CLHEP::RandPoisson::shoot(npart);
+  }
+  else npart = m_Nmcp;
 
   int nTotPartInFile = m_readTree->GetEntries();
   if(npart>nTotPartInFile){
diff --git a/Generator/src/BeamBackgroundFileParserV1.h b/Generator/src/BeamBackgroundFileParserV1.h
index 6485207c..bba73ab1 100644
--- a/Generator/src/BeamBackgroundFileParserV1.h
+++ b/Generator/src/BeamBackgroundFileParserV1.h
@@ -8,7 +8,7 @@
 
 class BeamBackgroundFileParserV1: public IBeamBackgroundFileParser {
 public:
-    BeamBackgroundFileParserV1(const std::string& filename, const std::string& treename, double beam_energy, double rate, double timewindow);
+    BeamBackgroundFileParserV1(const std::string& filename, const std::string& treename, double beam_energy, double rate, double timewindow, int Nmcp);
 
     bool load(IBeamBackgroundFileParser::BeamBackgroundData&) { return 0; }
     bool load(IBeamBackgroundFileParser::BeamBackgroundData&, int iEntry);
@@ -21,9 +21,10 @@ private:
     double m_beam_energy;
     double m_rate;
     double m_timewindow; 
+    int m_Nmcp;
 
     double x, y, z, cosx, cosy, dz, dp, cosz;
-    int pid;
+    int pid, charge;
 
 };
 
diff --git a/Generator/src/BeamBackgroundFileParserV2.cpp b/Generator/src/BeamBackgroundFileParserV2.cpp
index 748ed9b8..f9de4bf5 100644
--- a/Generator/src/BeamBackgroundFileParserV2.cpp
+++ b/Generator/src/BeamBackgroundFileParserV2.cpp
@@ -6,7 +6,9 @@
 
 BeamBackgroundFileParserV2::BeamBackgroundFileParserV2(const std::string& dirpath,
                                                        const std::string& treename,
-                                                       double beam_energy) {
+                                                       double beam_energy,
+                                                       double beam_time,
+                                                       int Nmcp) {
 
     //Choose one root file from the dir
     std::vector<std::string> rootFiles;
@@ -20,18 +22,23 @@ BeamBackgroundFileParserV2::BeamBackgroundFileParserV2(const std::string& dirpat
     int randomIndex = CLHEP::RandFlat::shoot(0., rootFiles.size()-1.);
     std::cout << "  Chosen file index "<<randomIndex<<", file name "<<rootFiles[randomIndex]<<std::endl;
     m_inputFile.reset(TFile::Open(rootFiles[randomIndex].c_str(), "read"));
-    std::cout << "BeamBackgroundFileParserV1: readin file name "<<m_inputFile->GetName()<<", status " << m_inputFile->IsOpen() << std::endl;
+    std::cout << "BeamBackgroundFileParserV2: readin file name "<<m_inputFile->GetName()<<", status " << m_inputFile->IsOpen() << std::endl;
     m_readTree = (TTree*)m_inputFile->Get(treename.c_str());
     std::cout << "  Get Tree: entries " << m_readTree->GetEntries() << std::endl;
     m_beam_energy = beam_energy;
-
-    m_readTree->SetBranchAddress("X", &x);
-    m_readTree->SetBranchAddress("Y", &y);
-    m_readTree->SetBranchAddress("Z", &z);
-    m_readTree->SetBranchAddress("PX", &px);
-    m_readTree->SetBranchAddress("PY", &py);
-    m_readTree->SetBranchAddress("DP", &dp);
-    m_readTree->SetBranchAddress("PID", &pid);
+    m_beam_time = beam_time;
+    m_Nmcp = Nmcp;
+
+    m_readTree->SetBranchAddress("x", &x);
+    m_readTree->SetBranchAddress("y", &y);
+    m_readTree->SetBranchAddress("z", &z);
+    m_readTree->SetBranchAddress("cosx", &cosx);
+    m_readTree->SetBranchAddress("cosy", &cosy);
+    m_readTree->SetBranchAddress("dp", &dp);
+    m_readTree->SetBranchAddress("dz", &dz);
+    m_readTree->SetBranchAddress("pid", &pid);
+    m_readTree->SetBranchAddress("charge", &charge);
+    m_readTree->SetBranchAddress("cosz", &cosz);    
 
 }
 
@@ -47,17 +54,16 @@ bool BeamBackgroundFileParserV2::load(IBeamBackgroundFileParser::BeamBackgroundD
 
         double p = m_beam_energy*(1+dp);
         const double m2mm = 1e3; // convert from m to mm
-        result.pdgid = (int)pid;
         result.x     = x * m2mm;
         result.y     = y * m2mm;
-        result.z     = z * m2mm;
-
-        result.px    = px;
-        result.py    = py;
-        result.pz    = sqrt(p*p - px*px - py*py);
-
-        if(pid==22) result.mass  = 0.;
-        else result.mass  = 0.000511; // assume e-/e+, mass is 0.511 MeV
+        result.z     = (z+dz) * m2mm;
+        result.px    = p * cosx;
+        result.py    = p * cosy;
+        result.pz    = p * cosz;
+        result.mass  = 0.000511; // assume e-/e+, mass is 0.511 MeV
+        result.t = m_beam_time; // bunch time
+        result.pdgid = pid;
+        result.charge = charge;
 
         return true;
 
@@ -67,8 +73,12 @@ bool BeamBackgroundFileParserV2::load(IBeamBackgroundFileParser::BeamBackgroundD
 }
 
 bool BeamBackgroundFileParserV2::SampleParticleNum(int& npart, int& start ){
-  npart = m_readTree->GetEntries();
-  start = 0;
 
+  if(m_Nmcp==-1){
+    npart = m_readTree->GetEntries();
+  }
+  else npart = m_Nmcp;
+
+  start = 0;
   return true;
 }
diff --git a/Generator/src/BeamBackgroundFileParserV2.h b/Generator/src/BeamBackgroundFileParserV2.h
index 0f9cb260..7799d94e 100644
--- a/Generator/src/BeamBackgroundFileParserV2.h
+++ b/Generator/src/BeamBackgroundFileParserV2.h
@@ -10,7 +10,7 @@
 
 class BeamBackgroundFileParserV2: public IBeamBackgroundFileParser {
 public:
-    BeamBackgroundFileParserV2(const std::string& dirname, const std::string& treename, double beam_energy);
+    BeamBackgroundFileParserV2(const std::string& dirname, const std::string& treename, double beam_energy, double beam_time, int Nmcp);
 
     bool load(IBeamBackgroundFileParser::BeamBackgroundData&) { return 0; }
     bool load(IBeamBackgroundFileParser::BeamBackgroundData&, int iEntry);
@@ -20,9 +20,11 @@ public:
 private:
     std::unique_ptr<TFile> m_inputFile;
     TTree* m_readTree;
-    double m_beam_energy;
+    double m_beam_energy, m_beam_time;;
+    int m_Nmcp;
 
-    float x, y, z, px, py, dp, pid;
+    double x, y, z, cosx, cosy, dz, dp, cosz;
+    int pid, charge;
 
 };
 
diff --git a/Generator/src/GtBeamBackgroundTool.cpp b/Generator/src/GtBeamBackgroundTool.cpp
index 82847a23..cd8a3727 100644
--- a/Generator/src/GtBeamBackgroundTool.cpp
+++ b/Generator/src/GtBeamBackgroundTool.cpp
@@ -136,8 +136,12 @@ bool GtBeamBackgroundTool::init_BeamBackgroundFileParserV1(const std::string& la
       error() << "Did not find the beam process rate for: " << label << endmsg;
       return false;
     }
-
-    m_beaminputs[label] = std::make_shared<BeamBackgroundFileParserV1>(inputfn, "BeamTree", m_Ebeam, itRate->second, m_timewindow);
+    auto itNmcp = m_Nmcpmaps.find(label);
+    if(itNmcp == m_Nmcpmaps.end()){ 
+      error() << "Did not find the beam process Nmcp for: " << label << endmsg;
+      return false;
+    }
+    m_beaminputs[label] = std::make_shared<BeamBackgroundFileParserV1>(inputfn, "BeamTree", m_Ebeam, itRate->second, m_timewindow, itNmcp->second);
 
     return true;
 }
@@ -146,7 +150,17 @@ bool GtBeamBackgroundTool::init_BeamBackgroundFileParserV2(const std::string& la
                                                            const std::string& inputfn) {
 
     info() << "Initializing beam background ... " << label << inputfn <<endmsg;
-    m_beaminputs[label] = std::make_shared<BeamBackgroundFileParserV2>(inputfn, "RBBG", m_Ebeam);
+    auto itTime = m_timebkgmaps.find(label);
+    if(itTime == m_timebkgmaps.end()){ 
+      error() << "Did not find the beam process time for: " << label << endmsg;
+      return false;
+    }
+    auto itNmcp = m_Nmcpmaps.find(label);
+    if(itNmcp == m_Nmcpmaps.end()){ 
+      error() << "Did not find the beam process Nmcp for: " << label << endmsg;
+      return false;
+    }
+    m_beaminputs[label] = std::make_shared<BeamBackgroundFileParserV2>(inputfn, "BeamTree", m_Ebeam, itTime->second, itNmcp->second);
 
     return true;
 }
diff --git a/Generator/src/GtBeamBackgroundTool.h b/Generator/src/GtBeamBackgroundTool.h
index 6affcb85..40a6ce66 100644
--- a/Generator/src/GtBeamBackgroundTool.h
+++ b/Generator/src/GtBeamBackgroundTool.h
@@ -67,6 +67,12 @@ private:
     // Rotation along Y for single beam background. Unit: rad
     Gaudi::Property<std::map<std::string, double>>      m_rotYMap {this, "RotationAlongYMap"};
 
+    // Time of bunch crossing. Unit: ns, consistent with simulation
+    Gaudi::Property<std::map<std::string, double>>      m_timebkgmaps{this, "TimeBkgMap"};
+
+    // Number of McParticles in different beambkg. -1: pair use one file and single beam use rate*time; >=0: fixed number
+    Gaudi::Property<std::map<std::string, int>>      m_Nmcpmaps{this, "NumberMcParticle"};
+
 private:
     std::map<std::string, std::shared_ptr<IBeamBackgroundFileParser>> m_beaminputs;
 
diff --git a/Generator/src/GtGunTool.cpp b/Generator/src/GtGunTool.cpp
index 7f9b56a3..48697c0b 100644
--- a/Generator/src/GtGunTool.cpp
+++ b/Generator/src/GtGunTool.cpp
@@ -95,6 +95,15 @@ GtGunTool::initialize() {
         return StatusCode::FAILURE;
     }
 
+    // Time
+    if (m_times.value().size()==0){
+      for(int i=0; i<m_particles.value().size(); i++) m_times.value().push_back(0);
+    }
+    else if (m_times.value().size() != m_particles.value().size()) {
+        error() << "Mismatched times and particles." << endmsg;
+        return StatusCode::FAILURE;
+    }
+
     return sc;
 }
 
@@ -163,7 +172,9 @@ GtGunTool::mutate(MyHepMC::GenEvent& event) {
         mcp.setGeneratorStatus(1);
         mcp.setSimulatorStatus(1);
         mcp.setCharge(static_cast<float>(charge));
-        mcp.setTime(0.0);
+        mcp.setTime(m_times.value()[i]);
+        //if (i<m_times.value().size()) { mcp.setTime(m_times.value()[i]); }
+        //else { mcp.setTime(0.0); }
         mcp.setMass(mass);
 
         // Unit is mm
diff --git a/Generator/src/GtGunTool.h b/Generator/src/GtGunTool.h
index d4b8d9f8..0f3f7716 100644
--- a/Generator/src/GtGunTool.h
+++ b/Generator/src/GtGunTool.h
@@ -57,6 +57,9 @@ private:
     Gaudi::Property<std::vector<double>> m_phimins{this, "PhiMins"};
     Gaudi::Property<std::vector<double>> m_phimaxs{this, "PhiMaxs"};
 
+    // For time
+    Gaudi::Property<std::vector<double>> m_times{this, "Times"};
+
 };
 
 
diff --git a/Generator/src/StdHepRdr.cpp b/Generator/src/StdHepRdr.cpp
index 4255e4bd..2eccb9f1 100644
--- a/Generator/src/StdHepRdr.cpp
+++ b/Generator/src/StdHepRdr.cpp
@@ -48,7 +48,7 @@ bool StdHepRdr::mutate(MyHepMC::GenEvent& event){
         mcp.setGeneratorStatus    (mc->getGeneratorStatus());
         mcp.setSimulatorStatus    (mc->getSimulatorStatus());
         mcp.setCharge             (mc->getCharge());
-        mcp.setTime               (mc->getTime());
+        mcp.setTime               (m_starttime + mc->getTime());
         mcp.setMass               (mc->getMass());
         mcp.setVertex             (mc->getVertex()); 
         mcp.setEndpoint           (mc->getEndpoint());
diff --git a/Generator/src/StdHepRdr.h b/Generator/src/StdHepRdr.h
index 7517430b..4796eeda 100644
--- a/Generator/src/StdHepRdr.h
+++ b/Generator/src/StdHepRdr.h
@@ -34,6 +34,7 @@ private:
 
     // input file name
     Gaudi::Property<std::string> m_filename{this, "Input"};
+    Gaudi::Property<double> m_starttime{this, "StartTime", 0, "Default start time"};    
 
 };
 
diff --git a/README.md b/README.md
index a438dd0d..12cca584 100644
--- a/README.md
+++ b/README.md
@@ -1,18 +1,21 @@
 # [CEPCSW](https://cepc.github.io/CEPCSW/)
 
-[![Build Status](https://www.travis-ci.com/cepc/CEPCSW.svg?branch=master)](https://www.travis-ci.com/cepc/CEPCSW)
-[![CI](https://github.com/cepc/CEPCSW/workflows/CI/badge.svg?branch=master)](https://github.com/cepc/CEPCSW/actions)
+[![pipeline status at GitLab](https://code.ihep.ac.cn/cepc/CEPCSW/badges/master/pipeline.svg)](https://code.ihep.ac.cn/cepc/CEPCSW/-/commits/master)
+[![CI at GitHub](https://github.com/cepc/CEPCSW/workflows/CI/badge.svg?branch=master)](https://github.com/cepc/CEPCSW/actions)
 
 CEPC offline software prototype based on [Key4hep](https://github.com/key4hep).
 
 ## Quick start
 
-SSH to lxslc7 (CentOS 7).
+SSH to lxlogin (Alma Linux 9) and start the container CentOS 7:
+```
+$ /cvmfs/container.ihep.ac.cn/bin/hep_container shell CentOS7
+```
 
 Before run following commands, please make sure you setup the CVMFS:
 
 ```
-$ git clone git@github.com:cepc/CEPCSW.git
+$ git clone git@code.ihep.ac.cn:cepc/CEPCSW.git
 $ cd CEPCSW
 $ git checkout master # branch name
 $ source setup.sh
@@ -35,11 +38,3 @@ $ ./run.sh Examples/options/helloalg.py
 * Reconstruction: Reconstruction
 
 
-## Conventions for collections
-Keep the collection names compatible between the prototype and the existing CEPC software.
-
-* MCParticle
-* VXDCollection
-* SITCollection
-* TPCCollection
-* SETCollection
diff --git a/Reconstruction/CMakeLists.txt b/Reconstruction/CMakeLists.txt
index 793d8828..20058b1c 100644
--- a/Reconstruction/CMakeLists.txt
+++ b/Reconstruction/CMakeLists.txt
@@ -6,3 +6,4 @@ add_subdirectory(Tracking)
 add_subdirectory(RecGenfitAlg)
 add_subdirectory(RecAssociationMaker)
 add_subdirectory(ParticleID)
+add_subdirectory(CrystalCaloRec)
diff --git a/Reconstruction/CrystalCaloRec/CMakeLists.txt b/Reconstruction/CrystalCaloRec/CMakeLists.txt
new file mode 100644
index 00000000..d577cb45
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/CMakeLists.txt
@@ -0,0 +1,68 @@
+# Modules
+gaudi_add_library(CrystalCaloRecLib
+                  SOURCES src/Objects/CaloUnit.cc
+                          src/Objects/CaloHit.cc
+                          src/Objects/Calo1DCluster.cc
+                          src/Objects/Calo2DCluster.cc
+                          src/Objects/Calo3DCluster.cc
+                          src/Objects/CaloHalfCluster.cc
+                          src/Objects/Track.cc
+                          src/Objects/HoughSpace.cc
+                          src/Objects/HoughObject.cc
+                          src/Objects/PFObject.cc
+                  LINK    k4FWCore::k4FWCore
+                          GearSvc
+                          CrystalEcalSvcLib
+                          DetInterface
+                          Gaudi::GaudiKernel
+                          Gaudi::GaudiAlgLib
+                          ${CLHEP_LIBRARIES}
+                          ${GEAR_LIBRARIES}
+                          ${GSL_LIBRARIES}
+                          ${LCIO_LIBRARIES}
+                          ${ROOT_LIBRARIES}
+                          EDM4HEP::edm4hep EDM4HEP::edm4hepDict
+
+)
+
+gaudi_add_module(CrystalCaloRec
+                 SOURCES src/PandoraPlusPFAlg.cpp
+                         src/PandoraPlusDataCol.cpp
+                         src/Tools/MCParticleCreator.cpp
+                         src/Tools/TrackCreator.cpp
+                         src/Tools/CaloHitsCreator.cpp
+                         src/Tools/OutputCreator.cpp
+                         src/Tools/TrackFitInEcal.cpp
+                         src/Algorithm/ExampleAlg.cpp
+                         src/Algorithm/TrackExtrapolatingAlg.cpp
+                         src/Algorithm/GlobalClusteringAlg.cpp
+                         src/Algorithm/LocalMaxFindingAlg.cpp
+                         src/Algorithm/TrackMatchingAlg.cpp
+                         src/Algorithm/HoughClusteringAlg.cpp
+                         src/Algorithm/ConeClustering2DAlg.cpp
+                         src/Algorithm/AxisMergingAlg.cpp
+                         src/Algorithm/EnergySplittingAlg.cpp
+                         src/Algorithm/EnergyTimeMatchingAlg.cpp
+                         src/Algorithm/HcalClusteringAlg.cpp
+                         src/Algorithm/ConeClusteringAlg.cpp
+                         src/Algorithm/PFOCreatingAlg.cpp
+                         src/Algorithm/TrackClusterConnectingAlg.cpp
+                         src/Algorithm/PFOReclusteringAlg.cpp
+                         src/Algorithm/TruthTrackMatchingAlg.cpp
+                         src/Algorithm/TruthMatchingAlg.cpp
+                         src/Algorithm/TruthPatternRecAlg.cpp
+                         src/Algorithm/TruthEnergySplittingAlg.cpp
+                         src/Algorithm/TruthClusteringAlg.cpp
+                         src/Algorithm/TruthClusterMergingAlg.cpp
+                 LINK    CrystalCaloRecLib
+)
+target_include_directories(CrystalCaloRec PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>/include
+  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+
+install(TARGETS CrystalCaloRecLib CrystalCaloRec
+  EXPORT CEPCSWTargets
+  RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin
+  LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shlib
+  COMPONENT dev)
+
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/AxisMergingAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/AxisMergingAlg.h
new file mode 100644
index 00000000..b93c4025
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/AxisMergingAlg.h
@@ -0,0 +1,48 @@
+#ifndef _AXISMERGING_ALG_H
+#define _AXISMERGING_ALG_H
+
+#include "Tools/Algorithm.h"
+
+using namespace PandoraPlus;
+class AxisMergingAlg: public PandoraPlus::Algorithm{
+public: 
+
+  AxisMergingAlg(){};
+  ~AxisMergingAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new AxisMergingAlg(); } 
+
+  };
+
+  StatusCode ReadSettings( PandoraPlus::Settings& m_settings );
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  //Self defined algorithms
+  StatusCode TrkMatchedMerging( std::vector<PandoraPlus::CaloHalfCluster*>& m_axisCol );
+  StatusCode OverlapMerging   ( std::vector<PandoraPlus::CaloHalfCluster*>& m_axisCol );
+  StatusCode BranchMerging   ( std::vector<PandoraPlus::CaloHalfCluster*>& m_axisCol );     // yyy: trying to merge fake photon to track axis
+  StatusCode ConeMerging      ( std::vector<PandoraPlus::CaloHalfCluster*>& m_axisCol );
+  StatusCode FragmentsMerging ( std::vector<PandoraPlus::CaloHalfCluster*>& m_axisCol );
+  bool MergeToClosestCluster( PandoraPlus::CaloHalfCluster* m_badaxis, std::vector<PandoraPlus::CaloHalfCluster*>& m_axisCol );
+
+private: 
+
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>* p_HalfClusterU = nullptr;
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>* p_HalfClusterV = nullptr;
+
+  std::vector<const PandoraPlus::CaloHalfCluster*> m_axisUCol;
+  std::vector<const PandoraPlus::CaloHalfCluster*> m_axisVCol;
+  std::vector<PandoraPlus::CaloHalfCluster*> m_newAxisUCol;
+  std::vector<PandoraPlus::CaloHalfCluster*> m_newAxisVCol;
+
+  static bool compLayer( const PandoraPlus::CaloHalfCluster* sh1, const PandoraPlus::CaloHalfCluster* sh2 )
+    { return sh1->getBeginningDlayer() < sh2->getBeginningDlayer(); }
+
+};
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/ClusterMergingAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/ClusterMergingAlg.h
new file mode 100644
index 00000000..8669f61e
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/ClusterMergingAlg.h
@@ -0,0 +1,31 @@
+#ifndef _CLUSTERMERGING_ALG_H
+#define _CLUSTERMERGING_ALG_H
+
+#include "Tools/Algorithm.h"
+
+using namespace PandoraPlus;
+class ClusterMergingAlg: public PandoraPlus::Algorithm{
+public: 
+
+  ClusterMergingAlg(){};
+  ~ClusterMergingAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new ClusterMergingAlg(); } 
+
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  //Self defined algorithms
+  StatusCode SelfAlg1(); 
+
+private: 
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/ConeClustering2DAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/ConeClustering2DAlg.h
new file mode 100644
index 00000000..cf1d9ada
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/ConeClustering2DAlg.h
@@ -0,0 +1,45 @@
+#ifndef _CONECLUSTERING2D_ALG_H
+#define _CONECLUSTERING2D_ALG_H
+
+#include "PandoraPlusDataCol.h"
+#include "Tools/Algorithm.h"
+#include "TMath.h"
+
+using namespace PandoraPlus;
+class ConeClustering2DAlg: public PandoraPlus::Algorithm{
+public: 
+
+  ConeClustering2DAlg(){};
+  ~ConeClustering2DAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new ConeClustering2DAlg(); } 
+
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  //Self defined algorithms
+  StatusCode LongiConeLinking( std::map<int, std::vector<const PandoraPlus::Calo1DCluster*> >& orderedShower, 
+                               std::vector<PandoraPlus::CaloHalfCluster*>& ClusterCol, 
+                               std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& bk_HFclus );
+  TVector2 GetProjectedAxis( const PandoraPlus::CaloHalfCluster* m_shower );
+  TVector2 GetProjectedRelR( const PandoraPlus::Calo1DCluster* m_shower1, const PandoraPlus::Calo1DCluster* m_shower2 );
+
+private: 
+
+  std::vector<PandoraPlus::CaloHalfCluster*> p_HalfClusterV;
+  std::vector<PandoraPlus::CaloHalfCluster*> p_HalfClusterU;
+
+  std::vector<const PandoraPlus::Calo1DCluster*> m_localMaxVCol;
+  std::vector<const PandoraPlus::Calo1DCluster*> m_localMaxUCol;
+  std::vector<const PandoraPlus::CaloHalfCluster*> const_longiClusVCol;
+  std::vector<const PandoraPlus::CaloHalfCluster*> const_longiClusUCol;
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/ConeClusteringAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/ConeClusteringAlg.h
new file mode 100644
index 00000000..56484d89
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/ConeClusteringAlg.h
@@ -0,0 +1,36 @@
+#ifndef _CONECLUSTERING_ALG_H
+#define _CONECLUSTERING_ALG_H
+
+#include "PandoraPlusDataCol.h"
+#include "Tools/Algorithm.h"
+#include "TMath.h"
+using namespace PandoraPlus;
+
+class ConeClusteringAlg: public PandoraPlus::Algorithm {
+public: 
+
+  ConeClusteringAlg(){};
+  ~ConeClusteringAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new ConeClusteringAlg(); }
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol);
+  StatusCode ClearAlgorithm(); 
+
+  StatusCode LongiConeLinking( const std::map<int, std::vector<const PandoraPlus::CaloHit*> >& orderedShower, std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& ClusterCol );
+  //StatusCode MergeGoodClusters( std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_clusCol); 
+  //StatusCode MergeBadToGoodCluster( std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_goodClusCol, std::shared_ptr<PandoraPlus::Calo3DCluster> m_badClus );
+  //PandoraPlus::Calo3DCluster* GetClosestGoodCluster( std::vector< PandoraPlus::Calo3DCluster* >& m_goodClusCol, PandoraPlus::Calo3DCluster* m_badClus );
+
+  //static bool compBegin( PandoraPlus::Calo3DCluster* clus1, PandoraPlus::Calo3DCluster* clus2 ) { return clus1->getBeginningDlayer() < clus2->getBeginningDlayer(); }
+
+private: 
+
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/EnergySplittingAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/EnergySplittingAlg.h
new file mode 100644
index 00000000..94ff532d
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/EnergySplittingAlg.h
@@ -0,0 +1,77 @@
+#ifndef _ENERGYSPLITTING_ALG_H
+#define _ENERGYSPLITTING_ALG_H
+
+#include "PandoraPlusDataCol.h"
+#include "Tools/Algorithm.h"
+#include "TVector3.h"
+#include "TVector.h"
+#include "TMatrix.h"
+
+using namespace PandoraPlus;
+class EnergySplittingAlg: public PandoraPlus::Algorithm{
+public: 
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public:
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new EnergySplittingAlg(); }
+
+  };
+
+  EnergySplittingAlg(){};
+  ~EnergySplittingAlg(){};
+
+  StatusCode ReadSettings( PandoraPlus::Settings& m_settings );
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm(); 
+
+
+  StatusCode LongitudinalLinking( std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>>& m_showers, 
+                                  std::vector<const PandoraPlus::CaloHalfCluster*>& m_oldClusCol, 
+                                  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_newClusCol );
+
+  StatusCode HalfClusterToTowers( std::vector<PandoraPlus::CaloHalfCluster*>& m_halfClusU, 
+                                  std::vector<PandoraPlus::CaloHalfCluster*>& m_halfClusV, 
+                                  std::vector<PandoraPlus::CaloHalfCluster*>& m_emptyClusU,
+                                  std::vector<PandoraPlus::CaloHalfCluster*>& m_emptyClusV,
+                                  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_towers );
+
+  StatusCode ClusterSplitting( const PandoraPlus::Calo1DCluster* m_cluster, 
+                               std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>>& outshCol );
+
+  StatusCode SplitOverlapCluster( std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_HFClusCol );
+
+  StatusCode MergeToClosestCluster( PandoraPlus::Calo1DCluster* iclus, std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>>& clusvec );
+
+  StatusCode MergeToClosestCluster( const PandoraPlus::Calo1DCluster* m_shower, std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_clusters );
+
+  void CalculateInitialEseed( const std::vector<const PandoraPlus::CaloUnit*>& Seeds, const TVector3* pos, double* Eseed);
+
+  double GetShowerProfile(const TVector3& p_bar, const TVector3& p_seed );
+
+
+private: 
+  std::vector<PandoraPlus::CaloHalfCluster*> p_HalfClusterU;
+  std::vector<PandoraPlus::CaloHalfCluster*> p_HalfClusterV;
+  std::vector<PandoraPlus::CaloHalfCluster*> p_emptyHalfClusterU;
+  std::vector<PandoraPlus::CaloHalfCluster*> p_emptyHalfClusterV;  
+  std::vector<const PandoraPlus::CaloHalfCluster*> m_axisUCol; 
+  std::vector<const PandoraPlus::CaloHalfCluster*> m_axisVCol; 
+
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_newClusUCol; 
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_newClusVCol; 
+  std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> m_1dShowerUCol; 
+  std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> m_1dShowerVCol; 
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_towerCol;
+
+  PandoraPlusDataCol m_bkCol;
+
+  //static bool compBar( const PandoraPlus::CaloUnit* bar1, const PandoraPlus::CaloUnit* bar2 )
+  //  { return bar1->getBar() < bar2->getBar(); }
+  static bool compLayer( const PandoraPlus::Calo1DCluster* sh1, const PandoraPlus::Calo1DCluster* sh2 )
+    { return sh1->getDlayer() < sh2->getDlayer(); }
+
+};
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/EnergyTimeMatchingAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/EnergyTimeMatchingAlg.h
new file mode 100644
index 00000000..46d6df6c
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/EnergyTimeMatchingAlg.h
@@ -0,0 +1,83 @@
+#ifndef ETMATCHING_ALG_H
+#define ETMATCHING_ALG_H
+
+#include "PandoraPlusDataCol.h"
+#include "Tools/Algorithm.h"
+
+#include "TVector3.h"
+using namespace PandoraPlus;
+
+class EnergyTimeMatchingAlg: public PandoraPlus::Algorithm{
+
+public: 
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public:
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new EnergyTimeMatchingAlg(); }
+
+  };
+
+  EnergyTimeMatchingAlg(){};
+  ~EnergyTimeMatchingAlg(){};
+
+  StatusCode ReadSettings( PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datasvc );
+  StatusCode ClearAlgorithm(); 
+
+  StatusCode Matching(std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusUCol, 
+                      std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusVCol,
+                      std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_clusterCol );
+
+  StatusCode PatternMatrixCalculation(std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusUCol,
+                                      std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusVCol,
+                                      vector<vector<int>>& matrix );
+
+  StatusCode Chi2MatrixCalculation( std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusUCol,
+                                    std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusVCol,
+                                    vector<vector<double>>& matrix, 
+                                    std::vector< std::pair<int, int> >& chi2order); 
+
+  StatusCode ParameterMatrixCalculation(const int& M, const int& N, vector<vector<int>>& parMatrix);
+
+  StatusCode SimplityMatrix(vector<vector<int>>& parMatrix, vector<double>& Eij, vector<vector<int>>& pattern);
+
+  double SolveMatrix(const int& M, const int& N, vector<vector<int>>& parMatrix, vector<double>& Eij, std::vector<double>& En_clusters); 
+
+  StatusCode leastSquares(const vector<vector<int>>& A, const vector<double>& b, vector<double>& x);
+
+  vector<vector<double>> GetClusterChi2Map( std::vector<std::vector<const PandoraPlus::Calo1DCluster*>>& barShowerUCol,
+                                            std::vector<std::vector<const PandoraPlus::Calo1DCluster*>>& barShowerVCol );
+
+  StatusCode ClusterBuilding( std::vector<std::vector<double>> Ematrix, 
+                              std::vector<const PandoraPlus::CaloHalfCluster*>& m_HFClusUCol, 
+                              std::vector<const PandoraPlus::CaloHalfCluster*>& m_HFClusVCol, 
+                              std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_clusterCol);
+
+  StatusCode XYClusterMatchingL0( const PandoraPlus::CaloHalfCluster* m_longiClU,
+                                  const PandoraPlus::CaloHalfCluster* m_longiClV,
+                                  std::shared_ptr<PandoraPlus::Calo3DCluster>& m_clus );
+
+  StatusCode GetMatchedShowersL0( const PandoraPlus::Calo1DCluster* barShowerU,
+                                  const PandoraPlus::Calo1DCluster* barShowerV,
+                                  PandoraPlus::Calo2DCluster* outsh );
+
+  StatusCode GetMatchedShowersL1( const PandoraPlus::Calo1DCluster* shower1,
+                                  std::vector<const PandoraPlus::Calo1DCluster*>& showerNCol,
+                                  std::vector<PandoraPlus::Calo2DCluster*>& outshCol );
+
+  StatusCode ClusterReconnecting(std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_clusterCol);
+
+
+private: 
+  std::vector<PandoraPlus::Calo3DCluster*> m_towerCol; 
+
+  std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusUCol;
+  std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusVCol;
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_clusterCol; 
+
+  PandoraPlusDataCol m_bkCol;
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/ExampleAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/ExampleAlg.h
new file mode 100644
index 00000000..d4d0733a
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/ExampleAlg.h
@@ -0,0 +1,31 @@
+#ifndef _EXAMPLE_ALG_H
+#define _EXAMPLE_ALG_H
+
+#include "Tools/Algorithm.h"
+
+using namespace PandoraPlus;
+class ExampleAlg: public PandoraPlus::Algorithm{
+public: 
+
+  ExampleAlg(){};
+  ~ExampleAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new ExampleAlg(); } 
+
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  //Self defined algorithms
+  StatusCode SelfAlg1(); 
+
+private: 
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/GlobalClusteringAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/GlobalClusteringAlg.h
new file mode 100644
index 00000000..5c4642b9
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/GlobalClusteringAlg.h
@@ -0,0 +1,53 @@
+#ifndef GLOBALCLUSTERING_ALG_H
+#define GLOBALCLUSTERING_ALG_H
+
+#include "Tools/Algorithm.h"
+
+#include "time.h"
+#include <TTimeStamp.h> 
+#include <ctime>
+
+#include <cstdlib>
+
+using namespace PandoraPlus;
+
+class GlobalClusteringAlg : public PandoraPlus::Algorithm{
+public: 
+
+  GlobalClusteringAlg(){};
+  ~GlobalClusteringAlg(){};
+ 
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public:
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new GlobalClusteringAlg(); }
+ 
+  };
+ 
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings); 
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  template<typename T1, typename T2> StatusCode Clustering(std::vector<std::shared_ptr<T1>>& m_input, std::vector<std::shared_ptr<T2>>& m_output);
+  
+  //geometry construction
+/*   int m_module = settings.map_intPars["m_module"];
+  int m_modulestart = settings.map_intPars["m_modulestart"];
+  int m_part = settings.map_intPars["m_part"];
+  int m_stave = settings.map_intPars["m_stave"];
+  int m_superlayer = settings.map_intPars["m_superlayer"];
+  int m_startnumber = settings.map_intPars["m_startnumber"];
+  int m_phibarnumber = settings.map_intPars["m_phibarnumber"];
+  int m_zbarnumber = settings.map_intPars["m_zbarnumber"]; */
+
+private:
+
+  std::vector<std::shared_ptr<PandoraPlus::CaloUnit>> m_bars; 
+  std::vector<std::shared_ptr<PandoraPlus::CaloUnit>> m_processbars;        
+  std::vector<std::shared_ptr<PandoraPlus::CaloUnit>> m_restbars;           
+  std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> m_1dclusters;    
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_halfclusters; 
+  
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/HcalClusteringAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/HcalClusteringAlg.h
new file mode 100644
index 00000000..31c4c762
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/HcalClusteringAlg.h
@@ -0,0 +1,33 @@
+#ifndef HCALCLUSTERING_ALG_H
+#define HCALCLUSTERING_ALG_H
+
+#include "Tools/Algorithm.h"
+
+using namespace PandoraPlus;
+
+class HcalClusteringAlg : public PandoraPlus::Algorithm{
+public: 
+
+  HcalClusteringAlg(){};
+  ~HcalClusteringAlg(){};
+ 
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public:
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new HcalClusteringAlg(); }
+ 
+  };
+ 
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings); 
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+  
+  template<typename T1, typename T2> StatusCode Clustering(std::vector<T1*>& m_input, std::vector<std::shared_ptr<T2>>& m_output);
+  StatusCode LongiConeLinking( const std::map<int, std::vector<PandoraPlus::CaloHit*> >& orderedHit, std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster> >& ClusterCol );
+
+private:
+
+//   std::vector<std::shared_ptr<PandoraPlus::CaloHit>> m_hcalHits;
+};
+#endif
\ No newline at end of file
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/HoughClusteringAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/HoughClusteringAlg.h
new file mode 100644
index 00000000..1f3940fb
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/HoughClusteringAlg.h
@@ -0,0 +1,49 @@
+#ifndef HOUGHCLUSTERINGALG_H
+#define HOUGHCLUSTERINGALG_H
+
+#include "PandoraPlusDataCol.h"
+#include "Tools/Algorithm.h"
+#include "TVector2.h"
+#include <vector>
+using namespace PandoraPlus;
+
+class HoughClusteringAlg: public PandoraPlus::Algorithm{
+
+public: 
+
+  HoughClusteringAlg () {};
+  ~HoughClusteringAlg() {};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public:
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new HoughClusteringAlg(); }
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  StatusCode HoughTransformation( std::vector<PandoraPlus::HoughObject>& Hobjects );
+  //StatusCode SetLineRange( int module, int slayer, double *range12, double* range34 );
+  StatusCode FillHoughSpace( std::vector<PandoraPlus::HoughObject>& Hobjects, 
+                             PandoraPlus::HoughSpace& Hspace );
+  StatusCode ClusterFinding( std::vector<PandoraPlus::HoughObject>& Hobjects, 
+                             PandoraPlus::HoughSpace& Hspace, 
+                             std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& longiClusCol );
+  StatusCode CleanClusters( std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_longiClusCol);
+  
+
+private:
+	std::vector<PandoraPlus::CaloHalfCluster*> p_HalfClusterV;
+  std::vector<PandoraPlus::CaloHalfCluster*> p_HalfClusterU;
+
+  std::vector<const PandoraPlus::Calo1DCluster*> m_localMaxVCol;
+  std::vector<const PandoraPlus::Calo1DCluster*> m_localMaxUCol;
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_longiClusVCol;
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_longiClusUCol;
+
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/LocalMaxFindingAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/LocalMaxFindingAlg.h
new file mode 100644
index 00000000..0ad55daf
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/LocalMaxFindingAlg.h
@@ -0,0 +1,39 @@
+#ifndef _LOCALMAXFINDING_ALG_H
+#define _LOCALMAXFINDING_ALG_H
+
+#include <set>
+#include "Tools/Algorithm.h"
+using namespace PandoraPlus;
+
+class LocalMaxFindingAlg: public PandoraPlus::Algorithm{
+public: 
+
+  LocalMaxFindingAlg(){};
+  ~LocalMaxFindingAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const { return new LocalMaxFindingAlg(); }
+
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  StatusCode GetLocalMax( const PandoraPlus::Calo1DCluster* m_1dClus, std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>>& m_output);
+  StatusCode GetLocalMaxBar( std::vector<const PandoraPlus::CaloUnit*>& barCol, std::vector<const PandoraPlus::CaloUnit*>& localMaxCol );
+  std::vector<const PandoraPlus::CaloUnit*>  getNeighbors(const PandoraPlus::CaloUnit* seed, std::vector<const PandoraPlus::CaloUnit*>& barCol);
+
+private: 
+
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>* p_HalfClusU = nullptr;
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>* p_HalfClusV = nullptr;
+
+  static bool compBar( const PandoraPlus::CaloUnit* bar1, const PandoraPlus::CaloUnit* bar2 )
+      { return bar1->getBar() < bar2->getBar(); }
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/PFOCreatingAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/PFOCreatingAlg.h
new file mode 100644
index 00000000..0d9b36e7
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/PFOCreatingAlg.h
@@ -0,0 +1,64 @@
+#ifndef _PFOCREATING_ALG_H
+#define _PFOCREATING_ALG_H
+
+#include "Tools/Algorithm.h"
+
+using namespace PandoraPlus;
+class PFOCreatingAlg: public PandoraPlus::Algorithm{
+public: 
+
+  PFOCreatingAlg(){};
+  ~PFOCreatingAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new PFOCreatingAlg(); } 
+
+  };
+
+  StatusCode ReadSettings( PandoraPlus::Settings& m_settings );
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  std::vector<PandoraPlus::Track*> getTracks() const { return m_tracks; }
+  std::vector<PandoraPlus::Calo3DCluster*> getECALClusters() const { return m_ecal_clusters; }
+  std::vector<PandoraPlus::Calo3DCluster*> getHCALClusters() const { return m_hcal_clusters; }
+
+  //Self defined algorithms
+  // Get canditate clusters in HCAL for charged particles
+  StatusCode GetChargedHCALCandidates(const PandoraPlus::Track* _track,
+                                      std::vector<PandoraPlus::Calo3DCluster*>& _hcal_clusters,
+                                      std::vector<PandoraPlus::Calo3DCluster*>& _hcal_clus_candidate);
+  // Get nearby HCAL clusters
+  StatusCode GetNearbyHCALCandidates( PandoraPlus::Calo3DCluster* _ecal_cluster,
+                                  std::vector<PandoraPlus::Calo3DCluster*>& _hcal_clusters,
+                                  std::vector<PandoraPlus::Calo3DCluster*>& _hcal_clus_candidate);
+  // If a neutral cluster in ECAL reach the outermost ECAL boundary
+  bool isReachOuterMostECAL(PandoraPlus::Calo3DCluster* _ecal_cluster);
+  // erase the used_elements in the left_elements
+  template<typename T1, typename T2> StatusCode CleanUsedElements(std::vector<T1>& _used_elements,
+                                                    std::vector<T2>& _left_elements);
+  template<typename T1, typename T2> StatusCode CleanUsedElement(T1 _used_elements,
+                                                    std::vector<T2>& _left_elements);
+  // Create PFO with:
+  //   1. tracks with no clusters in ECAL and HCAL
+  //   2. HCAL clusters
+  StatusCode CreateLeftPFO( std::vector<PandoraPlus::Track*>& _tracks,
+                            std::vector<PandoraPlus::Calo3DCluster*>& _hcal_clusters,
+                            std::vector<std::shared_ptr<PandoraPlus::PFObject>>& _pfobjects);
+  
+  
+
+private: 
+  std::vector<PandoraPlus::Track*> m_tracks;
+  std::vector<PandoraPlus::Calo3DCluster*> m_ecal_clusters;
+  std::vector<PandoraPlus::Calo3DCluster*> m_hcal_clusters;
+
+  std::vector<std::shared_ptr<PandoraPlus::PFObject>> m_pfobjects;
+  
+
+};
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/PFOReclusteringAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/PFOReclusteringAlg.h
new file mode 100644
index 00000000..97bd7048
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/PFOReclusteringAlg.h
@@ -0,0 +1,40 @@
+#ifndef _PFORECLUSTERING_ALG_H
+#define _PFORECLUSTERING_ALG_H
+
+#include "Tools/Algorithm.h"
+
+using namespace PandoraPlus;
+class PFOReclusteringAlg: public PandoraPlus::Algorithm{
+public: 
+
+  PFOReclusteringAlg(){};
+  ~PFOReclusteringAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new PFOReclusteringAlg(); } 
+
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  StatusCode ReCluster_MergeToChg( std::vector< std::shared_ptr<PandoraPlus::PFObject> >& m_chargedPFOs,
+                                   std::vector< std::shared_ptr<PandoraPlus::PFObject> >& m_neutralPFOs );
+
+  StatusCode ReCluster_SplitFromChg( std::vector< std::shared_ptr<PandoraPlus::PFObject> >& m_chargedPFOs,
+                                     std::vector< std::shared_ptr<PandoraPlus::PFObject> >& m_neutralPFOs );
+
+private: 
+  
+  std::vector<std::shared_ptr<PandoraPlus::PFObject>>* p_PFObjects; 
+
+  PandoraPlusDataCol m_bkCol;
+
+  static bool compTrkP( std::shared_ptr<PandoraPlus::PFObject> pfo1, std::shared_ptr<PandoraPlus::PFObject> pfo2 )
+    { return pfo1->getTrackMomentum() > pfo2->getTrackMomentum(); }
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/TrackClusterConnectingAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/TrackClusterConnectingAlg.h
new file mode 100644
index 00000000..483440ca
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/TrackClusterConnectingAlg.h
@@ -0,0 +1,52 @@
+#ifndef _TRACKCLUSTERCONNECTING_ALG_H
+#define _TRACKCLUSTERCONNECTING_ALG_H
+
+#include "Tools/Algorithm.h"
+
+using namespace PandoraPlus;
+class TrackClusterConnectingAlg: public PandoraPlus::Algorithm{
+public: 
+
+  TrackClusterConnectingAlg(){};
+  ~TrackClusterConnectingAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new TrackClusterConnectingAlg(); } 
+
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  //Self defined algorithms
+  double GetMinR2Trk( const PandoraPlus::Calo3DCluster* p_clus, const PandoraPlus::Track* m_trk);
+  StatusCode PFOCreating( std::vector<const PandoraPlus::Calo3DCluster*>& m_clusters,
+                          std::vector<const PandoraPlus::Track*>& m_trks,
+                          std::vector<std::shared_ptr<PandoraPlus::PFObject>>& m_PFOs );
+
+  StatusCode EcalChFragAbsorption( std::vector<const PandoraPlus::Calo3DCluster*>& m_clusters,
+                                   std::vector<const PandoraPlus::Track*>& m_trks,
+                                   std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_newclusCol ); 
+
+  StatusCode HcalExtrapolatingMatch(std::vector<const PandoraPlus::Calo3DCluster*>& m_clusters, std::vector<std::shared_ptr<PandoraPlus::PFObject>>& m_PFOs);
+
+private: 
+
+  std::vector<const PandoraPlus::Calo3DCluster*> m_EcalClusters;
+  std::vector<const PandoraPlus::Calo3DCluster*> m_HcalClusters;
+  std::vector<const PandoraPlus::Track*> m_tracks; 
+
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_absorbedEcal; 
+  std::vector<std::shared_ptr<PandoraPlus::PFObject>> m_PFObjects; 
+
+  PandoraPlusDataCol m_bkCol;
+
+  static bool compTrkP( std::shared_ptr<PandoraPlus::PFObject> pfo1, std::shared_ptr<PandoraPlus::PFObject> pfo2 )
+    { return pfo1->getTrackMomentum() > pfo2->getTrackMomentum(); }
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/TrackExtrapolatingAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/TrackExtrapolatingAlg.h
new file mode 100644
index 00000000..af6db11e
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/TrackExtrapolatingAlg.h
@@ -0,0 +1,76 @@
+#ifndef _TRACKEXTRAPOLATING_ALG_H
+#define _TRACKEXTRAPOLATING_ALG_H
+
+#include "PandoraPlusDataCol.h"
+#include "Tools/Algorithm.h"
+#include "Objects/Track.h"
+
+using namespace PandoraPlus;
+class TrackExtrapolatingAlg: public PandoraPlus::Algorithm{
+public: 
+
+  TrackExtrapolatingAlg(){};
+  ~TrackExtrapolatingAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new TrackExtrapolatingAlg(); } 
+
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  //Self defined algorithms
+  // StatusCode SelfAlg1(); 
+
+  // Get normal vectors of planes in each module
+  StatusCode GetPlaneNormalVector(std::vector<TVector2> & normal_vectors_Ecal, std::vector<TVector2> & normal_vectors_Hcal );
+  // Get points in each plane of layer
+  StatusCode GetLayerPoints(const std::vector<TVector2> & normal_vectors_Ecal, 
+                            const std::vector<TVector2> & normal_vectors_Hcal,
+                            std::vector<std::vector<TVector2>> & ECAL_layer_points, 
+                            std::vector<std::vector<TVector2>> & HCAL_layer_points);
+  // If the track reach barrel ECAL
+  bool IsReachECAL(PandoraPlus::Track * track);
+  // Get track state at calorimeter
+  StatusCode GetTrackStateAtCalo(PandoraPlus::Track * track, 
+                           PandoraPlus::TrackState & trk_state_at_calo);
+  // get extrapolated points
+  StatusCode ExtrapolateByLayer(const std::vector<TVector2> & normal_vectors_Ecal,
+                              const std::vector<TVector2> & normal_vectors_Hcal,
+                              const std::vector<std::vector<TVector2>> & ECAL_layer_points,
+                              const std::vector<std::vector<TVector2>> & HCAL_layer_points,
+                              const PandoraPlus::TrackState & ECAL_trk_state, 
+                              PandoraPlus::Track* p_track);
+  // Get the radius rho 
+  float GetRho(const PandoraPlus::TrackState & trk_state);
+  // Get coordinates of the center of the circle
+  TVector2 GetCenterOfCircle(const PandoraPlus::TrackState & trk_state, const float & rho);
+  // phase from center to reference point
+  float GetRefAlpha0(const PandoraPlus::TrackState & trk_state, const TVector2 & center);
+  // If the charged particle return back 
+  bool IsReturn(float rho, TVector2 & center);
+
+  std::vector<std::vector<float>> GetDeltaPhi(float rho, TVector2 center, float alpha0,
+                                              const std::vector<TVector2> & normal_vectors,
+                                              const std::vector<std::vector<TVector2>> & layer_points, 
+                                              const PandoraPlus::TrackState & CALO_trk_state);
+  std::vector<TVector3> GetExtrapoPoints(std::string calo_name, 
+                                         float rho, TVector2 center, float alpha0, 
+                                         const PandoraPlus::TrackState & CALO_trk_state,
+                                         const std::vector<std::vector<float>>& delta_phi);
+
+    // Get phi0 of extrapolated points. Note that this phi0 is not same as the definition of the phi0 in TrackState, but will be stored in TrackState
+  float GetExtrapolatedPhi0(float Kappa, float ECAL_phi0, TVector2 center, TVector3 ext_point);
+  // To sort the extrapolatedpoints, define the following comparison function
+  static bool SortByPhi0(const PandoraPlus::TrackState& trk_state1, const PandoraPlus::TrackState& trk_state2 ) 
+  { return TMath::Abs(trk_state1.phi0) < TMath::Abs(trk_state2.phi0); }
+  
+private: 
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/TrackMatchingAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/TrackMatchingAlg.h
new file mode 100644
index 00000000..78e65a0a
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/TrackMatchingAlg.h
@@ -0,0 +1,54 @@
+#ifndef TRACKMATCHING_H
+#define TRACKMATCHING_H
+
+#include "PandoraPlusDataCol.h"
+#include "Tools/Algorithm.h"
+#include "TVector3.h"
+using namespace PandoraPlus;
+
+class TrackMatchingAlg: public PandoraPlus::Algorithm{
+
+public:
+  TrackMatchingAlg () {};
+  ~TrackMatchingAlg() {};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public:
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new TrackMatchingAlg(); }
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+
+  StatusCode GetExtrpoECALPoints(const PandoraPlus::Track* track, std::vector<TVector3>& extrapo_points);
+  StatusCode CreateTrackAxis(vector<TVector3>& extrapo_points, std::vector<const PandoraPlus::Calo1DCluster*>& localMaxVCol,
+                             PandoraPlus::CaloHalfCluster* t_track_axis);
+  StatusCode GetNearby(const std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>* p_HalfClusterV, 
+                    const std::vector<TVector3>& extrapo_points, 
+                    std::vector<PandoraPlus::CaloHalfCluster*>& t_nearbyHalfClusters, 
+                    std::vector<const PandoraPlus::Calo1DCluster*>& t_nearbyLocalMax);
+  StatusCode LongiConeLinking(const std::vector<TVector3>& extrapo_points, 
+                            std::vector<const PandoraPlus::Calo1DCluster*>& nearbyLocalMax, 
+                            std::vector<const PandoraPlus::Calo1DCluster*>& cone_axis);
+  bool isStopLinking(const std::vector<TVector3>& extrapo_points, 
+                    const PandoraPlus::Calo1DCluster* final_cone_hit);
+  TVector2 GetProjectedRelR(const PandoraPlus::Calo1DCluster* m_shower1, const PandoraPlus::Calo1DCluster* m_shower2 );
+  TVector2 GetProjectedAxis(const std::vector<TVector3>& extrapo_points, const PandoraPlus::Calo1DCluster* m_shower);
+  StatusCode CreatConeAxis(PandoraPlusDataCol& m_datacol, PandoraPlus::Track* track, std::vector<PandoraPlus::CaloHalfCluster*>& nearbyHalfClusters, 
+                          std::vector<const PandoraPlus::Calo1DCluster*>& cone_axis);
+
+private:
+  std::vector<PandoraPlus::Track*> m_TrackCol;
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>* p_HalfClusterV;
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>* p_HalfClusterU;
+
+  // std::vector<const PandoraPlus::CaloHalfCluster*> m_trackAxisVCol;
+  // std::vector<const PandoraPlus::CaloHalfCluster*> m_trackAxisUCol;
+
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/TruthClusterMergingAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/TruthClusterMergingAlg.h
new file mode 100644
index 00000000..baf9c150
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/TruthClusterMergingAlg.h
@@ -0,0 +1,35 @@
+#ifndef _TRUTHCLUSTERMERGING_ALG_H
+#define _TRUTHCLUSTERMERGING_ALG_H
+
+#include "Tools/Algorithm.h"
+
+using namespace PandoraPlus;
+class TruthClusterMergingAlg: public PandoraPlus::Algorithm{
+public: 
+
+  TruthClusterMergingAlg(){};
+  ~TruthClusterMergingAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new TruthClusterMergingAlg(); } 
+
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+
+private: 
+
+  std::vector<const PandoraPlus::Calo3DCluster*> m_EcalClusterCol;
+  std::vector<const PandoraPlus::Calo3DCluster*> m_HcalClusterCol;
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> merged_EcalClusterCol;
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> merged_HcalClusterCol;
+  std::vector<std::shared_ptr<PandoraPlus::PFObject>> merged_CombClusterCol;
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/TruthClusteringAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/TruthClusteringAlg.h
new file mode 100644
index 00000000..24434fb8
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/TruthClusteringAlg.h
@@ -0,0 +1,46 @@
+#ifndef _TRUTHCLUS_ALG_H
+#define _TRUTHCLUS_ALG_H
+
+#include "Tools/Algorithm.h"
+
+using namespace PandoraPlus;
+class TruthClusteringAlg: public PandoraPlus::Algorithm{
+public: 
+
+  TruthClusteringAlg(){};
+  ~TruthClusteringAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new TruthClusteringAlg(); } 
+
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  StatusCode HalfClusterToTowers( std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_halfClusU,
+                                  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_halfClusV,
+                                  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_towers );
+
+private: 
+
+  PandoraPlusDataCol m_bkCol;
+
+  std::vector<std::shared_ptr<PandoraPlus::Track>> m_TrackCol;
+  //For ECAL
+  std::vector<std::shared_ptr<PandoraPlus::CaloUnit>> m_bars;
+  std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> m_1dclusterUCol;
+  std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> m_1dclusterVCol;
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_halfclusterU;
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_halfclusterV;
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_towers;
+  //For HCAL
+  std::vector<std::shared_ptr<PandoraPlus::CaloHit>> m_hits;
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_clusters;
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/TruthEnergySplittingAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/TruthEnergySplittingAlg.h
new file mode 100644
index 00000000..99a6190d
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/TruthEnergySplittingAlg.h
@@ -0,0 +1,39 @@
+#ifndef _TRUTHENERGYSPLITTING_ALG_H
+#define _TRUTHENERGYSPLITTING_ALG_H
+
+#include "Tools/Algorithm.h"
+
+using namespace PandoraPlus;
+class TruthEnergySplittingAlg: public PandoraPlus::Algorithm{
+public: 
+
+  TruthEnergySplittingAlg(){};
+  ~TruthEnergySplittingAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new TruthEnergySplittingAlg(); } 
+
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  StatusCode HalfClusterToTowers( std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_halfClusU,
+                                  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_halfClusV,
+                                  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_towers );
+
+private: 
+  std::vector<PandoraPlus::CaloHalfCluster*> p_HalfClusterU;
+  std::vector<PandoraPlus::CaloHalfCluster*> p_HalfClusterV;
+
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_newClusUCol;
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_newClusVCol;
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_towerCol;
+
+  PandoraPlusDataCol m_bkCol;
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/TruthMatchingAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/TruthMatchingAlg.h
new file mode 100644
index 00000000..f33dc4ff
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/TruthMatchingAlg.h
@@ -0,0 +1,52 @@
+#ifndef _TRUTHMATCHING_ALG_H
+#define _TRUTHMATCHING_ALG_H
+
+#include "Tools/Algorithm.h"
+
+using namespace PandoraPlus;
+class TruthMatchingAlg: public PandoraPlus::Algorithm{
+public: 
+
+  TruthMatchingAlg(){};
+  ~TruthMatchingAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new TruthMatchingAlg(); } 
+
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  StatusCode TruthMatching( std::vector<const PandoraPlus::CaloHalfCluster*>& m_ClUCol,
+                            std::vector<const PandoraPlus::CaloHalfCluster*>& m_ClVCol,
+                            std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_clusters );
+
+  StatusCode XYClusterMatchingL0( const PandoraPlus::CaloHalfCluster* m_longiClX,
+                                  const PandoraPlus::CaloHalfCluster* m_longiClY,
+                                  std::shared_ptr<PandoraPlus::Calo3DCluster>& m_clus );
+
+  StatusCode GetMatchedShowersL0( const PandoraPlus::Calo1DCluster* barShowerX,
+                                  const PandoraPlus::Calo1DCluster* barShowerY,
+                                  PandoraPlus::Calo2DCluster* outsh); //1*1  
+
+  StatusCode GetMatchedShowersL1( const PandoraPlus::Calo1DCluster* shower1,
+                                  std::vector<const PandoraPlus::Calo1DCluster*>& showerNCol,
+                                  std::vector<PandoraPlus::Calo2DCluster*>& outshCol ); //1*N
+
+private: 
+
+  std::vector<PandoraPlus::Calo3DCluster*> m_towerCol;
+
+  std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusUCol;
+  std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusVCol;
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_clusterCol;
+
+  PandoraPlusDataCol m_bkCol;
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/TruthPatternRecAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/TruthPatternRecAlg.h
new file mode 100644
index 00000000..e04e7be7
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/TruthPatternRecAlg.h
@@ -0,0 +1,33 @@
+#ifndef _TRUTHPATTERNREC_ALG_H
+#define _TRUTHPATTERNREC_ALG_H
+
+#include "Tools/Algorithm.h"
+
+using namespace PandoraPlus;
+class TruthPatternRecAlg: public PandoraPlus::Algorithm{
+public: 
+
+  TruthPatternRecAlg(){};
+  ~TruthPatternRecAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new TruthPatternRecAlg(); } 
+
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  StatusCode OverlapMerging   ( std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_axisCol );
+
+private: 
+
+  std::vector<PandoraPlus::CaloHalfCluster*> p_HalfClusterV;
+  std::vector<PandoraPlus::CaloHalfCluster*> p_HalfClusterU;
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Algorithm/TruthTrackMatchingAlg.h b/Reconstruction/CrystalCaloRec/include/Algorithm/TruthTrackMatchingAlg.h
new file mode 100644
index 00000000..580206ff
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Algorithm/TruthTrackMatchingAlg.h
@@ -0,0 +1,37 @@
+#ifndef _TRUTHTRKMATCHING_ALG_H
+#define _TRUTHTRKMATCHING_ALG_H
+
+#include "Tools/Algorithm.h"
+
+using namespace PandoraPlus;
+class TruthTrackMatchingAlg: public PandoraPlus::Algorithm{
+public: 
+
+  TruthTrackMatchingAlg(){};
+  ~TruthTrackMatchingAlg(){};
+
+  class Factory : public PandoraPlus::AlgorithmFactory
+  {
+  public: 
+    PandoraPlus::Algorithm* CreateAlgorithm() const{ return new TruthTrackMatchingAlg(); } 
+
+  };
+
+  StatusCode ReadSettings(PandoraPlus::Settings& m_settings);
+  StatusCode Initialize( PandoraPlusDataCol& m_datacol );
+  StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol );
+  StatusCode ClearAlgorithm();
+
+  //Self defined algorithms
+  StatusCode SelfAlg1(); 
+
+private: 
+
+  std::vector<PandoraPlus::Track*> m_TrackCol;
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>* p_HalfClusterV;
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>* p_HalfClusterU;  
+
+
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Objects/Calo1DCluster.h b/Reconstruction/CrystalCaloRec/include/Objects/Calo1DCluster.h
new file mode 100644
index 00000000..5b74ef49
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Objects/Calo1DCluster.h
@@ -0,0 +1,84 @@
+#ifndef CALO_1DCLUSTER_H
+#define CALO_1DCLUSTER_H
+#include <iostream>
+#include <cstdio>
+#include <algorithm>
+#include <cmath>
+#include <vector>
+
+#include "Objects/CaloUnit.h"
+
+namespace PandoraPlus{
+  class Calo1DCluster{
+  public: 
+
+    Calo1DCluster() {}; 
+    Calo1DCluster( std::vector<const PandoraPlus::CaloUnit*> _bars, std::vector<const PandoraPlus::CaloUnit*> _seeds)
+    : Bars(_bars), Seeds(_seeds) {};
+    ~Calo1DCluster() { Clear(); }
+
+    void Clear();
+    void Clean();
+    void Check();
+    std::shared_ptr<PandoraPlus::Calo1DCluster> Clone() const; 
+
+    inline bool operator == (const Calo1DCluster &x) const{
+      return ( Bars == x.getBars()  );
+    }
+
+    bool isNeighbor(const PandoraPlus::CaloUnit* m_bar) const; 
+    bool inCluster(const PandoraPlus::CaloUnit* iBar) const;
+    void sortByPos() { std::sort(Bars.begin(), Bars.end()); }
+
+    double getEnergy() const; 
+    TVector3 getPos() const;
+    double getT1() const;
+    double getT2() const;
+    double getWidth() const;
+    double getScndMoment() const;
+
+    int getNseeds() const { return Seeds.size(); }
+    std::vector<const PandoraPlus::CaloUnit*> getBars()  const { return Bars;  }
+	  std::vector<const PandoraPlus::CaloUnit*> getCluster()  const { return Bars;  }
+    std::vector<const PandoraPlus::CaloUnit*> getSeeds() const { return Seeds; }
+    std::vector< const PandoraPlus::Calo1DCluster* > getCousinClusters() const { return CousinClusters; }
+    std::vector< const PandoraPlus::Calo1DCluster* > getChildClusters() const { return ChildClusters; }
+    bool getGlobalRange( double& xmin,  double& ymin, double& zmin, double& xmax, double& ymax, double& zmax ) const;
+    int  getLeftEdge();
+    int  getRightEdge();
+    std::vector< std::pair<edm4hep::MCParticle, float> > getLinkedMCP() const { return MCParticleWeight; }
+    std::vector< std::pair<edm4hep::MCParticle, float> > getLinkedMCPfromUnit();
+    edm4hep::MCParticle getLeadingMCP() const;
+    float getLeadingMCPweight() const;
+
+	  void addUnit(const PandoraPlus::CaloUnit* _bar );
+    void addSeed(const PandoraPlus::CaloUnit* _seed ) { Seeds.push_back(_seed); }
+    void setBars( std::vector<const PandoraPlus::CaloUnit*> _bars ) { Bars = _bars; }
+    void setSeeds( std::vector<const PandoraPlus::CaloUnit*> _seeds) { Seeds = _seeds; }
+    void addCousinCluster( const PandoraPlus::Calo1DCluster* clus ) { CousinClusters.push_back(clus); }
+    void addChildCluster( const PandoraPlus::Calo1DCluster* clus ) { ChildClusters.push_back(clus); }
+    void deleteCousinCluster( const PandoraPlus::Calo1DCluster* _cl );
+    void addLinkedMCP( std::pair<edm4hep::MCParticle, float> _pair ) { MCParticleWeight.push_back(_pair); }
+    void setLinkedMCP( std::vector<std::pair<edm4hep::MCParticle, float>> _pairVec ) { MCParticleWeight.clear(); MCParticleWeight = _pairVec; }
+    void setSeed();  //Set the most energitic unit as seed, Eseed>5 MeV (hardcoded). 
+    void setIDInfo(); 
+
+    int getDlayer() const { if(Bars.size()>0) return Bars[0]->getDlayer(); return -99;  }
+    int getSlayer() const { if(Bars.size()>0) return Bars[0]->getSlayer(); return -99;  }
+    std::vector< std::vector<int> > getTowerID() const { return towerID; }
+    
+  private: 
+    std::vector<const PandoraPlus::CaloUnit*> Bars;
+    std::vector<const PandoraPlus::CaloUnit*> Seeds;
+    double Energy;
+    TVector3 pos;
+
+    std::vector< std::vector<int> > towerID; //[module, stave]
+
+    std::vector< const PandoraPlus::Calo1DCluster* > CousinClusters;
+    std::vector< const PandoraPlus::Calo1DCluster* > ChildClusters;
+
+    std::vector< std::pair<edm4hep::MCParticle, float> > MCParticleWeight;
+  };
+}
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Objects/Calo2DCluster.h b/Reconstruction/CrystalCaloRec/include/Objects/Calo2DCluster.h
new file mode 100644
index 00000000..88bdd2ae
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Objects/Calo2DCluster.h
@@ -0,0 +1,65 @@
+#ifndef CALO_2DCLUSTER_H
+#define CALO_2DCLUSTER_H
+
+#include "Objects/CaloHit.h"
+#include "Objects/CaloUnit.h"
+#include "Objects/Calo1DCluster.h"
+
+namespace PandoraPlus {
+
+  class CaloHit;
+  class Calo2DCluster {
+  public: 
+    Calo2DCluster() {};
+    ~Calo2DCluster() { Clear(); };
+
+    void Clear();
+    void ClearShower();
+    void Clean();
+    void Check(); 
+
+    inline bool operator == (const Calo2DCluster &x) const{
+      return ( barUCol == x.getBarUCol() &&   barVCol == x.getBarVCol());
+    }
+
+    bool isNeighbor(const PandoraPlus::Calo1DCluster* m_1dcluster) const; 
+
+    int getDlayer() const { if(barUCol.size()>0) return barUCol[0]->getDlayer(); else if(barVCol.size()>0) return barVCol[0]->getDlayer(); else return -99; }
+    std::vector< std::vector<int> > getTowerID() const { return towerID; }
+
+    std::vector<const CaloUnit*> getBars() const;
+    std::vector<const CaloHit*> getCaloHits() const { return hits; }
+    std::vector<const CaloUnit *> getBarUCol() const { return barUCol; }
+    std::vector<const CaloUnit *> getBarVCol() const { return barVCol; }
+    std::vector<const Calo1DCluster*> getShowerUCol() const {return barShowerUCol;}
+    std::vector<const Calo1DCluster*> getShowerVCol() const {return barShowerVCol;}
+    std::vector<const Calo1DCluster*> getCluster() const;
+    double getEnergy() const; 
+    TVector3 getPos() const; 
+
+    void setCaloHits( std::vector<const CaloHit*> _hits) { hits = _hits; }
+    void addBar(const CaloUnit* _bar) { if(_bar->getSlayer()==0) barUCol.push_back(_bar); if(_bar->getSlayer()==1) barVCol.push_back(_bar); }
+    void setBarUCol( std::vector<const CaloUnit*> _bars ) { barUCol=_bars; }
+    void setBarVCol( std::vector<const CaloUnit*> _bars ) { barVCol=_bars; }
+    void addShowerU( const Calo1DCluster* _sh) { barShowerUCol.push_back(_sh); }
+    void addShowerV( const Calo1DCluster* _sh) { barShowerVCol.push_back(_sh); }
+    void setShowerUCol(std::vector<const Calo1DCluster*> _sh) { barShowerUCol=_sh; }
+    void setShowerVCol(std::vector<const Calo1DCluster*> _sh) { barShowerVCol=_sh; }
+    void addUnit(const Calo1DCluster* _1dcluster);
+    void addTowerID(int _m, int _p, int _s) { std::vector<int> id(3); id[0] = _m; id[1] = _p; id[2] = _s; towerID.push_back(id); }
+    void addTowerID(std::vector<int> id) { towerID.push_back(id); }
+    void setTowerID(std::vector<int> id) { towerID.clear(); towerID.push_back(id); }
+
+  private:
+    std::vector< std::vector<int> > towerID; //[module, stave]
+
+    std::vector<const CaloHit*> hits; 
+    std::vector<const CaloUnit*> barUCol;  //slayer == 0.
+    std::vector<const CaloUnit*> barVCol;  //slayer == 1.
+    std::vector<const Calo1DCluster*> barShowerUCol; 
+    std::vector<const Calo1DCluster*> barShowerVCol; 
+
+  };
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Objects/Calo3DCluster.h b/Reconstruction/CrystalCaloRec/include/Objects/Calo3DCluster.h
new file mode 100644
index 00000000..5036e025
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Objects/Calo3DCluster.h
@@ -0,0 +1,116 @@
+#ifndef CALO_3DCLUSTER_H
+#define CALO_3DCLUSTER_H
+
+#include "Objects/Calo2DCluster.h"
+#include "Objects/Track.h"
+#include "Objects/CaloHalfCluster.h"
+//#include "Objects/CaloTower.h"
+#include "Tools/TrackFitInEcal.h"
+
+namespace PandoraPlus {
+
+  class Calo3DCluster {
+  public: 
+    Calo3DCluster() {};
+    ~Calo3DCluster() { Clear(); };
+
+    void Clear();
+    void Clean();
+    void Check(); 
+    void Clear2DClusters() { m_2dclusters.clear(); };
+    void CleanLongiClusters();
+    std::shared_ptr<PandoraPlus::Calo3DCluster> Clone() const;
+
+    inline bool operator == (const Calo3DCluster &x) const{
+      return ( m_2dclusters == x.getCluster()  );
+    }
+
+    std::vector< std::vector<int> > getTowerID() const { return towerID; }
+    //bool isNeighbor(const PandoraPlus::Calo2DCluster* m_2dcluster) const; 
+    std::vector<const PandoraPlus::CaloHit*> getCaloHits() const { return hits; }
+    std::vector<const Calo2DCluster*> getCluster() const { return m_2dclusters; }
+    std::vector<const Calo1DCluster*> getLocalMaxUCol(std::string name) const;
+    std::vector<const Calo1DCluster*> getLocalMaxVCol(std::string name) const;
+    std::map<std::string, std::vector<const PandoraPlus::Calo1DCluster*> >  getLocalMaxUMap() const { return map_localMaxU; }
+    std::map<std::string, std::vector<const PandoraPlus::Calo1DCluster*> >  getLocalMaxVMap() const { return map_localMaxV; }
+    std::vector<const CaloHalfCluster*> getHalfClusterUCol(std::string name) const;
+    std::vector<const CaloHalfCluster*> getHalfClusterVCol(std::string name) const; 
+    std::map<std::string, std::vector<const PandoraPlus::CaloHalfCluster*> > getHalfClusterUMap() const { return map_halfClusUCol; }
+    std::map<std::string, std::vector<const PandoraPlus::CaloHalfCluster*> > getHalfClusterVMap() const { return map_halfClusVCol; }
+    std::vector<const PandoraPlus::Track*> getAssociatedTracks() const { return m_TrackCol; }
+    std::vector< std::pair<edm4hep::MCParticle, float> > getLinkedMCP() const { return MCParticleWeight; }
+    std::vector< std::pair<edm4hep::MCParticle, float> > getLinkedMCPfromHFCluster(std::string name);
+    std::vector< std::pair<edm4hep::MCParticle, float> > getLinkedMCPfromHit();
+    edm4hep::MCParticle getLeadingMCP() const;
+    float getLeadingMCPweight() const;
+
+    std::vector<const Calo3DCluster*> getTowers() const {return m_towers; }
+    std::vector<const CaloUnit*> getBars() const;
+    double getEnergy() const; 
+    double getHitsE() const;
+    double getLongiE() const;
+    TVector3 getHitCenter() const;
+    TVector3 getShowerCenter() const; 
+    TVector3 getAxis() const { return axis; }
+    int getBeginningDlayer() const;
+    int getEndDlayer() const;
+    double getDepthToECALSurface() const;
+    int getType() const { return type; }
+
+    void setCaloHits( std::vector<const PandoraPlus::CaloHit*> _hits ) { hits = _hits; }
+    void setCaloHitsFrom2DCluster(); 
+    void setTowers(std::vector<const Calo3DCluster*> _t) { m_towers = _t; }
+    void setHalfClusters( std::string name1, std::vector<const CaloHalfCluster*>& _clU, 
+                           std::string name2, std::vector<const CaloHalfCluster*>& _clV )
+    { map_halfClusUCol[name1]=_clU; map_halfClusVCol[name2]=_clV;  }
+
+    void setLocalMax( std::string name1, std::vector<const Calo1DCluster*>& _colU,
+                      std::string name2, std::vector<const Calo1DCluster*>& _colV )
+    { map_localMaxU[name1]=_colU; map_localMaxV[name2]=_colV; }
+    void setClusters(std::vector<const Calo2DCluster*> _2dcol) { m_2dclusters = _2dcol; }
+    void setType(int _type) { type = _type; }
+
+    void addTowerID(int _m, int _p, int _s) { std::vector<int> id(3); id[0] = _m; id[1] = _p; id[2] = _s; towerID.push_back(id); }
+    void addTowerID( std::vector<int> id ) { towerID.push_back(id); }
+
+    void addUnit(const Calo2DCluster* _2dcluster);
+    void addHit(const PandoraPlus::CaloHit* _hit) { hits.push_back(_hit); };
+    void addTower( const Calo3DCluster* _tower ) { m_towers.push_back(_tower); }
+    void addHalfClusterU( std::string name, const CaloHalfCluster* _clU ) { map_halfClusUCol[name].push_back(_clU); }
+    void addHalfClusterV( std::string name, const CaloHalfCluster* _clV ) { map_halfClusVCol[name].push_back(_clV); }
+    void addLocalMaxU( std::string name, const Calo1DCluster* _shU ) { map_localMaxU[name].push_back(_shU); }
+    void addLocalMaxV( std::string name, const Calo1DCluster* _shV ) { map_localMaxV[name].push_back(_shV); }
+    void addAssociatedTrack(const PandoraPlus::Track* _track){ m_TrackCol.push_back(_track); }
+    void addLinkedMCP( std::pair<edm4hep::MCParticle, float> _pair ) { MCParticleWeight.push_back(_pair); }
+    void setLinkedMCP( std::vector<std::pair<edm4hep::MCParticle, float>> _pairVec ) { MCParticleWeight.clear(); MCParticleWeight = _pairVec; }
+    void mergeCluster( const PandoraPlus::Calo3DCluster* _clus );
+
+    //void FitProfile();
+    void FitAxis();
+    //void FitAxisHit();
+
+    bool isHCALNeighbor(const PandoraPlus::CaloHit* m_hit) const;
+
+  private:
+    std::vector<const PandoraPlus::CaloHit*> hits; 
+    std::vector<const PandoraPlus::Calo2DCluster*> m_2dclusters;
+    std::vector<const Calo3DCluster*> m_towers;
+    std::map<std::string, std::vector<const PandoraPlus::Calo1DCluster*> > map_localMaxU;
+    std::map<std::string, std::vector<const PandoraPlus::Calo1DCluster*> > map_localMaxV;
+    std::map<std::string, std::vector<const PandoraPlus::CaloHalfCluster*> > map_halfClusUCol;
+    std::map<std::string, std::vector<const PandoraPlus::CaloHalfCluster*> > map_halfClusVCol;
+    std::vector<const PandoraPlus::Track*> m_TrackCol;
+    std::vector< std::pair<edm4hep::MCParticle, float> > MCParticleWeight;
+
+    std::vector< std::vector<int> > towerID; //[module, part, stave]
+    TVector3 axis;
+    double chi2;
+    double alpha;
+    double beta;
+    int    type;  //0: MIP shower.  1: EM shower.  2: hadronic shower
+    TrackFitInEcal trackFitter;
+
+  };
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Objects/CaloHalfCluster.h b/Reconstruction/CrystalCaloRec/include/Objects/CaloHalfCluster.h
new file mode 100644
index 00000000..116efd04
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Objects/CaloHalfCluster.h
@@ -0,0 +1,108 @@
+#ifndef CALO_HALFCLUSTER_H
+#define CALO_HALFCLUSTER_H
+
+#include "Objects/CaloUnit.h"
+#include "Objects/Calo1DCluster.h"
+#include "Objects/Track.h"
+#include "Tools/TrackFitInEcal.h"
+
+namespace PandoraPlus {
+
+  class Track;
+  class CaloHalfCluster {
+  public: 
+    CaloHalfCluster() {};
+    ~CaloHalfCluster() { Clear(); };
+
+    void Clear();
+    void Clean();
+    void Check(); 
+
+    inline bool operator == (const CaloHalfCluster &x) const{
+      return m_1dclusters==x.getCluster();
+    }
+    std::shared_ptr<PandoraPlus::CaloHalfCluster> Clone() const; 
+
+    bool isNeighbor(const PandoraPlus::Calo1DCluster* m_1dcluster) const; 
+
+    double getEnergy() const; 
+    TVector3 getPos() const; 
+    TVector3 getAxis() const { return axis; }
+    TVector3 getEnergyCenter() const;
+    std::vector<int> getEnergyCenterTower() const;
+    int getSlayer() const { return slayer; }
+    std::vector< std::vector<int> > getTowerID() const { return towerID; }
+    double getHoughAlpha() const { return Hough_alpha; }
+    double getHoughRho() const { return Hough_rho; }
+    double getHoughIntercept() const { return Hough_intercept; }
+    int getType() const { return type; }
+
+    std::vector<const CaloUnit*> getBars() const;
+    std::vector<const Calo1DCluster*> getCluster() const { return m_1dclusters;};
+    std::vector<const Calo1DCluster*> getLocalMaxCol(std::string name) const;
+    std::vector<const Calo1DCluster*> getAllLocalMaxCol() const;
+    std::vector<const Calo1DCluster*> getClusterInLayer(int _layer) const;
+    std::vector<const CaloHalfCluster*> getHalfClusterCol(std::string name) const;
+    std::vector<const CaloHalfCluster*> getAllHalfClusterCol() const;
+    std::map<std::string, std::vector<const PandoraPlus::CaloHalfCluster*> > getHalfClusterMap() const {return map_halfClusCol; }
+    std::map<std::string, std::vector<const PandoraPlus::Calo1DCluster*> > getLocalMaxMap() const {return map_localMax; }
+    std::vector<const PandoraPlus::Track*> getAssociatedTracks() const { return m_TrackCol; }
+    std::vector< std::pair<edm4hep::MCParticle, float> > getLinkedMCP() const { return MCParticleWeight; }
+    std::vector< std::pair<edm4hep::MCParticle, float> > getLinkedMCPfromUnit();
+    edm4hep::MCParticle getLeadingMCP() const; 
+    float getLeadingMCPweight() const;
+
+    int getBeginningDlayer() const;
+    int getEndDlayer() const;
+    bool isContinue() const;
+    bool isContinueN(int n) const;
+    bool isSubset(const CaloHalfCluster* clus) const;
+    double OverlapRatioE( const CaloHalfCluster* clus ) const;
+
+    void fitAxis( std::string name );
+    void setType( int _type ) { type = _type; }
+    void sortBarShowersByLayer() { std::sort(m_1dclusters.begin(), m_1dclusters.end(), compLayer); }
+    void addUnit(const Calo1DCluster* _1dcluster);
+    void deleteUnit(const Calo1DCluster* _1dcluster);
+    void setLocalMax( std::string name, std::vector<const Calo1DCluster*> _col) { map_localMax[name]=_col; }
+    void setHalfClusters( std::string name, std::vector<const PandoraPlus::CaloHalfCluster*>& _cl) { map_halfClusCol[name]=_cl; }
+    void addHalfCluster(std::string name, const PandoraPlus::CaloHalfCluster* _cl) { map_halfClusCol[name].push_back(_cl); }
+    void addCousinCluster( const PandoraPlus::CaloHalfCluster* _cl ) { map_halfClusCol["CousinCluster"].push_back(_cl); }
+    void deleteCousinCluster( const PandoraPlus::CaloHalfCluster* _cl ); 
+    void setHoughPars(double _a, double _r) { Hough_alpha=_a; Hough_rho=_r; }
+    void setIntercept(double _in) { Hough_intercept=_in; }
+    void mergeHalfCluster( const CaloHalfCluster* clus );
+    void addTowerID(int _m, int _p, int _s) { std::vector<int> id(3); id[0] = _m; id[1] = _p; id[2] = _s; towerID.push_back(id); }
+    void addTowerID(std::vector<int> id) { towerID.push_back(id); }
+    void setTowerID(std::vector<int> id) { towerID.clear(); towerID.push_back(id); }
+    void addAssociatedTrack(const PandoraPlus::Track* _track){ m_TrackCol.push_back(_track); }
+    void addLinkedMCP( std::pair<edm4hep::MCParticle, float> _pair ) { MCParticleWeight.push_back(_pair); }
+    void setLinkedMCP( std::vector<std::pair<edm4hep::MCParticle, float>> _pairVec ) { MCParticleWeight.clear(); MCParticleWeight = _pairVec; }
+    void mergeClusterInLayer(); 
+
+
+  private:
+    int type; // yyy: new definition: track: 10000, Hough: 100, cone: 1, merge: sum them
+    std::vector< std::vector<int> > towerID; //[module, part, stave]
+    int slayer;
+    TVector3 axis;
+    double trk_dr;
+    double trk_dz;
+    double Hough_alpha;
+    double Hough_rho;
+    double Hough_intercept;
+    std::vector<const Calo1DCluster*> m_1dclusters; 
+    std::map<std::string, std::vector<const PandoraPlus::Calo1DCluster*> > map_localMax;
+    std::map<std::string, std::vector<const PandoraPlus::CaloHalfCluster*> > map_halfClusCol;  
+    std::vector<const PandoraPlus::Track*> m_TrackCol;
+    std::vector< std::pair<edm4hep::MCParticle, float> > MCParticleWeight;
+
+    TrackFitInEcal* track = new TrackFitInEcal();
+
+    static bool compLayer( const PandoraPlus::Calo1DCluster* hit1, const PandoraPlus::Calo1DCluster* hit2 )
+      { return hit1->getDlayer() < hit2->getDlayer(); }
+
+  };
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Objects/CaloHit.h b/Reconstruction/CrystalCaloRec/include/Objects/CaloHit.h
new file mode 100644
index 00000000..ecb6565c
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Objects/CaloHit.h
@@ -0,0 +1,52 @@
+#ifndef CALO_HIT_H
+#define CALO_HIT_H
+
+#include "TVector3.h"
+#include "edm4hep/MCParticle.h"
+#include "edm4hep/CalorimeterHit.h"
+#include "Objects/Calo2DCluster.h"
+
+namespace PandoraPlus{
+  class Calo2DCluster; 
+
+  class CaloHit{
+  public:
+    CaloHit() {};
+    ~CaloHit() { Clear(); };
+
+    void Clear() { cellID=0; position.SetXYZ(0.,0.,0.); energy=-1; module=-1; layer=-1; ParentShower=nullptr; }
+    std::shared_ptr<CaloHit> Clone() const;
+
+    void setOriginHit( edm4hep::CalorimeterHit& _hit ) { m_hit = _hit; }
+    TVector3 getPosition() const { return position; }
+    double   getEnergy() const { return energy; } 
+    int getLayer() const {return layer;}
+    int getModule() const {return module;}
+    std::vector< std::pair<edm4hep::MCParticle, float> > getLinkedMCP() const { return MCParticleWeight; }
+    edm4hep::MCParticle getLeadingMCP() const;
+    float getLeadingMCPweight() const;
+    edm4hep::CalorimeterHit getOriginHit() const { return m_hit; }
+
+    void setcellID(unsigned long long _id) { cellID=_id; }
+    void setcellID(int _m, int _l) { module=_m; layer=_l; }
+    void setEnergy(double _en) { energy=_en; }
+    void setPosition( TVector3 _vec ) { position=_vec; }
+    void setModule(int _m ) { module = _m; }
+    void setLayer(int _l) { layer = _l; }
+    void setParentShower( PandoraPlus::Calo2DCluster* _p ) { ParentShower=_p; }
+    void addLinkedMCP( std::pair<edm4hep::MCParticle, float> _pair ) {MCParticleWeight.push_back(_pair); } 
+    void setLinkedMCP( std::vector<std::pair<edm4hep::MCParticle, float>> _pairVec ) { MCParticleWeight.clear(); MCParticleWeight = _pairVec; }
+
+  private: 
+    int module; 
+    int layer; 
+    unsigned long long cellID; 
+    TVector3 position;
+    double   energy; 
+    edm4hep::CalorimeterHit m_hit;
+    PandoraPlus::Calo2DCluster* ParentShower; 
+    std::vector< std::pair<edm4hep::MCParticle, float> > MCParticleWeight;
+  };
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Objects/CaloUnit.h b/Reconstruction/CrystalCaloRec/include/Objects/CaloUnit.h
new file mode 100644
index 00000000..96c5d89c
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Objects/CaloUnit.h
@@ -0,0 +1,101 @@
+#ifndef CALOBAR_H
+#define CALOBAR_H
+
+#include <DD4hep/Objects.h>
+#include "edm4hep/MCParticle.h"
+#include "TVector3.h"
+
+namespace PandoraPlus{
+
+  class CaloUnit{
+
+  public:
+    CaloUnit(unsigned long long _cellID, int _system, int _module, int _slayer, int _dlayer, int _stave, int _bar, TVector3 _pos, double _Q1, double _Q2, double _T1, double _T2)
+    : cellID(_cellID), system(_system), module(_module), stave(_stave), dlayer(_dlayer), slayer(_slayer), bar(_bar), position(_pos), Q1(_Q1), Q2(_Q2), T1(_T1), T2(_T2) {}; 
+    CaloUnit() {};
+    ~CaloUnit() { Clear(); };
+    void Clear() { position.SetXYZ(0.,0.,0.); Q1=-1; Q2=-1; T1=-99; T2=-99; }
+
+    inline bool operator < (const CaloUnit &x) const {
+      if(x.cellID==cellID) return false; 
+
+      if(slayer==0) return ( (stave<x.stave) || (stave==x.stave && bar<x.bar) );
+      else{
+        if( module==Nmodule && x.module==0 ) return true;
+        else if( module==0 && x.module==Nmodule ) return false; 
+        else{
+          return ( (module<x.module) || (module==x.module && bar<x.bar) );
+        }
+      }
+    }
+
+    inline bool operator == (const CaloUnit &x) const{
+      return ( (cellID == x.cellID) && getEnergy()==x.getEnergy() );
+    }
+    unsigned long long getcellID() const { return cellID; }
+    int getSystem() const { return system; }
+    int getModule() const { return module; }
+    int getStave()  const { return stave;  }
+    int getDlayer() const { return dlayer; }
+    int getSlayer() const { return slayer; }
+    int getBar()    const { return bar;    }
+    double getQ1()  const { return Q1;     }
+    double getQ2()  const { return Q2;     }
+    double getT1()  const { return T1;     }
+    double getT2()  const { return T2;     }
+    double getBarLength() const {return barLength; }
+
+    TVector3 getPosition() const { return position; }
+    double getEnergy() const { return (Q1+Q2); }
+    std::vector< std::pair<edm4hep::MCParticle, float> > getLinkedMCP() const { return MCParticleWeight; }
+    edm4hep::MCParticle getLeadingMCP() const;
+    float getLeadingMCPweight() const;
+    bool isAtLowerEdgePhi() const; 
+    bool isAtUpperEdgePhi() const; 
+    bool isAtLowerEdgeZ() const; 
+    bool isAtUpperEdgeZ() const; 
+    bool isNeighbor(const CaloUnit* x) const;
+    //bool isModuleAdjacent( const CaloUnit* x ) const;
+    bool isLongiNeighbor(const CaloUnit* x) const;
+    //bool isLongiModuleAdjacent( const CaloUnit* x ) const;
+
+    void addLinkedMCP( std::pair<edm4hep::MCParticle, float> _pair ) { MCParticleWeight.push_back(_pair); }
+    void setLinkedMCP( std::vector<std::pair<edm4hep::MCParticle, float>> _pairVec ) { MCParticleWeight.clear(); MCParticleWeight = _pairVec; }
+    void setcellID(unsigned long long _cellid) { cellID = _cellid; }
+    void setcellID(int _system, int _module, int _stave, int _dlayer, int _slayer, int _bar) { system=_system; module=_module; stave=_stave; dlayer=_dlayer; slayer=_slayer; bar=_bar; }
+    void setPosition( TVector3 posv3) { position.SetXYZ( posv3.x(), posv3.y(), posv3.z() ); }
+    void setQ(double _q1, double _q2) { Q1=_q1; Q2=_q2; }
+    void setT(double _t1, double _t2) { T1=_t1; T2=_t2; }
+    void setBarLength(double _barLength) { barLength=_barLength; }
+    std::shared_ptr<CaloUnit> Clone() const;
+
+    static int Nmodule;
+    static int Nstave;
+    static int Nlayer;
+    static int NbarPhi_odd[14];
+    static int NbarPhi_even[14];
+    static int NbarZ;
+    static float barsize;
+    static float ecal_innerR;
+
+  private:
+		unsigned long long cellID;
+		int system;
+		int module;
+		int stave;
+		int dlayer;
+		int slayer;
+		int bar;
+		TVector3 position;
+		double Q1;      // Q in left readout
+		double Q2;      // Q in right readout;
+		double T1;    // T in left readout;
+		double T2;    // T in right readout;
+
+    double barLength;
+    std::vector< std::pair<edm4hep::MCParticle, float> > MCParticleWeight; 
+
+  };
+  
+}
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Objects/HoughObject.h b/Reconstruction/CrystalCaloRec/include/Objects/HoughObject.h
new file mode 100644
index 00000000..03a407ec
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Objects/HoughObject.h
@@ -0,0 +1,60 @@
+#ifndef CALOHOUGHOBJECT_H
+#define CALOHOUGHOBJECT_H
+
+#include "Objects/Calo1DCluster.h"
+#include "TVector2.h"
+#include "TF1.h"
+
+
+namespace PandoraPlus {
+
+  class HoughObject{
+  public:
+    HoughObject() {};
+    HoughObject( const PandoraPlus::Calo1DCluster* _localmax, double _cellSize, double _ecal_inner_radius, double _phi=0.);
+    ~HoughObject() { };
+
+
+    TVector2 getCenterPoint() const { return m_center_point; }
+    TVector2 getUpperPoint() const { return m_center_point + TVector2( m_cell_size*cos(m_center_point.Phi() + TMath::PiOver2()), m_cell_size*sin(m_center_point.Phi() + TMath::PiOver2()) ); }
+    TVector2 getLowerPoint() const { return m_center_point + TVector2( m_cell_size*cos(m_center_point.Phi() + 3*TMath::PiOver2()), m_cell_size*sin(m_center_point.Phi() + 3*TMath::PiOver2()) ); }
+    //TVector2 getPointU()  const { return m_center_point + TVector2(0,  m_cell_size/TMath::Sqrt(2)); }
+    //TVector2 getPointD()  const { return m_center_point + TVector2(0, -m_cell_size/TMath::Sqrt(2)); }
+    //TVector2 getPointL()  const { return m_center_point + TVector2(-m_cell_size/TMath::Sqrt(2), 0); }
+    //TVector2 getPointR()  const { return m_center_point + TVector2( m_cell_size/TMath::Sqrt(2), 0); }
+    //TVector2 getPointUR() const { return m_center_point + TVector2( m_cell_size/2,  m_cell_size/2); }
+    //TVector2 getPointDL() const { return m_center_point + TVector2(-m_cell_size/2, -m_cell_size/2); }
+    //TVector2 getPointUL() const { return m_center_point + TVector2(-m_cell_size/2,  m_cell_size/2); }
+    //TVector2 getPointDR() const { return m_center_point + TVector2( m_cell_size/2, -m_cell_size/2); }
+
+    TF1 getHoughLine1() const { return m_Hough_line_1; }
+    TF1 getHoughLine2() const { return m_Hough_line_2; }
+    //TF1 getHoughLine3() const { return m_Hough_line_3; }
+    //TF1 getHoughLine4() const { return m_Hough_line_4; }
+
+    //int getModule() const { return (m_local_max->getTowerID())[0][0]; }
+    int getSlayer() const { return m_local_max->getSlayer(); }
+    double getE() const { return m_local_max->getEnergy(); }
+    double getCellSize() const { return m_cell_size; }
+    const PandoraPlus::Calo1DCluster* getLocalMax() const { return m_local_max; }
+
+    void setCellSize(double _cs) { m_cell_size=_cs; }
+    void setCenterPoint(double& _ecal_inner_radius, double _phi=0.);
+    void setHoughLine(TF1& line1, TF1& line2);
+
+
+  private:
+    const PandoraPlus::Calo1DCluster* m_local_max;  //Local max
+    double m_cell_size;
+    TVector2 m_center_point;  // center position
+
+    TF1 m_Hough_line_1;    // ur or u
+    TF1 m_Hough_line_2;    // dl or d
+    //TF1 m_Hough_line_3;    // ul or l
+    //TF1 m_Hough_line_4;    // dr or r
+    // The above conversion is only for the octagon barrel ECAL
+
+  };
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Objects/HoughSpace.h b/Reconstruction/CrystalCaloRec/include/Objects/HoughSpace.h
new file mode 100644
index 00000000..3c3e2848
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Objects/HoughSpace.h
@@ -0,0 +1,60 @@
+#ifndef CALOHOUGHSPACE_H
+#define CALOHOUGHSPACE_H
+
+#include<map>
+#include<set>
+#include "TH2.h"
+
+using namespace std;
+
+namespace PandoraPlus {
+  class HoughSpace{
+  public:
+    HoughSpace(){};
+    HoughSpace(double _alpha_low, double _alpha_high, double _bin_width_alpha, int _Nbins_alpha, double _rho_low, double _rho_high, double _bin_width_rho, int _Nbins_rho){
+      alpha_low = _alpha_low;
+      alpha_high = _alpha_high;
+      bin_width_alpha = _bin_width_alpha;
+      Nbins_alpha = _Nbins_alpha;
+      rho_low = _rho_low;
+      rho_high = _rho_high;
+      bin_width_rho = _bin_width_rho;
+      Nbins_rho = _Nbins_rho;
+    };
+    ~HoughSpace() {};
+
+     // Functions
+    int getAlphaBin(double alpha) const;
+    double getAlphaBinCenter(int alpha_bin) const;
+    double getAlphaBinLowEdge(int alpha_bin) const;
+    double getAlphaBinUpEdge(int alpha_bin) const;
+    int getRhoBin(double rho) const;
+    double getRhoBinCenter(int rho_bin) const;
+    double getRhoBinLowEdge(int rho_bin) const;
+    double getRhoBinUpEdge(int rho_bin) const;
+
+    void AddBinHobj(int bin_alpha, int bin_rho, int index_Hobj);
+    map< pair<int, int>, set<int> > getHoughBins() const { return Hough_bins; }
+
+  private:
+    double alpha_low;
+    double alpha_high;
+    double bin_width_alpha;
+    int Nbins_alpha;
+
+    double rho_low;
+    double rho_high;
+    double bin_width_rho;
+    int Nbins_rho;
+
+    map< pair<int, int>, set<int> > Hough_bins;
+    // description of the above map:
+    // key: the pair represents index of alpha bin and rho bin. All start from 1 to Nbins
+    // value: a set of index of HoughObject
+    // With this map, we know the relationship of bin in Hough space and Hough object
+
+  };
+
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Objects/PFObject.h b/Reconstruction/CrystalCaloRec/include/Objects/PFObject.h
new file mode 100644
index 00000000..889d3d05
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Objects/PFObject.h
@@ -0,0 +1,44 @@
+#ifndef PFOBJECT_H
+#define PFOBJECT_H
+
+#include "Objects/Calo3DCluster.h"
+#include "Objects/Track.h"
+
+namespace PandoraPlus{
+  class PFObject{
+  public:
+    PFObject () {};
+    ~PFObject() { Clear(); }
+
+    void Clear();
+    std::shared_ptr<PandoraPlus::PFObject> Clone() const; 
+
+    void addTrack(const Track* _track);
+    void addECALCluster(const Calo3DCluster* _ecal_cluster);
+    void addHCALCluster(const Calo3DCluster* _hcal_cluster);
+
+    void setPID(int _pid) { m_pid = _pid; }
+    void setTrack(std::vector<const Track*> _trkCol) { m_tracks = _trkCol; } 
+    void setECALCluster( std::vector<const Calo3DCluster*> _cluCol ) { m_ecal_clusters = _cluCol; }
+    void setHCALCluster( std::vector<const Calo3DCluster*> _cluCol ) { m_hcal_clusters = _cluCol; }
+
+    std::vector<const Track*> getTracks() const { return m_tracks; }
+    std::vector<const Calo3DCluster*> getECALClusters() const { return m_ecal_clusters; }
+    std::vector<const Calo3DCluster*> getHCALClusters() const { return m_hcal_clusters; }
+
+    int getPID() const { return m_pid; }
+    double getECALClusterEnergy() const;
+    double getHCALClusterEnergy() const;
+    double getTrackMomentum() const;
+
+
+  private:
+    int m_pid; 
+    std::vector<const Track*> m_tracks;
+    std::vector<const Calo3DCluster*> m_ecal_clusters;
+    std::vector<const Calo3DCluster*> m_hcal_clusters;
+
+  };
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Objects/Track.h b/Reconstruction/CrystalCaloRec/include/Objects/Track.h
new file mode 100644
index 00000000..49dc55e2
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Objects/Track.h
@@ -0,0 +1,58 @@
+#ifndef _TRACK_H
+#define _TRACK_H
+
+#include "edm4hep/Track.h"
+#include "Objects/TrackState.h"
+#include "Objects/CaloHalfCluster.h"
+#include "TVector3.h"
+
+namespace PandoraPlus {
+
+  class CaloHalfCluster;
+  class Track{
+  public:
+    Track() {}
+    ~Track() { Clear(); }; 
+  void Clear() { m_trackStates.clear(); m_halfClusterUCol.clear(); m_halfClusterVCol.clear(); }
+
+  void setOriginTrack(edm4hep::Track& _trk) { m_track = _trk; }
+  int trackStates_size(std::string name) const;
+  int trackStates_size() const ;
+  edm4hep::Track getOriginTrack() const { return m_track; }
+  std::vector<TrackState> getTrackStates(std::string name) const;
+  std::vector<TrackState> getAllTrackStates() const;
+  std::map<std::string, std::vector<TrackState> > getTrackStatesMap() const { return m_trackStates; }
+  std::vector<PandoraPlus::CaloHalfCluster*> getAssociatedHalfClustersU() const { return m_halfClusterUCol; }  
+  std::vector<PandoraPlus::CaloHalfCluster*> getAssociatedHalfClustersV() const { return m_halfClusterVCol; }  
+  std::vector< std::pair<edm4hep::MCParticle, float> > getLinkedMCP() const { return MCParticleWeight; }
+  edm4hep::MCParticle getLeadingMCP() const;
+  float getLeadingMCPweight() const;
+  float getPt() const;
+  float getPz() const;
+  float getMomentum() const { return sqrt( getPt()*getPt() + getPz()*getPz() ); } 
+  TVector3 getP3() const; 
+  float getCharge() const;
+
+  void setTrackStates( std::string name, std::vector<TrackState>& _states ) { m_trackStates[name]=_states; }
+  void addAssociatedHalfClusterU( PandoraPlus::CaloHalfCluster* _cl ) { m_halfClusterUCol.push_back(_cl); }
+  void addAssociatedHalfClusterV( PandoraPlus::CaloHalfCluster* _cl ) { m_halfClusterVCol.push_back(_cl); }
+  void addLinkedMCP( std::pair<edm4hep::MCParticle, float> _pair ) { MCParticleWeight.push_back(_pair); }
+  void setLinkedMCP( std::vector<std::pair<edm4hep::MCParticle, float>> _pairVec ) { MCParticleWeight.clear(); MCParticleWeight = _pairVec; }
+
+  void setType(int _type) { m_type=_type; }
+  int getType() const { return m_type; }
+
+  private:
+    edm4hep::Track m_track;
+    static const double B ; //direction of magnetic field and charge need to check
+
+    std::map<std::string, std::vector<TrackState> > m_trackStates; // name = Input, Ecal, Hcal
+    std::vector<PandoraPlus::CaloHalfCluster*> m_halfClusterUCol; 
+    std::vector<PandoraPlus::CaloHalfCluster*> m_halfClusterVCol; 
+
+    int m_type;
+    std::vector< std::pair<edm4hep::MCParticle, float> > MCParticleWeight;
+  };
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Objects/TrackState.h b/Reconstruction/CrystalCaloRec/include/Objects/TrackState.h
new file mode 100644
index 00000000..280404f2
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Objects/TrackState.h
@@ -0,0 +1,33 @@
+#ifndef _TRACKSTATE_H
+#define _TRACKSTATE_H
+#include "TVector3.h"
+
+namespace PandoraPlus{
+
+  class TrackState{
+  public: 
+    TrackState() {}
+    ~TrackState() {};
+    void Clear() {};
+
+    int location; 
+    float D0;
+    float Z0; 
+    float phi0; 
+    float Kappa;  //Kappa = omega*1000/(0.3*B[T]) = 1/pT
+    float tanLambda; 
+    float Omega;  
+    TVector3 referencePoint; 
+
+    static const int AtOther = 0 ; // any location other than the ones defined below
+    static const int AtIP = 1 ;
+    static const int AtFirstHit = 2 ;
+    static const int AtLastHit = 3 ;
+    static const int AtCalorimeter = 4 ;
+    static const int AtVertex = 5 ;
+    static const int LastLocation = AtVertex  ;
+    
+
+  };
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/PandoraPlusDataCol.h b/Reconstruction/CrystalCaloRec/include/PandoraPlusDataCol.h
new file mode 100644
index 00000000..03a9676a
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/PandoraPlusDataCol.h
@@ -0,0 +1,70 @@
+#ifndef _PANDORAPLUS_DATA_H
+#define _PANDORAPLUS_DATA_H
+#include <iostream>
+#include <algorithm>
+#include <map>
+
+#include <CrystalEcalSvc/ICrystalEcalSvc.h>
+#include "Objects/CaloHit.h"
+#include "Objects/CaloUnit.h"
+#include "Objects/Calo1DCluster.h"
+#include "Objects/Calo2DCluster.h"
+#include "Objects/CaloHalfCluster.h"
+#include "Objects/Calo3DCluster.h"
+#include "Objects/HoughObject.h"
+#include "Objects/HoughSpace.h"
+#include "Objects/PFObject.h"
+#include "Objects/Track.h"
+
+#include "k4FWCore/DataHandle.h"
+#include "edm4hep/MCParticleCollection.h"
+#include "edm4hep/MCParticle.h"
+#include "edm4hep/Track.h"
+#include "edm4hep/TrackCollection.h"
+#include "edm4hep/CalorimeterHit.h"
+#include "edm4hep/CalorimeterHitCollection.h"
+#include "edm4hep/Vertex.h"
+#include "edm4hep/VertexCollection.h"
+#include "edm4hep/ClusterCollection.h"
+#include "edm4hep/ReconstructedParticleCollection.h"
+#include "edm4hep/MCRecoCaloAssociation.h"
+#include "edm4hep/MCRecoTrackerAssociation.h"
+#include "edm4hep/MCRecoParticleAssociationCollection.h"
+#include "edm4hep/MCRecoCaloParticleAssociationCollection.h"
+#include "edm4hep/MCRecoTrackParticleAssociationCollection.h"
+
+#define PI 3.141592653
+//#define C 299.79  // unit: mm/ns
+using namespace std;
+const double C = 299.79;
+class PandoraPlusDataCol{
+public:
+
+  PandoraPlusDataCol() {}; 
+  ~PandoraPlusDataCol() { Clear(); }
+  StatusCode Clear(); 
+
+  //Readin CollectionMap
+  std::map<std::string, std::vector<edm4hep::MCParticle> >       collectionMap_MC;
+  std::map<std::string, std::vector<edm4hep::CalorimeterHit> >   collectionMap_CaloHit;
+  std::map<std::string, std::vector<edm4hep::Vertex> >           collectionMap_Vertex;
+  std::map<std::string, std::vector<edm4hep::Track> >            collectionMap_Track;
+  std::map<std::string, std::vector<edm4hep::MCRecoCaloAssociation> > collectionMap_CaloRel;
+  std::map<std::string, std::vector<edm4hep::MCRecoTrackerAssociation> > collectionMap_TrkRel;
+
+  //Self used objects
+  //General objects for all PFA
+  std::vector<std::shared_ptr<PandoraPlus::Track>>       TrackCol;
+  std::map<std::string, std::vector<std::shared_ptr<PandoraPlus::CaloHit>>> map_CaloHit; //Hit
+  std::map<std::string, std::vector<std::shared_ptr<PandoraPlus::CaloUnit>>> map_BarCol; 
+  std::map<std::string, std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>>> map_1DCluster; 
+  std::map<std::string, std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>> map_HalfCluster;
+  std::map<std::string, std::vector<std::shared_ptr<PandoraPlus::Calo2DCluster>>> map_2DCluster; 
+  std::map<std::string, std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>> map_CaloCluster; //Cluster
+  std::map<std::string, std::vector<std::shared_ptr<PandoraPlus::PFObject>>> map_PFObjects;
+
+  //Energy calibration service
+  SmartIF<ICrystalEcalSvc> EnergyCorrSvc; 
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/PandoraPlusPFAlg.h b/Reconstruction/CrystalCaloRec/include/PandoraPlusPFAlg.h
new file mode 100644
index 00000000..b5836d94
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/PandoraPlusPFAlg.h
@@ -0,0 +1,321 @@
+#ifndef PANDORAPLUS_ALG_H
+#define PANDORAPLUS_ALG_H
+
+#include <string>
+#include "k4FWCore/DataHandle.h"
+#include "GaudiAlg/GaudiAlgorithm.h"
+#include <DDRec/DetectorData.h>
+#include <DDRec/CellIDPositionConverter.h>
+#include <DD4hep/Segmentations.h>
+#include "DetInterface/IGeomSvc.h"
+#include <CrystalEcalSvc/ICrystalEcalSvc.h>
+
+#include "k4FWCore/PodioDataSvc.h"
+#include "podio/CollectionBase.h"
+#include "podio/ROOTFrameWriter.h"
+
+#include "PandoraPlusDataCol.h"
+#include "Tools/MCParticleCreator.h"
+#include "Tools/TrackCreator.h"
+#include "Tools/CaloHitsCreator.h"
+#include "Tools/OutputCreator.h"
+#include "Tools/AlgorithmManager.h"
+#include "Algorithm/ExampleAlg.h"
+#include "Algorithm/GlobalClusteringAlg.h"
+#include "Algorithm/HcalClusteringAlg.h"
+#include "Algorithm/LocalMaxFindingAlg.h"
+#include "Algorithm/TrackMatchingAlg.h"
+#include "Algorithm/HoughClusteringAlg.h"
+#include "Algorithm/ConeClustering2DAlg.h"
+#include "Algorithm/AxisMergingAlg.h"
+#include "Algorithm/EnergySplittingAlg.h"
+#include "Algorithm/EnergyTimeMatchingAlg.h"
+#include "Algorithm/ConeClusteringAlg.h"
+#include "Algorithm/TrackExtrapolatingAlg.h"
+#include "Algorithm/PFOCreatingAlg.h"
+#include "Algorithm/TrackClusterConnectingAlg.h"
+#include "Algorithm/PFOReclusteringAlg.h"
+
+#include "Algorithm/TruthClusteringAlg.h"
+#include "Algorithm/TruthTrackMatchingAlg.h"
+#include "Algorithm/TruthPatternRecAlg.h"
+#include "Algorithm/TruthEnergySplittingAlg.h"
+#include "Algorithm/TruthMatchingAlg.h"
+#include "Algorithm/TruthClusterMergingAlg.h"
+
+#include "TVector3.h"
+#include "TRandom3.h"
+#include "TFile.h"
+#include "TTree.h"
+#include "TBranch.h"
+
+#include <cstdlib>
+
+using namespace PandoraPlus;
+using namespace std;
+
+class PandoraPlusPFAlg : public GaudiAlgorithm
+{
+ 
+public:
+ 
+  PandoraPlusPFAlg(const std::string& name, ISvcLocator* svcLoc);
+ 
+  /** Called at the begin of the job before anything is read.
+   * Use to initialize the processor, e.g. book histograms.
+   */
+  virtual StatusCode initialize() ;
+ 
+  /** Called for every event - the working horse.
+   */
+  virtual StatusCode execute() ; 
+ 
+  /** Called after data processing for clean up.
+   */
+  virtual StatusCode finalize() ;
+  
+
+protected:
+
+  int _nEvt ;
+  TRandom3 rndm;
+
+  SmartIF<IGeomSvc> m_geosvc;
+  SmartIF<ICrystalEcalSvc> m_energycorsvc;
+  std::map<std::string, dd4hep::DDSegmentation::BitFieldCoder*> map_readout_decoder;
+
+  //DataCollection: moved into execute() to ensure everything can be cleand after one event. 
+  //PandoraPlusDataCol     m_DataCol; 
+
+
+  //Creators and their setting
+  MCParticleCreator       *m_pMCParticleCreator;
+  TrackCreator            *m_pTrackCreator;
+  CaloHitsCreator         *m_pCaloHitsCreator;
+  OutputCreator           *m_pOutputCreator;
+
+  Settings   m_pMCParticleCreatorSettings;
+  Settings   m_pTrackCreatorSettings;
+  Settings   m_CaloHitsCreatorSettings;
+  Settings   m_OutputCreatorSettings;
+
+
+  //Parameters for PFA algorithm
+  Settings m_GlobalSettings; 
+
+
+  //Algorithm for PFA
+  PandoraPlus::AlgorithmManager m_algorithmManager; 
+
+
+  //Readin collection names
+  Gaudi::Property< std::string > name_MCParticleCol{ this, "MCParticleCollection", "MCParticle" };
+  Gaudi::Property< std::string > name_MCPTrkAssoCol{this, "MCRecoTrackParticleAssociationCollection", "MarlinTrkAssociation"};
+  Gaudi::Property< std::vector<std::string> > name_TrackCol{ this, "TrackCollections", {"MarlinTrkTracks"} };
+  Gaudi::Property< std::vector<std::string> > name_EcalHits{ this, "ECalCaloHitCollections", {"ECALBarrel"} };
+  Gaudi::Property< std::vector<std::string> > name_EcalReadout{ this, "ECalReadOutNames", {"EcalBarrelCollection"} }; 
+  Gaudi::Property< std::vector<std::string> > name_EcalMCPAssociation{ this, "ECalMCPAssociationName", {"ECALBarrelParticleAssoCol"} };
+  Gaudi::Property< std::vector<std::string> > name_HcalHits{ this, "HCalCaloHitCollections", {"HCALBarrel"} };
+  Gaudi::Property< std::vector<std::string> > name_HcalReadout{ this, "HCalReadOutNames", {"HcalBarrelCollection"} }; 
+  Gaudi::Property< std::vector<std::string> > name_HcalMCPAssociation{ this, "HCalMCPAssociationName", {"HCALBarrelParticleAssoCol"} };
+  
+
+  //---Readin collections
+  typedef DataHandle<edm4hep::TrackCollection>                          TrackType; 
+  typedef DataHandle<edm4hep::CalorimeterHitCollection>                 CaloType; 
+  typedef DataHandle<edm4hep::MCRecoCaloParticleAssociationCollection>  CaloParticleAssoType; 
+  DataHandle<edm4hep::MCParticleCollection>* r_MCParticleCol; 
+  DataHandle<edm4hep::MCRecoTrackParticleAssociationCollection>* r_MCPTrkAssoCol;  
+  std::vector<TrackType*> r_TrackCols; 
+  //std::vector<CaloType*>  r_ECalHitCols; 
+  //std::vector<CaloType*>  r_HCalHitCols; 
+  std::vector<CaloType*>  r_CaloHitCols; 
+  std::map<std::string, CaloParticleAssoType*> map_CaloMCPAssoCols;
+
+  //Global parameters.
+  Gaudi::Property<float>  m_BField{this,  "BField", 3., "Magnetic field"};
+  Gaudi::Property<float>  m_seed{this,    "Seed", 2131, "Random Seed"};
+  Gaudi::Property<int>    m_Debug{this,   "Debug", 0, "Debug level"};
+  Gaudi::Property<int>    m_Nskip{this,   "SkipEvt", 0, "Skip event"};
+  Gaudi::Property<std::string>   m_EcalType{this, "EcalType", "BarEcal", "ECAL type"};
+  Gaudi::Property<bool>   m_useTruthTrk{this,   "UseTruthTrack", 1, "Use truth track or reconstructed track"};
+  Gaudi::Property<float>  m_EcalCalib{this,  "EcalGlobalCalib", 1.02, "ECAL global calibration"};
+  Gaudi::Property<float>  m_HcalCalib{this,  "HcalGlobalCalib", 65.,  "HCAL global calibration"};
+  
+  //Algorithms: 
+  typedef std::vector<std::string> StringVector;
+  Gaudi::Property< StringVector > name_Algs{ this, "AlgList", {} };
+  Gaudi::Property< std::vector<StringVector> > name_AlgPars{ this, "AlgParNames", {} };
+  Gaudi::Property< std::vector<StringVector> > type_AlgPars{ this, "AlgParTypes", {} };
+  Gaudi::Property< std::vector<StringVector> > value_AlgPars{this, "AlgParValues", {} };
+
+
+  // Output collections
+  DataHandle<edm4hep::CalorimeterHitCollection>             w_RecEcalCol{"RecECALBarrel", Gaudi::DataHandle::Writer, this};
+  DataHandle<edm4hep::CalorimeterHitCollection>             w_RecCoreCol{"RecECALBarrelCore", Gaudi::DataHandle::Writer, this};
+  DataHandle<edm4hep::CalorimeterHitCollection>             w_RecHcalCol{"RecHCALBarrel", Gaudi::DataHandle::Writer, this};
+  DataHandle<edm4hep::TrackCollection>                      w_RecTrkCol{"RecTracks", Gaudi::DataHandle::Writer, this};
+  //Gaudi::Property< std::string > name_EcalCluster{this, "OutputEcalCluster", "TrkMergedECAL"};
+  //Gaudi::Property< std::string > name_EcalCore   {this, "OutputEcalCore", "EcalCore"};
+  //Gaudi::Property< std::string > name_HcalCluster{this, "OutputHcalCluster", "HcalCluster"};
+  Gaudi::Property< std::string > name_PFObject   {this, "OutputPFO", "outputPFO"};
+  DataHandle<edm4hep::ReconstructedParticleCollection>      w_ReconstructedParticleCollection {"PandoraPFOs"    ,Gaudi::DataHandle::Writer, this};
+  std::map<std::string, DataHandle<edm4hep::ClusterCollection>* > w_ClusterCollection;
+
+
+  //For Ana
+  Gaudi::Property<bool>  m_WriteAna {this, "WriteAna", false, "Write Ntuples for analysis"};
+  Gaudi::Property<std::string> m_filename{this, "AnaFileName", "testout.root", "Output file name"};
+
+  typedef std::vector<float> FloatVec;
+  typedef std::vector<int>   IntVec;
+
+  TFile* m_wfile;
+
+  // MC particle
+  TTree *t_MCParticle;
+  int m_Nmc;
+  IntVec m_mcPdgid, m_mcStatus;
+  FloatVec m_mcPx, m_mcPy, m_mcPz, m_mcEn, m_mcMass, m_mcCharge, m_mcEPx, m_mcEPy, m_mcEPz, m_depEn_ecal, m_depEn_hcal;
+
+  //Raw bars and hits
+  TTree* t_SimBar;
+  float m_totE_EcalSim, m_totE_HcalSim;
+  FloatVec m_simBar_x, m_simBar_y, m_simBar_z, m_simBar_T1, m_simBar_T2, m_simBar_Q1, m_simBar_Q2; 
+  FloatVec m_simBar_truthMC_tag, m_simBar_truthMC_pid, m_simBar_truthMC_px, m_simBar_truthMC_py, m_simBar_truthMC_pz, m_simBar_truthMC_E, 
+           m_simBar_truthMC_EPx, m_simBar_truthMC_EPy, m_simBar_truthMC_EPz, m_simBar_truthMC_weight;
+  IntVec m_simBar_dlayer, m_simBar_stave, m_simBar_slayer, m_simBar_module, m_simBar_bar;
+  FloatVec m_HcalHit_x, m_HcalHit_y, m_HcalHit_z, m_HcalHit_E, 
+           m_HcalHit_truthMC_tag, m_HcalHit_truthMC_pid, m_HcalHit_truthMC_px, m_HcalHit_truthMC_py, m_HcalHit_truthMC_pz, m_HcalHit_truthMC_E,
+           m_HcalHit_truthMC_EPx, m_HcalHit_truthMC_EPy, m_HcalHit_truthMC_EPz, m_HcalHit_truthMC_weight;
+  IntVec   m_HcalHit_layer;
+
+  //localMax
+  TTree *t_LocalMax;
+  int m_NlmU, m_NlmV;
+  IntVec m_localMaxU_mc_pdg, m_localMaxV_mc_pdg, m_localMaxU_mc_tag, m_localMaxV_mc_tag;
+  FloatVec m_localMaxU_tag, m_localMaxU_x, m_localMaxU_y, m_localMaxU_z, m_localMaxU_E,
+            m_localMaxU_mc_px, m_localMaxU_mc_py, m_localMaxU_mc_pz, m_localMaxU_mc_weight;
+  FloatVec m_localMaxV_tag, m_localMaxV_x, m_localMaxV_y, m_localMaxV_z, m_localMaxV_E,
+            m_localMaxV_mc_px, m_localMaxV_mc_py, m_localMaxV_mc_pz, m_localMaxV_mc_weight;
+
+
+  //1D showers
+  TTree *t_Layers;
+  int m_NshowerU, m_NshowerV;
+  IntVec m_barShowerU_mc_pdg, m_barShowerV_mc_pdg, m_barShowerU_mc_tag, m_barShowerV_mc_tag;
+  FloatVec m_barShowerU_tag, m_barShowerU_x, m_barShowerU_y, m_barShowerU_z, m_barShowerU_E,
+            m_barShowerU_mc_px, m_barShowerU_mc_py, m_barShowerU_mc_pz, m_barShowerU_mc_weight;
+  FloatVec m_barShowerV_tag, m_barShowerV_x, m_barShowerV_y, m_barShowerV_z, m_barShowerV_E,
+            m_barShowerV_mc_px, m_barShowerV_mc_py, m_barShowerV_mc_pz, m_barShowerV_mc_weight;
+  // Hough axis
+  TTree * t_Hough;
+  IntVec m_houghU_type, m_houghV_type;
+  FloatVec  m_houghU_tag, m_houghU_x, m_houghU_y, m_houghU_z, m_houghU_E,
+            m_houghU_truth_tag, m_houghU_truth_MC_px, m_houghU_truth_MC_py, m_houghU_truth_MC_pz, m_houghU_truth_MC_E, m_houghU_truth_MC_weight,
+            m_houghU_hit_tag, m_houghU_hit_x, m_houghU_hit_y, m_houghU_hit_z, m_houghU_hit_E;
+  FloatVec  m_houghV_tag, m_houghV_x, m_houghV_y, m_houghV_z, m_houghV_E, m_houghV_alpha, m_houghV_rho,
+            m_houghV_truth_tag, m_houghV_truth_MC_px, m_houghV_truth_MC_py, m_houghV_truth_MC_pz, m_houghV_truth_MC_E, m_houghV_truth_MC_weight,
+            m_houghV_hit_tag, m_houghV_hit_x, m_houghV_hit_y, m_houghV_hit_z, m_houghV_hit_E;
+  // Cone axis
+  TTree * t_Cone;
+  IntVec m_coneU_type, m_coneV_type;
+  FloatVec  m_coneU_tag, m_coneU_x, m_coneU_y, m_coneU_z, m_coneU_E,
+            m_coneU_truth_tag, m_coneU_truth_MC_px, m_coneU_truth_MC_py, m_coneU_truth_MC_pz, m_coneU_truth_MC_E, m_coneU_truth_MC_weight,
+            m_coneU_hit_tag, m_coneU_hit_x, m_coneU_hit_y, m_coneU_hit_z, m_coneU_hit_E;
+  FloatVec  m_coneV_tag, m_coneV_x, m_coneV_y, m_coneV_z, m_coneV_E,
+            m_coneV_truth_tag, m_coneV_truth_MC_px, m_coneV_truth_MC_py, m_coneV_truth_MC_pz, m_coneV_truth_MC_E, m_coneV_truth_MC_weight,
+            m_coneV_hit_tag, m_coneV_hit_x, m_coneV_hit_y, m_coneV_hit_z, m_coneV_hit_E;
+  // Track axis
+  TTree * t_TrackAxis;
+  IntVec m_trackU_type, m_trackV_type;
+  FloatVec  m_trackU_tag, m_trackU_x, m_trackU_y, m_trackU_z, m_trackU_E,
+            m_trackU_truth_tag, m_trackU_truth_MC_px, m_trackU_truth_MC_py, m_trackU_truth_MC_pz, m_trackU_truth_MC_E, m_trackU_truth_MC_weight,
+            m_trackU_hit_tag, m_trackU_hit_x, m_trackU_hit_y, m_trackU_hit_z, m_trackU_hit_E;
+  FloatVec  m_trackV_tag, m_trackV_x, m_trackV_y, m_trackV_z, m_trackV_E,
+            m_trackV_truth_tag, m_trackV_truth_MC_px, m_trackV_truth_MC_py, m_trackV_truth_MC_pz, m_trackV_truth_MC_E, m_trackV_truth_MC_weight,
+            m_trackV_hit_tag, m_trackV_hit_x, m_trackV_hit_y, m_trackV_hit_z, m_trackV_hit_E;
+  // axis
+  TTree *t_Axis;
+  IntVec m_axisU_type, m_axisV_type;
+  FloatVec  m_axisU_tag, m_axisU_x, m_axisU_y, m_axisU_z, m_axisU_E,
+            m_axisU_truth_tag, m_axisU_truth_MC_px, m_axisU_truth_MC_py, m_axisU_truth_MC_pz, m_axisU_truth_MC_E, m_axisU_truth_MC_weight,
+            m_axisU_hit_tag, m_axisU_hit_x, m_axisU_hit_y, m_axisU_hit_z, m_axisU_hit_E;
+  FloatVec  m_axisV_tag, m_axisV_x, m_axisV_y, m_axisV_z, m_axisV_E,
+            m_axisV_truth_tag, m_axisV_truth_MC_px, m_axisV_truth_MC_py, m_axisV_truth_MC_pz, m_axisV_truth_MC_E, m_axisV_truth_MC_weight,
+            m_axisV_hit_tag, m_axisV_hit_x, m_axisV_hit_y, m_axisV_hit_z, m_axisV_hit_E;
+  FloatVec  m_emptyAxisU_tag, m_emptyAxisU_x, m_emptyAxisU_y, m_emptyAxisU_z, m_emptyAxisU_E; 
+  FloatVec  m_emptyAxisV_tag, m_emptyAxisV_x, m_emptyAxisV_y, m_emptyAxisV_z, m_emptyAxisV_E; 
+
+
+  //HalfCluster after energy splitting
+  TTree *t_HalfCluster;
+  float m_totE_HFClusU, m_totE_HFClusV;
+  FloatVec m_HalfClusterV_x, m_HalfClusterV_y, m_HalfClusterV_z, m_HalfClusterV_E, m_HalfClusterV_tag, m_HalfClusterV_type, m_HalfClusterV_nTrk;
+  FloatVec m_HalfClusterV_hit_x, m_HalfClusterV_hit_y, m_HalfClusterV_hit_z, m_HalfClusterV_hit_E, m_HalfClusterV_hit_tag;
+  FloatVec m_HalfClusterV_truth_tag, m_HalfClusterV_truthMC_px, m_HalfClusterV_truthMC_py, m_HalfClusterV_truthMC_pz, m_HalfClusterV_truthMC_E, m_HalfClusterV_truthMC_weight;
+  FloatVec m_HalfClusterU_x, m_HalfClusterU_y, m_HalfClusterU_z, m_HalfClusterU_E, m_HalfClusterU_tag, m_HalfClusterU_type, m_HalfClusterU_nTrk;
+  FloatVec m_HalfClusterU_hit_x, m_HalfClusterU_hit_y, m_HalfClusterU_hit_z, m_HalfClusterU_hit_E, m_HalfClusterU_hit_tag;
+  FloatVec m_HalfClusterU_truth_tag, m_HalfClusterU_truthMC_px, m_HalfClusterU_truthMC_py, m_HalfClusterU_truthMC_pz, m_HalfClusterU_truthMC_E, m_HalfClusterU_truthMC_weight;
+
+
+  //Tower
+  TTree *t_Tower; 
+  int towerID[3];
+  int m_NclusU, m_NclusV; 
+  float m_totEn, m_totEn_U, m_totEn_V;
+
+
+  //3D clusters
+  TTree *t_Cluster;
+  float m_totE_Ecal, m_totE_Hcal;
+  int m_Nclus_Ecal, m_Nclus_Hcal;
+  IntVec m_EcalClus_trk_location, m_EcalClus_trk_tag;
+  FloatVec m_EcalClus_x, m_EcalClus_y, m_EcalClus_z, m_EcalClus_E, m_EcalClus_Escale, m_EcalClus_nTrk, m_EcalClus_pTrk, m_EcalClus_typeU, m_EcalClus_typeV,
+          m_EcalClus_hitU_x, m_EcalClus_hitU_y, m_EcalClus_hitU_z, m_EcalClus_hitU_E, m_EcalClus_hitU_tag,
+          m_EcalClus_hitV_x, m_EcalClus_hitV_y, m_EcalClus_hitV_z, m_EcalClus_hitV_E, m_EcalClus_hitV_tag,
+          m_EcalClus_trk_d0, m_EcalClus_trk_z0, m_EcalClus_trk_phi, m_EcalClus_trk_tanL, m_EcalClus_trk_omega, m_EcalClus_trk_kappa,
+          m_EcalClus_truthMC_tag, m_EcalClus_truthMC_pid, m_EcalClus_truthMC_px, m_EcalClus_truthMC_py, m_EcalClus_truthMC_pz, m_EcalClus_truthMC_E,
+          m_EcalClus_truthMC_EPx, m_EcalClus_truthMC_EPy, m_EcalClus_truthMC_EPz, m_EcalClus_truthMC_weight;
+  FloatVec m_HcalClus_x, m_HcalClus_y, m_HcalClus_z, m_HcalClus_E, m_HcalClus_nTrk, m_HcalClus_pTrk,
+          m_HcalClus_hit_x, m_HcalClus_hit_y, m_HcalClus_hit_z, m_HcalClus_hit_E, m_HcalClus_hit_tag,
+          m_HcalClus_truthMC_tag, m_HcalClus_truthMC_pid, m_HcalClus_truthMC_px, m_HcalClus_truthMC_py, m_HcalClus_truthMC_pz, m_HcalClus_truthMC_E,
+          m_HcalClus_truthMC_EPx, m_HcalClus_truthMC_EPy, m_HcalClus_truthMC_EPz, m_HcalClus_truthMC_weight;
+  FloatVec m_SimpleHcalClus_x, m_SimpleHcalClus_y, m_SimpleHcalClus_z, m_SimpleHcalClus_E, m_SimpleHcalClus_nTrk, m_SimpleHcalClus_pTrk,
+          m_SimpleHcalClus_hit_x, m_SimpleHcalClus_hit_y, m_SimpleHcalClus_hit_z, m_SimpleHcalClus_hit_E, m_SimpleHcalClus_hit_tag,
+          m_SimpleHcalClus_truthMC_tag, m_SimpleHcalClus_truthMC_pid, m_SimpleHcalClus_truthMC_px, m_SimpleHcalClus_truthMC_py, m_SimpleHcalClus_truthMC_pz, m_SimpleHcalClus_truthMC_E,
+          m_SimpleHcalClus_truthMC_EPx, m_SimpleHcalClus_truthMC_EPy, m_SimpleHcalClus_truthMC_EPz, m_SimpleHcalClus_truthMC_weight;
+
+  TTree *t_Track;
+  int m_Ntrk; 
+  FloatVec m_trkstate_d0, m_trkstate_z0, m_trkstate_phi, m_trkstate_tanL, m_trkstate_omega, m_trkstate_kappa;
+  FloatVec m_trkstate_refx, m_trkstate_refy, m_trkstate_refz; 
+  IntVec m_trkstate_tag, m_trkstate_location, m_type;
+
+  // yyy: output PFO information
+  TTree *t_PFO;
+  IntVec pfo_tag, pfo_n_track, pfo_n_ecal_clus, pfo_n_hcal_clus, pfo_ecal_tag, pfo_hcal_tag;
+  IntVec pfo_trk_location, pfo_trk_tag;
+  FloatVec pfo_trk_d0, pfo_trk_z0, pfo_trk_phi, pfo_trk_tanL, pfo_trk_omega, pfo_trk_kappa;
+  FloatVec  pfo_ecal_clus_x, pfo_ecal_clus_y, pfo_ecal_clus_z, pfo_ecal_clus_E, pfo_ecal_clus_Escale,
+            pfo_hcal_clus_x, pfo_hcal_clus_y, pfo_hcal_clus_z, pfo_hcal_clus_E;
+
+
+  void ClearMCParticle();
+  void ClearBar();
+  void ClearLocalMax();
+  void ClearLayer();
+  void ClearHough();
+  void ClearCone();
+  void ClearTrackAxis();
+  void ClearAxis();
+  void ClearHalfCluster();
+  void ClearTower();
+  void ClearCluster();
+  void ClearTrack();
+  void ClearPFO(); // yyy
+
+  double GetParticleDepEnergy(edm4hep::MCParticle& mcp, std::vector<std::shared_ptr<PandoraPlus::CaloUnit>>& barcol);
+  double GetParticleDepEnergy(edm4hep::MCParticle& mcp, std::vector<std::shared_ptr<PandoraPlus::CaloHit>>& hitcol);
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Tools/Algorithm.h b/Reconstruction/CrystalCaloRec/include/Tools/Algorithm.h
new file mode 100644
index 00000000..14cd8fad
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Tools/Algorithm.h
@@ -0,0 +1,46 @@
+#ifndef ALGORITHM_TEMP_H
+#define ALGORITHM_TEMP_H
+
+#include "GaudiAlg/GaudiAlgorithm.h"
+
+#include "PandoraPlusDataCol.h"
+
+namespace PandoraPlus{
+
+  class Settings{
+  public: 
+    Settings(){};
+    ~Settings(){ Clear(); }
+   
+    void Clear() { map_intPars.clear();  map_floatPars.clear(); map_boolPars.clear(); map_stringPars.clear(); map_stringVecPars.clear(); }
+    std::map<std::string, int>    map_intPars; 
+    std::map<std::string, double> map_floatPars; 
+    std::map<std::string, bool>   map_boolPars; 
+    std::map<std::string, std::string> map_stringPars; 
+    std::map<std::string, std::vector<std::string> > map_stringVecPars;
+
+  };
+
+  class Algorithm{
+  public:
+    Algorithm() {};
+    virtual ~Algorithm() {};
+
+    virtual StatusCode ReadSettings(Settings& m_settings) = 0;
+    virtual StatusCode Initialize( PandoraPlusDataCol& m_datacol ) = 0;
+    virtual StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol ) = 0;
+    virtual StatusCode ClearAlgorithm() = 0;
+
+
+    Settings settings; 
+  };
+
+  class AlgorithmFactory{
+  public:
+
+    virtual ~AlgorithmFactory(){}; 
+    virtual Algorithm* CreateAlgorithm() const = 0;
+  };
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Tools/AlgorithmManager.h b/Reconstruction/CrystalCaloRec/include/Tools/AlgorithmManager.h
new file mode 100644
index 00000000..1089aaf1
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Tools/AlgorithmManager.h
@@ -0,0 +1,66 @@
+#ifndef ALGORITHM_MANAGER_H
+#define ALGORITHM_MANAGER_H
+
+#include "Tools/Algorithm.h"
+
+namespace PandoraPlus{
+
+  class Algorithm;
+  class AlgorithmFactory;
+
+  class AlgorithmManager{
+  public: 
+
+    AlgorithmManager(){};
+    ~AlgorithmManager(){ Clean(); };
+   
+    void Clean(){
+      m_algorithmNames.clear();
+      for(auto iter : m_algorithmFactoryMap) delete iter.second;
+      for(auto iter : m_algorithmMap) delete iter.second;
+   
+      m_algorithmFactoryMap.clear();
+      m_algorithmMap.clear();
+    }
+   
+    StatusCode RegisterAlgorithmFactory(const std::string& algorithmName, AlgorithmFactory *const algorithmFactory)
+    {
+      if(!m_algorithmFactoryMap.insert(AlgorithmFactoryMap::value_type(algorithmName, algorithmFactory)).second ){
+        std::cout<<"ERROR in Register AlgorithmFactory: can not register "<<algorithmName<<std::endl;
+        return StatusCode::FAILURE; 
+      }
+      return StatusCode::SUCCESS;
+    }
+   
+    StatusCode RegisterAlgorithm( const std::string& algorithmName, Settings& m_algorithmSetting ){
+      m_algorithmNames.push_back(algorithmName);
+      m_algorithmMap.insert(AlgorithmMap::value_type(algorithmName, m_algorithmFactoryMap[algorithmName]->CreateAlgorithm()) );
+      m_algorithmMap[algorithmName]->ReadSettings(m_algorithmSetting);
+   
+      return StatusCode::SUCCESS;
+    }
+   
+    //StatusCode CreateAlgorithm(const std::map<std::string, Settings>& m_algorithmSettings); 
+    StatusCode RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+      for(auto iter : m_algorithmNames){
+cout<<"Processing Algorithm: "<<iter<<endl;
+        m_algorithmMap[iter]->Initialize(m_datacol);
+        m_algorithmMap[iter]->RunAlgorithm(m_datacol);
+        m_algorithmMap[iter]->ClearAlgorithm();
+      }
+      return StatusCode::SUCCESS;
+    }
+
+  private: 
+    typedef std::map<const std::string, Algorithm *const> AlgorithmMap;
+    typedef std::map< std::string, AlgorithmFactory *const> AlgorithmFactoryMap;
+
+    AlgorithmMap              m_algorithmMap;
+    AlgorithmFactoryMap       m_algorithmFactoryMap;
+    std::vector<std::string>  m_algorithmNames;
+
+  };
+
+};
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Tools/CaloHitsCreator.h b/Reconstruction/CrystalCaloRec/include/Tools/CaloHitsCreator.h
new file mode 100644
index 00000000..26b47771
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Tools/CaloHitsCreator.h
@@ -0,0 +1,37 @@
+#ifndef ECALHIT_CREATOR_H
+#define ECALHIT_CREATOR_H
+
+#include "k4FWCore/DataHandle.h"
+#include "PandoraPlusDataCol.h"
+#include "Tools/Algorithm.h"
+#include <DDRec/DetectorData.h>
+#include <DDRec/CellIDPositionConverter.h>
+#include <DD4hep/Segmentations.h>
+
+namespace PandoraPlus{
+
+  class CaloHitsCreator{
+
+  public: 
+    //initialize a CaloHitCreator
+    CaloHitsCreator( const Settings& m_settings );
+    ~CaloHitsCreator() {};
+   
+    StatusCode CreateCaloHits( PandoraPlusDataCol& m_DataCol,
+                               std::vector<DataHandle<edm4hep::CalorimeterHitCollection>*>& r_CaloHitCols, 
+                               std::map<std::string, dd4hep::DDSegmentation::BitFieldCoder*>& map_decoder, 
+                               std::map<std::string, DataHandle<edm4hep::MCRecoCaloParticleAssociationCollection>*>& map_CaloParticleAssoCol ); 
+
+    //StatusCode CreateMCParticleCaloHitsAsso( std::vector<DataHandle<edm4hep::CalorimeterHitCollection>*>& r_CaloHitCols, 
+    //                                         DataHandle<edm4hep::MCRecoCaloParticleAssociationCollection>* r_MCParticleRecoCaloCol );
+
+    //StatusCode Clustering( PandoraPlusDataCol& m_DataCol ) { return StatusCode::SUCCESS; };
+   
+    StatusCode Reset() { return StatusCode::SUCCESS; };
+
+  private: 
+    const PandoraPlus::Settings  settings; 
+
+  };
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Tools/MCParticleCreator.h b/Reconstruction/CrystalCaloRec/include/Tools/MCParticleCreator.h
new file mode 100644
index 00000000..9ed7795e
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Tools/MCParticleCreator.h
@@ -0,0 +1,34 @@
+#ifndef MCPARTICLE_CREATOR_H
+#define MCPARTICLE_CREATOR_H
+
+#include "PandoraPlusDataCol.h"
+#include "Tools/Algorithm.h"
+
+namespace PandoraPlus{
+
+  class MCParticleCreator{
+
+  public: 
+
+    //initialize a CaloHitCreator
+    MCParticleCreator( const Settings& m_settings );
+    ~MCParticleCreator() {};
+
+    StatusCode CreateMCParticle( PandoraPlusDataCol& m_DataCol, 
+                                 DataHandle<edm4hep::MCParticleCollection>& r_MCParticleCol ); 
+
+    //StatusCode CreateTrackMCParticleRelation(){ return StatusCode::SUCCESS; };
+
+    //StatusCode CreateEcalBarMCParticleRelation(){ return StatusCode::SUCCESS; };
+
+    //StatusCode CreateHcalHitsMCParticleRelation(){ return StatusCode::SUCCESS; };
+
+    StatusCode Reset() { return StatusCode::SUCCESS; };
+
+  private: 
+    const PandoraPlus::Settings   settings;  
+
+  };
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Tools/OutputCreator.h b/Reconstruction/CrystalCaloRec/include/Tools/OutputCreator.h
new file mode 100644
index 00000000..485dc7c2
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Tools/OutputCreator.h
@@ -0,0 +1,37 @@
+#ifndef OUTPUT_CREATOR_H
+#define OUTPUT_CREATOR_H
+
+#include "k4FWCore/DataHandle.h"
+#include "edm4hep/MutableCalorimeterHit.h"
+#include "edm4hep/Vector3f.h"
+#include "PandoraPlusDataCol.h"
+#include "Tools/Algorithm.h"
+//#include "Tools/HelixClassD.h"
+
+namespace PandoraPlus{
+
+  class OutputCreator{
+  public: 
+
+    OutputCreator( const Settings& m_settings);
+    ~OutputCreator() {};
+
+    StatusCode CreateOutputCollections( PandoraPlusDataCol& m_DataCol, 
+                                        DataHandle<edm4hep::CalorimeterHitCollection>& m_outRecHitsHandler, 
+                                        DataHandle<edm4hep::CalorimeterHitCollection>& m_outRecCoreHandler, 
+                                        DataHandle<edm4hep::CalorimeterHitCollection>& m_outRecHcalHitsHandler, 
+                                        DataHandle<edm4hep::TrackCollection>& m_outTrkHandler, 
+                                        std::map<std::string, DataHandle<edm4hep::ClusterCollection>*>& m_outClusterColHandler, 
+                                        DataHandle<edm4hep::ReconstructedParticleCollection>& m_recPFOHandler );
+
+
+    StatusCode Reset() { return StatusCode::SUCCESS; }
+
+    edm4hep::Track TruthTrack(edm4hep::MCParticle _mcp, edm4hep::TrackCollection* _trkCol );
+
+  private: 
+    const PandoraPlus::Settings   settings;
+  
+  };
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Tools/TrackCreator.h b/Reconstruction/CrystalCaloRec/include/Tools/TrackCreator.h
new file mode 100644
index 00000000..a35cfaa5
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Tools/TrackCreator.h
@@ -0,0 +1,36 @@
+#ifndef TRACK_CREATOR_H
+#define TRACK_CREATOR_H
+
+#include "PandoraPlusDataCol.h"
+#include "Tools/Algorithm.h"
+#include "Algorithm/TrackExtrapolatingAlg.h"
+#include "TVector3.h"
+
+namespace PandoraPlus{
+  class TrackCreator{
+
+  public: 
+
+    //initialize a CaloHitCreator
+    TrackCreator( const Settings& m_settings );
+    ~TrackCreator() { delete m_TrkExtraAlg; };
+   
+    StatusCode CreateTracks( PandoraPlusDataCol& m_DataCol, 
+                             std::vector<DataHandle<edm4hep::TrackCollection>*>& r_TrackCols, 
+                             DataHandle<edm4hep::MCRecoTrackParticleAssociationCollection>* r_MCParticleTrkCol ); 
+   
+    StatusCode CreateTracksFromMCParticle(PandoraPlusDataCol& m_DataCol, 
+                                          DataHandle<edm4hep::MCParticleCollection>& r_MCParticleCol);
+
+
+    StatusCode Reset(){};
+
+  private: 
+    const PandoraPlus::Settings  settings; 
+    PandoraPlus::Algorithm*      m_TrkExtraAlg; 
+    PandoraPlus::Settings        m_TrkExtraSettings;  
+
+
+  };
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/include/Tools/TrackFitInEcal.h b/Reconstruction/CrystalCaloRec/include/Tools/TrackFitInEcal.h
new file mode 100644
index 00000000..3d9e9e1f
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/include/Tools/TrackFitInEcal.h
@@ -0,0 +1,59 @@
+#ifndef TRACKFITINECAL_H
+#define TRACKFITINECAL_H
+
+#include <iostream>
+#include <vector>
+
+#include "Rtypes.h"
+
+class TrackFitInEcal{
+public:
+	 TrackFitInEcal(double barAngle=0.);
+	 ~TrackFitInEcal();
+
+   void setBarAngle(double barAngle) { m_barAngle=barAngle; }
+   void setPoint(int flagUZ, double uzPos, double uzPosErr, double depth, double depthErr);
+   void setGlobalPoint(int flagUZ, double x, double xerr, double y, double yerr, double z, double zerr);
+   void setImpactParameter( double dr, double dz ) { m_IPvar[0]=dr; m_IPvar[1]=dz; m_FixIP=true; } 
+	 bool fitTrack();
+	 void clear();
+
+	 bool fit2D();				/* fit in x-y and w-z respectively */
+	 bool mnFit3D();			/* fit with minuit */
+
+	 double getChisquare() const{return m_chisq;}
+	 double getTrkPar(int i) const;
+	 double getTrkParErr(int i) const{return m_trkParErr[i];}
+	 double getCovariance(int i, int k) const{return m_covariance[i][k];}
+
+	 double getDr() const{return m_trkPar[0];}
+	 double getDz() const{return m_trkPar[1];}
+	 double getPhi() const{return m_trkPar[2]+m_barAngle;}
+	 double getTheta() const{return m_trkPar[3];}
+
+	 static const int NTRKPAR = 4;
+
+	 static void fcnTrk(Int_t &npar, Double_t *gin, Double_t &f, Double_t *par, Int_t iflag);
+
+	 static std::vector<int> m_flagUZ; /* direction in transverse plane, 0 for u, 1 for z */
+	 static std::vector<double> m_uzPos; /* transverse position */
+	 static std::vector<double> m_uzPosErr; /* transverse position error */
+	 static std::vector<double> m_depth; /* longitudinal position */
+	 static std::vector<double> m_depthErr; /* longitudinal position error */
+
+private:
+	 double m_barAngle;
+	 double m_chisq;
+	 double m_trkPar[NTRKPAR];		/* dr, dz, phi, theta */
+	 double m_trkParErr[NTRKPAR];
+	 double m_covariance[NTRKPAR][NTRKPAR];
+    double m_IPvar[2];  //dr, dz
+    bool   m_FixIP; 
+};
+
+inline double TrackFitInEcal::getTrkPar(int i) const{
+	 if(2==i) return m_trkPar[i]+m_barAngle;
+	 else return m_trkPar[i];
+}
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/script/digi.py b/Reconstruction/CrystalCaloRec/script/digi.py
new file mode 100644
index 00000000..b587c61d
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/script/digi.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+import os
+from Gaudi.Configuration import *
+
+from Configurables import k4DataSvc
+dsvc = k4DataSvc("EventDataSvc", input="Tracking_TDR_o1_v01_Pi-_10GeV.root")
+
+from Configurables import RndmGenSvc, HepRndm__Engine_CLHEP__RanluxEngine_
+seed = [12340]
+# rndmengine = HepRndm__Engine_CLHEP__RanluxEngine_() # The default engine in Gaudi                                                                                                                                            
+rndmengine = HepRndm__Engine_CLHEP__HepJamesRandom_("RndmGenSvc.Engine") # The default engine in Geant4                                                                                                                        
+rndmengine.SetSingleton = True
+rndmengine.Seeds = seed
+
+rndmgensvc = RndmGenSvc("RndmGenSvc")
+rndmgensvc.Engine = rndmengine.name()
+
+geometry_option = "TDR_o1_v01/TDR_o1_v01.xml"
+
+if not os.getenv("DETCRDROOT"):
+    print("Can't find the geometry. Please setup envvar DETCRDROOT." )
+    sys.exit(-1)
+
+geometry_path = os.path.join(os.getenv("DETCRDROOT"), "compact", geometry_option)
+if not os.path.exists(geometry_path):
+    print("Can't find the compact geometry file: %s"%geometry_path)
+    sys.exit(-1)
+
+from Configurables import GeomSvc
+geosvc = GeomSvc("GeomSvc")
+geosvc.compact = geometry_path
+
+from Configurables import MarlinEvtSeeder
+evtseeder = MarlinEvtSeeder("EventSeeder")
+
+from Configurables import GearSvc
+gearsvc = GearSvc("GearSvc")
+
+from Configurables import TrackSystemSvc
+tracksystemsvc = TrackSystemSvc("TrackSystemSvc")
+
+from Configurables import SimplePIDSvc
+pidsvc = SimplePIDSvc("SimplePIDSvc")
+cepcswdatatop = "/cvmfs/cepcsw.ihep.ac.cn/prototype/releases/data/latest"
+pidsvc.ParFile = os.path.join(cepcswdatatop, "CEPCSWData/offline-data/Service/SimplePIDSvc/data/dNdx_TPC.root")
+
+from Configurables import PodioInput
+podioinput = PodioInput("PodioReader", collections=[
+#    "EventHeader",
+    "MCParticle",
+    "EcalBarrelCollection", 
+    "EcalBarrelContributionCollection", 
+    "HcalBarrelCollection", 
+    "HcalBarrelContributionCollection", 
+    "CompleteTracks", 
+    "CompleteTracksParticleAssociation"
+    ])
+
+########## Digitalization ################
+
+##ECAL##
+from Configurables import EcalDigiAlg
+EcalDigi = EcalDigiAlg("EcalDigiAlg")
+EcalDigi.ReadOutName = "EcalBarrelCollection"
+EcalDigi.SimCaloHitCollection = "EcalBarrelCollection"
+EcalDigi.CaloHitCollection = "ECALBarrel"
+EcalDigi.CaloAssociationCollection = "ECALBarrelAssoCol"
+EcalDigi.CaloMCPAssociationCollection = "ECALBarrelParticleAssoCol"
+EcalDigi.SkipEvt = 0
+EcalDigi.Seed = 2079
+#Digitalization parameters
+EcalDigi.CalibrECAL = 1.
+EcalDigi.AttenuationLength = 7e10
+EcalDigi.TimeResolution = 0.5        #unit: ns
+EcalDigi.ChargeThresholdFrac = 0.05
+EcalDigi.Debug=1
+EcalDigi.WriteNtuple = 0
+EcalDigi.OutFileName = "Digi_ECAL.root"
+#########################################
+
+##HCAL##
+from Configurables import HcalDigiAlg
+HcalDigi = HcalDigiAlg("HcalDigiAlg")
+HcalDigi.ReadOutName = "HcalBarrelCollection"
+HcalDigi.SimCaloHitCollection = "HcalBarrelCollection"
+HcalDigi.CaloHitCollection = "HCALBarrel"
+HcalDigi.CaloAssociationCollection = "HCALBarrelAssoCol"
+HcalDigi.CaloMCPAssociationCollection = "HCALBarrelParticleAssoCol"
+HcalDigi.SkipEvt = 0
+HcalDigi.Seed = 2079
+#Digitalization parameters
+HcalDigi.MIPResponse = 0.01  # 0.5 MeV / MIP
+HcalDigi.MIPThreshold = 0.5    # Unit: MIP
+HcalDigi.CalibrHCAL = 1.
+HcalDigi.Debug=0
+HcalDigi.WriteNtuple = 0
+HcalDigi.OutFileName = "Digi_HCAL.root"
+
+
+# output
+from Configurables import PodioOutput
+out = PodioOutput("outputalg")
+out.filename = "CaloDigi_TDR_o1_v01_Pi-_10GeV.root"
+out.outputCommands = ["keep *"]
+
+# ApplicationMgr
+from Configurables import ApplicationMgr
+mgr = ApplicationMgr(
+    TopAlg = [podioinput, EcalDigi, HcalDigi, out],
+    EvtSel = 'NONE',
+    EvtMax = 10,
+    ExtSvc = [dsvc, rndmengine, rndmgensvc, geosvc],
+    HistogramPersistency = 'ROOT',
+    OutputLevel = ERROR
+)
diff --git a/Reconstruction/CrystalCaloRec/script/rec.py b/Reconstruction/CrystalCaloRec/script/rec.py
new file mode 100644
index 00000000..51f1e6d6
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/script/rec.py
@@ -0,0 +1,139 @@
+import os
+from Gaudi.Configuration import *
+
+############## GeomSvc #################
+geometry_option = "TDR_o1_v01/TDR_o1_v01.xml"
+
+if not os.getenv("DETCRDROOT"):
+    print("Can't find the geometry. Please setup envvar DETCRDROOT." )
+    sys.exit(-1)
+
+geometry_path = os.path.join(os.getenv("DETCRDROOT"), "compact", geometry_option)
+if not os.path.exists(geometry_path):
+    print("Can't find the compact geometry file: %s"%geometry_path)
+    sys.exit(-1)
+
+from Configurables import GeomSvc
+geomsvc = GeomSvc("GeomSvc")
+geomsvc.compact = geometry_path
+#######################################
+
+########### k4DataSvc ####################
+from Configurables import k4DataSvc
+podioevent = k4DataSvc("EventDataSvc", input="CaloDigi_TDR_o1_v01_Pi-_10GeV.root")
+##########################################
+
+
+########## Podio Input ###################
+from Configurables import PodioInput
+inp = PodioInput("InputReader")
+inp.collections = [ "EcalBarrelCollection",
+                    "EcalBarrelContributionCollection",  
+                    "ECALBarrel", 
+                    "ECALBarrelParticleAssoCol",
+                    "HcalBarrelCollection", 
+                    "HcalBarrelContributionCollection", 
+                    "HCALBarrel",
+                    "HCALBarrelParticleAssoCol",
+                    "MCParticle", 
+                    "CompleteTracks", 
+                    "CompleteTracksParticleAssociation"]
+##########################################
+
+######### Reconstruction ################
+from Configurables import PandoraPlusPFAlg
+PandoraPlusPFAlg = PandoraPlusPFAlg("PandoraPlusPFAlg")
+##----Global parameters----
+PandoraPlusPFAlg.Seed = 1024
+PandoraPlusPFAlg.BField = 3.
+PandoraPlusPFAlg.Debug = 0
+PandoraPlusPFAlg.SkipEvt = 0
+PandoraPlusPFAlg.WriteAna = 1
+PandoraPlusPFAlg.AnaFileName = "RecAnaTuple_TDR_o1_v01_Pi-_10GeV.root"
+PandoraPlusPFAlg.UseTruthTrack = 0
+PandoraPlusPFAlg.EcalGlobalCalib = 1.05
+PandoraPlusPFAlg.HcalGlobalCalib = 4.5
+##----Readin collections----
+PandoraPlusPFAlg.MCParticleCollection = "MCParticle"
+PandoraPlusPFAlg.TrackCollections = ["CompleteTracks"]
+PandoraPlusPFAlg.MCRecoTrackParticleAssociationCollection = "CompleteTracksParticleAssociation"
+PandoraPlusPFAlg.ECalCaloHitCollections = ["ECALBarrel"]
+PandoraPlusPFAlg.ECalReadOutNames = ["EcalBarrelCollection"]
+PandoraPlusPFAlg.ECalMCPAssociationName = ["ECALBarrelParticleAssoCol"]
+PandoraPlusPFAlg.HCalCaloHitCollections = ["HCALBarrel"]
+PandoraPlusPFAlg.HCalReadOutNames = ["HcalBarrelCollection"]
+PandoraPlusPFAlg.HCalMCPAssociationName = ["HCALBarrelParticleAssoCol"]
+
+##--- Output collections ---
+PandoraPlusPFAlg.OutputPFO = "outputPFO";
+
+#----Algorithms----
+PandoraPlusPFAlg.AlgList = ["GlobalClusteringAlg",      #1
+                            "LocalMaxFindingAlg",       #2
+                            "TrackMatchingAlg",         #3
+                            "HoughClusteringAlg",       #4
+                            "ConeClustering2DAlg",      #5
+                            "AxisMergingAlg",           #6
+                            "EnergySplittingAlg",       #9
+                            "EnergyTimeMatchingAlg",    #11
+                            "HcalClusteringAlg",        #12
+                            "TruthClusteringAlg",       #15
+                            "TrackClusterConnectingAlg",  #16
+                            "PFOReclusteringAlg" ]  #17
+PandoraPlusPFAlg.AlgParNames = [ ["InputECALBars","OutputECAL1DClusters","OutputECALHalfClusters"],#1
+                                 ["OutputLocalMaxName"],#2
+                                 ["ReadinLocalMaxName","OutputLongiClusName"],#3
+                                 ["ReadinLocalMaxName","LeftLocalMaxName","OutputLongiClusName"],#4
+                                 ["ReadinLocalMaxName", "OutputLongiClusName"], #5
+                                 ["OutputAxisName"], #6
+                                 ["ReadinAxisName", "OutputClusName", "OutputTowerName"],  #9
+                                 ["ReadinHFClusterName", "ReadinTowerName","OutputClusterName"], #11
+                                 ["InputHCALHits", "OutputHCALClusters"], #12
+                                 ["DoECALClustering","DoHCALClustering","InputHCALHits","OutputHCALClusters"], #15
+                                 ["ReadinECALClusterName", "ReadinHCALClusterName", "OutputCombPFO"],  #16
+                                 ["ECALCalib", "HCALCalib", "MinAngleForNeuMerge"] ]#17
+PandoraPlusPFAlg.AlgParTypes = [ ["string","string","string"],#1
+                                 ["string"],#2
+                                 ["string","string"],#3
+                                 ["string","string","string"],#4
+                                 ["string","string"], #5
+                                 ["string"], #6
+                                 ["string","string","string"],  #9
+                                 ["string","string","string"], #11
+                                 ["string", "string"], #12
+                                 ["bool","bool","string","string"], #15
+                                 ["string","string","string"],  #16
+                                 ["double","double","double"] ]#17
+PandoraPlusPFAlg.AlgParValues = [ ["BarCol","Cluster1DCol","HalfClusterCol"],#1
+                                  ["AllLocalMax"],#2
+                                  ["AllLocalMax","TrackAxis"],#3
+                                  ["AllLocalMax","LeftLocalMax","HoughAxis"],#4
+                                  ["LeftLocalMax","ConeAxis"], #5
+                                  ["MergedAxis"], #6
+                                  ["MergedAxis","ESHalfCluster","ESTower"],  #9
+                                  ["ESHalfCluster","ESTower","EcalCluster"], #11
+                                  ["HCALBarrel", "SimpleHCALCluster"], #12
+                                  ["0","1","HCALBarrel","HCALCluster"], #15
+                                  ["EcalCluster", "SimpleHCALCluster", "outputPFO"],  #16
+                                  ["1.05","4.5","0.12"]  ]#17
+
+
+##############################################################################
+# POD I/O
+##############################################################################
+from Configurables import PodioOutput
+out = PodioOutput("outputalg")
+out.filename = "Rec_TDR_o1_v01_Pi-_10GeV.root"
+out.outputCommands = ["keep *"]
+
+
+########################################
+
+from Configurables import ApplicationMgr
+ApplicationMgr( 
+    TopAlg=[inp, PandoraPlusPFAlg, out ],
+    EvtSel="NONE",
+    EvtMax=10,
+    ExtSvc=[podioevent, geomsvc],
+    #OutputLevel=DEBUG
+)
diff --git a/Reconstruction/CrystalCaloRec/script/sim.py b/Reconstruction/CrystalCaloRec/script/sim.py
new file mode 100644
index 00000000..3ffd55da
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/script/sim.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+import os
+from Gaudi.Configuration import *
+
+from Configurables import k4DataSvc
+dsvc = k4DataSvc("EventDataSvc")
+
+from Configurables import RndmGenSvc, HepRndm__Engine_CLHEP__RanluxEngine_
+seed = [12340]
+# rndmengine = HepRndm__Engine_CLHEP__RanluxEngine_() # The default engine in Gaudi
+rndmengine = HepRndm__Engine_CLHEP__HepJamesRandom_("RndmGenSvc.Engine") # The default engine in Geant4
+rndmengine.SetSingleton = True
+rndmengine.Seeds = seed
+
+rndmgensvc = RndmGenSvc("RndmGenSvc")
+rndmgensvc.Engine = rndmengine.name()
+
+# option for standalone tracker study
+geometry_option = "TDR_o1_v01/TDR_o1_v01.xml"
+
+if not os.getenv("DETCRDROOT"):
+    print("Can't find the geometry. Please setup envvar DETCRDROOT." )
+    sys.exit(-1)
+
+geometry_path = os.path.join(os.getenv("DETCRDROOT"), "compact", geometry_option)
+if not os.path.exists(geometry_path):
+    print("Can't find the compact geometry file: %s"%geometry_path)
+    sys.exit(-1)
+
+from Configurables import GeomSvc
+geosvc = GeomSvc("GeomSvc")
+geosvc.compact = geometry_path
+
+##############################################################################
+# Physics Generator
+##############################################################################
+from Configurables import GenAlgo
+from Configurables import GtGunTool
+from Configurables import StdHepRdr
+from Configurables import SLCIORdr
+from Configurables import HepMCRdr
+from Configurables import GenPrinter
+
+gun = GtGunTool("GtGunTool")
+gun.PositionXs = [0]
+gun.PositionYs = [0]
+gun.PositionZs = [0]
+gun.Particles = ["pi-"]
+gun.EnergyMins = [10]
+gun.EnergyMaxs = [10]
+gun.ThetaMins  = [60]
+gun.ThetaMaxs  = [120]
+gun.PhiMins    = [0]
+gun.PhiMaxs    = [360]
+
+genprinter = GenPrinter("GenPrinter")
+
+genalg = GenAlgo("GenAlgo")
+genalg.GenTools = ["GtGunTool"]
+
+##############################################################################
+# Detector Simulation
+##############################################################################
+from Configurables import DetSimSvc
+detsimsvc = DetSimSvc("DetSimSvc")
+
+from Configurables import DetSimAlg
+detsimalg = DetSimAlg("DetSimAlg")
+detsimalg.RandomSeeds = seed
+# detsimalg.VisMacs = ["vis.mac"]
+detsimalg.RunCmds = [
+#    "/tracking/verbose 1",
+]
+detsimalg.AnaElems = [
+    # example_anatool.name()
+    # "ExampleAnaElemTool"
+    "Edm4hepWriterAnaElemTool"
+]
+detsimalg.RootDetElem = "WorldDetElemTool"
+
+from Configurables import TimeProjectionChamberSensDetTool
+tpc_sensdettool = TimeProjectionChamberSensDetTool("TimeProjectionChamberSensDetTool")
+tpc_sensdettool.TypeOption = 1
+
+from Configurables import MarlinEvtSeeder
+evtseeder = MarlinEvtSeeder("EventSeeder")
+    
+# output
+from Configurables import PodioOutput
+out = PodioOutput("outputalg")
+out.filename = "Sim_TDR_o1_v01_Pi-_10GeV.root"
+out.outputCommands = ["keep *"]
+
+# ApplicationMgr
+from Configurables import ApplicationMgr
+mgr = ApplicationMgr(
+    TopAlg = [genalg, detsimalg, out],
+    EvtSel = 'NONE',
+    EvtMax = 10,
+    ExtSvc = [rndmengine, rndmgensvc, dsvc, geosvc],
+    HistogramPersistency = 'ROOT',
+    OutputLevel = ERROR
+)
diff --git a/Reconstruction/CrystalCaloRec/script/tracking.py b/Reconstruction/CrystalCaloRec/script/tracking.py
new file mode 100644
index 00000000..38f47ad3
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/script/tracking.py
@@ -0,0 +1,249 @@
+#!/usr/bin/env python
+import os
+from Gaudi.Configuration import *
+
+from Configurables import k4DataSvc
+dsvc = k4DataSvc("EventDataSvc", input="Sim_TDR_o1_v01_Pi-_10GeV.root")
+
+from Configurables import RndmGenSvc, HepRndm__Engine_CLHEP__RanluxEngine_
+seed = [12340]
+# rndmengine = HepRndm__Engine_CLHEP__RanluxEngine_() # The default engine in Gaudi                                                                                                                                            
+rndmengine = HepRndm__Engine_CLHEP__HepJamesRandom_("RndmGenSvc.Engine") # The default engine in Geant4                                                                                                                        
+rndmengine.SetSingleton = True
+rndmengine.Seeds = seed
+
+rndmgensvc = RndmGenSvc("RndmGenSvc")
+rndmgensvc.Engine = rndmengine.name()
+
+geometry_option = "TDR_o1_v01/TDR_o1_v01.xml"
+
+if not os.getenv("DETCRDROOT"):
+    print("Can't find the geometry. Please setup envvar DETCRDROOT." )
+    sys.exit(-1)
+
+geometry_path = os.path.join(os.getenv("DETCRDROOT"), "compact", geometry_option)
+if not os.path.exists(geometry_path):
+    print("Can't find the compact geometry file: %s"%geometry_path)
+    sys.exit(-1)
+
+from Configurables import GeomSvc
+geosvc = GeomSvc("GeomSvc")
+geosvc.compact = geometry_path
+
+from Configurables import MarlinEvtSeeder
+evtseeder = MarlinEvtSeeder("EventSeeder")
+
+from Configurables import GearSvc
+gearsvc = GearSvc("GearSvc")
+
+from Configurables import TrackSystemSvc
+tracksystemsvc = TrackSystemSvc("TrackSystemSvc")
+
+from Configurables import SimplePIDSvc
+pidsvc = SimplePIDSvc("SimplePIDSvc")
+cepcswdatatop = "/cvmfs/cepcsw.ihep.ac.cn/prototype/releases/data/latest"
+pidsvc.ParFile = os.path.join(cepcswdatatop, "CEPCSWData/offline-data/Service/SimplePIDSvc/data/dNdx_TPC.root")
+
+from Configurables import PodioInput
+podioinput = PodioInput("PodioReader", collections=[
+#    "EventHeader",
+    "MCParticle",
+    "VXDCollection",
+    "SITCollection",
+    "TPCCollection",
+    "SETCollection",
+    "FTDCollection"
+    ])
+
+# digitization
+vxdhitname  = "VXDTrackerHits"
+sithitname  = "SITTrackerHits"
+gashitname  = "TPCTrackerHits"
+sethitname  = "SETTrackerHits"
+setspname   = "SETSpacePoints"
+ftdhitname  = "FTDTrackerHits"
+ftdspname   = "FTDSpacePoints"
+from Configurables import PlanarDigiAlg
+digiVXD = PlanarDigiAlg("VXDDigi")
+digiVXD.SimTrackHitCollection = "VXDCollection"
+digiVXD.TrackerHitCollection = vxdhitname
+digiVXD.TrackerHitAssociationCollection = "VXDTrackerHitAssociation"
+digiVXD.ResolutionU = [0.004, 0.004, 0.004, 0.004, 0.004, 0.004]
+digiVXD.ResolutionV = [0.004, 0.004, 0.004, 0.004, 0.004, 0.004]
+digiVXD.UsePlanarTag = True
+digiVXD.ParameterizeResolution = False
+digiVXD.ParametersU = [5.60959e-03, 5.74913e-03, 7.03433e-03, 1.99516, -663.952, 3.752e-03, 0, -0.0704734, 0.0454867e-03, 1.07359]
+digiVXD.ParametersV = [5.60959e-03, 5.74913e-03, 7.03433e-03, 1.99516, -663.952, 3.752e-03, 0, -0.0704734, 0.0454867e-03, 1.07359]
+#digiVXD.OutputLevel = DEBUG
+
+digiSIT = PlanarDigiAlg("SITDigi")
+digiSIT.IsStrip = False
+digiSIT.SimTrackHitCollection = "SITCollection"
+digiSIT.TrackerHitCollection = sithitname
+digiSIT.TrackerHitAssociationCollection = "SITTrackerHitAssociation"
+digiSIT.ResolutionU = [0.0072]
+digiSIT.ResolutionV = [0.086]
+digiSIT.UsePlanarTag = True
+digiSIT.ParameterizeResolution = False
+digiSIT.ParametersU = [2.29655e-03, 0.965899e-03, 0.584699e-03, 17.0856, 84.566, 12.4695e-03, -0.0643059, 0.168662, 1.87998e-03, 0.514452]
+digiSIT.ParametersV = [1.44629e-02, 2.20108e-03, 1.03044e-02, 4.39195e+00, 3.29641e+00, 1.55167e+18, -5.41954e+01, 5.72986e+00, -6.80699e-03, 5.04095e-01]
+#digiSIT.OutputLevel = DEBUG
+
+digiSET = PlanarDigiAlg("SETDigi")
+digiSET.IsStrip = False
+digiSET.SimTrackHitCollection = "SETCollection"
+digiSET.TrackerHitCollection = sethitname
+digiSET.TrackerHitAssociationCollection = "SETTrackerHitAssociation"
+digiSET.ResolutionU = [0.0072]
+digiSET.ResolutionV = [0.086]
+digiSET.UsePlanarTag = True
+digiSET.ParameterizeResolution = False
+digiSET.ParametersU = [2.29655e-03, 0.965899e-03, 0.584699e-03, 17.0856, 84.566, 12.4695e-03, -0.0643059, 0.168662, 1.87998e-03, 0.514452]
+digiSET.ParametersV = [1.44629e-02, 2.20108e-03, 1.03044e-02, 4.39195e+00, 3.29641e+00, 1.55167e+18, -5.41954e+01, 5.72986e+00, -6.80699e-03, 5.04095e-01]
+#digiSET.OutputLevel = DEBUG
+
+digiFTD = PlanarDigiAlg("FTDDigi")
+digiFTD.IsStrip = False
+digiFTD.SimTrackHitCollection = "FTDCollection"
+digiFTD.TrackerHitCollection = ftdhitname
+digiFTD.TrackerHitAssociationCollection = "FTDTrackerHitAssociation"
+digiFTD.ResolutionU = [0.0072]
+digiFTD.ResolutionV = [0.086]
+digiFTD.UsePlanarTag = True
+digiFTD.ParameterizeResolution = False
+digiFTD.ParametersU = [2.29655e-03, 0.965899e-03, 0.584699e-03, 17.0856, 84.566, 12.4695e-03, -0.0643059, 0.168662, 1.87998e-03, 0.514452]
+digiFTD.ParametersV = [1.44629e-02, 2.20108e-03, 1.03044e-02, 4.39195e+00, 3.29641e+00, 1.55167e+18, -5.41954e+01, 5.72986e+00, -6.80699e-03, 5.04095e-01]
+#digiFTD.OutputLevel = DEBUG
+
+from Configurables import TPCDigiAlg
+digiTPC = TPCDigiAlg("TPCDigi")
+digiTPC.TPCCollection = "TPCCollection"
+digiTPC.TPCLowPtCollection = "TPCLowPtCollection"
+digiTPC.TPCTrackerHitsCol = gashitname
+#digiTPC.OutputLevel = DEBUG
+
+# tracking
+from Configurables import KalTestTool
+# Close multiple scattering and smooth, used by clupatra
+kt010 = KalTestTool("KalTest010")
+kt010.MSOn = False
+kt010.Smooth = False
+#kt010.OutputLevel = DEBUG
+
+# Open multiple scattering, energy loss and smooth (default)
+kt111 = KalTestTool("KalTest111")
+#kt111.OutputLevel = DEBUG
+
+# Close smooth
+kt110 = KalTestTool("KalTest110")
+kt110.Smooth = False
+#kt110.OutputLevel = DEBUG
+
+from Configurables import SiliconTrackingAlg
+tracking = SiliconTrackingAlg("SiliconTracking")
+tracking.LayerCombinationsFTD = []
+tracking.HeaderCol = "EventHeader"
+tracking.VTXHitCollection = vxdhitname
+tracking.SITHitCollection = sithitname
+tracking.FTDPixelHitCollection = ftdhitname
+tracking.FTDSpacePointCollection = ftdspname
+tracking.SITRawHitCollection = "NotNeedForPixelSIT"
+tracking.FTDRawHitCollection = ftdhitname
+tracking.UseSIT = True
+tracking.SmoothOn = False
+tracking.NDivisionsInTheta = 10
+tracking.NDivisionsInPhi = 60
+tracking.NDivisionsInPhiFTD = 16
+tracking.MinDistCutAttach = 50
+tracking.Chi2FitCut = 200
+tracking.MaxChi2PerHit = 200
+tracking.Chi2WZTriplet = 0.1
+tracking.Chi2WZQuartet = 0.1
+tracking.Chi2WZSeptet  = 0.1
+#tracking.FitterTool = "KalTestTool/KalTest111"
+#tracking.OutputLevel = DEBUG
+
+from Configurables import ForwardTrackingAlg
+forward = ForwardTrackingAlg("ForwardTracking")
+forward.FTDPixelHitCollection = ftdhitname
+forward.FTDSpacePointCollection = ftdspname
+forward.FTDRawHitCollection = ftdhitname
+forward.Chi2ProbCut = 0.0
+forward.HitsPerTrackMin = 3
+forward.BestSubsetFinder = "SubsetSimple"
+forward.Criteria = ["Crit2_DeltaPhi","Crit2_StraightTrackRatio","Crit3_3DAngle","Crit3_ChangeRZRatio","Crit3_IPCircleDist","Crit4_3DAngleChange","Crit4_DistToExtrapolation",
+                    "Crit2_DeltaRho","Crit2_RZRatio","Crit3_PT"]
+forward.CriteriaMin = [0,  0.9,  0,  0.995, 0,  0.8, 0,   20,  1.002, 0.1,      0,   0.99, 0,    0.999, 0,   0.99, 0]
+forward.CriteriaMax = [30, 1.02, 10, 1.015, 20, 1.3, 1.0, 150, 1.08,  99999999, 0.8, 1.01, 0.35, 1.001, 1.5, 1.01, 0.05]
+#forward.FitterTool = "KalTestTool/KalTest110"
+#forward.OutputLevel = DEBUG
+
+from Configurables import TrackSubsetAlg
+subset = TrackSubsetAlg("TrackSubset")
+subset.TrackInputCollections = ["ForwardTracks", "SiTracks"]
+subset.RawTrackerHitCollections = [vxdhitname, sithitname, ftdhitname, ftdspname]
+subset.TrackSubsetCollection = "SubsetTracks"
+#subset.FitterTool = "KalTestTool/KalTest111"
+#subset.OutputLevel = DEBUG
+
+from Configurables import ClupatraAlg
+clupatra = ClupatraAlg("Clupatra")
+clupatra.TPCHitCollection = gashitname
+#clupatra.OutputLevel = DEBUG
+
+from Configurables import FullLDCTrackingAlg
+full = FullLDCTrackingAlg("FullTracking")
+full.VTXTrackerHits = vxdhitname
+full.SITTrackerHits = sithitname
+full.TPCTrackerHits = gashitname
+full.SETTrackerHits = sethitname
+full.FTDPixelTrackerHits = ftdhitname
+full.FTDSpacePoints = ftdspname
+full.SITRawHits     = "NotNeedForPixelSIT"
+full.SETRawHits     = "NotNeedForPixelSET"
+full.FTDRawHits     = ftdhitname
+full.TPCTracks = "ClupatraTracks" # add standalone TPC track
+full.SiTracks  = "SubsetTracks"
+full.OutputTracks  = "CompleteTracks" # default name
+full.VTXHitToTrackDistance = 5.
+full.FTDHitToTrackDistance = 5.
+full.SITHitToTrackDistance = 3.
+full.SETHitToTrackDistance = 5.
+full.MinChi2ProbForSiliconTracks = 0
+#full.OutputLevel = DEBUG
+
+from Configurables import TPCDndxAlg
+tpc_dndx = TPCDndxAlg("TPCDndxAlg")
+tpc_dndx.Method = "Simple"
+
+from Configurables import TrackParticleRelationAlg
+tpr = TrackParticleRelationAlg("Track2Particle")
+tpr.MCParticleCollection = "MCParticle"
+tpr.TrackList = ["CompleteTracks", "ClupatraTracks"]
+tpr.TrackerAssociationList = ["VXDTrackerHitAssociation", "SITTrackerHitAssociation", "SETTrackerHitAssociation", "FTDTrackerHitAssociation", "TPCTrackerHitAss"]
+#tpr.OutputLevel = DEBUG
+
+from Configurables import TrueMuonTagAlg
+tmt = TrueMuonTagAlg("TrueMuonTag")
+tmt.MCParticleCollection = "MCParticle"
+tmt.TrackList = ["CompleteTracks"]
+tmt.MuonTagEfficiency = 0.95 # muon true tag efficiency, default is 1.0 (100%)
+tmt.MuonDetTanTheta = 1.2 # muon det barrel/endcap separation tan(theta)
+#tmt.OutputLevel = DEBUG
+
+# output
+from Configurables import PodioOutput
+out = PodioOutput("outputalg")
+out.filename = "Tracking_TDR_o1_v01_Pi-_10GeV.root"
+out.outputCommands = ["keep *"]
+
+# ApplicationMgr
+from Configurables import ApplicationMgr
+mgr = ApplicationMgr(
+    TopAlg = [podioinput, digiVXD, digiSIT, digiSET, digiFTD, digiTPC, tracking, forward, subset, clupatra, full, tpr, tpc_dndx, tmt, out],
+    EvtSel = 'NONE',
+    EvtMax = 10,
+    ExtSvc = [rndmengine, rndmgensvc, dsvc, evtseeder, geosvc, gearsvc, tracksystemsvc, pidsvc],
+    HistogramPersistency = 'ROOT',
+    OutputLevel = ERROR
+)
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/ArborClusteringAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/ArborClusteringAlg.cpp
new file mode 100644
index 00000000..1d03dcf0
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/ArborClusteringAlg.cpp
@@ -0,0 +1,509 @@
+#ifndef _ARBORCLUSTERING_ALG_C
+#define _ARBORCLUSTERING_ALG_C
+
+#include "Algorithm/ArborClusteringAlg.h"
+#include <map>
+
+void ArborClusteringAlg::Settings::SetInitialValue(){
+
+    clusType="";
+
+    Lth_start = 2; 
+    Rth_start = 30; 
+    Rth_value = 40; 
+    Rth_slope = 0.5; 
+
+    wiB = 1;
+    wiF = 1;
+    pTheta = 1;
+    pR = 1;
+    pE = 1; 
+    Rth_nbrRoot = 40; // Root node distance threshold when merging neighbor trees.
+    Debug = 0; 
+}
+
+void ArborClusteringAlg::Settings::PrintSettings() const{
+  std::cout<<"  ArborClusteringAlg Settings: "<<std::endl;
+  std::cout<<"Rth_value: "<<'\t'<<Rth_value<<std::endl; 
+  std::cout<<"Rth_slope: "<<'\t'<<Rth_slope<<std::endl; 
+  std::cout<<"wiB: "<<'\t'<<wiB<<std::endl; 
+  std::cout<<"wiF: "<<'\t'<<wiF<<std::endl; 
+  std::cout<<"pTheta: "<<'\t'<<pTheta<<std::endl; 
+  std::cout<<"pR: "<<'\t'<<pR<<std::endl; 
+  std::cout<<"pE: "<<'\t'<<pE<<std::endl; 
+  std::cout<<"Rth_nbrRoot: "<<'\t'<<Rth_nbrRoot<<std::endl; 
+  std::cout<<"Debug: "<<'\t'<<Debug<<std::endl; 
+
+}
+
+StatusCode ArborClusteringAlg::Initialize(){
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode ArborClusteringAlg::RunAlgorithm( ArborClusteringAlg::Settings& m_settings, PandoraPlusDataCol& m_datacol ){
+  settings = m_settings;
+
+  std::vector<CRDEcalEDM::CRDCaloHit2DShower> m_2DshowerCol; m_2DshowerCol.clear();
+  if( settings.clusType=="MIP" )     m_2DshowerCol = m_datacol.MIPShower2DCol;
+  else if( settings.clusType=="EM" ) m_2DshowerCol = m_datacol.EMShower2DCol;
+  else m_2DshowerCol = m_datacol.Shower2DCol;
+
+
+  if(m_2DshowerCol.size()==0){ 
+    std::cout<<"Warning: Empty input in ArborClusteringAlg. Please check previous algorithm!"<<endl;  
+    m_datacol.ClearArbor(); 
+    return StatusCode::SUCCESS; 
+  }
+//m_datacol.PrintShower(); 
+
+  std::map<int, std::vector<CRDEcalEDM::CRDArborNode*> > m_orderedNodes;  m_orderedNodes.clear(); //map<layer, showers>
+
+  //Transform showers to nodes, order the nodes by layer. 
+  for(int is=0; is<m_2DshowerCol.size(); is++ ){
+    int m_dlayer = m_2DshowerCol[is].getDlayer();
+    //CRDEcalEDM::CRDArborNode m_node(m_2DshowerCol[is]);
+    CRDEcalEDM::CRDArborNode* m_node = new CRDEcalEDM::CRDArborNode(m_2DshowerCol[is]);
+    m_orderedNodes[m_dlayer].push_back(m_node);
+  }
+
+  int NtotNodes=0; 
+  std::map<int, std::vector<CRDEcalEDM::CRDArborNode*> >::iterator iter_count = m_orderedNodes.begin(); 
+  for(iter_count; iter_count!=m_orderedNodes.end(); iter_count++){
+    NtotNodes += iter_count->second.size(); 
+  }
+  if(settings.Debug>=1) std::cout<<"Total node size: "<<NtotNodes<<std::endl; 
+
+/*
+std::cout<<"Print ordered nodes: "<<std::endl;
+for(auto iter=m_orderedNodes.begin(); iter!=m_orderedNodes.end(); iter++){
+  std::cout<<"#Layer: "<<iter->first<<'\t';
+  for(int i=0; i<iter->second.size(); i++) printf("(%.2f, %.2f, %.2f, %d) \t", iter->second[i].GetPosition().x(), iter->second[i].GetPosition().y(), iter->second[i].GetPosition().z(), iter->second[i].GetType());
+  std::cout<<std::endl;
+}
+*/
+  std::vector<CRDEcalEDM::CRDArborTree> m_ArborTreeCol; m_ArborTreeCol.clear();
+  std::vector<CRDEcalEDM::CRDArborNode*> m_isoNodes; m_isoNodes.clear();
+  
+
+  //Build tree
+  InitArborTree(m_orderedNodes, m_ArborTreeCol, m_isoNodes);
+
+  if(settings.Debug>=1) std::cout<<"Initial Ntrees = "<<m_ArborTreeCol.size()<<std::endl;
+  if(settings.Debug>=2){
+    for(int it=0; it<m_ArborTreeCol.size(); it++) m_ArborTreeCol[it].PrintTree();
+    std::cout<<"Print Isolated Nodes: "<<std::endl;
+    for(int i=0; i<m_isoNodes.size(); i++)
+      printf("(%.2f, %.2f, %.2f, %d) \n", m_isoNodes[i]->GetPosition().x(), m_isoNodes[i]->GetPosition().y(), m_isoNodes[i]->GetPosition().z(), m_isoNodes[i]->GetType());
+    std::cout<<std::endl;
+  }  
+
+
+  std::vector<CRDEcalEDM::CRDArborTree> tmpTrees; tmpTrees.clear();
+  MergeConnectedTrees( m_ArborTreeCol, tmpTrees );
+  m_ArborTreeCol.clear(); m_ArborTreeCol = tmpTrees;
+
+  if(settings.Debug>=1) std::cout<<"After ConnectedTree merging: Ntree = "<<m_ArborTreeCol.size()<<std::endl;
+  if(settings.Debug>=2){
+    for(int it=0; it<m_ArborTreeCol.size(); it++) m_ArborTreeCol[it].PrintTree();
+    std::cout<<"Print Isolated Nodes: "<<std::endl;
+    for(int i=0; i<m_isoNodes.size(); i++)
+      printf("(%.2f, %.2f, %.2f, %d) \n", m_isoNodes[i]->GetPosition().x(), m_isoNodes[i]->GetPosition().y(), m_isoNodes[i]->GetPosition().z(), m_isoNodes[i]->GetType());
+    std::cout<<std::endl;
+  }
+
+  //Clean connection
+  for(int it=0; it<m_ArborTreeCol.size(); it++) CleanConnection(m_ArborTreeCol[it]);
+
+  if(settings.Debug>=1) std::cout<<"After connection cleaning: Ntree = "<<m_ArborTreeCol.size()<<std::endl;
+  if(settings.Debug>=2){ 
+    for(int it=0; it<m_ArborTreeCol.size(); it++) m_ArborTreeCol[it].PrintTree();
+    std::cout<<"Print Isolated Nodes: "<<std::endl;
+    for(int i=0; i<m_isoNodes.size(); i++)
+      printf("(%.2f, %.2f, %.2f, %d) \n", m_isoNodes[i]->GetPosition().x(), m_isoNodes[i]->GetPosition().y(), m_isoNodes[i]->GetPosition().z(), m_isoNodes[i]->GetType());
+    std::cout<<std::endl;
+  }
+
+  //Depart trees after connection cleaning. 
+  std::vector<CRDEcalEDM::CRDArborTree> m_departedTrees; m_departedTrees.clear(); 
+  for(int it=0; it<m_ArborTreeCol.size(); it++){ 
+    tmpTrees.clear(); 
+    DepartArborTree(m_ArborTreeCol[it], tmpTrees, m_isoNodes);
+    m_departedTrees.insert(m_departedTrees.end(), tmpTrees.begin(), tmpTrees.end());
+    //m_ArborTreeCol.clear(); m_ArborTreeCol = tmpTrees; 
+  }
+  m_ArborTreeCol.clear(); m_ArborTreeCol = m_departedTrees; m_departedTrees.clear(); 
+
+  if(settings.Debug>=1) std::cout<<"After DepartArborTree: Ntree = "<<m_ArborTreeCol.size()<<std::endl;
+  if(settings.Debug>=2){ 
+    for(int it=0; it<m_ArborTreeCol.size(); it++) m_ArborTreeCol[it].PrintTree();
+    std::cout<<"Print Isolated Nodes: "<<std::endl;
+    for(int i=0; i<m_isoNodes.size(); i++)
+      printf("(%.2f, %.2f, %.2f, %d) \n", m_isoNodes[i]->GetPosition().x(), m_isoNodes[i]->GetPosition().y(), m_isoNodes[i]->GetPosition().z(), m_isoNodes[i]->GetType());
+    std::cout<<std::endl;
+  }
+
+  //Merge Neighbor tree (roots are close within the same layer)
+  //tmpTrees.clear();
+  //MergeNeighborTree(m_ArborTreeCol, tmpTrees);
+  //m_ArborTreeCol.clear(); m_ArborTreeCol = tmpTrees;
+
+  m_datacol.ArborTreeCol = m_ArborTreeCol; 
+  m_datacol.IsoNodes = m_isoNodes; 
+
+  int Nnodes_end = 0; 
+  for(int it=0; it<m_ArborTreeCol.size(); it++) Nnodes_end += m_ArborTreeCol[it].GetNodes().size(); 
+  Nnodes_end += m_isoNodes.size(); 
+  if(settings.Debug>=1) std::cout<<"At the end: Node size = "<<NtotNodes<<std::endl;
+  if(Nnodes_end != NtotNodes) 
+    std::cout<<"WARNING!  ArborClusteringAlg: Initial node size("<<NtotNodes<<") is not equal to final node size("<<Nnodes_end<<")! May have memory leakage! "<<std::endl; 
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode ArborClusteringAlg::ClearAlgorithm() {
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode ArborClusteringAlg::InitArborTree( std::map<int, std::vector<CRDEcalEDM::CRDArborNode*> >& m_orderedNodes, 
+                                              std::vector<CRDEcalEDM::CRDArborTree>& m_treeCol,
+                                              std::vector<CRDEcalEDM::CRDArborNode*>& m_isoNodes )
+{
+
+//std::cout<<"Ordered node collection size: "<<m_orderedNodes.size()<<std::endl;
+//std::cout<<"  Layer(Nnode): ";
+//for(auto iter = m_orderedNodes.begin(); iter!=m_orderedNodes.end(); iter++) std::cout<<iter->first<<"("<<iter->second.size()<<")  ";
+//std::cout<<std::endl;
+
+  if( m_orderedNodes.size()==0 ) return StatusCode::SUCCESS;
+  m_treeCol.clear();
+  m_isoNodes.clear();
+
+  std::map<int, std::vector<CRDEcalEDM::CRDArborNode*> >::iterator iter = m_orderedNodes.begin();
+
+  //Nodes in First layer: initialize the trees. 
+  for(int is=0; is<iter->second.size(); is++){
+//std::cout<<"#Layer: "<<iter->first<<", Nnode: "<<iter->second.size()<<endl;
+    int m_dlayer = iter->first; 
+    double Rth; 
+    if(m_dlayer<=settings.Lth_start) Rth = settings.Rth_start; 
+    else Rth = settings.Rth_value + (double)m_dlayer * settings.Rth_slope; 
+
+    CRDEcalEDM::CRDArborTree m_tree; 
+    std::vector<CRDEcalEDM::CRDArborNode*>* m_nextLayer = &m_orderedNodes[iter->first+1]; 
+    for(int js=0; js<m_nextLayer->size(); js++)
+      if( (iter->second[is]->GetPosition()-m_nextLayer->at(js)->GetPosition()).Mag()<Rth ) 
+        iter->second[is]->ConnectDaughter( (m_nextLayer->at(js)) ); 
+
+    m_tree.AddNode( (iter->second[is]) );
+    m_tree.AddNode( iter->second[is]->GetDaughterNodes() );
+    m_treeCol.push_back( m_tree );
+  }
+  iter++;
+//std::cout<<"End tree seeding. Tree number: "<< m_treeCol.size()<<", Next layer: "<<iter->first<<std::endl;
+//for(int it=0; it<m_treeCol.size(); it++) m_treeCol[it].PrintTree();
+
+  //In later layers
+  for(iter; iter!=m_orderedNodes.end(); iter++){
+
+//std::cout<<"  #Layer: "<<iter->first<<", Nnode: "<<iter->second.size()<<", Present Ntrees: "<<m_treeCol.size()<<std::endl;
+//for(int it=0; it<m_treeCol.size(); it++) m_treeCol[it].PrintTree();
+
+    int m_dlayer = iter->first;
+    double Rth;
+    if(m_dlayer<=settings.Lth_start) Rth = settings.Rth_start;
+    else Rth = settings.Rth_value + (double)m_dlayer * settings.Rth_slope;
+
+    for(int in=0; in<iter->second.size(); in++){
+      CRDEcalEDM::CRDArborNode* m_node = iter->second[in];
+//printf("    #Node: (%.2f, %.2f, %.2f) \n", m_node->GetPosition().x(),  m_node->GetPosition().y(),  m_node->GetPosition().z());
+      //Loop all trees:
+      for(int it=0; it<m_treeCol.size(); it++){
+//std::cout<<"    Loop in tree: "<<std::endl;
+//m_treeCol[it].PrintTree();
+//std::cout<<"    Node in this tree: "<<m_node->isInTree(m_treeCol[it])<<std::endl; 
+        //If node in this tree: 
+        if( m_node->isInTree(m_treeCol[it]) ){          
+          //Get daughter nodes and add them in tree
+          std::vector<CRDEcalEDM::CRDArborNode*>* m_nextLayer = &m_orderedNodes[m_dlayer+1];
+          for(int jn=0; jn<m_nextLayer->size(); jn++)
+            if( (m_node->GetPosition()-m_nextLayer->at(jn)->GetPosition()).Mag()<Rth ) 
+              m_node->ConnectDaughter( (m_nextLayer->at(jn)) );
+          m_treeCol[it].AddNode( m_node->GetDaughterNodes() );
+          break;
+        }
+      }//end loop trees
+//std::cout<<"End loop nodes in this layer! "<<std::endl; 
+//std::cout<<std::endl;
+    } //end loop nodes in this layer
+
+    //if collection is not empty, create a new tree.
+
+    for(int in=0; in<iter->second.size(); in++){
+      CRDEcalEDM::CRDArborNode* m_node = iter->second[in];
+      if( m_node->GetDaughterNodes().size()==0 && m_node->GetParentNodes().size()==0){
+        CRDEcalEDM::CRDArborTree m_tree;
+        m_tree.AddNode( m_node );
+        std::vector<CRDEcalEDM::CRDArborNode*>* m_nextLayer = &m_orderedNodes[m_dlayer+1];
+        for(int jn=0; jn<m_nextLayer->size(); jn++)
+          if( (m_node->GetPosition() - m_nextLayer->at(jn)->GetPosition()).Mag()<Rth ) 
+            m_node->ConnectDaughter( (m_nextLayer->at(jn)) );        
+        m_tree.AddNode( m_node->GetDaughterNodes() );
+        m_treeCol.push_back( m_tree );
+      }
+    } //end new tree.
+
+  }//end loop all nodes.
+
+//std::cout<<"End loop all nodes! Print all trees: "<<std::endl;
+//for(int it=0; it<m_treeCol.size(); it++) m_treeCol[it].PrintTree();
+
+  int NtotNodes=0;
+//  for(int it=0; it<m_treeCol.size(); it++){
+//    NtotNodes += m_treeCol[it].GetNodes().size();
+//  }
+//  std::cout<<"Total nodes before picking out isoNodes: "<<NtotNodes<<std::endl;
+//  std::cout<<"Print all trees: "<<std::endl;
+//  for(int it=0; it<m_treeCol.size(); it++) m_treeCol[it].PrintTree();
+
+  //Loop treeCol, pickout isoNodes, update the orderedNodes.
+  for(int it=0; it<m_treeCol.size(); it++){
+    if(m_treeCol[it].GetNodes().size()==1){ 
+      CRDEcalEDM::CRDArborNode* m_node = m_treeCol[it].GetNodes()[0];
+      m_node->SetType(0);
+      m_isoNodes.push_back( m_node );
+      m_treeCol.erase( m_treeCol.begin()+it );
+      it--;
+    }
+  }
+
+  NtotNodes=0;
+  for(int it=0; it<m_treeCol.size(); it++){
+    NtotNodes += m_treeCol[it].GetNodes().size();
+  }
+//std::cout<<"Final tree size: "<<m_treeCol.size(); 
+//std::cout<<"  Total nodes in tree: "<<NtotNodes; 
+//std::cout<<"  Isolated note size: "<< m_isoNodes.size()<<std::endl;
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode ArborClusteringAlg::MergeConnectedTrees( std::vector<CRDEcalEDM::CRDArborTree>& m_inTreeCol,
+                                                    std::vector<CRDEcalEDM::CRDArborTree>& m_outTreeCol )
+{
+  m_outTreeCol.clear(); 
+  if(m_inTreeCol.size()==0){ return StatusCode::SUCCESS;}
+  if(m_inTreeCol.size()==1){ m_outTreeCol=m_inTreeCol; return StatusCode::SUCCESS;}
+
+  //Create a tree with topological link, with one node as seed. 
+  for(int it=0; it<m_inTreeCol.size(); it++){
+    m_inTreeCol[it].SortNodes(); 
+    std::vector<CRDEcalEDM::CRDArborNode*> m_nodeCol = m_inTreeCol[it].GetNodes(); 
+    if(m_nodeCol.size()==0) continue; 
+
+    CRDEcalEDM::CRDArborTree m_tree; 
+    m_tree.CreateWithTopo( m_nodeCol[0] );
+    m_outTreeCol.push_back(m_tree);
+    break;
+  }
+
+  //Check if other nodes in this tree. If not, create a new tree. 
+  for(int it=0; it<m_inTreeCol.size(); it++){
+    std::vector<CRDEcalEDM::CRDArborNode*> m_nodeCol = m_inTreeCol[it].GetNodes();
+    for(int in=0; in<m_nodeCol.size(); in++){
+      bool f_inTree = false; 
+      for(int jt=0; jt<m_outTreeCol.size(); jt++)
+        if( m_nodeCol[in]->isInTree(m_outTreeCol[jt]) ) {f_inTree=true; break;}
+      if(!f_inTree){
+        CRDEcalEDM::CRDArborTree m_tree;
+        m_tree.CreateWithTopo( m_nodeCol[in] );
+        m_outTreeCol.push_back(m_tree);
+      }      
+    }
+
+  }
+//std::cout<<"Ntrees before: "<<m_inTreeCol.size()<<", Ntrees after: "<<m_outTreeCol.size()<<std::endl;
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode ArborClusteringAlg::CleanConnection( CRDEcalEDM::CRDArborTree& m_tree ){
+
+  m_tree.SortNodes(); 
+//std::cout<<"First node: Layer="<<m_tree.GetNodes()[0]->GetDlayer()<<" Type="<<m_tree.GetNodes()[0]->GetType()<<std::endl;
+
+  std::vector<CRDEcalEDM::CRDArborNode*> m_nodeCol = m_tree.GetNodes();
+  std::vector< std::pair<CRDEcalEDM::CRDArborNode*, CRDEcalEDM::CRDArborNode*> > m_connectorCol; m_connectorCol.clear();  
+  for(int in=0; in<m_nodeCol.size(); in++){
+    if(in==0 && m_nodeCol[in]->GetDaughterNodes().size()!=0 ){ std::cout<<"WARNING in ArborClusteringAlg: Last node still have daughters! Check it!"<<std::endl; continue; }
+
+//std::cout<<"#Layer: "<<m_nodeCol[in]->GetDlayer()<<" Parent size:"<<m_nodeCol[in]->GetParentNodes().size()<<", Daughter size: "<<m_nodeCol[in]->GetDaughterNodes().size()<<std::endl;
+
+    //Get the connection with minimum kappa order
+    TVector3 Cref = m_nodeCol[in]->GetRefDir(settings.wiF, settings.wiB);
+    double kappaMin = 9999.;
+    double deltaMax = -999.; 
+    double deltaEmax = -999.; 
+    CRDEcalEDM::CRDArborNode* m_foundnode = nullptr;
+    std::vector<CRDEcalEDM::CRDArborNode*> m_parentNodes = m_nodeCol[in]->GetParentNodes();
+    if(m_parentNodes.size()<=1) continue; 
+
+    for(int ip=0; ip<m_parentNodes.size(); ip++){
+      TVector3 relP = m_nodeCol[in]->GetPosition() - m_parentNodes[ip]->GetPosition();
+      if(relP.Mag()>deltaMax) deltaMax = relP.Mag();
+      double deltaE = fabs( m_nodeCol[in]->GetEnergy()-m_parentNodes[ip]->GetEnergy() ); 
+      if(deltaE>deltaEmax) deltaEmax = deltaE; 
+    }
+
+    for(int ip=0; ip<m_parentNodes.size(); ip++){
+      TVector3 relP = m_nodeCol[in]->GetPosition() - m_parentNodes[ip]->GetPosition();
+      double theta  = relP.Angle(Cref)/(PI) ;
+      double deltaR = relP.Mag();
+      double deltaE = fabs( m_nodeCol[in]->GetEnergy()-m_parentNodes[ip]->GetEnergy() ); 
+
+      double kappa = pow(theta, settings.pTheta) * pow(deltaR/deltaMax, settings.pR ) * pow(deltaE/deltaEmax, settings.pE ); 
+//std::cout<<"   Node kappa order: "<<theta<<"  "<<deltaR<<"  "<<kappa<<std::endl;
+      if(kappa<kappaMin) { kappaMin=kappa; m_foundnode=m_parentNodes[ip]; }
+    }
+//std::cout<<"  Min Kappa order: "<<kappaMin<<std::endl;
+    if( m_foundnode==nullptr ){ std::cout<<"WARNING in ArborClusteringAlg: Did not find minimum kappa order!"<<std::endl; continue; }
+
+    //Save two nodes into connector collection
+    for(int ip=0; ip<m_parentNodes.size(); ip++){
+      if(m_parentNodes[ip]==m_foundnode) continue; 
+      std::pair<CRDEcalEDM::CRDArborNode*, CRDEcalEDM::CRDArborNode*> m_connector;
+      m_connector.first  = m_nodeCol[in]; 
+      m_connector.second = m_parentNodes[ip]; 
+      m_connectorCol.push_back( m_connector );
+    }
+  }
+
+  //Clean the connections in tree
+//std::cout<<" Connection size: "<<m_connectorCol.size()<<std::endl;
+
+  m_tree.CleanConnection( m_connectorCol );
+  m_tree.NodeClassification(); 
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode ArborClusteringAlg::DepartArborTree( CRDEcalEDM::CRDArborTree& m_tree, 
+                                                std::vector<CRDEcalEDM::CRDArborTree>& m_departedTrees,
+                                                std::vector<CRDEcalEDM::CRDArborNode*>& m_isoNodes ) 
+{
+//std::cout<<std::endl;
+//std::cout<<"ArborClusteringAlg::DepartArborTree  Input tree node size: "<<m_tree.GetNodes().size()<<std::endl;
+//std::cout<<"ArborClusteringAlg::DepartArborTree  Input isonode size: "<<m_isoNodes.size()<<std::endl;
+
+  std::vector<CRDEcalEDM::CRDArborNode*> m_rootNodes; m_rootNodes.clear(); 
+  for(int in=0; in<m_tree.GetNodes().size(); in++){
+    CRDEcalEDM::CRDArborNode* m_node = m_tree.GetNodes()[in];
+//printf("  Debug: Loop in node: (%.2f, %.2f, %.2f, %d) \n", m_node->GetPosition().x(), m_node->GetPosition().y(), m_node->GetPosition().z(), m_node->GetType() );
+    if(m_node->GetType()==0){ m_isoNodes.push_back(m_node); m_tree.CleanNode(m_node); in--; }
+    if(m_node->GetType()==4 || m_node->GetType()==5) m_rootNodes.push_back(m_node); 
+//std::cout<<"  Debug: Iso node size: "<<m_isoNodes.size()<<"  Root node size: "<<m_rootNodes.size()<<std::endl;
+  }
+//std::cout<<"ArborClusteringAlg::DepartArborTree  isonode size after classification: "<<m_isoNodes.size()<<std::endl;
+//std::cout<<"ArborClusteringAlg::DepartArborTree  tree node size after classification: "<<m_tree.GetNodes().size()<<std::endl;
+//std::cout<<"ArborClusteringAlg::DepartArborTree  Root node size: "<<m_rootNodes.size()<<std::endl;
+//std::cout<<std::endl;
+
+  //Only one tree: 
+  if( m_rootNodes.size()<=1 ){ 
+    if(m_rootNodes.size()==0) std::cout<<"ERROR in ArborClusteringAlg::DepartArborTree: Did not find root node! Check it!"<<std::endl; 
+    m_departedTrees.push_back(m_tree);
+    return StatusCode::SUCCESS;
+  }
+
+  //Multiple trees: 
+  else{
+  for(int ir=0; ir<m_rootNodes.size(); ir++){
+    CRDEcalEDM::CRDArborTree m_newTree; m_newTree.Clear(); 
+    m_newTree.CreateWithRoot( m_rootNodes[ir] ); //Create a tree with recursion 
+    m_departedTrees.push_back(m_newTree);
+  }}
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode ArborClusteringAlg::MergeNeighborTree( std::vector<CRDEcalEDM::CRDArborTree>& m_inTreeCol, 
+                                                  std::vector<CRDEcalEDM::CRDArborTree>& m_outTreeCol )
+{
+  if(m_inTreeCol.size()<=1){ m_outTreeCol=m_inTreeCol; return StatusCode::SUCCESS; }
+
+  std::vector<CRDEcalEDM::CRDArborNode*> m_rootCol; m_rootCol.clear();
+  m_rootCol.resize(m_inTreeCol.size());
+  for(int it=0; it<m_inTreeCol.size(); it++)
+    m_rootCol[it] = m_inTreeCol[it].GetRootNode();
+
+  //Merge trees whose root nodes are in the same layer and close to each other. 
+  for(int in=0; in<m_rootCol.size(); in++){
+  for(int jn=in+1; jn<m_rootCol.size(); jn++){
+    if( m_rootCol[in]->GetDlayer() != m_rootCol[jn]->GetDlayer() ) continue; 
+    if( (m_rootCol[in]->GetPosition()-m_rootCol[jn]->GetPosition()).Mag()<settings.Rth_nbrRoot ){
+      CRDEcalEDM::CRDArborTree m_newTree;
+      m_newTree.AddNode( m_inTreeCol[in].GetNodes() );
+      m_newTree.AddNode( m_inTreeCol[jn].GetNodes() );
+      m_outTreeCol.push_back( m_newTree );
+    }
+  }}
+
+  return StatusCode::SUCCESS;
+}
+
+/*
+StatusCode ArborClusteringAlg::MergeBranches( std::vector<CRDEcalEDM::CRDArborTree>& m_inTreeCol, 
+                                              std::vector<CRDEcalEDM::CRDArborTree>& m_goodTreeCol, 
+                                              std::vector<CRDEcalEDM::CRDArborTree>& m_badTreeCol)
+{
+  if(m_inTreeCol.size()==0) return StatusCode::SUCCESS; 
+
+  m_goodTreeCol.clear(); m_badTreeCol.clear(); 
+
+  for(int it=0; it<m_inTreeCol.size(); it++){
+    if( (m_inTreeCol[it].GetMaxDlayer()-m_inTreeCol[it].GetMinDlayer()) >= settings.fl_GoodTreeLevel && 
+         m_inTreeCol[it].GetNodes().size()>=settings.th_GoodTreeNodes) 
+      m_goodTreeCol.push_back( m_inTreeCol[it] ); 
+    else m_badTreeCol.push_back( m_inTreeCol[it] );
+  }
+
+//std::cout<<"  ArborClusteringAlg::MergeBranches  Ngoodtrees: "<<m_goodTreeCol.size()<<"  Nbadtrees: "<<m_badTreeCol.size()<<std::endl;
+
+//  for(int it=0; it<m_badTreeCol.size(); it++){
+//    CRDEcalEDM::CRDArborTree m_tree = GetClosestTree( m_badTreeCol[it], m_goodTreeCol );
+//    std::vector<CRDEcalEDM::CRDArborTree>::iterator iter = find(m_goodTreeCol.begin(), m_goodTreeCol.end(), m_tree);
+//    if( iter==m_goodTreeCol.end() ) { cout<<"Warning in MergeBranches: Tree Merging Fail!"<<std::endl; continue; }
+
+//    iter->AddNode( m_badTreeCol[it].GetNodes() );
+//  }
+
+
+  return StatusCode::SUCCESS;
+}
+
+
+CRDEcalEDM::CRDArborTree ArborClusteringAlg::GetClosestTree( CRDEcalEDM::CRDArborTree m_badTree, std::vector<CRDEcalEDM::CRDArborTree> m_goodTreeCol ){
+
+  CRDEcalEDM::CRDArborTree m_tree; m_tree.Clear(); 
+
+  TVector3 cent_bad = m_badTree.GetBarycenter();
+  double minR = 9999; 
+  int index = -1; 
+  for(int it=0; it<m_goodTreeCol.size(); it++){
+    TVector3 cent_good = m_goodTreeCol[it].GetBarycenter();
+    double dis = (cent_bad-cent_good).Mag();
+    if(dis<minR) { minR = dis; index = it;}
+  }
+
+  if(index<0){ std::cout<<"Warning in ArborClusteringAlg::GetClosestTree: Can not find closest good tree!"<<std::endl; return m_tree; }
+  m_tree = m_goodTreeCol[index];
+  return m_tree; 
+}
+*/
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/ArborTreeMergingAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/ArborTreeMergingAlg.cpp
new file mode 100644
index 00000000..beade7ae
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/ArborTreeMergingAlg.cpp
@@ -0,0 +1,281 @@
+#ifndef _ARBORTREEMERGING_ALG_C
+#define _ARBORTREEMERGING_ALG_C
+#include "Algorithm/ArborTreeMergingAlg.h"
+void ArborTreeMergingAlg::Settings::SetInitialValue(){
+
+    th_Nsigma = 2; 
+    th_daughterR = 40.; 
+    th_GoodTreeLayer1 = 6;
+    th_GoodTreeLayer2 = 3;
+    th_GoodTreeNodes = 10;
+    th_MergeR = 50; 
+    th_MergeTheta = PI/10.;
+    fl_MergeTrees = true; 
+    fl_overwrite = true;
+    clusType = "";
+}
+
+StatusCode ArborTreeMergingAlg::Initialize(){
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode ArborTreeMergingAlg::RunAlgorithm( ArborTreeMergingAlg::Settings& m_settings, PandoraPlusDataCol& m_datacol){
+  settings = m_settings; 
+
+  std::vector<CRDEcalEDM::CRDCaloHit2DShower> m_2DshowerCol; m_2DshowerCol.clear();
+  if( settings.clusType=="MIP" )      m_2DshowerCol = m_datacol.MIPShower2DCol;
+  else if( settings.clusType=="EM")  m_2DshowerCol = m_datacol.EMShower2DCol;
+  else m_2DshowerCol = m_datacol.Shower2DCol;
+  std::vector<CRDEcalEDM::CRDArborTree> m_ArborTreeCol = m_datacol.ArborTreeCol; 
+  std::vector<CRDEcalEDM::CRDArborNode*> m_isoNodes = m_datacol.IsoNodes; 
+
+  if(m_2DshowerCol.size()==0){ 
+    std::cout<<"Warning: Empty input in ArborTreeMergingAlg. Please check previous algorithm!"<<endl;  
+//std::cout<<"  Tree size: "<<m_ArborTreeCol.size()<<"  isoNode size: "<<m_isoNodes.size()<<std::endl;
+//  std::cout<<"Print Tree: "<<std::endl;
+//  for(int it=0; it<m_ArborTreeCol.size(); it++) m_ArborTreeCol[it].PrintTree();
+//  std::cout<<"Print Isolated Nodes: "<<std::endl;
+//  for(int i=0; i<m_isoNodes.size(); i++)
+//    printf("(%.2f, %.2f, %.2f, %d) \n", m_isoNodes[i]->GetPosition().x(), m_isoNodes[i]->GetPosition().y(), m_isoNodes[i]->GetPosition().z(), m_isoNodes[i]->GetType());
+    for(int i=0; i<m_ArborTreeCol.size(); i++) { m_ArborTreeCol[i].Clear(); }
+    for(int i=0; i<m_isoNodes.size(); i++)    { delete m_isoNodes[i]; }
+    return StatusCode::SUCCESS; 
+  }
+
+//std::cout<<"  Tree size: "<<m_ArborTreeCol.size()<<"  isoNode size: "<<m_isoNodes.size()<<std::endl;
+//  std::cout<<"Print Tree: "<<std::endl;
+//  for(int it=0; it<m_ArborTreeCol.size(); it++) m_ArborTreeCol[it].PrintTree();
+//  std::cout<<"Print Isolated Nodes: "<<std::endl;
+//  for(int i=0; i<m_isoNodes.size(); i++)
+//    printf("(%.2f, %.2f, %.2f, %d) \n", m_isoNodes[i]->GetPosition().x(), m_isoNodes[i]->GetPosition().y(), m_isoNodes[i]->GetPosition().z(), m_isoNodes[i]->GetType());
+
+
+  //Classify good and bad Tree.
+  std::vector<CRDEcalEDM::CRDArborTree*> m_goodTreeCol; m_goodTreeCol.clear();
+  std::vector<CRDEcalEDM::CRDArborTree*> m_badTreeCol; m_badTreeCol.clear();
+  for(int it=0; it<m_ArborTreeCol.size(); it++){
+//std::cout<<"    MinDlayer: "<<m_ArborTreeCol[it].GetMinDlayer()<<", MaxDlayer: "<<m_ArborTreeCol[it].GetMaxDlayer()<<", Node size: "<<m_ArborTreeCol[it].GetNodes().size()<<std::endl;
+
+    if(  ( (m_ArborTreeCol[it].GetMaxDlayer()-m_ArborTreeCol[it].GetMinDlayer()) >= settings.th_GoodTreeLayer1 ) ||
+         ( (m_ArborTreeCol[it].GetMaxDlayer()-m_ArborTreeCol[it].GetMinDlayer()) >= settings.th_GoodTreeLayer2 &&
+         m_ArborTreeCol[it].GetNodes().size()>=settings.th_GoodTreeNodes) )
+      m_goodTreeCol.push_back( &m_ArborTreeCol[it] );
+    else m_badTreeCol.push_back( &m_ArborTreeCol[it] );
+  }
+//std::cout<<"  Good tree size: "<<m_goodTreeCol.size()<<", Bad tree size: "<<m_badTreeCol.size()<<std::endl;
+
+  if(settings.fl_MergeTrees){
+  std::map<CRDEcalEDM::CRDArborNode*, CRDEcalEDM::CRDArborTree*> m_svtx; m_svtx.clear();
+  //Type 1 secondary vertex: large variation in variance. 
+  GetSecondaryVtxT1(m_ArborTreeCol, m_2DshowerCol, m_svtx);
+  //Type 2 secondary vertex: branch node in a tree & large distance between daughter nodes.
+  //GetSecondaryVtxT2(m_ArborTreeCol, m_svtx);
+
+//std::cout<<"  Vtx size: "<<m_svtx.size()<<std::endl;
+  //Merge clusters pointing to the vertex. 
+  std::map<CRDEcalEDM::CRDArborNode*, CRDEcalEDM::CRDArborTree*>::iterator iter = m_svtx.begin(); 
+  for(iter; iter!=m_svtx.end(); iter++){
+
+    //Get vertex
+    CRDEcalEDM::CRDArborNode* m_node = iter->first; 
+    int m_layer = m_node->GetDlayer(); 
+    TVector3 m_vtxPos = m_node->GetPosition(); 
+
+//printf("vtx: (%.2f, %.2f, %.2f) (Layer %d) \n", m_vtxPos.x(), m_vtxPos.y(), m_vtxPos.z(), m_layer);
+
+    for(int it=0; it<m_goodTreeCol.size(); it++){
+//std::cout<<"  MinDlayer: "<<m_goodTreeCol[it]->GetMinDlayer()<<", MaxDlayer: "<<m_goodTreeCol[it]->GetMaxDlayer()<<std::endl;
+      if( m_goodTreeCol[it]->GetMinDlayer()<0      || m_goodTreeCol[it]->GetMaxDlayer()<0 ||  
+          (m_layer>m_goodTreeCol[it]->GetMinDlayer() && m_layer<m_goodTreeCol[it]->GetMaxDlayer()) ) continue; 
+
+      m_goodTreeCol[it]->SortNodes();  //Sort from outer layer to inner layer. 
+//m_goodTreeCol[it]->PrintTree();
+
+      TVector3 m_nodePos; 
+      if( m_layer<=m_goodTreeCol[it]->GetMinDlayer() )   m_nodePos = m_goodTreeCol[it]->GetNodes().back()->GetPosition(); 
+      if( m_layer>=m_goodTreeCol[it]->GetMaxDlayer() )   m_nodePos = m_goodTreeCol[it]->GetNodes().front()->GetPosition(); 
+
+      CRDEcalEDM::CRDCaloHit3DCluster m_clus = m_goodTreeCol[it]->ConvertTreeToCluster(); 
+      m_clus.FitAxis(); 
+      TVector3 m_treeAxis = m_clus.getAxis(); 
+
+//printf("  Root/leaf node: (%.2f, %.2f, %.2f) \t", m_nodePos.x(), m_nodePos.y(), m_nodePos.z());
+//printf("  cluster axis: (%.2f, %.2f, %.2f) \n", m_treeAxis.x(), m_treeAxis.y(), m_treeAxis.z()); 
+
+      //Merge clusters
+      if( (m_vtxPos-m_nodePos).Mag()<settings.th_MergeR && 
+          ( m_treeAxis.Angle(m_vtxPos-m_nodePos)<settings.th_MergeTheta || (PI-m_treeAxis.Angle(m_vtxPos-m_nodePos))<settings.th_MergeTheta ) ){
+        iter->second->AddNode( m_goodTreeCol[it]->GetNodes() );
+        m_goodTreeCol.erase( m_goodTreeCol.begin()+it );
+        it--; 
+      }
+    }
+  }
+  }
+
+
+  //Re-classify trees after merging: 
+  for(int it=0; it<m_badTreeCol.size(); it++){
+    if(  ( (m_badTreeCol[it]->GetMaxDlayer()-m_badTreeCol[it]->GetMinDlayer()) >= settings.th_GoodTreeLayer1 ) ||
+         ( (m_badTreeCol[it]->GetMaxDlayer()-m_badTreeCol[it]->GetMinDlayer()) >= settings.th_GoodTreeLayer2 &&
+         m_badTreeCol[it]->GetNodes().size()>=settings.th_GoodTreeNodes) ){
+      m_goodTreeCol.push_back( m_badTreeCol[it] );
+      m_badTreeCol.erase( m_badTreeCol.begin()+it );
+      it--; 
+    }
+  }
+
+//std::cout<<"  Good tree size: "<<m_goodTreeCol.size()<<", Bad tree size: "<<m_badTreeCol.size()<<std::endl;
+  //Save Trees into CRDCaloHit3DCluster
+  std::vector<CRDEcalEDM::CRDCaloHit3DCluster> m_goodClusterCol;  m_goodClusterCol.clear();
+  std::vector<CRDEcalEDM::CRDCaloHit3DCluster> m_badClusterCol;  m_badClusterCol.clear();
+  std::vector<CRDEcalEDM::CRDCaloHit3DCluster> m_ClusterCol;  m_ClusterCol.clear();
+
+  for(int it=0; it<m_goodTreeCol.size(); it++)
+    m_goodClusterCol.push_back( m_goodTreeCol[it]->ConvertTreeToCluster() );
+  for(int it=0; it<m_badTreeCol.size(); it++)
+    m_badClusterCol.push_back( m_badTreeCol[it]->ConvertTreeToCluster() );
+  for(int in=0; in<m_isoNodes.size(); in++){
+    CRDEcalEDM::CRDCaloHit3DCluster clus_isonodes;
+    CRDEcalEDM::CRDCaloHit2DShower m_shower = m_isoNodes[in]->GetOriginShower();
+    clus_isonodes.AddShower( m_shower );
+    m_badClusterCol.push_back( clus_isonodes );
+  }
+
+  m_ClusterCol.insert(m_ClusterCol.end(), m_goodClusterCol.begin(), m_goodClusterCol.end());
+  m_ClusterCol.insert(m_ClusterCol.end(), m_badClusterCol.begin(), m_badClusterCol.end());
+
+//  std::cout<<"Print Good Tree: "<<std::endl;
+//  for(int it=0; it<m_goodTreeCol.size(); it++) m_goodTreeCol[it]->PrintTree();
+//  std::cout<<"Print Bad Tree: "<<std::endl;
+//  for(int it=0; it<m_badTreeCol.size(); it++) m_badTreeCol[it]->PrintTree();
+//  std::cout<<"Print Isolated Nodes: "<<std::endl;
+//  for(int i=0; i<m_isoNodes.size(); i++)
+//    printf("(%.2f, %.2f, %.2f, %d) \n", m_isoNodes[i]->GetPosition().x(), m_isoNodes[i]->GetPosition().y(), m_isoNodes[i]->GetPosition().z(), m_isoNodes[i]->GetType());  
+//  cout<<"After convertion: goodClus: "<<m_goodClusterCol.size()<<"  badClus: "<<m_badClusterCol.size()<<"  total: "<<m_ClusterCol.size()<<std::endl;
+
+
+  if(settings.fl_overwrite) m_datacol.ClearCluster(); 
+  m_datacol.GoodClus3DCol.insert(m_datacol.GoodClus3DCol.end(), m_goodClusterCol.begin(), m_goodClusterCol.end() );
+  m_datacol.BadClus3DCol.insert( m_datacol.BadClus3DCol.end(),  m_badClusterCol.begin(), m_badClusterCol.end() );
+  m_datacol.Clus3DCol.insert( m_datacol.Clus3DCol.end(), m_ClusterCol.begin(), m_ClusterCol.end() );
+
+  //Clear nodes pointer. 
+  for(int i=0; i<m_goodTreeCol.size(); i++) { m_goodTreeCol[i]->Clear(); }
+  for(int i=0; i<m_badTreeCol.size(); i++)  { m_badTreeCol[i]->Clear(); }
+  for(int i=0; i<m_isoNodes.size(); i++)    { delete m_isoNodes[i]; }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode ArborTreeMergingAlg::ClearAlgorithm(){
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode ArborTreeMergingAlg::GetSecondaryVtxT1(  std::vector<CRDEcalEDM::CRDArborTree>& m_ArborTreeCol,  
+                                                    std::vector<CRDEcalEDM::CRDCaloHit2DShower>& m_2DshowerCol,
+                                                    std::map<CRDEcalEDM::CRDArborNode*, CRDEcalEDM::CRDArborTree*>& m_svtx )
+{
+
+  if( m_ArborTreeCol.size()==0 || m_2DshowerCol.size()==0 )  return StatusCode::SUCCESS;
+
+  //Get the variance in each layer:
+  std::vector<float> m_scndM; m_scndM.clear(); m_scndM.resize(14);
+  std::map<int, std::vector<CRDEcalEDM::CRDCaloHit2DShower> > m_orderedShower; m_orderedShower.clear();
+  for(int is=0;is<m_2DshowerCol.size();is++){
+    m_orderedShower[m_2DshowerCol[is].getDlayer()].push_back(m_2DshowerCol[is]);
+  }
+  for(int il=0; il<14; il++){
+    std::vector<CRDEcalEDM::CRDCaloHit2DShower> m_showers = m_orderedShower[il+1];
+    if(m_showers.size()==0){ m_scndM[il]=0; continue; }
+
+    double sumE=0;
+    TVector3 cent(0,0,0);
+    for(int is=0; is<m_showers.size(); is++){
+      sumE+=m_showers[is].getShowerE();
+      TVector3 pos(m_showers[is].getPos().x(), m_showers[is].getPos().y(), m_showers[is].getPos().z());
+      cent += pos;
+    }
+    cent = cent*(1./(double)m_showers.size());
+
+    double scndM=0;
+    for(int is=0; is<m_showers.size(); is++){
+      TVector3 pos(m_showers[is].getPos().x(), m_showers[is].getPos().y(), m_showers[is].getPos().z());
+      double rpos = (pos-cent).Mag2();
+      double wi = m_showers[is].getShowerE()/sumE;
+      scndM += wi*rpos;
+    }
+    m_scndM[il] = scndM;
+//std::cout<<"  Layer: "<<il<<"  variance: "<<m_scndM[il]<<std::endl;
+  }
+//std::cout<<std::endl; 
+
+  //Get secondary vertex layer and position
+  vector<int> Layer_svtx; Layer_svtx.clear();
+  for(int il=1; il<14; il++)
+    if( m_scndM[il-1]!=0 && (fabs(m_scndM[il]-m_scndM[il-1]) > settings.th_Nsigma * m_scndM[il-1]) ){ Layer_svtx.push_back(il-1); }
+
+//std::cout<<std::endl; 
+//std::cout<<"  Layer with svtx: "; 
+//for(int i=0; i<Layer_svtx.size(); i++) std::cout<<Layer_svtx[i]<<'\t'; 
+//std::cout<<std::endl; 
+
+
+  for(int iv=0; iv<Layer_svtx.size(); iv++){
+    for(int it=0; it<m_ArborTreeCol.size(); it++){
+      if( Layer_svtx[iv]>m_ArborTreeCol[it].GetMaxDlayer() || Layer_svtx[iv]<m_ArborTreeCol[it].GetMinDlayer() ||
+          m_ArborTreeCol[it].GetMinDlayer()==-1 || m_ArborTreeCol[it].GetMaxDlayer()==-1 ) continue;
+
+      std::vector<CRDEcalEDM::CRDArborNode*> m_nodes = m_ArborTreeCol[it].GetNodes(Layer_svtx[iv]);
+      for(int in=0; in<m_nodes.size(); in++){
+//printf("  vtx node: (%.2f, %.2f, %.2f) \n", m_nodes[in]->GetPosition().x(), m_nodes[in]->GetPosition().y(), m_nodes[in]->GetPosition().z());
+        m_svtx[m_nodes[in]] = &m_ArborTreeCol[it];
+      }
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode ArborTreeMergingAlg::GetSecondaryVtxT2(  std::vector<CRDEcalEDM::CRDArborTree>& m_ArborTreeCol,
+                                                    std::map<CRDEcalEDM::CRDArborNode*, CRDEcalEDM::CRDArborTree*>& m_svtx )
+{
+
+  if(m_ArborTreeCol.size()==0) return StatusCode::SUCCESS;
+
+  //Get first branch node
+  for(int it=0; it<m_ArborTreeCol.size(); it++){
+    m_ArborTreeCol[it].SortNodes(); 
+    std::vector<CRDEcalEDM::CRDArborNode*> m_nodes = m_ArborTreeCol[it].GetNodes(); 
+
+    std::vector<CRDEcalEDM::CRDArborNode*> m_BrNodes; m_BrNodes.clear(); 
+    for(int in=0; in<m_nodes.size(); in++){
+      int nodeType = m_nodes[in]->GetType(); 
+      if(nodeType!=3) continue; 
+
+      double maxR_daughter = -999;
+      std::vector<CRDEcalEDM::CRDArborNode*> m_dtrNodes = m_nodes[in]->GetDaughterNodes(); 
+      for(int id=0; id<m_dtrNodes.size(); id++){
+      for(int jd=id+1; jd<m_dtrNodes.size(); jd++){
+        double m_Dis = (m_dtrNodes[id]->GetPosition() - m_dtrNodes[jd]->GetPosition()).Mag(); 
+        if( m_Dis>maxR_daughter ) maxR_daughter = m_Dis; 
+      }}
+      if(maxR_daughter<0){ std::cout<<"Error in GetSecondaryVtxT2: failed to get daughter node distance! Check it!"<<std::endl; continue; }
+
+      if( nodeType==3 && maxR_daughter>settings.th_daughterR ) m_BrNodes.push_back(m_nodes[in]); 
+    }
+
+    for(int in=0; in<m_BrNodes.size(); in++){
+      std::map<CRDEcalEDM::CRDArborNode*, CRDEcalEDM::CRDArborTree*>::iterator it_find = m_svtx.find(m_BrNodes[in]);
+      if( it_find==m_svtx.end() ) m_svtx[m_BrNodes[in]] = &m_ArborTreeCol[it]; 
+    }
+
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/AxisMergingAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/AxisMergingAlg.cpp
new file mode 100644
index 00000000..1b43f0fc
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/AxisMergingAlg.cpp
@@ -0,0 +1,982 @@
+#ifndef _AXISMERGING_ALG_C
+#define _AXISMERGING_ALG_C
+
+#include "Algorithm/AxisMergingAlg.h"
+
+StatusCode AxisMergingAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  //Initialize parameters
+  if(settings.map_stringPars.find("OutputAxisName")==settings.map_stringPars.end()) settings.map_stringPars["OutputAxisName"] = "MergedAxis";
+  if(settings.map_floatPars.find("th_overlap")==settings.map_floatPars.end()) settings.map_floatPars["th_overlap"] = 0.5;
+  if(settings.map_intPars.find("th_CoreNhit")==settings.map_intPars.end()) settings.map_intPars["th_CoreNhit"] = 3;
+  if(settings.map_floatPars.find("axis_Angle")==settings.map_floatPars.end()) settings.map_floatPars["axis_Angle"] = TMath::Pi()/4.;
+  if(settings.map_floatPars.find("relP_Angle")==settings.map_floatPars.end()) settings.map_floatPars["relP_Angle"] = TMath::Pi()/4.;
+  if(settings.map_floatPars.find("relP_Dis")==settings.map_floatPars.end()) settings.map_floatPars["relP_Dis"] = 5*PandoraPlus::CaloUnit::barsize;
+  if(settings.map_intPars.find("th_Nhit")==settings.map_intPars.end()) settings.map_intPars["th_Nhit"] = 5;
+  if(settings.map_floatPars.find("th_branch_distance")==settings.map_floatPars.end()) settings.map_floatPars["th_branch_distance"] = 30;
+
+  if(settings.map_intPars.find("th_Nhit_low")==settings.map_intPars.end()) settings.map_intPars["th_Nhit_low"] = 1;
+  if(settings.map_intPars.find("th_Eclus_low")==settings.map_intPars.end()) settings.map_intPars["th_Eclus_low"] = 0.005;
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode AxisMergingAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  m_axisUCol.clear();
+  m_axisVCol.clear();
+  m_newAxisUCol.clear();
+  m_newAxisVCol.clear();
+
+  p_HalfClusterU = nullptr;
+  p_HalfClusterV = nullptr;
+
+  p_HalfClusterU = &(m_datacol.map_HalfCluster["HalfClusterColU"]);
+  p_HalfClusterV = &(m_datacol.map_HalfCluster["HalfClusterColV"]);
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode AxisMergingAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+  //cout << "yyy: ----------------------------  Running Axis MergingAlg  -----------------------------------" << endl;
+
+  if( p_HalfClusterU->size() + p_HalfClusterV->size()==0 ) {
+    std::cout<<"AxisMergingAlg: No HalfCluster input"<<std::endl;
+    return StatusCode::SUCCESS;
+  }
+
+  //cout << "yyy: Readin halfcluster size in U, V: "<<p_HalfClusterU->size()<<", "<<p_HalfClusterV->size()<<endl;
+
+  //cout << "yyy: Merge axis in U pHalfClusterU" << endl;
+  //Merge axis in HalfClusterU: 
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> p_emptyHFClusU; p_emptyHFClusU.clear();
+  for(int ih=0; ih<p_HalfClusterU->size(); ih++){
+    m_axisUCol.clear(); m_newAxisUCol.clear();
+    m_axisUCol = p_HalfClusterU->at(ih)->getAllHalfClusterCol();
+
+//printf("  HalfClusterU #%d: Energy %.4f, axis size %d, 1DCluster size %d, address %p \n", ih, p_HalfClusterU->at(ih)->getEnergy(), m_axisUCol.size(), p_HalfClusterU->at(ih)->getCluster().size(), p_HalfClusterU->at(ih).get() );
+    if(m_axisUCol.size()==0){ //No axis: save out and merge to other HFClusters later.
+      //if(p_HalfClusterU->at(ih)->getEnergy()>settings.map_intPars["th_Eclus_low"] || p_HalfClusterU->at(ih)->getCluster().size()>settings.map_intPars["th_Nhit_low"])
+      //  m_datacol.map_HalfCluster["emptyHalfClusterU"].push_back(p_HalfClusterU->at(ih));
+      p_emptyHFClusU.push_back(p_HalfClusterU->at(ih));
+      p_HalfClusterU->erase(p_HalfClusterU->begin()+ih);
+      ih--;
+      continue;
+    }
+
+    for(int ic=0; ic<m_axisUCol.size(); ic++){ 
+      std::shared_ptr<CaloHalfCluster> ptr_cloned = m_axisUCol[ic]->Clone();
+      m_datacol.map_HalfCluster["bkHalfCluster"].push_back(ptr_cloned);
+      m_newAxisUCol.push_back( ptr_cloned.get() );
+    }
+
+    // cout << "  yyy: For p_HalfClusterU[" << ih << "], m_newAxisUCol.size() = " << m_newAxisUCol.size() 
+    //      << ", if size<2, no need to merge" << endl;
+    if(m_newAxisUCol.size()<2){ //No need to merge, save into OutputAxis. 
+      std::vector<const PandoraPlus::CaloHalfCluster*> tmp_axisCol; tmp_axisCol.clear();
+      for(int ic=0; ic<m_newAxisUCol.size(); ic++) tmp_axisCol.push_back(m_newAxisUCol[ic]);
+      p_HalfClusterU->at(ih)->setHalfClusters(settings.map_stringPars["OutputAxisName"], tmp_axisCol);
+      continue; 
+    }
+
+
+    std::sort( m_newAxisUCol.begin(), m_newAxisUCol.end(), compLayer );
+
+
+//printf("  In HalfClusterU #%d: readin axis size %d \n", ih, m_newAxisUCol.size());
+/*
+std::map<std::string, std::vector<const PandoraPlus::CaloHalfCluster*> > tmp_HClusMap =  p_HalfClusterU->at(ih)->getHalfClusterMap();
+cout<<"Print Readin AxisU: "<<endl;
+for(auto iter : tmp_HClusMap){
+  cout<<"  Axis name: "<<iter.first<<endl;
+  for(int ia=0; ia<iter.second.size(); ia++){
+    cout<<"    No. #"<<ia<<endl;
+    for(int il=0 ;il<iter.second[ia]->getCluster().size(); il++)
+    printf("    LocalMax %d: (%.3f, %.3f, %.3f, %.3f), towerID [%d, %d, %d], %p \n", il, 
+        iter.second[ia]->getCluster()[il]->getPos().x(), 
+        iter.second[ia]->getCluster()[il]->getPos().y(), 
+        iter.second[ia]->getCluster()[il]->getPos().z(), 
+        iter.second[ia]->getCluster()[il]->getEnergy(), 
+        iter.second[ia]->getCluster()[il]);
+  }
+cout<<endl;
+}
+*/
+/*
+cout<<"Print Readin AxisU: "<<endl;
+for(int ia=0; ia<m_newAxisUCol.size(); ia++){
+cout<<"  Axis #"<<ia<<": type "<<m_newAxisUCol[ia]->getType()<<endl;
+for(int il=0 ;il<m_newAxisUCol[ia]->getCluster().size(); il++)
+  printf("    LocalMax %d: (%.3f, %.3f, %.3f, %.3f), towerID [%d, %d, %d], %p \n", il, 
+    m_newAxisUCol[ia]->getCluster()[il]->getPos().x(), 
+    m_newAxisUCol[ia]->getCluster()[il]->getPos().y(), 
+    m_newAxisUCol[ia]->getCluster()[il]->getPos().z(), 
+    m_newAxisUCol[ia]->getCluster()[il]->getTowerID()[0][0],
+    m_newAxisUCol[ia]->getCluster()[il]->getTowerID()[0][1],
+    m_newAxisUCol[ia]->getCluster()[il]->getTowerID()[0][2],
+    m_newAxisUCol[ia]->getCluster()[il]->getEnergy(), 
+    m_newAxisUCol[ia]->getCluster()[il]);
+cout<<endl;
+}
+*/
+    //Case1: Merge axes associated to the same track. 
+    TrkMatchedMerging(m_newAxisUCol);    
+    // cout << "  yyy: after TrkMatchedMerging(), axis size = " <<  m_newAxisUCol.size() << endl;
+
+    //Case2: Merge axes that share same localMax.
+    OverlapMerging(m_newAxisUCol);
+    // cout << "  yyy: after OverlapMerging(), axis size = " <<  m_newAxisUCol.size() << endl;
+
+    // Case3: Merge fake photon to track axis.
+    BranchMerging(m_newAxisUCol);
+    // cout << "  yyy: after BranchMerging(), axis size = " <<  m_newAxisUCol.size() << endl;
+
+    //Case4: Merge fragments to core axes. 
+    FragmentsMerging(m_newAxisUCol);
+    // cout << "  yyy: after FragmentsMerging(), axis size = " <<  m_newAxisUCol.size() << endl;
+
+    //Case4: Merge nearby axes. 
+    //ConeMerging(m_newAxisUCol);
+    //printf("  In HalfClusterU #%d: After Step4: axis size %d \n", ih, m_newAxisUCol.size());
+
+
+    // cout<<"  yyy: after all merging: axis size "<<m_newAxisUCol.size()<<endl;
+    // for(int ia=0; ia<m_newAxisUCol.size(); ia++){
+    //   cout<<"  yyy: axis #"<<ia<<endl;
+    //   for(int il=0 ;il<m_newAxisUCol[ia]->getCluster().size(); il++)
+    //     printf("         LocalMax %d: (%.2f, %.2f, %.2f, %.4f), towerID [%d, %d, %d], %p \n", il, 
+    //       m_newAxisUCol[ia]->getCluster()[il]->getPos().x(), 
+    //       m_newAxisUCol[ia]->getCluster()[il]->getPos().y(), 
+    //       m_newAxisUCol[ia]->getCluster()[il]->getPos().z(), 
+    //       m_newAxisUCol[ia]->getCluster()[il]->getEnergy(), 
+    //       m_newAxisUCol[ia]->getCluster()[il]->getTowerID()[0][0], 
+    //       m_newAxisUCol[ia]->getCluster()[il]->getTowerID()[0][1], 
+    //       m_newAxisUCol[ia]->getCluster()[il]->getTowerID()[0][2], 
+    //       m_newAxisUCol[ia]->getCluster()[il]);
+    // }
+
+
+//cout << "  yyy: check axis quality" << endl;
+//printf("  In HalfClusterU #%d: After Step4: axis size %d \n", ih, m_newAxisUCol.size());
+
+/*
+cout<<"  After merging: axis size "<<m_newAxisUCol.size()<<", Check the overlap"<<endl;
+cout<<"Print Merged AxisU: "<<endl;
+for(int ia=0; ia<m_newAxisUCol.size(); ia++){
+cout<<"  Axis #"<<ia<<": type "<<m_newAxisUCol[ia]->getType()<<endl;
+for(int il=0 ;il<m_newAxisUCol[ia]->getCluster().size(); il++)
+  printf("    LocalMax %d: (%.3f, %.3f, %.3f, %.3f), towerID [%d, %d, %d], %p \n", il, 
+    m_newAxisUCol[ia]->getCluster()[il]->getPos().x(), 
+    m_newAxisUCol[ia]->getCluster()[il]->getPos().y(), 
+    m_newAxisUCol[ia]->getCluster()[il]->getPos().z(), 
+    m_newAxisUCol[ia]->getCluster()[il]->getEnergy(), 
+    m_newAxisUCol[ia]->getCluster()[il]->getTowerID()[0][0], 
+    m_newAxisUCol[ia]->getCluster()[il]->getTowerID()[0][1], 
+    m_newAxisUCol[ia]->getCluster()[il]->getTowerID()[0][2], 
+    m_newAxisUCol[ia]->getCluster()[il]);
+cout<<endl;
+}
+*/
+    //Check axis quality
+    std::vector<PandoraPlus::CaloHalfCluster*> tmp_goodAxis; tmp_goodAxis.clear(); 
+    std::vector<PandoraPlus::CaloHalfCluster*> tmp_badAxis; tmp_badAxis.clear();
+    for(int ic=0; ic<m_newAxisUCol.size(); ic++){
+      m_newAxisUCol[ic]->getLinkedMCPfromUnit();
+      if( (m_newAxisUCol[ic]->getType()==100 && m_newAxisUCol[ic]->getCluster().size()>=settings.map_intPars["th_CoreNhit"] ) ||
+          m_newAxisUCol[ic]->getType()>100 ){
+            tmp_goodAxis.push_back( m_newAxisUCol[ic] );
+          }
+        
+
+      else {
+        tmp_badAxis.push_back( m_newAxisUCol[ic] );
+      }
+    }  
+
+    for(int ic=0; ic<tmp_badAxis.size(); ic++){
+      MergeToClosestCluster( tmp_badAxis[ic], tmp_goodAxis );
+    }
+    
+    //Split the double-used 1DClusters in axis  
+    //SplitOverlapCluster(p_HalfClusterU->at(ih), tmp_goodAxis, m_datacol.map_1DCluster["bk1DCluster"], m_datacol.map_BarCol["bkBar"]);
+
+    //convert to constant object and save in HalfCluster: 
+    std::vector<const PandoraPlus::CaloHalfCluster*> tmp_axisCol; tmp_axisCol.clear();
+    for(int ic=0; ic<tmp_goodAxis.size(); ic++) tmp_axisCol.push_back(tmp_goodAxis[ic]);
+    // for(int ic=0; ic<m_newAxisUCol.size(); ic++) tmp_axisCol.push_back(m_newAxisUCol[ic]);
+    p_HalfClusterU->at(ih)->setHalfClusters(settings.map_stringPars["OutputAxisName"], tmp_axisCol);
+
+  }
+
+  //Merge empty HalfCluster into the existing clusters. 
+  for(int ih=0; ih<p_emptyHFClusU.size(); ih++){
+    if(!p_emptyHFClusU[ih]) continue;
+    std::vector<PandoraPlus::CaloHalfCluster*> m_HalfCluster; m_HalfCluster.clear();
+    for(int i=0; i<p_HalfClusterU->size(); i++) m_HalfCluster.push_back(p_HalfClusterU->at(i).get());
+    if(!MergeToClosestCluster(p_emptyHFClusU[ih].get(), m_HalfCluster)){
+      if(p_emptyHFClusU[ih]->getEnergy()>settings.map_intPars["th_Eclus_low"] || p_emptyHFClusU[ih]->getCluster().size()>settings.map_intPars["th_Nhit_low"]){
+cout<<"  Save cluster #"<<ih<<" as empty, energy "<<p_emptyHFClusU[ih]->getEnergy()<<endl;
+        m_datacol.map_HalfCluster["emptyHalfClusterU"].push_back(p_emptyHFClusU[ih]);
+      }
+    }
+  }
+
+  //cout << "yyy: Merge axis in V pHalfClusterV" << endl;
+  //Merge axis in HalfClusterV:
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> p_emptyHFClusV; p_emptyHFClusV.clear();
+  for(int ih=0; ih<p_HalfClusterV->size(); ih++){
+    m_axisVCol.clear(); m_newAxisVCol.clear();
+    m_axisVCol = p_HalfClusterV->at(ih)->getAllHalfClusterCol();
+//printf("  HalfClusterV #%d: Energy %.4f, axis size %d, 1DCluster size %d \n", ih, p_HalfClusterV->at(ih)->getEnergy(), m_axisVCol.size(), p_HalfClusterV->at(ih)->getCluster().size());
+
+    if(m_axisVCol.size()==0){ //No axis: save out and merge to other HFClusters later.
+      //m_datacol.map_HalfCluster["emptyHalfClusterV"].push_back(p_HalfClusterV->at(ih));
+      p_emptyHFClusV.push_back(p_HalfClusterV->at(ih));
+      p_HalfClusterV->erase(p_HalfClusterV->begin()+ih);
+      ih--;
+      continue;
+    }
+
+    for(int ic=0; ic<m_axisVCol.size(); ic++){
+      std::shared_ptr<CaloHalfCluster> ptr_cloned = m_axisVCol[ic]->Clone();
+      m_datacol.map_HalfCluster["bkHalfCluster"].push_back(ptr_cloned);
+      m_newAxisVCol.push_back( ptr_cloned.get() );
+    }
+
+    // cout << "  yyy: For p_HalfClusterV[" << ih << "], m_newAxisVCol.size() = " << m_newAxisVCol.size() 
+    //      << ", if size<2, no need to merge" << endl;
+    if(m_newAxisVCol.size()<2){
+      std::vector<const PandoraPlus::CaloHalfCluster*> tmp_axisCol; tmp_axisCol.clear();
+      for(int ic=0; ic<m_newAxisVCol.size(); ic++) tmp_axisCol.push_back(m_newAxisVCol[ic]);
+      p_HalfClusterV->at(ih)->setHalfClusters(settings.map_stringPars["OutputAxisName"], tmp_axisCol);
+      continue;
+    }
+    std::sort( m_newAxisVCol.begin(), m_newAxisVCol.end(), compLayer );
+    //Case1: Merge axes associated to the same track. 
+    TrkMatchedMerging(m_newAxisVCol);
+    // cout << "  yyy: after TrkMatchedMerging(), axis size = " <<  m_newAxisVCol.size() << endl;
+
+    //Case2: Merge axes that share same localMax.
+    OverlapMerging(m_newAxisVCol);
+    // cout << "  yyy: after OverlapMerging(), axis size = " <<  m_newAxisVCol.size() << endl;
+
+    //Case3: Merge fake photon to track axis.
+    BranchMerging(m_newAxisVCol);
+    // cout << "  yyy: after BranchMerging(), axis size = " <<  m_newAxisVCol.size() << endl;
+
+    //Case4: Merge fragments to core axes. 
+    FragmentsMerging(m_newAxisVCol);
+    // cout << "  yyy: after FragmentsMerging(), axis size = " <<  m_newAxisVCol.size() << endl;
+
+    //Case4: Merge nearby axes.
+    //ConeMerging(m_newAxisVCol);
+    //printf("  In HalfClusterV #%d: After Step4: axis size %d \n", ih, m_newAxisVCol.size());
+
+    // cout<<"  yyy: after all merging: axis size "<<m_newAxisVCol.size()<<endl;
+    // for(int ia=0; ia<m_newAxisVCol.size(); ia++){
+    //   cout<<"  yyy: axis #"<<ia<<endl;
+    //   for(int il=0 ;il<m_newAxisVCol[ia]->getCluster().size(); il++)
+    //     printf("         LocalMax %d: (%.2f, %.2f, %.2f, %.4f), towerID [%d, %d, %d], %p \n", il, 
+    //       m_newAxisVCol[ia]->getCluster()[il]->getPos().x(), 
+    //       m_newAxisVCol[ia]->getCluster()[il]->getPos().y(), 
+    //       m_newAxisVCol[ia]->getCluster()[il]->getPos().z(), 
+    //       m_newAxisVCol[ia]->getCluster()[il]->getEnergy(), 
+    //       m_newAxisVCol[ia]->getCluster()[il]->getTowerID()[0][0], 
+    //       m_newAxisVCol[ia]->getCluster()[il]->getTowerID()[0][1], 
+    //       m_newAxisVCol[ia]->getCluster()[il]->getTowerID()[0][2], 
+    //       m_newAxisVCol[ia]->getCluster()[il]);
+    // }
+
+    //cout << "  yyy: check axis quality" << endl;
+    //Check axis quality
+    std::vector<PandoraPlus::CaloHalfCluster*> tmp_goodAxis; tmp_goodAxis.clear();
+    std::vector<PandoraPlus::CaloHalfCluster*> tmp_badAxis; tmp_badAxis.clear();
+    for(int ic=0; ic<m_newAxisVCol.size(); ic++){
+      m_newAxisVCol[ic]->getLinkedMCPfromUnit();
+      if( (m_newAxisVCol[ic]->getType()==100 && m_newAxisVCol[ic]->getCluster().size()>=settings.map_intPars["th_CoreNhit"] ) ||
+          m_newAxisVCol[ic]->getType()>100 ){
+            tmp_goodAxis.push_back( m_newAxisVCol[ic] );
+          }
+        
+
+      else {
+        tmp_badAxis.push_back( m_newAxisVCol[ic] );
+      }
+    }
+
+    for(int ic=0; ic<tmp_badAxis.size(); ic++){
+      MergeToClosestCluster( tmp_badAxis[ic], tmp_goodAxis );
+    }
+      
+    //Split the double-used 1DClusters in axis  
+    //SplitOverlapCluster(p_HalfClusterV->at(ih), tmp_goodAxis, m_datacol.map_1DCluster["bk1DCluster"], m_datacol.map_BarCol["bkBar"]);
+
+    //convert to constant object and save in HalfCluster:
+    std::vector<const PandoraPlus::CaloHalfCluster*> tmp_axisCol; tmp_axisCol.clear();
+    for(int ic=0; ic<tmp_goodAxis.size(); ic++) tmp_axisCol.push_back(tmp_goodAxis[ic]);
+    // for(int ic=0; ic<m_newAxisVCol.size(); ic++) tmp_axisCol.push_back(m_newAxisVCol[ic]);
+    p_HalfClusterV->at(ih)->setHalfClusters(settings.map_stringPars["OutputAxisName"], tmp_axisCol);
+
+  }
+
+  //Merge empty HalfClusters into the existing cluster.
+  for(int ih=0; ih<p_emptyHFClusV.size(); ih++){
+    if(!p_emptyHFClusV[ih]) continue;
+    std::vector<PandoraPlus::CaloHalfCluster*> m_HalfCluster; m_HalfCluster.clear();
+    for(int i=0; i<p_HalfClusterV->size(); i++) m_HalfCluster.push_back(p_HalfClusterV->at(i).get());
+    if(!MergeToClosestCluster(p_emptyHFClusV[ih].get(), m_HalfCluster)){
+      if(p_emptyHFClusV[ih]->getEnergy()>settings.map_intPars["th_Eclus_low"] || p_emptyHFClusV[ih]->getCluster().size()>settings.map_intPars["th_Nhit_low"])
+        m_datacol.map_HalfCluster["emptyHalfClusterV"].push_back(p_emptyHFClusV[ih]);
+    }
+  }
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode AxisMergingAlg::ClearAlgorithm(){
+  p_HalfClusterU = nullptr;
+  p_HalfClusterV = nullptr;
+  m_axisUCol.clear();
+  m_axisVCol.clear();
+  m_newAxisUCol.clear();
+  m_newAxisVCol.clear();
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode AxisMergingAlg::TrkMatchedMerging( std::vector<PandoraPlus::CaloHalfCluster*>& m_axisCol ){
+  // cout << "  yyy: calling TrkMatchedMerging(), m_axisCol.size()=" << m_axisCol.size() << endl;
+  if(m_axisCol.size()<2) return StatusCode::SUCCESS;
+
+  for(int iax=0; iax<m_axisCol.size(); iax++){
+    std::vector<const PandoraPlus::Track*> m_trkCol = m_axisCol[iax]->getAssociatedTracks();
+    for(int jax=iax+1; jax<m_axisCol.size(); jax++){
+      std::vector<const PandoraPlus::Track*> p_trkCol = m_axisCol[jax]->getAssociatedTracks();
+
+
+      bool fl_match = false;
+      for(int itrk=0; itrk<m_trkCol.size(); itrk++){
+        if( find(p_trkCol.begin(), p_trkCol.end(), m_trkCol[itrk])!=p_trkCol.end() ){
+          fl_match = true; break;
+        }
+      }
+
+      if(fl_match){
+        // cout << "    yyy: m_axisCol[" << iax << "] with type=" << m_axisCol[iax]->getType() 
+        //      << " and [" << jax << "] with type=" << m_axisCol[jax]->getType() << "share the same associated track, merge them" << endl;
+        // cout << "         hit in m_axisCol["<<iax<<"]:"<<endl;
+        // for(int yii=0; yii<m_axisCol[iax]->getCluster().size(); yii++){
+        //   cout << "           (" << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().x()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().y()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().z()
+        //        << ")" << endl;
+        // }
+        // cout << "         hit in m_axisCol["<<jax<<"]:"<<endl;
+        // for(int yii=0; yii<m_axisCol[jax]->getCluster().size(); yii++){
+        //   cout << "           (" << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().x()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().y()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().z()
+        //        << ")" << endl;
+        // }
+
+        m_axisCol[iax]->mergeHalfCluster( m_axisCol[jax] );
+        // cout << "    yyy: after merge them, new type=" << m_axisCol[iax]->getType() << endl;
+        //delete m_axisCol[jax]; m_axisCol[jax]=nullptr;
+        m_axisCol.erase(m_axisCol.begin()+jax);
+        jax--;
+        if(iax>jax+1) iax--;
+      }
+    }
+  }
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode AxisMergingAlg::OverlapMerging( std::vector<PandoraPlus::CaloHalfCluster*>& m_axisCol ){
+  // cout << "  yyy: calling OverlapMerging(), m_axisCol.size()=" << m_axisCol.size() << endl;
+  if(m_axisCol.size()<2) return StatusCode::SUCCESS;
+
+  // cout << "  yyy: first interate" << endl;
+  for(int iax=0; iax<m_axisCol.size(); iax++){
+    const PandoraPlus::CaloHalfCluster* m_axis = m_axisCol[iax];
+    for(int jax=iax+1; jax<m_axisCol.size(); jax++){
+      const PandoraPlus::CaloHalfCluster* p_axis = m_axisCol[jax];
+
+      // Do not merge two different track axes
+      if ( m_axis->getAssociatedTracks().size()>0 && p_axis->getAssociatedTracks().size()>0 ) continue;
+
+      std::vector<const Calo1DCluster*> tmp_localMax = p_axis->getCluster();
+
+      int nsharedHits = 0;
+      for(int ihit=0; ihit<m_axis->getCluster().size(); ihit++)
+        if( find(tmp_localMax.begin(), tmp_localMax.end(), m_axis->getCluster()[ihit])!=tmp_localMax.end() ) nsharedHits++;
+
+      
+      if( (m_axis->getCluster().size()<=p_axis->getCluster().size() && (float)nsharedHits/m_axis->getCluster().size()>settings.map_floatPars["th_overlap"] ) || 
+          (p_axis->getCluster().size()<m_axis->getCluster().size() && (float)nsharedHits/p_axis->getCluster().size()>settings.map_floatPars["th_overlap"] ) ){
+
+        // cout << "    yyy: m_axisCol[" << iax << "] with type=" << m_axisCol[iax]->getType() 
+        //      << " and [" << jax << "] with type=" << m_axisCol[jax]->getType() 
+        //      << " share " << nsharedHits << " hits, merge them" << endl;
+        // cout << "         hit in m_axisCol["<<iax<<"]:"<<endl;
+        // for(int yii=0; yii<m_axisCol[iax]->getCluster().size(); yii++){
+        //   cout << "           (" << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().x()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().y()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().z()
+        //        << ")" << endl;
+        // }
+        // cout << "         hit in m_axisCol["<<jax<<"]:"<<endl;
+        // for(int yii=0; yii<m_axisCol[jax]->getCluster().size(); yii++){
+        //   cout << "           (" << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().x()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().y()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().z()
+        //        << ")" << endl;
+        // }
+
+        m_axisCol[iax]->mergeHalfCluster( m_axisCol[jax] );
+
+        // track axis + neutral axis = track axis
+        int axis_type = m_axisCol[iax]->getType() + m_axisCol[jax]->getType();
+        m_axisCol[iax]->setType(axis_type);
+
+        // cout << "    yyy: after merge them, new type=" << m_axisCol[iax]->getType() << endl;
+        //delete m_axisCol[jax]; m_axisCol[jax]=nullptr;
+        m_axisCol.erase(m_axisCol.begin()+jax);
+        jax--;
+        if(iax>jax+1) iax--;
+
+      }
+
+      p_axis=nullptr;
+    }
+    m_axis=nullptr;
+  }
+
+
+  // cout << "  yyy: second interate" << endl;
+  //iterate
+  for(int iax=0; iax<m_axisCol.size(); iax++){
+    const PandoraPlus::CaloHalfCluster* m_axis = m_axisCol[iax];
+    for(int jax=iax+1; jax<m_axisCol.size(); jax++){
+      const PandoraPlus::CaloHalfCluster* p_axis = m_axisCol[jax];
+
+      // Do not merge two different track axes
+      if ( m_axis->getAssociatedTracks().size()>0 && p_axis->getAssociatedTracks().size()>0 ) continue;
+      
+      std::vector<const Calo1DCluster*> tmp_localMax = p_axis->getCluster();
+
+      int nsharedHits = 0;
+      for(int ihit=0; ihit<m_axis->getCluster().size(); ihit++)
+        if( find(tmp_localMax.begin(), tmp_localMax.end(), m_axis->getCluster()[ihit])!=tmp_localMax.end() ) nsharedHits++;
+
+  //printf("  In pair (%d, %d): hit size (%d, %d), shared hit size %d \n",iax, jax, m_axis->getCluster().size(), p_axis->getCluster().size(), nsharedHits);
+
+      if( (m_axis->getCluster().size()<=p_axis->getCluster().size() && (float)nsharedHits/m_axis->getCluster().size()>settings.map_floatPars["th_overlap"] ) ||
+          (p_axis->getCluster().size()<m_axis->getCluster().size() && (float)nsharedHits/p_axis->getCluster().size()>settings.map_floatPars["th_overlap"] ) ){
+
+  //cout<<"  Merge: Yes. "<<endl;
+        // cout << "    yyy: m_axisCol[" << iax << "] with type=" << m_axisCol[iax]->getType() 
+        //      << " and [" << jax << "] with type=" << m_axisCol[jax]->getType() 
+        //      << " share " << nsharedHits << " hits, merge them" << endl;
+        // cout << "         hit in m_axisCol["<<iax<<"]:"<<endl;
+        // for(int yii=0; yii<m_axisCol[iax]->getCluster().size(); yii++){
+        //   cout << "           (" << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().x()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().y()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().z()
+        //        << ")" << endl;
+        // }
+        // cout << "         hit in m_axisCol["<<jax<<"]:"<<endl;
+        // for(int yii=0; yii<m_axisCol[jax]->getCluster().size(); yii++){
+        //   cout << "           (" << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().x()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().y()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().z()
+        //        << ")" << endl;
+        // }
+
+        m_axisCol[iax]->mergeHalfCluster( m_axisCol[jax] );  
+
+        int axis_type = m_axisCol[iax]->getType() + m_axisCol[jax]->getType();
+        m_axisCol[iax]->setType(axis_type);
+
+        // cout << "    yyy: after merge them, new type=" << m_axisCol[iax]->getType() << endl;
+
+        //delete m_axisCol[jax]; m_axisCol[jax]=nullptr;
+        m_axisCol.erase(m_axisCol.begin()+jax);
+        jax--;
+        if(iax>jax+1) iax--;
+
+      }
+
+      p_axis=nullptr;
+    }
+    m_axis=nullptr;
+  }
+
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode AxisMergingAlg::BranchMerging( std::vector<PandoraPlus::CaloHalfCluster*>& m_axisCol ){
+  // cout << "  yyy: calling BranchMerging(), m_axisCol.size()=" << m_axisCol.size() << endl;
+  if(m_axisCol.size()<2) return StatusCode::SUCCESS;
+
+  // Merge fake Hough axis to track axis
+  std::sort( m_axisCol.begin(), m_axisCol.end(), compLayer );
+  for(int iax=0; iax<m_axisCol.size(); iax++){  // select track axis
+    const PandoraPlus::CaloHalfCluster* m_axis = m_axisCol[iax];
+
+    if (m_axis->getType()<10000){
+      // cout << "yyy:  m_axisCol[" << iax << "] is not a track axis. skip" << endl;
+      continue;
+    }
+
+    for(int jax=0; jax<m_axisCol.size(); jax++){ // select Hough axis
+      if(jax==iax) continue;
+      const PandoraPlus::CaloHalfCluster* p_axis = m_axisCol[jax];
+      
+      if (p_axis->getType()>=10000) continue; // Do not merge two track axis
+      if (p_axis->getType()/100%100<1){
+        // cout << "yyy:  m_axisCol[" << jax << "] is not a Hough axis. skip" << endl;
+        continue;
+      }
+
+      // // Determine if two axes are close to each other
+      // bool is_close = false;
+      // for(int ibar=0; ibar<m_axis->getCluster().size(); ibar++){
+      //   for(int jbar=0; jbar<p_axis->getCluster().size(); jbar++){
+      //     double distance = ( m_axis->getCluster()[ibar]->getPos() - p_axis->getCluster()[jbar]->getPos() ).Mag();
+      //     if(distance<100){ // yyy: hard coding here. min distance of the two axes must be < 50 mm
+      //       is_close = true;
+      //       break;
+      //     }
+      //   }
+      //   if(is_close) break;
+      // }
+
+
+      // if(!is_close) continue;  // if the two axes are not close to each other, no need to merge
+
+      double hough_rho = p_axis->getHoughRho();
+      double hough_alpha = p_axis->getHoughAlpha();
+
+      // V plane
+      if(m_axis->getSlayer()==1){
+        double x0 = m_axis->getEnergyCenter().x();
+        double y0 = m_axis->getEnergyCenter().y();
+        double distance = TMath::Abs( x0*TMath::Cos(hough_alpha) + y0*TMath::Sin(hough_alpha) - hough_rho );
+
+        // cout << "    yyy:V rho = " << hough_rho << ", alpha = " << hough_alpha << ", x0 = " << x0 << ", y0 = " << y0 << endl;
+        // cout << "         distanceV = " << distance << endl;
+
+        if (distance<settings.map_floatPars["th_branch_distance"]){
+          m_axisCol[iax]->mergeHalfCluster( m_axisCol[jax] );
+          int axis_type = m_axisCol[iax]->getType() + m_axisCol[jax]->getType();
+          m_axisCol[iax]->setType(axis_type);
+          m_axisCol.erase(m_axisCol.begin()+jax);
+          jax--;
+          if(iax>jax+1) iax--;
+          // cout << "  yyy: axis " << jax << " is merged into axis " << iax << endl;
+        }
+      }
+
+      // U plane
+      else{
+        int m_module = m_axis->getEnergyCenterTower()[0];
+        // cout << "  yyy:branceU: m_module = " << m_module << endl;
+        int p_module = p_axis->getTowerID()[0][0];
+        // cout << "  yyy:branceU: p_module = " << p_module << endl;
+        // Do not merge the two axis in two different modules
+        //if (m_module != p_module) continue;
+
+        TVector3 t_pos = m_axis->getEnergyCenter();
+        // cout << "  yyy:branceU: t_pos = " << t_pos.x() << ", " << t_pos.y() << ", " << t_pos.z() << endl;
+        t_pos.RotateZ( TMath::TwoPi()/PandoraPlus::CaloUnit::Nmodule*(int(PandoraPlus::CaloUnit::Nmodule*3./4.)-m_module) );
+        // cout << "  yyy:branceU: t_pos after rotate to module 6 = " << t_pos.x() << ", " << t_pos.y() << ", " << t_pos.z() << endl;
+        double x0 = t_pos.x();
+        double y0 = t_pos.z();
+        double distance = TMath::Abs( x0*TMath::Cos(hough_alpha) + y0*TMath::Sin(hough_alpha) - hough_rho );
+
+        // cout << "    yyy:U rho = " << hough_rho << ", alpha = " << hough_alpha << ", x0 = " << x0 << ", z0 = " << y0 << endl;
+        // cout << "         distanceU = " << distance << endl;
+
+        if (distance<settings.map_floatPars["th_branch_distance"]){
+          m_axisCol[iax]->mergeHalfCluster( m_axisCol[jax] );
+          int axis_type = m_axisCol[iax]->getType() + m_axisCol[jax]->getType();
+          m_axisCol[iax]->setType(axis_type);
+          m_axisCol.erase(m_axisCol.begin()+jax);
+          jax--;
+          if(iax>jax+1) iax--;
+          // cout << "  yyy: axis " << jax << " is merged into axis " << iax << endl;
+        }
+
+
+      }
+
+      p_axis=nullptr;
+    }
+    m_axis=nullptr;
+  }
+}
+
+
+StatusCode AxisMergingAlg::FragmentsMerging( std::vector<PandoraPlus::CaloHalfCluster*>& m_axisCol ){
+  // cout << "  yyy: calling FragmentsMerging(), m_axisCol.size()=" << m_axisCol.size() << endl;
+  if(m_axisCol.size()<2) return StatusCode::SUCCESS;
+
+  /*
+  cout<<"FragmentsMerging: print readin axes "<<endl;
+  for(int ia=0; ia<m_axisCol.size(); ia++){
+  cout<<"  Axis #"<<ia<<", Energy center ";
+  printf("(%.3f, %.3f, %.3f, %.3f) \n", m_axisCol[ia]->getEnergyCenter().x(), m_axisCol[ia]->getEnergyCenter().y(), m_axisCol[ia]->getEnergyCenter().z());
+  for(int il=0 ;il<m_axisCol[ia]->getCluster().size(); il++)
+    printf("    LocalMax %d: (%.3f, %.3f, %.3f, %.3f) \n", il,
+      m_axisCol[ia]->getCluster()[il]->getPos().x(),
+      m_axisCol[ia]->getCluster()[il]->getPos().y(),
+      m_axisCol[ia]->getCluster()[il]->getPos().z(),
+      m_axisCol[ia]->getCluster()[il]->getEnergy() );
+  cout<<endl;
+  }
+  */
+
+  //Merge fragments to core.
+  std::sort( m_axisCol.begin(), m_axisCol.end(), compLayer );
+  for(int iax=0; iax<m_axisCol.size(); iax++){
+    const PandoraPlus::CaloHalfCluster* m_axis = m_axisCol[iax];
+    //Define the Core: Hough+Nhit || Trk+Hough || Trk+Cone.
+    //cout<<"  Readin core axis #"<<iax<<" type: "<<m_axis->getType()<<", Nclus: "<<m_axis->getCluster().size();
+    if( !( (m_axis->getType()==100 && m_axis->getCluster().size()>=settings.map_intPars["th_CoreNhit"] ) ||
+        m_axis->getType()>100 ) ) 
+      { m_axis=nullptr; continue; }
+
+    // cout << "    yyy: m_axisCol[" << iax << "] is a core" << endl;
+
+    for(int jax=0; jax<m_axisCol.size(); jax++){
+      if(jax==iax) continue;
+      const PandoraPlus::CaloHalfCluster* p_axis = m_axisCol[jax];
+      //Do not merge 2 cores. 
+      if( (p_axis->getType()==100 && p_axis->getCluster().size()>=settings.map_intPars["th_CoreNhit"] ) ||
+          p_axis->getType()>100 ){
+            // cout << "    yyy: m_axisCol[" << jax << "] is also a core, do not merge 2 cores" << endl;
+            // cout << "         hit in m_axisCol["<<iax<<"]:"<<endl;
+            //for(int yii=0; yii<m_axisCol[iax]->getCluster().size(); yii++){
+            //  cout << "           (" << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().x()
+            //      << ", " << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().y()
+            //      << ", " << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().z()
+            //      << ")" << endl;
+            //}
+            // cout << "         hit in m_axisCol["<<jax<<"]:"<<endl;
+            //for(int yii=0; yii<m_axisCol[jax]->getCluster().size(); yii++){
+            //  cout << "           (" << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().x()
+            //      << ", " << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().y()
+            //      << ", " << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().z()
+            //      << ")" << endl;
+            //}
+            
+            p_axis=nullptr; 
+            continue; 
+          }
+
+      TVector3 relP1 = p_axis->getClusterInLayer( p_axis->getBeginningDlayer() )[0]->getPos() - m_axis->getClusterInLayer( m_axis->getEndDlayer() )[0]->getPos();
+      TVector3 relP2 = p_axis->getClusterInLayer( p_axis->getBeginningDlayer() )[0]->getPos() - m_axis->getEnergyCenter();
+      TVector3 relP3 = m_axis->getEnergyCenter() - p_axis->getClusterInLayer( p_axis->getEndDlayer() )[0]->getPos();
+      TVector3 relP4 = p_axis->getPos() - m_axis->getPos();
+
+      // cout << "    yyy: m_axisCol[" << jax << "] is NOT a core, comparing them" << endl;
+      // cout << "    yyy: relP1 = (" << std::fixed << std::setprecision(2) << relP1.x() 
+      //      <<                 ", " << std::fixed << std::setprecision(2) << relP1.y() 
+      //      <<                 ", " << std::fixed << std::setprecision(2) << relP1.z()
+      //      << endl
+      //      << "         relP2 = (" << std::fixed << std::setprecision(2) << relP2.x() 
+      //      <<                 ", " << std::fixed << std::setprecision(2) << relP2.y() 
+      //      <<                 ", " << std::fixed << std::setprecision(2) << relP2.z() 
+      //      << endl
+      //      << "         relP3 = (" << std::fixed << std::setprecision(2) << relP3.x() 
+      //      <<                 ", " << std::fixed << std::setprecision(2) << relP3.y() 
+      //      <<                 ", " << std::fixed << std::setprecision(2) << relP3.z() 
+      //      << endl
+      //      << "         relP4 = (" << std::fixed << std::setprecision(2) << relP4.x() 
+      //      <<                 ", " << std::fixed << std::setprecision(2) << relP4.y() 
+      //      <<                 ", " << std::fixed << std::setprecision(2) << relP4.z() 
+      //      << endl
+      //      << "         p_axis->getAxis() = (" << std::fixed << std::setprecision(2) << p_axis->getAxis().x() 
+      //      <<                             ", " << std::fixed << std::setprecision(2) << p_axis->getAxis().y() 
+      //      <<                             ", " << std::fixed << std::setprecision(2) << p_axis->getAxis().z() 
+      //      << endl
+      //      << "         m_axis->getAxis() = (" << std::fixed << std::setprecision(2) << m_axis->getAxis().x() 
+      //      <<                             ", " << std::fixed << std::setprecision(2) << m_axis->getAxis().y() 
+      //      <<                             ", " << std::fixed << std::setprecision(2) << m_axis->getAxis().z() 
+      //      << endl;
+
+      if( (relP1.Angle(p_axis->getAxis()) < settings.map_floatPars["axis_Angle"] && relP1.Mag()<=settings.map_floatPars["relP_Dis"]) ||
+          (relP2.Angle(p_axis->getAxis()) < settings.map_floatPars["axis_Angle"] && relP2.Mag()<=settings.map_floatPars["relP_Dis"]) ||
+          (relP3.Angle(p_axis->getAxis()) < settings.map_floatPars["axis_Angle"] && relP3.Mag()<=settings.map_floatPars["relP_Dis"]) || 
+          ( p_axis->getType()<100 && relP4.Angle(m_axis->getAxis())<settings.map_floatPars["axis_Angle"] && relP4.Mag()<=settings.map_floatPars["relP_Dis"]) )
+      {
+        // cout << "    yyy: m_axisCol[" << iax << "] (core) with type=" << m_axisCol[iax]->getType() 
+        //      << " and [" << jax << "] (branch) with type=" << m_axisCol[jax]->getType() 
+        //      << " are merged" << endl;
+        // cout << "         hit in m_axisCol["<<iax<<"]:"<<endl;
+        // for(int yii=0; yii<m_axisCol[iax]->getCluster().size(); yii++){
+        //   cout << "           (" << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().x()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().y()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[iax]->getCluster()[yii]->getPos().z()
+        //        << ")" << endl;
+        // }
+        // cout << "         hit in m_axisCol["<<jax<<"]:"<<endl;
+        // for(int yii=0; yii<m_axisCol[jax]->getCluster().size(); yii++){
+        //   cout << "           (" << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().x()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().y()
+        //        << ", " << std::fixed << std::setprecision(2) << m_axisCol[jax]->getCluster()[yii]->getPos().z()
+        //        << ")" << endl;
+        // }
+ 
+        m_axisCol[iax]->mergeHalfCluster( m_axisCol[jax] );
+
+        int axis_type = m_axisCol[iax]->getType() + m_axisCol[jax]->getType();
+        m_axisCol[iax]->setType(axis_type);
+
+        //delete m_axisCol[jax]; m_axisCol[jax]=nullptr;
+        m_axisCol.erase(m_axisCol.begin()+jax);
+        jax--;
+        if(iax>jax+1) iax--;
+      }
+
+      p_axis=nullptr;
+    }
+    m_axis = nullptr;
+  }
+
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode AxisMergingAlg::ConeMerging( std::vector<PandoraPlus::CaloHalfCluster*>& m_axisCol ){
+  if(m_axisCol.size()<2) return StatusCode::SUCCESS;
+
+  std::sort( m_axisCol.begin(), m_axisCol.end(), compLayer );
+  for(int iax=0; iax<m_axisCol.size(); iax++){
+    const PandoraPlus::CaloHalfCluster* m_axis = m_axisCol[iax];
+    for(int jax=iax+1; jax<m_axisCol.size(); jax++){
+      const PandoraPlus::CaloHalfCluster* p_axis = m_axisCol[jax];
+
+  //printf("  Layer range: axis %d: [%d, %d], axis %d: [%d, %d] \n", iax, m_axis->getBeginningDlayer(), m_axis->getEndDlayer(), jax, p_axis->getBeginningDlayer(), p_axis->getEndDlayer());
+
+      TVector3 m_beginPoint = m_axis->getClusterInLayer( m_axis->getBeginningDlayer() )[0]->getPos();
+      TVector3 m_endPoint   = m_axis->getClusterInLayer( m_axis->getEndDlayer() )[0]->getPos();
+      TVector3 p_beginPoint = p_axis->getClusterInLayer( p_axis->getBeginningDlayer() )[0]->getPos();
+      TVector3 p_endPoint   = p_axis->getClusterInLayer( p_axis->getEndDlayer() )[0]->getPos();
+      double relDis = -1.; 
+      if( m_endPoint.Mag()<p_beginPoint.Mag() )      relDis = p_beginPoint.Mag() - m_endPoint.Mag(); 
+      else if( p_endPoint.Mag()<m_beginPoint.Mag() ) relDis = m_beginPoint.Mag() - p_endPoint.Mag();
+      else if( m_beginPoint.Mag()>p_beginPoint.Mag() && m_endPoint.Mag()>p_endPoint.Mag() )  relDis = p_endPoint.Mag() - m_beginPoint.Mag();
+      else if( p_beginPoint.Mag()>m_beginPoint.Mag() && p_endPoint.Mag()>m_endPoint.Mag() )  relDis = m_endPoint.Mag() - p_beginPoint.Mag();
+      else if( (m_beginPoint.Mag()>p_beginPoint.Mag() && m_endPoint.Mag()<p_endPoint.Mag()) ||
+               (m_beginPoint.Mag()<p_beginPoint.Mag() && m_endPoint.Mag()>p_endPoint.Mag()) ) relDis = 9999.;
+
+      double minRelAngle = min( sin( (m_axis->getPos()-p_axis->getPos()).Angle(m_axis->getAxis())),
+                                sin( (m_axis->getPos()-p_axis->getPos()).Angle(p_axis->getAxis())) );
+  //      int skipLayer = m_axis->getBeginningDlayer()<p_axis->getBeginningDlayer() ?
+  //                      (p_axis->getBeginningDlayer()-m_axis->getEndDlayer()) : (m_axis->getBeginningDlayer()-p_axis->getEndDlayer());
+
+  //printf("  AxisMerging: axes pair (%d, %d): axisAngel = %.3f, RelPAngle = %.3f, dis = %d \n", iax, jax, m_axis->getAxis().Angle(p_axis->getAxis()), minRelAngle, relDis);
+
+
+      if( ( sin( m_axis->getAxis().Angle(p_axis->getAxis()) ) < sin(settings.map_floatPars["axis_Angle"]) ||
+            sin(minRelAngle) < sin(settings.map_floatPars["relP_Angle"]) )  &&
+          relDis <= settings.map_floatPars["relP_Dis"] && relDis>=0 ){
+  //cout<<"    Merge! "<<endl;
+
+        m_axisCol[iax]->mergeHalfCluster( m_axisCol[jax] );
+        if(m_axisCol[iax]->getType()==1 || m_axisCol[jax]->getType()==1 || m_axisCol[iax]->getType()==5 || m_axisCol[jax]->getType()==5) m_axisCol[iax]->setType(5);
+        else if(m_axisCol[iax]->getType()==3 || m_axisCol[jax]->getType()==3 || m_axisCol[iax]->getType()==6 || m_axisCol[jax]->getType()==6) m_axisCol[iax]->setType(6);
+        else if(m_axisCol[iax]->getType()==4 || m_axisCol[jax]->getType()==4) m_axisCol[iax]->setType(4);
+        else m_axisCol[iax]->setType(7);
+
+        //delete m_axisCol[jax]; m_axisCol[jax]=nullptr;
+        m_axisCol.erase(m_axisCol.begin()+jax);
+        jax--;
+        if(iax>jax+1) iax--;
+      }
+      p_axis = nullptr;
+    }
+    m_axis = nullptr;
+  }
+
+  //cout<<"Iteration merge"<<endl;
+
+  //iterate
+  for(int iax=0; iax<m_axisCol.size(); iax++){
+    const PandoraPlus::CaloHalfCluster* m_axis = m_axisCol[iax];
+    for(int jax=iax+1; jax<m_axisCol.size(); jax++){
+      const PandoraPlus::CaloHalfCluster* p_axis = m_axisCol[jax];
+
+      TVector3 m_beginPoint = m_axis->getClusterInLayer( m_axis->getBeginningDlayer() )[0]->getPos();
+      TVector3 m_endPoint   = m_axis->getClusterInLayer( m_axis->getEndDlayer() )[0]->getPos();
+      TVector3 p_beginPoint = p_axis->getClusterInLayer( p_axis->getBeginningDlayer() )[0]->getPos();
+      TVector3 p_endPoint   = p_axis->getClusterInLayer( p_axis->getEndDlayer() )[0]->getPos();
+      double relDis = -1.;
+      if( m_endPoint.Mag()<p_beginPoint.Mag() )      relDis = p_beginPoint.Mag() - m_endPoint.Mag();
+      else if( p_endPoint.Mag()<m_beginPoint.Mag() ) relDis = m_beginPoint.Mag() - p_endPoint.Mag();
+      else if( m_beginPoint.Mag()>p_beginPoint.Mag() && m_endPoint.Mag()>p_endPoint.Mag() )  relDis = p_endPoint.Mag() - m_beginPoint.Mag();
+      else if( p_beginPoint.Mag()>m_beginPoint.Mag() && p_endPoint.Mag()>m_endPoint.Mag() )  relDis = m_endPoint.Mag() - p_beginPoint.Mag();
+      else if( (m_beginPoint.Mag()>p_beginPoint.Mag() && m_endPoint.Mag()<p_endPoint.Mag()) ||
+               (m_beginPoint.Mag()<p_beginPoint.Mag() && m_endPoint.Mag()>p_endPoint.Mag()) ) relDis = 9999.;
+      double minRelAngle = min( sin( (m_axis->getEnergyCenter()-p_axis->getEnergyCenter()).Angle(m_axis->getAxis())),
+                                sin( (m_axis->getEnergyCenter()-p_axis->getEnergyCenter()).Angle(p_axis->getAxis())) );
+      //int skipLayer = m_axis->getBeginningDlayer()<p_axis->getBeginningDlayer() ?
+      //                (p_axis->getBeginningDlayer()-m_axis->getEndDlayer()) : (m_axis->getBeginningDlayer()-p_axis->getEndDlayer());
+
+  //printf("  AxisMerging: axes pair (%d, %d): axisAngel = %.3f, RelPAngle = %.3f, dis = %d \n", iax, jax, m_axis->getAxis().Angle(p_axis->getAxis()), minRelAngle, relDis);
+  //printf("  Layer range: axis %d: [%d, %d], axis %d: [%d, %d] \n", iax, m_axis->getBeginningDlayer(), m_axis->getEndDlayer(), jax, p_axis->getBeginningDlayer(), p_axis->getEndDlayer());
+
+      if( ( sin( m_axis->getAxis().Angle(p_axis->getAxis()) ) < sin(settings.map_floatPars["axis_Angle"]) ||
+            sin(minRelAngle) < sin(settings.map_floatPars["relP_Angle"]) ) &&
+          relDis <= settings.map_floatPars["relP_Dis"] && relDis>=0 ){
+
+        m_axisCol[iax]->mergeHalfCluster( m_axisCol[jax] );
+        if(m_axisCol[iax]->getType()==1 || m_axisCol[jax]->getType()==1 || m_axisCol[iax]->getType()==5 || m_axisCol[jax]->getType()==5) m_axisCol[iax]->setType(5);
+        else if(m_axisCol[iax]->getType()==3 || m_axisCol[jax]->getType()==3 || m_axisCol[iax]->getType()==6 || m_axisCol[jax]->getType()==6) m_axisCol[iax]->setType(6);
+        else if(m_axisCol[iax]->getType()==4 || m_axisCol[jax]->getType()==4) m_axisCol[iax]->setType(4);
+        else m_axisCol[iax]->setType(7);
+
+        //delete m_axisCol[jax]; m_axisCol[jax]=nullptr;
+        m_axisCol.erase(m_axisCol.begin()+jax);
+        jax--;
+        if(iax>jax+1) iax--;
+      }
+      p_axis = nullptr;
+    }
+    m_axis = nullptr;
+  }
+
+  return StatusCode::SUCCESS;
+};
+
+
+bool AxisMergingAlg::MergeToClosestCluster( PandoraPlus::CaloHalfCluster* m_badaxis, std::vector<PandoraPlus::CaloHalfCluster*>& m_axisCol ){
+  if(m_axisCol.size()==0){ 
+    //m_axisCol.push_back(m_badaxis);
+    // cout << "  yyy: no good axis, treat the bad axis as good axis" << endl;
+    return false;
+  }
+
+  float minR = 9999;
+  int index = -1;
+  for(int iax=0; iax<m_axisCol.size(); iax++){
+    float tmp_R = (m_badaxis->getEnergyCenter()-m_axisCol[iax]->getEnergyCenter()).Mag();
+    if( tmp_R<minR ){
+      minR = tmp_R;
+      index = iax;
+    }
+  }
+  if(index>=0){
+    m_axisCol[index]->mergeHalfCluster(m_badaxis);
+
+    int axis_type = m_axisCol[index]->getType() + m_badaxis->getType();
+    m_axisCol[index]->setType(axis_type);
+
+    return true;
+  } 
+  return false;
+};
+
+/*
+StatusCode AxisMergingAlg::SplitOverlapCluster( std::shared_ptr<PandoraPlus::CaloHalfCluster>& m_HFClus,
+                                                std::vector<PandoraPlus::CaloHalfCluster*>& m_axisCol,
+                                                std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>>& bk_1Dclus,
+                                                std::vector<std::shared_ptr<PandoraPlus::CaloUnit>>& bk_Unit){
+
+  if(m_axisCol.size()<2) return StatusCode::SUCCESS;
+
+  for(int ic=0; ic<m_axisCol.size()-1; ic++){
+    for(int jc=ic+1; jc<m_axisCol.size(); jc++){
+      double E_ratio = m_axisCol[ic]->OverlapRatioE(m_axisCol[jc]);
+      if(E_ratio>0){
+
+        std::vector<const Calo1DCluster*> m_ClusCol1 =  m_axisCol[ic]->getCluster();
+        std::vector<const Calo1DCluster*> m_ClusCol2 =  m_axisCol[jc]->getCluster();
+        std::vector<const Calo1DCluster*> m_overlapCl; m_overlapCl.clear();
+        for(int i1d=0; i1d<m_ClusCol1.size(); i1d++){
+          if(find(m_ClusCol2.begin(), m_ClusCol2.end(), m_ClusCol1[i1d])!=m_ClusCol2.end() ) m_overlapCl.push_back(m_ClusCol1[i1d]);
+        }
+
+        for(int io=0; io<m_overlapCl.size(); io++){
+
+          //Split the cluster with +-1 layer cluster energy.
+          int dlayer = m_overlapCl[io]->getDlayer();
+
+          //  For  m_axisCol[ic]
+          std::vector<const Calo1DCluster*> m_clus1_front = m_axisCol[ic]->getClusterInLayer(dlayer-1);
+          std::vector<const Calo1DCluster*> m_clus1_back  = m_axisCol[ic]->getClusterInLayer(dlayer+1);
+          std::vector<const Calo1DCluster*> m_clus2_front = m_axisCol[jc]->getClusterInLayer(dlayer-1);
+          std::vector<const Calo1DCluster*> m_clus2_back  = m_axisCol[jc]->getClusterInLayer(dlayer+1);
+
+          if( m_clus1_front.size()==0 && m_clus1_back.size()==0 && m_clus2_front.size()==0 && m_clus2_back.size()==0 ){
+            //An isolated hit in both: delete in m_axisCol[ic]. TODO: maybe need to optimize based on profile?
+            m_axisCol[ic]->deleteUnit(m_overlapCl[io]);
+            continue;
+          }
+          else if( m_clus1_front.size()==0 && m_clus1_back.size()==0 ){
+            //Only isolated in m_axisCol[ic]: delete in m_axisCol[ic].
+            m_axisCol[ic]->deleteUnit(m_overlapCl[io]);
+            continue;
+          }
+          else if( m_clus2_front.size()==0 && m_clus2_back.size()==0 ){
+            m_axisCol[jc]->deleteUnit(m_overlapCl[io]);
+            continue;
+          }
+          else{
+            //Use the average energy
+            double aveEn1 = 0.;
+            for(int i1d=0; i1d<m_clus1_front.size(); i1d++) aveEn1 += m_clus1_front[i1d]->getEnergy();
+            for(int i1d=0; i1d<m_clus1_back.size(); i1d++) aveEn1 += m_clus1_back[i1d]->getEnergy();
+            aveEn1 = aveEn1/(m_clus1_front.size()+m_clus1_back.size());
+
+            double aveEn2 = 0.;
+            for(int i1d=0; i1d<m_clus2_front.size(); i1d++) aveEn2 += m_clus2_front[i1d]->getEnergy();
+            for(int i1d=0; i1d<m_clus2_back.size(); i1d++) aveEn2 += m_clus2_back[i1d]->getEnergy();
+            aveEn2 = aveEn2/(m_clus2_front.size()+m_clus2_back.size());
+
+            std::shared_ptr<PandoraPlus::Calo1DCluster> m_splitCl1 = std::make_shared<PandoraPlus::Calo1DCluster>();
+            std::shared_ptr<PandoraPlus::Calo1DCluster> m_splitCl2 = std::make_shared<PandoraPlus::Calo1DCluster>();
+
+            for(int ib=0; ib<m_overlapCl[io]->getBars().size(); ib++){
+              auto bar1 = m_overlapCl[io]->getBars()[ib]->Clone();
+              bar1->setQ( bar1->getQ1()*aveEn1/(aveEn1+aveEn2), bar1->getQ2()*aveEn1/(aveEn1+aveEn2) );
+              m_splitCl1->addUnit(bar1.get());
+
+              auto bar2 = m_overlapCl[io]->getBars()[ib]->Clone();
+              bar2->setQ( bar2->getQ1()*aveEn2/(aveEn1+aveEn2), bar2->getQ2()*aveEn2/(aveEn1+aveEn2) );
+              m_splitCl2->addUnit(bar2.get());
+
+              bk_Unit.push_back(bar1);
+              bk_Unit.push_back(bar2);
+            }
+
+            m_axisCol[ic]->deleteUnit(m_overlapCl[io]);
+            m_axisCol[ic]->addUnit(m_splitCl1.get());
+
+            m_axisCol[jc]->deleteUnit(m_overlapCl[io]);
+            m_axisCol[jc]->addUnit(m_splitCl2.get());
+
+            //m_HFClus->deleteUnit(m_overlapCl[io]);
+            //m_HFClus->addUnit(m_splitCl1.get());
+            //m_HFClus->addUnit(m_splitCl2.get());
+
+            bk_1Dclus.push_back(m_splitCl1);
+            bk_1Dclus.push_back(m_splitCl2);
+          }
+
+
+        }//end loop m_axisCol
+      }
+
+    }
+  }
+  return StatusCode::SUCCESS;
+}
+*/
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/ClusterMergingAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/ClusterMergingAlg.cpp
new file mode 100644
index 00000000..c0dcbf9a
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/ClusterMergingAlg.cpp
@@ -0,0 +1,32 @@
+#ifndef _CLUSTERMERGING_ALG_C
+#define _CLUSTERMERGING_ALG_C
+
+#include "Algorithm/ClusterMergingAlg.h"
+
+StatusCode ClusterMergingAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  //Initialize parameters
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode ClusterMergingAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode ClusterMergingAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode ClusterMergingAlg::ClearAlgorithm(){
+
+
+  return StatusCode::SUCCESS;
+};
+
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/ConeClustering2DAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/ConeClustering2DAlg.cpp
new file mode 100644
index 00000000..4696277c
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/ConeClustering2DAlg.cpp
@@ -0,0 +1,212 @@
+#ifndef _CONECLUSTERING2D_ALG_C
+#define _CONECLUSTERING2D_ALG_C
+
+#include "Algorithm/ConeClustering2DAlg.h"
+
+StatusCode ConeClustering2DAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  //Initialize parameters
+  if(settings.map_floatPars.find("th_beginLayer")==settings.map_floatPars.end())   settings.map_floatPars["th_beginLayer"] = 1;
+  if(settings.map_floatPars.find("th_stopLayer")==settings.map_floatPars.end())    settings.map_floatPars["th_stopLayer"] = 15;
+  if(settings.map_floatPars.find("th_ConeTheta")==settings.map_floatPars.end())    settings.map_floatPars["th_ConeTheta"] = TMath::Pi()/4.;
+  if(settings.map_floatPars.find("th_ConeR")==settings.map_floatPars.end())        settings.map_floatPars["th_ConeR"] = 50;
+  if(settings.map_intPars.find("th_Nshowers")==settings.map_intPars.end())       settings.map_intPars["th_Nshowers"] = 4;
+  if(settings.map_stringPars.find("ReadinLocalMaxName")==settings.map_stringPars.end()) settings.map_stringPars["ReadinLocalMaxName"] = "LeftLocalMax";
+  if(settings.map_stringPars.find("OutputLongiClusName")==settings.map_stringPars.end()) settings.map_stringPars["OutputLongiClusName"] = "ConeAxis";
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode ConeClustering2DAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  p_HalfClusterU.clear();
+  p_HalfClusterV.clear();
+
+
+  for(int ih=0; ih<m_datacol.map_HalfCluster["HalfClusterColU"].size(); ih++)
+    p_HalfClusterU.push_back( m_datacol.map_HalfCluster["HalfClusterColU"][ih].get() );
+  for(int ih=0; ih<m_datacol.map_HalfCluster["HalfClusterColV"].size(); ih++)
+    p_HalfClusterV.push_back( m_datacol.map_HalfCluster["HalfClusterColV"][ih].get() );
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode ConeClustering2DAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+
+  if( (p_HalfClusterU.size()+p_HalfClusterV.size())<1 ){
+    std::cout << "ConeClustering2DAlg: No HalfCluster input"<<std::endl;
+    return StatusCode::SUCCESS;
+  }   
+ 
+  std::vector<PandoraPlus::CaloHalfCluster*> tmp_longiClusCol; tmp_longiClusCol.clear(); 
+  std::map<int, std::vector<const PandoraPlus::Calo1DCluster*> > m_orderedLocalMax; 
+
+//cout<<"  ConeClustering: Input HalfCluster size "<<p_HalfClusterU.size()<<", "<<p_HalfClusterV.size()<<endl;
+
+  //Processing U plane:
+  for(int ic=0; ic<p_HalfClusterU.size(); ic++){
+    //Get LocalMax
+    m_localMaxUCol.clear(); 
+    m_localMaxUCol = p_HalfClusterU[ic]->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+
+//cout<<"    In ClusU #"<<ic<<": localMax size "<<m_localMaxUCol.size()<<endl;
+
+    //Get ordered LocalMax
+    m_orderedLocalMax.clear();
+    for(int is=0; is<m_localMaxUCol.size(); is++)
+      m_orderedLocalMax[m_localMaxUCol[is]->getDlayer()].push_back(m_localMaxUCol[is]);
+
+    tmp_longiClusCol.clear();
+    LongiConeLinking( m_orderedLocalMax, tmp_longiClusCol, m_datacol.map_HalfCluster["bkHalfCluster"] );
+
+//cout<<"    Cluster size after ConeLinking: "<<tmp_longiClusCol.size()<<endl;
+
+    //Convert LongiClusters to const object.    
+    const_longiClusUCol.clear();
+    for(int ic=0; ic<tmp_longiClusCol.size(); ic++){
+      tmp_longiClusCol[ic]->Check();
+      if(tmp_longiClusCol[ic]->getCluster().size()>=settings.map_intPars["th_Nshowers"]) const_longiClusUCol.push_back(tmp_longiClusCol[ic]);
+    }
+//cout<<"    Cluster size after requiring Nhit: "<<const_longiClusUCol.size()<<endl;
+
+    p_HalfClusterU[ic]->setHalfClusters(settings.map_stringPars["OutputLongiClusName"], const_longiClusUCol);    
+  }
+
+  //Processing V plane:
+  for(int ic=0; ic<p_HalfClusterV.size(); ic++){
+    m_localMaxVCol.clear();
+    m_localMaxVCol = p_HalfClusterV[ic]->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+
+    m_orderedLocalMax.clear();
+    for(int is=0; is<m_localMaxVCol.size(); is++)
+      m_orderedLocalMax[m_localMaxVCol[is]->getDlayer()].push_back(m_localMaxVCol[is]);
+
+    tmp_longiClusCol.clear();
+    LongiConeLinking(m_orderedLocalMax, tmp_longiClusCol, m_datacol.map_HalfCluster["bkHalfCluster"]);
+
+
+    //Convert LongiClusters to const object.
+    const_longiClusVCol.clear();
+    for(int ic=0; ic<tmp_longiClusCol.size(); ic++){
+      tmp_longiClusCol[ic]->Check();
+      if(tmp_longiClusCol[ic]->getCluster().size()>=settings.map_intPars["th_Nshowers"]) const_longiClusVCol.push_back(tmp_longiClusCol[ic]);
+    }
+
+    p_HalfClusterV[ic]->setHalfClusters(settings.map_stringPars["OutputLongiClusName"], const_longiClusVCol);
+  }
+
+  tmp_longiClusCol.clear();
+  m_orderedLocalMax.clear();
+  return StatusCode::SUCCESS;
+}
+
+StatusCode ConeClustering2DAlg::ClearAlgorithm(){
+  p_HalfClusterV.clear(); 
+  p_HalfClusterU.clear();
+  m_localMaxVCol.clear();
+  m_localMaxUCol.clear();
+  const_longiClusVCol.clear();
+  const_longiClusUCol.clear();
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode ConeClustering2DAlg::LongiConeLinking(  std::map<int, std::vector<const PandoraPlus::Calo1DCluster*> >& orderedShower,
+                                                   std::vector<PandoraPlus::CaloHalfCluster*>& ClusterCol, 
+                                                   std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& bk_HFclus )
+{
+  if(orderedShower.size()==0) return StatusCode::SUCCESS;
+ 
+  vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_clusCol; m_clusCol.clear();
+  std::map<int, std::vector<const PandoraPlus::Calo1DCluster*>>::iterator iter = orderedShower.begin();
+  //In first layer: initial clusters. All showers in the first layer are regarded as cluster seed.
+  //cluster initial direction = R.
+  std::vector<const PandoraPlus::Calo1DCluster*> ShowersinFirstLayer;  ShowersinFirstLayer.clear();
+  ShowersinFirstLayer = iter->second;
+  for(int i=0;i<ShowersinFirstLayer.size(); i++){
+    if(iter->first < settings.map_floatPars["th_beginLayer"] || iter->first > settings.map_floatPars["th_stopLayer"] ) continue; 
+    std::shared_ptr<PandoraPlus::CaloHalfCluster> m_clus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+    m_clus->addUnit(ShowersinFirstLayer[i]);
+    m_clus->setType(1);
+    m_clusCol.push_back(m_clus);
+  }
+  iter++;
+
+
+  for(iter; iter!=orderedShower.end(); iter++){
+    if(iter->first < settings.map_floatPars["th_beginLayer"] || iter->first > settings.map_floatPars["th_stopLayer"] ) continue; 
+    std::vector<const PandoraPlus::Calo1DCluster*> ShowersinLayer = iter->second;
+//cout<<"  In Layer "<<iter->first<<": hit size "<<ShowersinLayer.size()<<endl;
+
+    for(int ic=0; ic<m_clusCol.size(); ic++){
+      const PandoraPlus::Calo1DCluster* shower_in_clus = m_clusCol[ic].get()->getCluster().back();
+      if(!shower_in_clus) continue;
+      for(int is=0; is<ShowersinLayer.size(); is++){
+        TVector2 relR = GetProjectedRelR(shower_in_clus, ShowersinLayer[is]);  //Return vec: 1->2.
+        TVector2 clusaxis = GetProjectedAxis( m_clusCol[ic].get() );
+//printf("    Cluster axis (%.3f, %.3f), last hit (%.3f, %.3f, %.3f), coming hit (%.3f, %.3f, %.3f), projected relR (%.3f, %.3f) \n",
+//clusaxis.Px(), clusaxis.Py(), shower_in_clus->getPos().x(),  shower_in_clus->getPos().y(),  shower_in_clus->getPos().z(),
+//ShowersinLayer[is]->getPos().x(), ShowersinLayer[is]->getPos().y(), ShowersinLayer[is]->getPos().z(), relR.Px(), relR.Py() );
+
+        if( relR.DeltaPhi(clusaxis)<settings.map_floatPars["th_ConeTheta"] && relR.Mod()<settings.map_floatPars["th_ConeR"] ){
+          m_clusCol[ic].get()->addUnit(ShowersinLayer[is]);
+          ShowersinLayer.erase(ShowersinLayer.begin()+is);
+          is--;
+        }
+      }
+    }//end loop showers in layer.
+
+    if(ShowersinLayer.size()>0){
+      for(int i=0;i<ShowersinLayer.size(); i++){
+        std::shared_ptr<PandoraPlus::CaloHalfCluster> m_clus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+        m_clus->addUnit(ShowersinLayer[i]);
+        m_clus->setType(1);
+        m_clusCol.push_back(m_clus);
+    }}//end new cluster
+  }//end loop layers.
+
+  bk_HFclus.insert(bk_HFclus.end(), m_clusCol.begin(), m_clusCol.end());
+  for(int icl=0; icl<m_clusCol.size(); icl++){
+    m_clusCol[icl]->getLinkedMCPfromUnit();
+    ClusterCol.push_back( m_clusCol[icl].get() );  
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+
+TVector2 ConeClustering2DAlg::GetProjectedAxis( const PandoraPlus::CaloHalfCluster* m_shower ){
+  TVector2 axis(0., 0.);
+  TVector3 m_axis = m_shower->getAxis();
+  if( m_shower->getSlayer()==1 )  axis.Set(m_axis.x(), m_axis.y()); //For V-bars: (x, y) 
+  else                            axis.Set( sqrt(m_axis.x()*m_axis.x() + m_axis.y()*m_axis.y()), m_axis.z()); //For U-bars: (R, z)
+  axis.Unit();
+
+  return axis;
+}
+
+TVector2 ConeClustering2DAlg::GetProjectedRelR( const PandoraPlus::Calo1DCluster* m_shower1, const PandoraPlus::Calo1DCluster* m_shower2 ){
+  TVector2 paxis1, paxis2;
+  if(m_shower1->getSlayer()==1){ //For V-bars
+    paxis1.Set(m_shower1->getPos().x(), m_shower1->getPos().y());
+    paxis2.Set(m_shower2->getPos().x(), m_shower2->getPos().y());
+  }
+  else{
+    float rotAngle = -m_shower1->getTowerID()[0][0]*TMath::TwoPi()/PandoraPlus::CaloUnit::Nmodule;
+    TVector3 raw_pos1 = m_shower1->getPos(); 
+    TVector3 raw_pos2 = m_shower2->getPos(); 
+    raw_pos1.RotateZ(rotAngle); raw_pos2.RotateZ(rotAngle);
+    paxis1.Set(raw_pos1.y(), raw_pos1.z());
+    paxis2.Set(raw_pos2.y(), raw_pos2.z());
+
+    //paxis1.Set( sqrt(m_shower1->getPos().x()*m_shower1->getPos().x()+m_shower1->getPos().y()*m_shower1->getPos().y()), m_shower1->getPos().z());
+    //paxis2.Set( sqrt(m_shower2->getPos().x()*m_shower2->getPos().x()+m_shower2->getPos().y()*m_shower2->getPos().y()), m_shower2->getPos().z());
+  }
+
+  return paxis2 - paxis1;
+}
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/ConeClusteringAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/ConeClusteringAlg.cpp
new file mode 100644
index 00000000..95d6f440
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/ConeClusteringAlg.cpp
@@ -0,0 +1,220 @@
+#ifndef _CONECLUSTERING_ALG_C
+#define _CONECLUSTERING_ALG_C
+
+#include "Algorithm/ConeClusteringAlg.h"
+
+StatusCode ConeClusteringAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  //Option: readin information
+  if(settings.map_stringPars.find("ReadinHit")==settings.map_stringPars.end())        settings.map_stringPars["ReadinHit"] = "EcalTransShower";
+  if(settings.map_stringPars.find("OutputCluster")==settings.map_stringPars.end())    settings.map_stringPars["OutputCluster"] = "EcalBarCluster";
+
+
+  //Set initial values
+  if(settings.map_floatPars.find("th_ConeTheta_l1")==settings.map_floatPars.end())    settings.map_floatPars["th_ConeTheta_l1"] = TMath::Pi()/2.;
+  if(settings.map_floatPars.find("th_ConeR_l1")==settings.map_floatPars.end())        settings.map_floatPars["th_ConeR_l1"] = 70.;
+  if(settings.map_floatPars.find("th_ConeTheta_l2")==settings.map_floatPars.end())    settings.map_floatPars["th_ConeTheta_l2"] = TMath::Pi()/3.;
+  if(settings.map_floatPars.find("th_ConeR_l2")==settings.map_floatPars.end())        settings.map_floatPars["th_ConeR_l2"] = 120.;
+  if(settings.map_floatPars.find("th_ClusChi2")==settings.map_floatPars.end())        settings.map_floatPars["th_ClusChi2"] = 10e17;
+  if(settings.map_floatPars.find("fl_GoodClusLevel")==settings.map_floatPars.end())   settings.map_floatPars["fl_GoodClusLevel"] = 10;
+  //For Cluster merging 
+  if(settings.map_floatPars.find("axis_Angle")==settings.map_floatPars.end())         settings.map_floatPars["axis_Angle"] = TMath::Pi()/6.;
+  if(settings.map_floatPars.find("relP_Angle")==settings.map_floatPars.end())         settings.map_floatPars["relP_Angle"] = TMath::Pi()/5.;
+  if(settings.map_floatPars.find("skipLayer")==settings.map_floatPars.end())          settings.map_floatPars["skipLayer"] = 3;
+  if(settings.map_floatPars.find("fl_MergeGoodClus")==settings.map_floatPars.end())   settings.map_floatPars["fl_MergeGoodClus"] = 1;
+  if(settings.map_floatPars.find("fl_MergeBadClus")==settings.map_floatPars.end())    settings.map_floatPars["fl_MergeBadClus"] = 1;
+  if(settings.map_floatPars.find("fl_MergeEMTail")==settings.map_floatPars.end())     settings.map_floatPars["fl_MergeEMTail"] = 1;
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode ConeClusteringAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode ConeClusteringAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol){
+
+  std::vector<std::shared_ptr<PandoraPlus::CaloHit>> m_hitCol; m_hitCol.clear(); 
+  //Get readin info
+  if(settings.map_stringPars["ReadinHit"] == "EcalTransShower"){
+
+    std::vector<std::shared_ptr<PandoraPlus::Calo2DCluster>>* p_Tshowers = &(m_datacol.map_2DCluster["EcalShowerInLayer"]);
+    if(p_Tshowers->size()==0){ 
+      std::cout<<"Warning: Empty input in ConeClusteringAlg. Please check previous algorithm!"<<endl;  
+      return StatusCode::SUCCESS; 
+    }
+    //Convert transShower to hit
+    for(int is=0; is<p_Tshowers->size(); is++){
+      std::shared_ptr<PandoraPlus::CaloHit> m_hit = std::make_shared<PandoraPlus::CaloHit>();
+      m_hit->setEnergy( p_Tshowers->at(is)->getEnergy() );
+      m_hit->setPosition( p_Tshowers->at(is)->getPos() );
+      m_hit->setLayer( p_Tshowers->at(is)->getDlayer() );
+      //m_hit->setParentShower( p_Tshowers->at(is) );
+      m_hitCol.push_back(m_hit);
+      m_datacol.map_CaloHit["bkHit"].push_back(m_hit);
+    }
+    p_Tshowers = nullptr;
+  }
+  else m_hitCol = m_datacol.map_CaloHit[settings.map_stringPars["ReadinHit"]]; 
+  if(m_hitCol.size()==0) { std::cout<<"Warning: Empty input in ConeClusteringAlg. Please check previous algorithm!"<<endl; return StatusCode::SUCCESS; }
+
+cout<<"  ConeClusteringAlg: input hit size "<<m_hitCol.size()<<endl;
+/*
+cout<<"ConeClustering: Print Input Hits"<<endl;
+for(int i=0; i<m_hitCol.size(); i++)
+  printf("  Hit #%d: pos/E (%.2f, %.2f, %.2f, %.3f), Layer %d. \n", i,  
+        m_hitCol[i]->getPosition().x(), m_hitCol[i]->getPosition().y(), m_hitCol[i]->getPosition().z(), m_hitCol[i]->getEnergy(), m_hitCol[i]->getLayer() );
+cout<<"ConeClustering: End Print"<<endl;
+*/
+
+  //Store the ordered hits.
+  std::map<int, std::vector<const PandoraPlus::CaloHit*> > m_orderedHit;  m_orderedHit.clear(); //map<layer, showers>
+  for(int ih=0;ih<m_hitCol.size();ih++)
+    m_orderedHit[m_hitCol[ih]->getLayer()].push_back(m_hitCol[ih].get());
+
+
+  //Longitudinal linking
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_clusterCol;  m_clusterCol.clear();
+  LongiConeLinking( m_orderedHit, m_clusterCol );
+  for(int icl=0; icl<m_clusterCol.size(); icl++) m_clusterCol[icl]->getLinkedMCPfromHit();
+  m_datacol.map_CaloCluster["bk3DCluster"].insert(  m_datacol.map_CaloCluster["bk3DCluster"].end(), m_clusterCol.begin(), m_clusterCol.end() );
+
+cout<<"  Cluster size: "<<m_clusterCol.size()<<endl;
+
+
+  //Check cluster quality.
+/*
+  std::vector<PandoraPlus::Calo3DCluster*>  goodClus;
+  std::vector<PandoraPlus::Calo3DCluster*>  badClus;
+  for(int icl=0; icl<m_clusterCol.size(); icl++){
+    if( m_clusterCol[icl]->getCaloHits().size() >= settings.map_floatPars["fl_GoodClusLevel"]) goodClus.push_back(m_clusterCol[icl]);
+    else badClus.push_back(m_clusterCol[icl]);
+  }
+
+cout<<"  Good cluster: "<<goodClus.size()<<endl;
+cout<<"  Bad cluster: "<<badClus.size()<<endl;
+
+  //Merge clusters
+  //MergeGoodClusters( goodClus );
+  //for(int icl=0;icl<badClus.size();icl++) MergeBadToGoodCluster(goodClus, badClus[icl] ); 
+  //MergeGoodClusters( goodClus );
+  //MergeEMTail( goodClus );
+
+  //m_datacol.GoodClus3DCol.insert(m_datacol.GoodClus3DCol.end(), goodClus.begin(), goodClus.end() );
+  //m_datacol.BadClus3DCol.insert( m_datacol.BadClus3DCol.end(),  badClus.begin(), badClus.end() );
+  //m_datacol.Clus3DCol.insert( m_datacol.Clus3DCol.end(), m_clusterCol.begin(), m_clusterCol.end() );
+*/
+  m_datacol.map_CaloCluster[settings.map_stringPars["OutputCluster"]] = m_clusterCol;
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode ConeClusteringAlg::ClearAlgorithm(){
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode ConeClusteringAlg::LongiConeLinking(  const std::map<int, std::vector<const PandoraPlus::CaloHit*> >& orderedHit, 
+                                                 std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& ClusterCol)
+{
+
+  if(orderedHit.size()==0) return StatusCode::SUCCESS;
+
+  auto iter = orderedHit.begin();
+  //In first layer: initial clusters. All showers in the first layer are regarded as cluster seed.
+  //cluster initial direction = R.
+  std::vector<const PandoraPlus::CaloHit*> HitsinFirstLayer = iter->second;
+  for(int i=0;i<HitsinFirstLayer.size(); i++){
+    std::shared_ptr<PandoraPlus::Calo3DCluster> m_clus = std::make_shared<PandoraPlus::Calo3DCluster>();
+    m_clus->addHit(HitsinFirstLayer[i]);
+    ClusterCol.push_back(m_clus);
+  }
+  iter++;
+
+//cout<<"    LongiConeLinking: Cluster seed in first layer: "<<ClusterCol.size()<<endl;
+
+  //Use different cone angle for 1->2/2->3 and 3->n case
+  //Loop later layers
+  for(iter; iter!=orderedHit.end(); iter++){
+    std::vector<const PandoraPlus::CaloHit*> HitsinLayer = iter->second;
+//cout<<"    In Layer: "<<iter->first<<"  Hit size: "<<HitsinLayer.size()<<endl;
+
+    for(int is=0; is<HitsinLayer.size(); is++){
+      const PandoraPlus::CaloHit* m_hit = HitsinLayer[is];
+//printf("     New Hit: (%.3f, %.3f, %.3f), Layer %d \n", m_hit->getPosition().x(), m_hit->getPosition().y(), m_hit->getPosition().z(),  m_hit->getLayer() );
+//cout<<"     Cluster size: "<<ClusterCol.size()<<endl;
+
+      for(int ic=0; ic<ClusterCol.size(); ic++ ){
+        int m_Nhits = ClusterCol[ic]->getCaloHits().size();
+        const PandoraPlus::CaloHit* hit_in_clus = ClusterCol[ic]->getCaloHits().back();
+        TVector3 relR_vec = m_hit->getPosition() - hit_in_clus->getPosition();
+//printf("      New hit: (%.2f, %.2f, %.2f), Cluster last: (%.2f, %.2f, %.2f, %d), Cluster axis: (%.2f, %.2f, %.2f) \n",
+//    m_hit->getPosition().x(), m_hit->getPosition().y(),m_hit->getPosition().z(),
+//    hit_in_clus->getPosition().x(), hit_in_clus->getPosition().y(), hit_in_clus->getPosition().z(), hit_in_clus->getLayer(), 
+//    ClusterCol[ic]->getAxis().x(), ClusterCol[ic]->getAxis().y(), ClusterCol[ic]->getAxis().z() );
+
+        if(  (m_Nhits<3 && m_Nhits>0 && relR_vec.Angle(ClusterCol[ic]->getAxis())< settings.map_floatPars["th_ConeTheta_l1"] && relR_vec.Mag()< settings.map_floatPars["th_ConeR_l1"]) ||
+             (m_Nhits>=3             && relR_vec.Angle(ClusterCol[ic]->getAxis())< settings.map_floatPars["th_ConeTheta_l2"] && relR_vec.Mag()< settings.map_floatPars["th_ConeR_l2"])  ){
+
+          ClusterCol[ic]->addHit(m_hit);
+          HitsinLayer.erase(HitsinLayer.begin()+is);
+          is--;
+          break;
+        }
+      }
+    }//end loop showers in layer.
+    if(HitsinLayer.size()>0){
+      for(int i=0;i<HitsinLayer.size(); i++){
+        std::shared_ptr<PandoraPlus::Calo3DCluster> m_clus = std::make_shared<PandoraPlus::Calo3DCluster>();
+        m_clus->addHit(HitsinLayer[i]);
+        ClusterCol.push_back(m_clus);
+    }}//end new cluster
+  }//end loop layers.
+
+
+  return StatusCode::SUCCESS;
+}
+
+/*
+StatusCode ConeClusteringAlg::MergeGoodClusters( std::vector<PandoraPlus::Calo3DCluster*>& m_clusCol ){
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode ConeClusteringAlg::MergeBadToGoodCluster( std::vector<PandoraPlus::Calo3DCluster*>& m_goodClusCol, PandoraPlus::Calo3DCluster* m_badClus ){
+  PandoraPlus::Calo3DCluster* m_clus = GetClosestGoodCluster( m_goodClusCol, m_badClus );
+  if(!m_clus) return StatusCode::FAILURE;
+
+  auto iter = find( m_goodClusCol.begin(), m_goodClusCol.end(), m_clus );
+  if(iter==m_goodClusCol.end()) return StatusCode::FAILURE;
+  else m_clus->mergeCluster( m_badClus );
+    
+  return StatusCode::SUCCESS;
+}
+
+
+
+PandoraPlus::Calo3DCluster* ConeClusteringAlg::GetClosestGoodCluster( std::vector< PandoraPlus::Calo3DCluster* >& m_goodClusCol, PandoraPlus::Calo3DCluster* m_badClus ){
+
+  TVector3 m_clusCent = m_badClus->getShowerCenter();
+  PandoraPlus::Calo3DCluster* m_clus = nullptr;
+  double minTheta=999;
+  for(int i=0;i<m_goodClusCol.size();i++){
+     TVector3 vec_goodClus = m_goodClusCol[i]->getShowerCenter();
+     TVector3 vec_goodAxis = m_goodClusCol[i]->getAxis();
+     double theta = vec_goodAxis.Cross(m_clusCent-vec_goodClus).Mag();
+
+     if(theta<minTheta){
+        minTheta=theta;
+        m_clus = m_goodClusCol[i];
+     }
+  }
+
+  return m_clus;
+}
+*/
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/EnergySplittingAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/EnergySplittingAlg.cpp
new file mode 100644
index 00000000..58f00c70
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/EnergySplittingAlg.cpp
@@ -0,0 +1,1289 @@
+#ifndef _ENERGYSPLITTING_ALG_C
+#define _ENERGYSPLITTING_ALG_C
+
+#include "Algorithm/EnergySplittingAlg.h"
+
+StatusCode EnergySplittingAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  if(settings.map_floatPars.find("th_split")==settings.map_floatPars.end()) settings.map_floatPars["th_split"] = -1;
+  //if(settings.map_floatPars.find("Eth_Seed")==settings.map_floatPars.end()) settings.map_floatPars["Eth_Seed"] = 0.005;
+  //if(settings.map_floatPars.find("Eth_ShowerAbs")==settings.map_floatPars.end()) settings.map_floatPars["Eth_ShowerAbs"] = 0.005;
+  if(settings.map_floatPars.find("Eth_unit")==settings.map_floatPars.end())  settings.map_floatPars["Eth_unit"] = 0.001;
+  if(settings.map_floatPars.find("Eth_HFClus")==settings.map_floatPars.end())        settings.map_floatPars["Eth_HFClus"] = 0.05;
+  if(settings.map_intPars.find("th_Nhit")==settings.map_intPars.end()) settings.map_intPars["th_Nhit"] = 2;
+  if(settings.map_boolPars.find("CompactHFCluster")==settings.map_boolPars.end()) settings.map_boolPars["CompactHFCluster"] = true;
+  if(settings.map_stringPars.find("ReadinAxisName")==settings.map_stringPars.end()) settings.map_stringPars["ReadinAxisName"] = "MergedAxis";
+  if(settings.map_stringPars.find("OutputClusName")==settings.map_stringPars.end()) settings.map_stringPars["OutputClusName"] = "ESHalfCluster";
+  if(settings.map_stringPars.find("OutputTowerName")==settings.map_stringPars.end()) settings.map_stringPars["OutputTowerName"] = "ESTower";
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode EnergySplittingAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  m_axisUCol.clear();
+  m_axisVCol.clear();
+  m_newClusUCol.clear();
+  m_newClusVCol.clear();
+  m_1dShowerUCol.clear();
+  m_1dShowerVCol.clear();
+  m_towerCol.clear(); 
+  m_bkCol.Clear();
+
+  p_HalfClusterU.clear();
+  p_HalfClusterV.clear(); 
+
+  for(int ih=0; ih<m_datacol.map_HalfCluster["HalfClusterColU"].size(); ih++)
+    p_HalfClusterU.push_back( m_datacol.map_HalfCluster["HalfClusterColU"][ih].get() );
+  for(int ih=0; ih<m_datacol.map_HalfCluster["HalfClusterColV"].size(); ih++)
+    p_HalfClusterV.push_back( m_datacol.map_HalfCluster["HalfClusterColV"][ih].get() );
+  for(int ih=0; ih<m_datacol.map_HalfCluster["emptyHalfClusterU"].size(); ih++){
+    p_emptyHalfClusterU.push_back( m_datacol.map_HalfCluster["emptyHalfClusterU"][ih].get() );
+  }
+  for(int ih=0; ih<m_datacol.map_HalfCluster["emptyHalfClusterV"].size(); ih++)
+    p_emptyHalfClusterV.push_back( m_datacol.map_HalfCluster["emptyHalfClusterV"][ih].get() );
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode EnergySplittingAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+
+  //Input: HalfCluster (with 1D clusters and HoughAxis)
+  //Output: Create Towers for the matching. 
+
+  if( p_HalfClusterU.size() + p_HalfClusterV.size()==0 ) {
+    if( p_emptyHalfClusterU.size() + p_emptyHalfClusterV.size() == 0 ){
+      std::cout<<"EnergySplittingAlg: No HalfCluster input"<<std::endl;
+      return StatusCode::SUCCESS;
+    }
+    m_towerCol.clear();
+    std::vector<PandoraPlus::CaloHalfCluster*> tmp_newClusUCol, tmp_newClusVCol;
+    HalfClusterToTowers( tmp_newClusUCol, tmp_newClusVCol, p_emptyHalfClusterU, p_emptyHalfClusterV, m_towerCol );
+
+    m_datacol.map_CaloCluster[settings.map_stringPars["OutputTowerName"]] = m_towerCol;
+
+    m_datacol.map_BarCol["bkBar"].insert( m_datacol.map_BarCol["bkBar"].end(), m_bkCol.map_BarCol["bkBar"].begin(), m_bkCol.map_BarCol["bkBar"].end() );
+    m_datacol.map_1DCluster["bk1DCluster"].insert( m_datacol.map_1DCluster["bk1DCluster"].end(), m_bkCol.map_1DCluster["bk1DCluster"].begin(), m_bkCol.map_1DCluster["bk1DCluster"].end() );
+    m_datacol.map_2DCluster["bk2DCluster"].insert( m_datacol.map_2DCluster["bk2DCluster"].end(), m_bkCol.map_2DCluster["bk2DCluster"].begin(), m_bkCol.map_2DCluster["bk2DCluster"].end() );
+    m_datacol.map_HalfCluster["bkHalfCluster"].insert( m_datacol.map_HalfCluster["bkHalfCluster"].end(), m_bkCol.map_HalfCluster["bkHalfCluster"].begin(), m_bkCol.map_HalfCluster["bkHalfCluster"].end() );
+
+    return StatusCode::SUCCESS;
+  }
+
+/*
+float tmp_totE_U = 0;
+for(int ih=0; ih<p_HalfClusterU.size(); ih++) tmp_totE_U += p_HalfClusterU[ih]->getEnergy();
+float tmp_totE_V = 0;
+for(int ih=0; ih<p_HalfClusterV.size(); ih++) tmp_totE_V += p_HalfClusterV[ih]->getEnergy();
+cout<<"Total energy in HFCluster: "<<tmp_totE_U<<'\t'<<tmp_totE_V<<endl;
+cout<<"Print readin axesU"<<endl;
+for(int ih=0; ih<p_HalfClusterU.size(); ih++){
+  cout<<"  HalfCluster #"<<ih<<": cluster En "<<p_HalfClusterU[ih]->getEnergy()<<endl;
+  auto tmp_axisUCol = p_HalfClusterU[ih]->getHalfClusterCol(settings.map_stringPars["ReadinAxisName"]);
+  for(int icl=0; icl<tmp_axisUCol.size(); icl++){
+    cout<<"  Axis #"<<icl<<": cluster En "<<tmp_axisUCol[icl]->getEnergy()<<", type "<<tmp_axisUCol[icl]->getType()<<endl;
+    for(auto ish : tmp_axisUCol[icl]->getCluster()){
+      printf("    Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Address %p \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish );
+    }
+  }
+}
+cout<<"Print readin axesV"<<endl;
+for(int ih=0; ih<p_HalfClusterV.size(); ih++){
+  cout<<"  HalfCluster #"<<ih<<": cluster En "<<p_HalfClusterV[ih]->getEnergy()<<endl;
+  auto tmp_axisVCol = p_HalfClusterV[ih]->getHalfClusterCol(settings.map_stringPars["ReadinAxisName"]);
+  for(int icl=0; icl<tmp_axisVCol.size(); icl++){
+    cout<<"  Axis #"<<icl<<": cluster En "<<tmp_axisVCol[icl]->getEnergy()<<", type "<<tmp_axisVCol[icl]->getType()<<endl;
+    for(auto ish : tmp_axisVCol[icl]->getCluster()){
+      printf("    Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Address %p \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish );
+    }
+  }
+}
+
+
+cout<<"Readin empty half cluster size "<<p_emptyHalfClusterU.size()<<", "<<p_emptyHalfClusterV.size()<<endl;
+tmp_totE_U = 0;
+for(int ih=0; ih<p_emptyHalfClusterU.size(); ih++) tmp_totE_U += p_emptyHalfClusterU[ih]->getEnergy();
+tmp_totE_V = 0;
+for(int ih=0; ih<p_emptyHalfClusterV.size(); ih++) tmp_totE_V += p_emptyHalfClusterV[ih]->getEnergy();
+cout<<"Empty half cluster energy "<<tmp_totE_U<<", "<<tmp_totE_V<<endl;
+*/
+
+  //Start in U direction. 
+  m_newClusUCol.clear();
+  for(int ih=0; ih<p_HalfClusterU.size(); ih++){
+
+    //Get all axis in U cluster.
+    m_axisUCol.clear();
+    if( settings.map_stringPars["ReadinAxisName"] == "AllAxis" ) m_axisUCol = p_HalfClusterU[ih]->getAllHalfClusterCol(); 
+    else m_axisUCol = p_HalfClusterU[ih]->getHalfClusterCol(settings.map_stringPars["ReadinAxisName"]);
+   
+    //  Loop for 1DClusters.
+    m_1dShowerUCol.clear();
+    std::vector<const PandoraPlus::Calo1DCluster*> m_1dclusCol = p_HalfClusterU[ih]->getCluster();
+    for(int icl=0; icl<m_1dclusCol.size(); icl++){
+      std::vector<const PandoraPlus::CaloUnit*> m_bars = m_1dclusCol[icl]->getBars();
+
+      //Find the seed with axis in 1DCluster: 
+      for(auto iaxis: m_axisUCol){
+        for(auto iseed: iaxis->getCluster() ){
+
+          if(iseed->getBars().size()!=1) {
+            std::cout<<"WARNING: Axis has more than one bars! Check!"<<'\t'<<iseed->getBars().size()<<std::endl;
+            continue;
+          }
+
+          bool findSeed = false;
+          if(find( m_bars.begin(), m_bars.end(), iseed->getBars()[0]) != m_bars.end()) findSeed = true;
+          for(int ib=0; ib<m_bars.size(); ib++){
+            if(findSeed) break;
+            if(m_bars[ib]->getcellID()==iseed->getBars()[0]->getcellID()) {findSeed=true; break; }
+          }
+          if(findSeed) const_cast<PandoraPlus::Calo1DCluster*>(m_1dclusCol[icl])->addSeed( iseed->getBars()[0] );
+
+        }
+      }
+      //if(m_1dclusCol[icl]->getNseeds()==0) const_cast<PandoraPlus::Calo1DCluster*>(m_1dclusCol[icl])->setSeed();   
+
+      //Split cluster to showers
+      std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> tmp_showers; tmp_showers.clear();
+      ClusterSplitting( m_1dclusCol[icl], tmp_showers );
+      //if(tmp_showers.size()==0) continue;
+      m_1dShowerUCol.insert( m_1dShowerUCol.end(), tmp_showers.begin(), tmp_showers.end() );
+      tmp_showers.clear();
+    }
+
+//double tmp_En = 0.;
+//for(int i1d=0; i1d<m_1dShowerUCol.size(); i1d++) tmp_En += m_1dShowerUCol[i1d]->getEnergy();
+//cout<<"  HalfClusterU #"<<ih<<": total En after energy splitting "<<tmp_En<<endl;
+
+    //Clean showers without seed.
+    for(int ic=0; ic<m_1dShowerUCol.size(); ic++){
+      if(m_1dShowerUCol[ic]->getNseeds()==0){
+        if(MergeToClosestCluster( m_1dShowerUCol[ic].get(), m_1dShowerUCol )==StatusCode::SUCCESS){
+          m_1dShowerUCol.erase(m_1dShowerUCol.begin()+ic);
+          ic--;
+        }
+        else
+          m_1dShowerUCol[ic]->setSeed();
+      }
+    }
+    for(int i1d=0; i1d<m_1dShowerUCol.size(); i1d++) m_1dShowerUCol[i1d]->getLinkedMCPfromUnit();
+    m_datacol.map_1DCluster["bk1DCluster"].insert( m_datacol.map_1DCluster["bk1DCluster"].end(), m_1dShowerUCol.begin(), m_1dShowerUCol.end() );
+
+//tmp_En = 0.;
+//for(int i1d=0; i1d<m_1dShowerUCol.size(); i1d++) tmp_En += m_1dShowerUCol[i1d]->getEnergy();
+//cout<<"  HalfClusterU #"<<ih<<": total En after shower cleaning "<<tmp_En<<endl;
+
+    //  Longitudinal linking: update clusters' energy.
+    std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> tmp_newClus; tmp_newClus.clear();
+    LongitudinalLinking(m_1dShowerUCol, m_axisUCol, tmp_newClus);
+    m_newClusUCol.insert(m_newClusUCol.end(), tmp_newClus.begin(), tmp_newClus.end());
+
+//double tmp_En = 0.;
+//for(int acl=0; acl<tmp_newClus.size(); acl++) tmp_En += tmp_newClus[acl]->getEnergy();
+//cout<<"  HalfClusterU #"<<ih<<": total En after LongiLinking "<<tmp_En<<endl;
+    tmp_newClus.clear();
+  }
+
+  //Start in V direction
+  m_newClusVCol.clear();
+  for(int ih=0; ih<p_HalfClusterV.size(); ih++){
+    m_axisVCol.clear();
+//cout<<"Readin axis in V: "<<settings.map_stringPars["ReadinAxisName"]<<endl;
+    if( settings.map_stringPars["ReadinAxisName"] == "AllAxis" ) m_axisVCol = p_HalfClusterV[ih]->getAllHalfClusterCol(); 
+    else m_axisVCol = p_HalfClusterV[ih]->getHalfClusterCol(settings.map_stringPars["ReadinAxisName"]);
+
+    m_1dShowerVCol.clear();
+    std::vector<const PandoraPlus::Calo1DCluster*> m_1dclusCol = p_HalfClusterV[ih]->getCluster();
+
+//cout<<"1D Cluster size: "<<m_1dclusCol.size()<<", axis size: "<<m_axisVCol.size()<<endl;
+    for(int icl=0; icl<m_1dclusCol.size(); icl++){
+      std::vector<const PandoraPlus::CaloUnit*> m_bars = m_1dclusCol[icl]->getBars();
+//cout<<"  In 1DCluster #"<<icl<<": layer = "<<m_1dclusCol[icl]->getDlayer();   
+    
+      //Find the seed with axis in 1DCluster:
+      for(auto iaxis: m_axisVCol){
+        for(auto iseed: iaxis->getCluster() ){
+          if(iseed->getBars().size()!=1) {
+            std::cout<<"WARNING: Axis has more than one bars! Check!"<<'\t'<<iseed->getBars().size()<<std::endl;
+            continue;
+          }
+          if(find( m_bars.begin(), m_bars.end(), iseed->getBars()[0]) != m_bars.end())
+            const_cast<PandoraPlus::Calo1DCluster*>(m_1dclusCol[icl])->addSeed( iseed->getBars()[0] );
+        }
+      }
+//cout<<", matched seed size "<<m_1dclusCol[icl]->getNseeds()<<endl;
+      //if(m_1dclusCol[icl]->getNseeds()==0) const_cast<PandoraPlus::Calo1DCluster*>(m_1dclusCol[icl])->setSeed();   
+
+      //Split cluster to showers
+      std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> tmp_showers; tmp_showers.clear();
+      ClusterSplitting( m_1dclusCol[icl], tmp_showers );
+//cout<<"  In 1DCluster #"<<icl<<": splitted into "<<tmp_showers.size ()<<" showers: ";
+//for(int is=0; is<tmp_showers.size(); is++){
+//  printf(" (%.3f, %.3f, %.3f, %.3f) \t", tmp_showers[is].get()->getPos().x(), tmp_showers[is].get()->getPos().y(), tmp_showers[is].get()->getPos().z(), tmp_showers[is].get()->getEnergy());
+//}
+//cout<<endl;
+//cout<<endl;
+      //if(tmp_showers.size()==0) continue;
+      m_1dShowerVCol.insert( m_1dShowerVCol.end(), tmp_showers.begin(), tmp_showers.end() );
+      tmp_showers.clear();
+    }
+
+//double tmp_En = 0.;
+//for(int i1d=0; i1d<m_1dShowerVCol.size(); i1d++) tmp_En += m_1dShowerVCol[i1d]->getEnergy();
+//cout<<"  HalfClusterV #"<<ih<<": total En after energy splitting "<<tmp_En<<endl;
+
+    //Clean showers without seed.
+    for(int ic=0; ic<m_1dShowerVCol.size(); ic++){
+      if(m_1dShowerVCol[ic]->getNseeds()==0){
+        if(MergeToClosestCluster( m_1dShowerVCol[ic].get(), m_1dShowerVCol )==StatusCode::SUCCESS){
+          m_1dShowerVCol.erase(m_1dShowerVCol.begin()+ic);
+          ic--;
+        }
+        else
+          m_1dShowerVCol[ic]->setSeed();
+      }
+    }
+
+//cout<<"After splitting: print all 1D showers"<<endl;
+//for(int is=0; is<m_1dShowerVCol.size(); is++){
+//printf("  Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Nbars %d, NSeed %d, address %p \n", m_1dShowerVCol[is].get()->getDlayer(), m_1dShowerVCol[is].get()->getPos().x(), m_1dShowerVCol[is].get()->getPos().y(), m_1dShowerVCol[is].get()->getPos().z(), m_1dShowerVCol[is].get()->getEnergy(), m_1dShowerVCol[is].get()->getBars().size(), m_1dShowerVCol[is].get()->getNseeds(), m_1dShowerVCol[is].get() );
+//}
+//cout<<endl;
+//tmp_En = 0.;
+//for(int i1d=0; i1d<m_1dShowerVCol.size(); i1d++) tmp_En += m_1dShowerVCol[i1d]->getEnergy();
+//cout<<"  HalfClusterV #"<<ih<<": total En after shower cleaning "<<tmp_En<<endl;
+
+    for(int i1d=0; i1d<m_1dShowerVCol.size(); i1d++) m_1dShowerVCol[i1d]->getLinkedMCPfromUnit();
+    m_datacol.map_1DCluster["bk1DCluster"].insert( m_datacol.map_1DCluster["bk1DCluster"].end(), m_1dShowerVCol.begin(), m_1dShowerVCol.end() );
+
+
+    //  Longitudinal linking: update clusters' energy.
+    std::vector<std::shared_ptr<CaloHalfCluster>> tmp_newClus; tmp_newClus.clear();
+    LongitudinalLinking(m_1dShowerVCol, m_axisVCol, tmp_newClus);
+    m_newClusVCol.insert(m_newClusVCol.end(), tmp_newClus.begin(), tmp_newClus.end());
+//double tmp_En = 0.;
+//for(int acl=0; acl<tmp_newClus.size(); acl++) tmp_En += tmp_newClus[acl]->getEnergy();
+//cout<<"  HalfClusterV #"<<ih<<": total En after LongiLinking "<<tmp_En<<endl;
+    tmp_newClus.clear();
+  }
+
+
+  //Assign MCtruth info to the HalfCluster. 
+  for(int ic=0; ic<m_newClusUCol.size(); ic++) m_newClusUCol[ic].get()->getLinkedMCPfromUnit();
+  for(int ic=0; ic<m_newClusVCol.size(); ic++) m_newClusVCol[ic].get()->getLinkedMCPfromUnit();
+
+  m_datacol.map_HalfCluster[settings.map_stringPars["OutputClusName"]+"U"] = m_newClusUCol; 
+  m_datacol.map_HalfCluster[settings.map_stringPars["OutputClusName"]+"V"] = m_newClusVCol; 
+
+/*
+cout<<"  After splitting: HalfCluster U size "<<m_newClusUCol.size()<<", print check"<<endl;
+for(int icl=0; icl<m_newClusUCol.size(); icl++){
+  cout<<"      In HFClusU #"<<icl<<": shower size = "<<m_newClusUCol[icl]->getCluster().size()<<endl;
+  for(auto ish : m_newClusUCol[icl]->getCluster()){
+    printf("          Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Nbars %d, NSeed %d, MC map size %d \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish->getBars().size(),  ish->getNseeds(), ish->getLinkedMCP().size() );
+    //printf(", cover tower size: %d: ", ish->getTowerID().size());
+    //for(int atw=0; atw<ish->getTowerID().size(); atw++) printf("[%d, %d, %d], ", ish->getTowerID()[atw][0], ish->getTowerID()[atw][1], ish->getTowerID()[atw][2] );
+    //cout<<endl;
+  }
+}
+cout<<endl;
+
+cout<<"  After splitting: HalfCluster V size "<<m_newClusVCol.size()<<", print check"<<endl;
+for(int icl=0; icl<m_newClusVCol.size(); icl++){
+  cout<<"      In HFClusV #"<<icl<<": shower size = "<<m_newClusVCol[icl]->getCluster().size()<<endl;
+  for(auto ish : m_newClusVCol[icl]->getCluster()){
+    printf("          Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Nbars %d, NSeed %d, MC map size %d \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish->getBars().size(),  ish->getNseeds(), ish->getLinkedMCP().size() );
+    //printf(", cover tower size: %d: ", ish->getTowerID().size());
+    //for(int atw=0; atw<ish->getTowerID().size(); atw++) printf("[%d, %d, %d], ", ish->getTowerID()[atw][0], ish->getTowerID()[atw][1], ish->getTowerID()[atw][2] );
+    //cout<<endl;
+  }
+}
+
+
+float totE_U = 0.;
+float totE_V = 0.;
+for(auto iter : m_newClusUCol) totE_U += iter->getEnergy();
+for(auto iter : m_newClusVCol) totE_V += iter->getEnergy();
+printf("    Before split to tower: HalfCluster size: (%d, %d), energy (%.3f, %.3f) \n", m_newClusUCol.size(), m_newClusVCol.size(), totE_U, totE_V );
+cout<<"    Loop print HalfClusterU: "<<endl;
+for(int icl=0; icl<m_newClusUCol.size(); icl++){
+  cout<<"      In HFClusU #"<<icl<<": shower size = "<<m_newClusUCol[icl]->getCluster().size()<<", En = "<<m_newClusUCol[icl]->getEnergy()<<", type "<<m_newClusUCol[icl]->getType();
+  printf(", Position (%.3f, %.3f, %.3f), address %p ",m_newClusUCol[icl]->getPos().x(), m_newClusUCol[icl]->getPos().y(), m_newClusUCol[icl]->getPos().z(), m_newClusUCol[icl]);
+  printf(", cousin size %d, address: ", m_newClusUCol[icl]->getHalfClusterCol("CousinCluster").size());
+  for(int ics=0; ics<m_newClusUCol[icl]->getHalfClusterCol("CousinCluster").size(); ics++) printf("%p, ", m_newClusUCol[icl]->getHalfClusterCol("CousinCluster")[ics]);
+  printf(", track size %d, address: ", m_newClusUCol[icl]->getAssociatedTracks().size());
+  for(int itrk=0; itrk<m_newClusUCol[icl]->getAssociatedTracks().size(); itrk++) printf("%p, ", m_newClusUCol[icl]->getAssociatedTracks()[itrk]);
+  cout<<endl;
+  for(auto ish : m_newClusUCol[icl]->getCluster()){
+    printf("          Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Nbars %d, NSeed %d, seedID [%d, %d, %d, %d], Address %p \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish->getBars().size(),  ish->getNseeds(), ish->getSeeds()[0]->getModule(), ish->getSeeds()[0]->getPart(), ish->getSeeds()[0]->getStave(), ish->getSeeds()[0]->getBar(), ish );
+  }
+}
+cout<<endl;
+
+cout<<"    Loop print HalfClusterV: "<<endl;
+for(int icl=0; icl<m_newClusVCol.size(); icl++){
+  cout<<"      In HFClusV #"<<icl<<": shower size = "<<m_newClusVCol[icl]->getCluster().size()<<", En = "<<m_newClusVCol[icl]->getEnergy()<<", type "<<m_newClusVCol[icl]->getType();
+  printf(", Position (%.3f, %.3f, %.3f), address %p ",m_newClusVCol[icl]->getPos().x(), m_newClusVCol[icl]->getPos().y(), m_newClusVCol[icl]->getPos().z(), m_newClusVCol[icl]);
+  printf(", cousin size %d, address: ", m_newClusVCol[icl]->getHalfClusterCol("CousinCluster").size());
+  for(int ics=0; ics<m_newClusVCol[icl]->getHalfClusterCol("CousinCluster").size(); ics++) printf("%p, ", m_newClusVCol[icl]->getHalfClusterCol("CousinCluster")[ics]);
+  printf(", track size %d, address: ", m_newClusVCol[icl]->getAssociatedTracks().size());
+  for(int itrk=0; itrk<m_newClusVCol[icl]->getAssociatedTracks().size(); itrk++) printf("%p, ", m_newClusVCol[icl]->getAssociatedTracks()[itrk]);
+  cout<<endl;
+  for(auto ish : m_newClusVCol[icl]->getCluster()){
+    printf("          Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Nbars %d, NSeed %d, seedID [%d, %d, %d, %d], Address %p \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish->getBars().size(), ish->getNseeds(), ish->getSeeds()[0]->getModule(), ish->getSeeds()[0]->getPart(), ish->getSeeds()[0]->getStave(), ish->getSeeds()[0]->getBar(), ish );
+  }
+}
+cout<<endl;
+*/
+
+
+  //Make tower 
+  m_towerCol.clear(); 
+  std::vector<PandoraPlus::CaloHalfCluster*> tmp_newClusUCol, tmp_newClusVCol;
+  tmp_newClusUCol.clear(); tmp_newClusVCol.clear();
+  for(int icl=0; icl<m_newClusUCol.size(); icl++) tmp_newClusUCol.push_back( m_newClusUCol[icl].get() );
+  for(int icl=0; icl<m_newClusVCol.size(); icl++) tmp_newClusVCol.push_back( m_newClusVCol[icl].get() );
+  HalfClusterToTowers( tmp_newClusUCol, tmp_newClusVCol, p_emptyHalfClusterU, p_emptyHalfClusterV, m_towerCol );
+
+  m_datacol.map_CaloCluster[settings.map_stringPars["OutputTowerName"]] = m_towerCol;
+
+  m_datacol.map_BarCol["bkBar"].insert( m_datacol.map_BarCol["bkBar"].end(), m_bkCol.map_BarCol["bkBar"].begin(), m_bkCol.map_BarCol["bkBar"].end() );
+  m_datacol.map_1DCluster["bk1DCluster"].insert( m_datacol.map_1DCluster["bk1DCluster"].end(), m_bkCol.map_1DCluster["bk1DCluster"].begin(), m_bkCol.map_1DCluster["bk1DCluster"].end() );
+  m_datacol.map_2DCluster["bk2DCluster"].insert( m_datacol.map_2DCluster["bk2DCluster"].end(), m_bkCol.map_2DCluster["bk2DCluster"].begin(), m_bkCol.map_2DCluster["bk2DCluster"].end() );
+  m_datacol.map_HalfCluster["bkHalfCluster"].insert( m_datacol.map_HalfCluster["bkHalfCluster"].end(), m_bkCol.map_HalfCluster["bkHalfCluster"].begin(), m_bkCol.map_HalfCluster["bkHalfCluster"].end() );
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode EnergySplittingAlg::ClearAlgorithm(){
+  p_HalfClusterU.clear();
+  p_HalfClusterV.clear();
+  p_emptyHalfClusterU.clear();
+  p_emptyHalfClusterV.clear();
+
+  m_axisUCol.clear();
+  m_axisVCol.clear();
+
+  m_newClusUCol.clear();
+  m_newClusVCol.clear();  
+  m_1dShowerUCol.clear();
+  m_1dShowerVCol.clear();
+  m_towerCol.clear(); 
+  m_bkCol.Clear();
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode EnergySplittingAlg::LongitudinalLinking( std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>>& m_showers,
+                                                    std::vector<const PandoraPlus::CaloHalfCluster*>& m_oldClusCol,
+                                                    std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_newClusCol )
+
+{
+  if(m_showers.size()==0 || m_oldClusCol.size()==0) return StatusCode::SUCCESS;
+  m_newClusCol.clear();
+
+//double tmp_totE = 0;
+//for(int i=0; i<m_showers.size(); i++) tmp_totE += m_showers[i]->getEnergy();
+//cout<<"  In LongitudinalLinking: input 1DShower total energy : "<<tmp_totE<<". old axis size "<<m_oldClusCol.size()<<endl;
+//cout<<"  Print all 1DShower (address)"<<endl;
+//for(int i=0; i<m_showers.size(); i++){
+//  printf("    Shower #%d, pos+E [%.3f, %.3f, %.3f, %.3f], address %p \n", i, m_showers[i]->getPos().x(), m_showers[i]->getPos().y(), m_showers[i]->getPos().z(), m_showers[i]->getEnergy(), m_showers[i] );
+//}
+
+  //Update old Hough clusters
+  for(int ic=0; ic<m_oldClusCol.size(); ic++){
+    std::shared_ptr<PandoraPlus::CaloHalfCluster> m_newClus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+
+    for(int is=0; is<m_oldClusCol[ic]->getCluster().size(); is++){
+      const PandoraPlus::Calo1DCluster* m_shower = m_oldClusCol[ic]->getCluster()[is];
+
+      const PandoraPlus::Calo1DCluster* m_selshower = NULL;
+      bool fl_foundshower = false; 
+      for(int js=0; js<m_showers.size(); js++){
+        bool fl_inTower = false;
+        for(int itw=0; itw<m_showers[js].get()->getTowerID().size() && !fl_inTower; itw++){
+        for(int jtw=0; jtw<m_shower->getTowerID().size(); jtw++){
+          if(m_showers[js].get()->getTowerID()[itw]==m_shower->getTowerID()[jtw]) {fl_inTower=true; break;}
+        }}
+
+        if( fl_inTower &&
+            m_showers[js].get()->getDlayer() == m_shower->getDlayer() && 
+            m_showers[js].get()->getSeeds().size()==1 && 
+            (m_showers[js].get()->getSeeds()[0]->getPosition()-m_shower->getPos()).Mag()<10 ) 
+        {m_selshower = m_showers[js].get(); fl_foundshower=true; break; }
+      }
+      if(fl_foundshower && m_selshower!=NULL) m_newClus->addUnit( m_selshower );
+    }
+
+    for(int itrk=0; itrk<m_oldClusCol[ic]->getAssociatedTracks().size(); itrk++) m_newClus->addAssociatedTrack( m_oldClusCol[ic]->getAssociatedTracks()[itrk] );
+    m_newClus->setType( m_oldClusCol[ic]->getType() );
+    m_newClus->setHoughPars(m_oldClusCol[ic]->getHoughAlpha(), m_oldClusCol[ic]->getHoughRho() );
+    m_newClus->setIntercept(m_oldClusCol[ic]->getHoughIntercept());
+    m_newClus->fitAxis("");
+    m_newClus->addHalfCluster(settings.map_stringPars["ReadinAxisName"], m_oldClusCol[ic]);
+    m_newClusCol.push_back( m_newClus );
+  }
+
+//tmp_totE = 0.;
+//for(int i=0; i<m_newClusCol.size(); i++) tmp_totE += m_newClusCol[i]->getEnergy();
+//cout<<"  In Longi-Linking: raw new HFCluster size : "<<m_newClusCol.size()<<", totE "<<tmp_totE<<endl;
+/*
+cout<<"  Print 1DShower in new HFCluster"<<endl;
+for(int ih=0; ih<m_newClusCol.size(); ih++){
+  cout<<"    In new HFCluster #"<<ih<<endl;
+  for(int i=0; i<m_newClusCol[ih]->getCluster().size(); i++){
+    printf("      Shower #%d, pos+E [%.3f, %.3f, %.3f, %.3f], address %p \n", i, m_newClusCol[ih]->getCluster()[i]->getPos().x(), m_newClusCol[ih]->getCluster()[i]->getPos().y(), m_newClusCol[ih]->getCluster()[i]->getPos().z(), m_newClusCol[ih]->getCluster()[i]->getEnergy(), m_newClusCol[ih]->getCluster()[i] );
+  }
+}
+*/
+
+  std::vector<const PandoraPlus::Calo1DCluster*> m_leftshowers; m_leftshowers.clear(); 
+  for(int ish=0; ish<m_showers.size(); ish++){
+    bool fl_inclus = false; 
+    for(int icl=0; icl<m_newClusCol.size(); icl++){
+      std::vector<const Calo1DCluster*> p_showerCol = m_newClusCol[icl]->getCluster();
+      if( find(p_showerCol.begin(), p_showerCol.end(), m_showers[ish].get())!=p_showerCol.end() ){ fl_inclus=true; break; }
+    }
+
+    if(!fl_inclus) m_leftshowers.push_back( m_showers[ish].get() );
+  }
+
+//tmp_totE = 0.;
+//for(int i=0; i<m_leftshowers.size(); i++) tmp_totE += m_leftshowers[i]->getEnergy();
+//cout<<"  In Longi-Linking: left 1D showers size: "<<m_leftshowers.size()<<", totE "<<tmp_totE<<endl;
+//cout<<"  Print left 1DShower (address)"<<endl;
+//for(int i=0; i<m_leftshowers.size(); i++){
+//  printf("    Shower #%d, pos+E [%.3f, %.3f, %.3f, %.3f], address %p \n", i, m_leftshowers[i]->getPos().x(), m_leftshowers[i]->getPos().y(), m_leftshowers[i]->getPos().z(), m_leftshowers[i]->getEnergy(), m_leftshowers[i] );
+//}
+
+
+  //Merge showers into closest Half cluster
+  std::sort( m_leftshowers.begin(), m_leftshowers.end(), compLayer );
+  for(int is=0; is<m_leftshowers.size(); is++) 
+    MergeToClosestCluster( m_leftshowers[is], m_newClusCol );
+
+//tmp_totE = 0.;
+//for(int i=0; i<m_newClusCol.size(); i++) tmp_totE += m_newClusCol[i]->getEnergy();
+//cout<<"  In Longi-Linking: after merge left showers: HFCluster size "<<m_newClusCol.size()<<", totE "<<tmp_totE<<endl;
+
+ //Split the double-used 1DClusters  
+  SplitOverlapCluster(m_newClusCol);
+  for(int is=0; is<m_newClusCol.size(); is++) m_newClusCol[is]->sortBarShowersByLayer();
+
+  //Merge showers in the same layer
+  if(settings.map_boolPars["CompactHFCluster"]){
+    for(int icl=0; icl<m_newClusCol.size(); icl++) 
+      m_newClusCol[icl].get()->mergeClusterInLayer();
+  }
+/*
+cout<<"  Print 1DShower in new HFCluster"<<endl;
+for(int ih=0; ih<m_newClusCol.size(); ih++){
+  cout<<"    In new HFCluster #"<<ih<<endl;
+  for(int i=0; i<m_newClusCol[ih]->getCluster().size(); i++){
+    printf("      Shower #%d, pos+E [%.3f, %.3f, %.3f, %.3f], address %p \n", i, m_newClusCol[ih]->getCluster()[i]->getPos().x(), m_newClusCol[ih]->getCluster()[i]->getPos().y(), m_newClusCol[ih]->getCluster()[i]->getPos().z(), m_newClusCol[ih]->getCluster()[i]->getEnergy(), m_newClusCol[ih]->getCluster()[i] );
+  }
+}
+*/
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode EnergySplittingAlg::HalfClusterToTowers( std::vector<PandoraPlus::CaloHalfCluster*>& m_halfClusU,
+                                                    std::vector<PandoraPlus::CaloHalfCluster*>& m_halfClusV,
+                                                    std::vector<PandoraPlus::CaloHalfCluster*>& m_emptyClusU,
+                                                    std::vector<PandoraPlus::CaloHalfCluster*>& m_emptyClusV,
+                                                    std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_towers )
+{
+//cout<<"  HalfClusterToTowers: Input halfcluster size "<<m_halfClusU.size()<<", "<<m_halfClusV.size()<<endl;
+//cout<<"    Input empty cluster size "<<m_emptyClusU.size()<<", "<<m_emptyClusV.size()<<endl;
+
+  m_towers.clear(); 
+
+  std::map<std::vector<int>, std::vector<const PandoraPlus::Calo2DCluster*> > map_2DCluster;
+  std::map<std::vector<int>, std::vector<PandoraPlus::CaloHalfCluster*> > map_HalfClusterU; 
+  std::map<std::vector<int>, std::vector<PandoraPlus::CaloHalfCluster*> > map_HalfClusterV; 
+
+  std::map<std::vector<int>, std::vector<PandoraPlus::CaloHalfCluster*> > map_emptyHalfClusterU;
+  std::map<std::vector<int>, std::vector<PandoraPlus::CaloHalfCluster*> > map_emptyHalfClusterV;  
+
+  //Assign the empty clusters into tower
+  for(int il=0; il<m_emptyClusU.size(); il++){
+//cout<<"  Empty clusterU #"<<il<<": towerID size "<<m_emptyClusU[il]->getTowerID().size()<<endl;
+    map_emptyHalfClusterU[m_emptyClusU[il]->getTowerID()[0]].push_back(m_emptyClusU[il]);
+  }
+  for(int il=0; il<m_emptyClusV.size(); il++){
+//cout<<"  Empty clusterV #"<<il<<": towerID size "<<m_emptyClusV[il]->getTowerID().size()<<endl;
+    map_emptyHalfClusterV[m_emptyClusV[il]->getTowerID()[0]].push_back(m_emptyClusV[il]);
+  }
+
+
+  //Split CaloHalfClusterU
+  for(int il=0; il<m_halfClusU.size(); il++){
+    if(m_halfClusU[il]->getCluster().size()==0) {std::cout<<"WARNING: Have an empty CaloHalfCluster! Skip it! "<<std::endl; continue;}
+
+    //HalfCluster does not cover tower: 
+    if( m_halfClusU[il]->getTowerID().size()==1 && m_halfClusU[il]->getCluster().size()>=settings.map_intPars["th_Nhit"]){
+      std::vector<int> cl_towerID =  m_halfClusU[il]->getTowerID()[0]; 
+      if(settings.map_boolPars["CompactHFCluster"]) m_halfClusU[il]->mergeClusterInLayer();
+      map_HalfClusterU[cl_towerID].push_back(m_halfClusU[il]);
+      continue; 
+    }
+
+    //CaloHalfCluster covers towers: Loop check showers. 
+    std::map<std::vector<int>, PandoraPlus::CaloHalfCluster* > tmp_LongiClusMaps; tmp_LongiClusMaps.clear();
+    for(int is=0; is<m_halfClusU[il]->getCluster().size(); is++){
+      const PandoraPlus::Calo1DCluster* p_shower = m_halfClusU[il]->getCluster()[is];
+
+      std::map<std::vector<int>, PandoraPlus::Calo1DCluster* > tmp_1DClusMaps; tmp_1DClusMaps.clear();
+      for(int ib=0; ib<p_shower->getBars().size(); ib++){
+        std::vector<int> towerID(2);
+        towerID[0] = p_shower->getBars()[ib]->getModule();
+        towerID[1] = p_shower->getBars()[ib]->getStave();        
+
+        if(tmp_1DClusMaps.find(towerID)!=tmp_1DClusMaps.end()){
+          tmp_1DClusMaps[towerID]->addUnit(p_shower->getBars()[ib]);
+          tmp_1DClusMaps[towerID]->setIDInfo();
+        }
+        else{
+          std::shared_ptr<PandoraPlus::Calo1DCluster> tmp_clus = std::make_shared<PandoraPlus::Calo1DCluster>();
+          tmp_clus->addUnit( p_shower->getBars()[ib] );
+          tmp_clus->setIDInfo();
+          tmp_1DClusMaps[towerID] = tmp_clus.get();
+          m_bkCol.map_1DCluster["bk1DCluster"].push_back( tmp_clus );
+        }
+      }
+
+      if(tmp_1DClusMaps.size()>1){
+        for(auto &iter : tmp_1DClusMaps){
+          for(auto &iter1 : tmp_1DClusMaps){
+            if(iter!= iter1) iter.second->addCousinCluster(iter1.second);
+          }
+        }
+      }
+
+      for(auto &iter: tmp_1DClusMaps){
+        std::vector<int> towerID = iter.first;
+        iter.second->setSeed();
+        iter.second->setIDInfo();
+        iter.second->getLinkedMCPfromUnit();
+
+        if( tmp_LongiClusMaps.find( towerID )!=tmp_LongiClusMaps.end() ){
+          tmp_LongiClusMaps[towerID]->addUnit( iter.second );
+          tmp_LongiClusMaps[towerID]->setTowerID( towerID );
+        }
+        else{
+          std::shared_ptr<PandoraPlus::CaloHalfCluster> tmp_clus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+          tmp_clus->addUnit( iter.second );
+          tmp_clus->setTowerID( towerID );
+          tmp_LongiClusMaps[towerID] = tmp_clus.get();
+          m_bkCol.map_HalfCluster["bkHalfCluster"].push_back( tmp_clus );
+        }        
+      }
+
+      p_shower = nullptr;
+    }
+
+    //Connect cousins
+    if(tmp_LongiClusMaps.size()>1){
+      for(auto &iter : tmp_LongiClusMaps){
+        for(auto &iter1 : tmp_LongiClusMaps){
+          if(iter!= iter1 && 
+             iter.second->getEnergy()>settings.map_floatPars["Eth_HFClus"] &&
+             iter1.second->getEnergy()>settings.map_floatPars["Eth_HFClus"] &&
+             iter.second->getCluster().size()>=settings.map_intPars["th_Nhit"] && 
+             iter1.second->getCluster().size()>=settings.map_intPars["th_Nhit"] ){ iter.second->addCousinCluster(iter1.second); }
+        }
+      }
+    }
+    for(auto &iter : tmp_LongiClusMaps){
+      iter.second->setType(m_halfClusU[il]->getType());
+      if(m_halfClusU[il]->getHalfClusterCol(settings.map_stringPars["ReadinAxisName"]).size()>0 ) 
+        iter.second->addHalfCluster(settings.map_stringPars["ReadinAxisName"], m_halfClusU[il]->getHalfClusterCol(settings.map_stringPars["ReadinAxisName"])[0]);
+
+      if(iter.second->getEnergy()<settings.map_floatPars["Eth_HFClus"]) continue;
+      iter.second->addHalfCluster("ParentCluster", m_halfClusU[il]);
+      for(int itrk=0; itrk<m_halfClusU[il]->getAssociatedTracks().size(); itrk++) 
+        iter.second->addAssociatedTrack( m_halfClusU[il]->getAssociatedTracks()[itrk] );
+      if(iter.second->getCluster().size()>=settings.map_intPars["th_Nhit"]){
+        if(settings.map_boolPars["CompactHFCluster"]){ 
+          iter.second->mergeClusterInLayer();
+          iter.second->setTowerID(iter.first);
+        }
+        map_HalfClusterU[iter.first].push_back(iter.second);
+      }
+    }
+
+  }
+
+
+  //Split CaloHalfClusterV
+  for(int il=0; il<m_halfClusV.size(); il++){
+    if(m_halfClusV[il]->getCluster().size()==0) {std::cout<<"WARNING: Have an empty CaloHalfCluster! Skip it! "<<std::endl; continue;}
+
+    //HalfCluster does not cover tower:
+    if( m_halfClusV[il]->getTowerID().size()==1 && m_halfClusV[il]->getCluster().size()>=settings.map_intPars["th_Nhit"]){
+      std::vector<int> cl_towerID =  m_halfClusV[il]->getTowerID()[0];
+      if(settings.map_boolPars["CompactHFCluster"]) m_halfClusV[il]->mergeClusterInLayer();
+      map_HalfClusterV[cl_towerID].push_back(m_halfClusV[il]);
+      continue;
+    }
+
+    //CaloHalfCluster covers towers: Loop check showers.
+    std::map<std::vector<int>, PandoraPlus::CaloHalfCluster* > tmp_LongiClusMaps; tmp_LongiClusMaps.clear();
+    for(int is=0; is<m_halfClusV[il]->getCluster().size(); is++){
+      const PandoraPlus::Calo1DCluster* p_shower = m_halfClusV[il]->getCluster()[is];
+
+      std::map<std::vector<int>, PandoraPlus::Calo1DCluster* > tmp_1DClusMaps; tmp_1DClusMaps.clear();
+      for(int ib=0; ib<p_shower->getBars().size(); ib++){
+        std::vector<int> towerID(2);
+        towerID[0] = p_shower->getBars()[ib]->getModule();
+        towerID[1] = p_shower->getBars()[ib]->getStave();
+
+        if(tmp_1DClusMaps.find(towerID)!=tmp_1DClusMaps.end()){
+          tmp_1DClusMaps[towerID]->addUnit(p_shower->getBars()[ib]);
+          tmp_1DClusMaps[towerID]->setIDInfo();
+        }
+        else{
+          std::shared_ptr<PandoraPlus::Calo1DCluster> tmp_clus = std::make_shared<PandoraPlus::Calo1DCluster>();
+          tmp_clus->addUnit( p_shower->getBars()[ib] );
+          tmp_clus->setIDInfo();
+          tmp_1DClusMaps[towerID] = tmp_clus.get();
+          m_bkCol.map_1DCluster["bk1DCluster"].push_back( tmp_clus );
+        }
+      }
+
+      if(tmp_1DClusMaps.size()>1){
+        for(auto &iter : tmp_1DClusMaps){
+          for(auto &iter1 : tmp_1DClusMaps){
+            if(iter!= iter1) iter.second->addCousinCluster(iter1.second);
+          }
+        }
+      }
+
+      for(auto &iter: tmp_1DClusMaps){
+        std::vector<int> towerID = iter.first;
+        iter.second->setSeed();
+        iter.second->setIDInfo();
+        iter.second->getLinkedMCPfromUnit();
+
+        if( tmp_LongiClusMaps.find( towerID )!=tmp_LongiClusMaps.end() ){
+          tmp_LongiClusMaps[towerID]->addUnit( iter.second );
+          tmp_LongiClusMaps[towerID]->setTowerID( towerID );
+        }
+        else{
+          std::shared_ptr<PandoraPlus::CaloHalfCluster> tmp_clus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+          tmp_clus->addUnit( iter.second );
+          tmp_clus->setTowerID( towerID );
+          tmp_LongiClusMaps[towerID] = tmp_clus.get();
+          m_bkCol.map_HalfCluster["bkHalfCluster"].push_back( tmp_clus );
+        }
+      }
+      p_shower = nullptr;
+
+    }
+
+    //Connect cousins
+    if(tmp_LongiClusMaps.size()>1){
+      for(auto &iter : tmp_LongiClusMaps){
+        for(auto &iter1 : tmp_LongiClusMaps){
+          if(iter!= iter1 &&
+             iter.second->getEnergy()>settings.map_floatPars["Eth_HFClus"] &&
+             iter1.second->getEnergy()>settings.map_floatPars["Eth_HFClus"] &&
+             iter.second->getCluster().size()>=settings.map_intPars["th_Nhit"] &&
+             iter1.second->getCluster().size()>=settings.map_intPars["th_Nhit"] ){ iter.second->addCousinCluster(iter1.second); }
+        }
+      }
+    }
+    for(auto &iter : tmp_LongiClusMaps){
+      iter.second->setType(m_halfClusV[il]->getType());
+      if(m_halfClusV[il]->getHalfClusterCol(settings.map_stringPars["ReadinAxisName"]).size()>0 )
+        iter.second->addHalfCluster(settings.map_stringPars["ReadinAxisName"], m_halfClusV[il]->getHalfClusterCol(settings.map_stringPars["ReadinAxisName"])[0]);
+
+      if(iter.second->getEnergy()<settings.map_floatPars["Eth_HFClus"]) continue;
+      iter.second->addHalfCluster("ParentCluster", m_halfClusV[il]);
+      for(int itrk=0; itrk<m_halfClusV[il]->getAssociatedTracks().size(); itrk++)
+        iter.second->addAssociatedTrack( m_halfClusV[il]->getAssociatedTracks()[itrk] );
+      if(iter.second->getCluster().size()>=settings.map_intPars["th_Nhit"]){
+        if(settings.map_boolPars["CompactHFCluster"]){ 
+          iter.second->mergeClusterInLayer();
+          iter.second->setTowerID(iter.first);
+        }
+        map_HalfClusterV[iter.first].push_back(iter.second);
+      }
+    }
+
+  }
+
+  //Build 2DCluster
+  for(auto &iterU : map_HalfClusterU){
+    if( map_HalfClusterV.find(iterU.first)==map_HalfClusterV.end() ){ 
+      iterU.second.clear();
+      continue; 
+    }
+
+    std::vector<PandoraPlus::CaloHalfCluster*> p_halfClusU = iterU.second; 
+    std::vector<PandoraPlus::CaloHalfCluster*> p_halfClusV = map_HalfClusterV[iterU.first]; 
+
+    //Get ordered showers for looping in layers. 
+    std::map<int, std::vector<const PandoraPlus::Calo1DCluster*>> m_orderedShowerU; m_orderedShowerU.clear();
+    std::map<int, std::vector<const PandoraPlus::Calo1DCluster*>> m_orderedShowerV; m_orderedShowerV.clear();
+
+    for(int ic=0; ic<p_halfClusU.size(); ic++){
+      for(int is=0; is<p_halfClusU.at(ic)->getCluster().size(); is++)
+        m_orderedShowerU[p_halfClusU.at(ic)->getCluster()[is]->getDlayer()].push_back( p_halfClusU.at(ic)->getCluster()[is] );
+    }
+    for(int ic=0; ic<p_halfClusV.size(); ic++){
+      for(int is=0; is<p_halfClusV.at(ic)->getCluster().size(); is++)
+        m_orderedShowerV[p_halfClusV.at(ic)->getCluster()[is]->getDlayer()].push_back( p_halfClusV.at(ic)->getCluster()[is] );
+    }
+    p_halfClusU.clear(); p_halfClusV.clear();
+
+
+    //Create super-layers (block)
+    std::vector<const PandoraPlus::Calo2DCluster*> m_blocks; m_blocks.clear();
+    for(auto &iter1 : m_orderedShowerU){
+      if( m_orderedShowerV.find( iter1.first )==m_orderedShowerV.end() ) continue; 
+      std::shared_ptr<PandoraPlus::Calo2DCluster> tmp_block = std::make_shared<PandoraPlus::Calo2DCluster>();
+      for(int is=0; is<iter1.second.size(); is++) tmp_block->addUnit( iter1.second.at(is) );
+      for(int is=0; is<m_orderedShowerV[iter1.first].size(); is++) tmp_block->addUnit( m_orderedShowerV[iter1.first].at(is) );
+      tmp_block->setTowerID( iterU.first );
+      m_blocks.push_back( tmp_block.get() );
+      m_bkCol.map_2DCluster["bk2DCluster"].push_back( tmp_block );
+    }
+    map_2DCluster[iterU.first] = m_blocks;
+  }
+  for(auto &iterV : map_HalfClusterV){
+    if( map_HalfClusterU.find(iterV.first)==map_HalfClusterU.end() ){
+      iterV.second.clear();
+    }
+  }
+
+  //Form a tower:
+  for(auto &iter : map_2DCluster){
+    std::vector<int> m_towerID = iter.first;
+//printf("  In tower: [%d, %d, %d] \n", m_towerID[0], m_towerID[1], m_towerID[2]);
+    //Check cousin clusters: 
+    std::vector<PandoraPlus::CaloHalfCluster*> m_HFClusUInTower = map_HalfClusterU[m_towerID];
+    for(auto &m_HFclus : m_HFClusUInTower){
+      std::vector<const CaloHalfCluster*> tmp_delClus; tmp_delClus.clear();
+      for(int ics=0; ics<m_HFclus->getHalfClusterCol("CousinCluster").size(); ics++){
+        std::vector<int> tmp_towerID = m_HFclus->getHalfClusterCol("CousinCluster")[ics]->getTowerID()[0];
+
+        if( map_2DCluster.find( tmp_towerID )==map_2DCluster.end() )
+          tmp_delClus.push_back( m_HFclus->getHalfClusterCol("CousinCluster")[ics] );
+      }
+      for(int ics=0; ics<tmp_delClus.size(); ics++) m_HFclus->deleteCousinCluster( tmp_delClus[ics] );
+    }
+
+    std::vector<PandoraPlus::CaloHalfCluster*> m_HFClusVInTower = map_HalfClusterV[m_towerID];
+    for(auto &m_HFclus : m_HFClusVInTower){
+      std::vector<const CaloHalfCluster*> tmp_delClus; tmp_delClus.clear();
+      for(int ics=0; ics<m_HFclus->getHalfClusterCol("CousinCluster").size(); ics++){
+        std::vector<int> tmp_towerID = m_HFclus->getHalfClusterCol("CousinCluster")[ics]->getTowerID()[0];
+
+        if( map_2DCluster.find( tmp_towerID )==map_2DCluster.end() )
+          tmp_delClus.push_back( m_HFclus->getHalfClusterCol("CousinCluster")[ics] );
+      }
+      for(int ics=0; ics<tmp_delClus.size(); ics++) m_HFclus->deleteCousinCluster( tmp_delClus[ics] );
+    }
+
+    std::vector<const PandoraPlus::CaloHalfCluster*> const_emptyClusU; const_emptyClusU.clear();
+    std::vector<const PandoraPlus::CaloHalfCluster*> const_emptyClusV; const_emptyClusV.clear();
+    if(map_emptyHalfClusterU.find(m_towerID)!=map_emptyHalfClusterU.end()){
+      for(int icl=0; icl<map_emptyHalfClusterU[m_towerID].size(); icl++){
+        map_emptyHalfClusterU[m_towerID][icl]->getLinkedMCPfromUnit();
+        const_emptyClusU.push_back(map_emptyHalfClusterU[m_towerID][icl]);
+      }
+      map_emptyHalfClusterU.erase(m_towerID);
+    }
+    if(map_emptyHalfClusterV.find(m_towerID)!=map_emptyHalfClusterV.end()){
+      for(int icl=0; icl<map_emptyHalfClusterV[m_towerID].size(); icl++){
+        map_emptyHalfClusterV[m_towerID][icl]->getLinkedMCPfromUnit();
+        const_emptyClusV.push_back(map_emptyHalfClusterV[m_towerID][icl]);
+      }
+      map_emptyHalfClusterV.erase(m_towerID);
+    }
+//cout<<"  Found empty half cluster size: "<<const_emptyClusU.size()<<", "<<const_emptyClusV.size()<<endl;
+
+    //Convert to const
+    std::vector<const PandoraPlus::CaloHalfCluster*> const_HFClusU; const_HFClusU.clear();
+    std::vector<const PandoraPlus::CaloHalfCluster*> const_HFClusV; const_HFClusV.clear();
+    for(int ics=0; ics<m_HFClusUInTower.size(); ics++){ m_HFClusUInTower[ics]->getLinkedMCPfromUnit(); const_HFClusU.push_back(m_HFClusUInTower[ics]); }
+    for(int ics=0; ics<m_HFClusVInTower.size(); ics++){ m_HFClusVInTower[ics]->getLinkedMCPfromUnit(); const_HFClusV.push_back(m_HFClusVInTower[ics]); }
+
+    std::shared_ptr<PandoraPlus::Calo3DCluster> m_tower = std::make_shared<PandoraPlus::Calo3DCluster>();
+//printf("  Creating tower: [%d, %d, %d] \n", m_towerID[0], m_towerID[1], m_towerID[2]);
+    m_tower->addTowerID( m_towerID );
+    for(int i2d=0; i2d<map_2DCluster[m_towerID].size(); i2d++) m_tower->addUnit(map_2DCluster[m_towerID][i2d]);
+    m_tower->setHalfClusters( settings.map_stringPars["OutputClusName"]+"U", const_HFClusU, 
+                              settings.map_stringPars["OutputClusName"]+"V", const_HFClusV );
+    m_tower->setHalfClusters( "emptyHalfClusterU", const_emptyClusU,
+                              "emptyHalfClusterV", const_emptyClusV );
+    m_towers.push_back(m_tower);
+  }
+
+  if(map_emptyHalfClusterU.size()>0 && map_emptyHalfClusterV.size()>0){
+    for(auto iter: map_emptyHalfClusterU){
+      std::vector<int> m_towerID = iter.first;
+
+      if( map_emptyHalfClusterV.find(m_towerID)==map_emptyHalfClusterV.end() ){
+        iter.second.clear();
+        //map_emptyHalfClusterU.erase(m_towerID);
+        continue;
+      }
+
+      std::vector<const PandoraPlus::CaloHalfCluster*> const_emptyClusU; const_emptyClusU.clear();
+      std::vector<const PandoraPlus::CaloHalfCluster*> const_emptyClusV; const_emptyClusV.clear();
+      for(int icl=0; icl<map_emptyHalfClusterU[m_towerID].size(); icl++){
+        map_emptyHalfClusterU[m_towerID][icl]->getLinkedMCPfromUnit();
+        const_emptyClusU.push_back(map_emptyHalfClusterU[m_towerID][icl]);
+      }
+      //map_emptyHalfClusterU.erase(m_towerID);
+      for(int icl=0; icl<map_emptyHalfClusterV[m_towerID].size(); icl++){
+        map_emptyHalfClusterV[m_towerID][icl]->getLinkedMCPfromUnit();
+        const_emptyClusV.push_back(map_emptyHalfClusterV[m_towerID][icl]);
+      }
+      //map_emptyHalfClusterV.erase(m_towerID);
+
+      std::shared_ptr<PandoraPlus::Calo3DCluster> m_tower = std::make_shared<PandoraPlus::Calo3DCluster>();
+      m_tower->addTowerID( m_towerID );
+      m_tower->setHalfClusters( "emptyHalfClusterU", const_emptyClusU,
+                                "emptyHalfClusterV", const_emptyClusV );
+      m_towers.push_back(m_tower);
+    }
+  }
+
+/*
+cout<<"  After splitting: tower size "<<m_towers.size()<<". Print Tower: "<<endl;
+for(auto it : m_towers){
+  std::vector<const CaloHalfCluster*> m_HFClusUInTower = it->getHalfClusterUCol(settings.map_stringPars["OutputClusName"]+"U");
+  std::vector<const CaloHalfCluster*> m_HFClusVInTower = it->getHalfClusterVCol(settings.map_stringPars["OutputClusName"]+"V");
+  //std::vector<const CaloHalfCluster*> m_HFClusUInTower = it->getHalfClusterUCol("emptyHalfClusterU");
+  //std::vector<const CaloHalfCluster*> m_HFClusVInTower = it->getHalfClusterVCol("emptyHalfClusterV");
+
+
+cout<<"Check tower ID: ";
+for(int i=0; i<it->getTowerID().size(); i++) printf("[%d, %d, %d], ", it->getTowerID()[i][0], it->getTowerID()[i][1], it->getTowerID()[i][2]);
+cout<<endl;
+
+  printf("    In Tower [%d, %d, %d], ", it->getTowerID()[0][0], it->getTowerID()[0][1], it->getTowerID()[0][2] );
+  printf("    HalfCluster size: (%d, %d) \n", m_HFClusUInTower.size(), m_HFClusVInTower.size() );
+  cout<<"    Loop print HalfClusterU: "<<endl;
+  for(int icl=0; icl<m_HFClusUInTower.size(); icl++){
+    cout<<"      In HFClusU #"<<icl<<": shower size = "<<m_HFClusUInTower[icl]->getCluster().size()<<", En = "<<m_HFClusUInTower[icl]->getEnergy()<<", type "<<m_HFClusUInTower[icl]->getType();
+    printf(", Position (%.3f, %.3f, %.3f), address %p ",m_HFClusUInTower[icl]->getPos().x(), m_HFClusUInTower[icl]->getPos().y(), m_HFClusUInTower[icl]->getPos().z(), m_HFClusUInTower[icl]);
+    printf(", cousin size %d, address: ", m_HFClusUInTower[icl]->getHalfClusterCol("CousinCluster").size());
+    for(int ics=0; ics<m_HFClusUInTower[icl]->getHalfClusterCol("CousinCluster").size(); ics++) printf("%p, ", m_HFClusUInTower[icl]->getHalfClusterCol("CousinCluster")[ics]);
+    printf(", track size %d, address: ", m_HFClusUInTower[icl]->getAssociatedTracks().size());
+    for(int itrk=0; itrk<m_HFClusUInTower[icl]->getAssociatedTracks().size(); itrk++) printf("%p, ", m_HFClusUInTower[icl]->getAssociatedTracks()[itrk]);
+    cout<<endl;
+    for(auto ish : m_HFClusUInTower[icl]->getCluster()){
+      printf("          Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Nbars %d, NSeed %d, Address %p \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish->getBars().size(),  ish->getNseeds(), ish );
+    }
+  }
+  cout<<endl;
+
+  cout<<"    Loop print HalfClusterV: "<<endl;
+  for(int icl=0; icl<m_HFClusVInTower.size(); icl++){
+    cout<<"      In HFClusV #"<<icl<<": shower size = "<<m_HFClusVInTower[icl]->getCluster().size()<<", En = "<<m_HFClusVInTower[icl]->getEnergy()<<", type "<<m_HFClusVInTower[icl]->getType();
+    printf(", Position (%.3f, %.3f, %.3f), address %p ",m_HFClusVInTower[icl]->getPos().x(), m_HFClusVInTower[icl]->getPos().y(), m_HFClusVInTower[icl]->getPos().z(), m_HFClusVInTower[icl]);
+    printf(", cousin size %d, address: ", m_HFClusVInTower[icl]->getHalfClusterCol("CousinCluster").size());
+    for(int ics=0; ics<m_HFClusVInTower[icl]->getHalfClusterCol("CousinCluster").size(); ics++) printf("%p, ", m_HFClusVInTower[icl]->getHalfClusterCol("CousinCluster")[ics]);
+    printf(", track size %d, address: ", m_HFClusVInTower[icl]->getAssociatedTracks().size());
+    for(int itrk=0; itrk<m_HFClusVInTower[icl]->getAssociatedTracks().size(); itrk++) printf("%p, ", m_HFClusVInTower[icl]->getAssociatedTracks()[itrk]);
+    cout<<endl;
+    for(auto ish : m_HFClusVInTower[icl]->getCluster()){
+      printf("          Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Nbars %d, NSeed %d, Address %p \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish->getBars().size(), ish->getNseeds(), ish );
+    }
+  }
+  cout<<endl;
+}
+*/
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode EnergySplittingAlg::ClusterSplitting(const PandoraPlus::Calo1DCluster* m_cluster, std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>>& outshCol ){
+
+//cout<<"ClusterSplitting: input cluster seed size = "<<m_cluster->getSeeds().size()<<endl;
+//cout<<"ClusterSplitting: input bar size = "<<m_cluster->getBars().size()<<endl;
+//cout<<"Seed position and E: "<<endl;
+//for(int a=0; a<m_cluster->getNseeds(); a++) printf("\t (%.3f, %.3f, %.3f, %.3f), barID %d \n", m_cluster->getSeeds()[a]->getPosition().x(), 
+//                                                                                             m_cluster->getSeeds()[a]->getPosition().y(),
+//                                                                                             m_cluster->getSeeds()[a]->getPosition().z(),
+//                                                                                             m_cluster->getSeeds()[a]->getEnergy(), 
+//                                                                                             m_cluster->getSeeds()[a]->getBar() );
+//cout<<endl;
+//double tmp_totE = 0.;
+//for(int i=0; i<m_cluster->getBars().size(); i++) tmp_totE += m_cluster->getBars()[i]->getEnergy();
+//cout<<"Total bar energy: "<<tmp_totE<<endl;
+
+  //No seed in cluster: return origin cluster.
+  if(m_cluster->getNseeds()==0) { 
+    auto shower = m_cluster->Clone();
+    outshCol.push_back(shower);
+    //std::cout<<"WARNING: Still have no-seed cluster!!"<<std::endl; 
+    return StatusCode::SUCCESS; 
+  }
+
+  //1 seed or second moment less than threshold: Not split. Turn cluster to shower and return
+  else if(m_cluster->getNseeds()<2 || m_cluster->getScndMoment()<settings.map_floatPars["th_split"]){
+    auto shower = m_cluster->Clone();
+    outshCol.push_back(shower);
+    return StatusCode::SUCCESS;
+  }
+
+  
+  //Separated bars: 
+  else if( m_cluster->getNseeds()>=m_cluster->getBars().size() ){
+    for(int ish=0; ish<m_cluster->getNseeds(); ish++){
+      std::shared_ptr<PandoraPlus::Calo1DCluster> shower = std::make_shared<PandoraPlus::Calo1DCluster>();
+      shower->addUnit(m_cluster->getSeeds()[ish]);
+      shower->addSeed(m_cluster->getSeeds()[ish]);
+      shower->setIDInfo();
+
+      outshCol.push_back(shower);
+    }
+    return StatusCode::SUCCESS;
+  }
+  
+  //2 or more seeds, large second moment: Split
+  int Nshower = m_cluster->getNseeds();
+  int Nbars = m_cluster->getBars().size();
+  double Eseed[Nshower] = {0};
+  double weight[Nbars][Nshower] = {0};
+  TVector3 SeedPos[Nshower];
+  TVector3 SeedPos_Origin[Nshower];
+  for(int is=0;is<Nshower;is++){ 
+    SeedPos[is] = m_cluster->getSeeds()[is]->getPosition(); 
+    SeedPos_Origin[is]=SeedPos[is]; 
+    Eseed[is]=m_cluster->getSeeds()[is]->getEnergy(); 
+  }
+  //CalculateInitialEseed(m_cluster->getSeeds(), SeedPos, Eseed);
+/*
+  bool isConverge = false;
+  bool isShifted = false; 
+  int iter=0;
+  TVector3 SeedPos_prev[Nshower];
+  do{
+
+    for(int ibar=0;ibar<m_cluster->getBars().size();ibar++){
+      double Eexp[Nshower];
+      double Eexp_tot=0;
+      for(int is=0;is<Nshower;is++){ Eexp[is] = Eseed[is]*GetShowerProfile(m_cluster->getBars()[ibar]->getPosition(), SeedPos[is] ); Eexp_tot+= Eexp[is];}
+      for(int is=0;is<Nshower;is++) weight[ibar][is] = Eexp[is]/Eexp_tot;
+    }
+    for(int is=0;is<Nshower;is++){
+      SeedPos_prev[is]=SeedPos[is];
+
+      double Etot=0;
+      double Ebar[Nbars] = {0};
+      double Emax = -99;
+      for(int ib=0;ib<Nbars;ib++){
+        Ebar[ib] = ( m_cluster->getBars()[ib]->getQ1()*weight[ib][is] + m_cluster->getBars()[ib]->getQ2()*weight[ib][is] )/2.; 
+        Etot += Ebar[ib];
+        if(Ebar[ib]>Emax) Emax = Ebar[ib];
+      }
+
+      TVector3 pos(0,0,0);
+      for(int ib=0; ib<Nbars; ib++) pos += m_cluster->getBars()[ib]->getPosition() * (Ebar[ib]/Etot);
+      SeedPos[is] = pos; 
+      Eseed[is] = Emax;
+    }
+    isConverge=true;
+    for(int is=0;is<Nshower;is++) if( (SeedPos_prev[is]-SeedPos[is]).Mag2()>2.89 ){ isConverge=false; break;}
+ 
+    isShifted = false;
+    for(int is=0;is<Nshower;is++) if( (SeedPos[is]-SeedPos_Origin[is]).Mag2()<15 ){ isShifted=true; break;}
+    iter++;
+
+  }
+  while(iter<20 && !isConverge && !isShifted );
+  if(iter>=20){
+    std::cout<<"WARNING: Iteration time larger than 20! Might not converge!"<<std::endl;
+    std::cout<<"  For Check: NBars: "<<m_cluster->getBars().size()<<"  Nseeds: "<<Nshower<<std::endl;
+  }
+*/
+
+
+//Check: no iteration, just calculate the weight.
+  for(int ibar=0;ibar<m_cluster->getBars().size();ibar++){
+    double Eexp[Nshower];
+    double Eexp_tot=0;
+    for(int is=0;is<Nshower;is++){ Eexp[is] = Eseed[is]*GetShowerProfile(m_cluster->getBars()[ibar]->getPosition(), SeedPos[is] ); Eexp_tot+= Eexp[is];}
+    for(int is=0;is<Nshower;is++) weight[ibar][is] = Eexp[is]/Eexp_tot;
+  }
+
+
+
+//cout<<"Print weight: "<<endl;
+//for(int abar=0; abar<Nbars; abar++){
+//for(int ash=0; ash<Nshower; ash++){
+//  printf("%.3f \t",weight[abar][ash]);
+//}
+//cout<<endl;
+//}
+
+//cout<<"After weight calculation. "<<endl;
+  //for(int is=0;is<Nshower;is++) SeedPos[is] = m_cluster->getSeeds()[is]->getPosition();
+  for(int is=0;is<Nshower;is++){
+//cout<<"  In shower #"<<is<<endl;
+    std::vector<const PandoraPlus::CaloUnit*> Bars; Bars.clear();
+    int iseed=-1;
+    int icount=0;
+    double _Emax = -99;
+    
+    for(int ib=0;ib<Nbars;ib++){
+      double barEn = (m_cluster->getBars()[ib]->getQ1()*weight[ib][is] + m_cluster->getBars()[ib]->getQ2()*weight[ib][is])/2.;
+//cout<<"    bar#"<<ib<<" barID "<<m_cluster->getBars()[ib]->getBar()<<" En: "<<barEn<<endl;
+      if(barEn<settings.map_floatPars["Eth_unit"]) continue;
+
+      auto bar = m_cluster->getBars()[ib]->Clone();
+      bar->setQ( bar->getQ1()*weight[ib][is], bar->getQ2()*weight[ib][is]  );
+      if( bar->getEnergy()>_Emax ) { _Emax=bar->getEnergy(); iseed=icount; }
+      Bars.push_back(bar.get());
+      icount++;
+      m_bkCol.map_BarCol["bkBar"].push_back( bar );
+//cout<<"      Found seed: iseed="<<iseed<<", icount="<<icount<<endl;
+    }
+    if(iseed<0) { std::cout<<"ERROR: Can not find seed(max energy bar) in this shower! Nbars = "<<Nbars<<". Please Check!"<<std::endl; continue; }
+    //if( (Bars[iseed]->getPosition()-SeedPos[is]).Mag()>15 ) { std::cout<<"ERROR: MaxEnergy bar is too far with original seed! Please Check! iSeed = "<<iseed<<std::endl; }
+
+    std::shared_ptr<PandoraPlus::Calo1DCluster> shower = std::make_shared<PandoraPlus::Calo1DCluster>();
+    shower->setBars(Bars);
+    shower->addSeed(Bars[iseed]);
+    shower->setIDInfo(); 
+
+
+    outshCol.push_back(shower);
+  }
+  
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode EnergySplittingAlg::SplitOverlapCluster( std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_HFClusCol ){
+
+  if(m_HFClusCol.size()<2) return StatusCode::SUCCESS;
+
+  for(int ic=0; ic<m_HFClusCol.size()-1; ic++){
+    for(int jc=ic+1; jc<m_HFClusCol.size(); jc++){
+      double E_ratio = m_HFClusCol[ic]->OverlapRatioE(m_HFClusCol[jc].get());
+      if(E_ratio>0){
+
+        std::vector<const Calo1DCluster*> m_ClusCol1 =  m_HFClusCol[ic]->getCluster();
+        std::vector<const Calo1DCluster*> m_ClusCol2 =  m_HFClusCol[jc]->getCluster();
+        std::vector<const Calo1DCluster*> m_overlapCl; m_overlapCl.clear();
+        for(int i1d=0; i1d<m_ClusCol1.size(); i1d++){
+          if(find(m_ClusCol2.begin(), m_ClusCol2.end(), m_ClusCol1[i1d])!=m_ClusCol2.end() ) m_overlapCl.push_back(m_ClusCol1[i1d]);
+        }
+
+        for(int io=0; io<m_overlapCl.size(); io++){
+
+          //Split the cluster with +-1 layer cluster energy.
+          int dlayer = m_overlapCl[io]->getDlayer();
+
+          //  For  m_HFClusCol[ic]
+          std::vector<const Calo1DCluster*> m_clus1_front = m_HFClusCol[ic]->getClusterInLayer(dlayer-1);
+          std::vector<const Calo1DCluster*> m_clus1_back  = m_HFClusCol[ic]->getClusterInLayer(dlayer+1);
+          std::vector<const Calo1DCluster*> m_clus2_front = m_HFClusCol[jc]->getClusterInLayer(dlayer-1);
+          std::vector<const Calo1DCluster*> m_clus2_back  = m_HFClusCol[jc]->getClusterInLayer(dlayer+1);
+
+          if( m_clus1_front.size()==0 && m_clus1_back.size()==0 && m_clus2_front.size()==0 && m_clus2_back.size()==0 ){
+            //An isolated hit in both: delete in m_HFClusCol[ic]. TODO: maybe need to optimize based on profile?
+            m_HFClusCol[ic]->deleteUnit(m_overlapCl[io]);
+            continue;
+          }
+          else if( m_clus1_front.size()==0 && m_clus1_back.size()==0 ){
+            //Only isolated in m_HFClusCol[ic]: delete in m_HFClusCol[ic].
+            m_HFClusCol[ic]->deleteUnit(m_overlapCl[io]);
+            continue;
+          }
+          else if( m_clus2_front.size()==0 && m_clus2_back.size()==0 ){
+            m_HFClusCol[jc]->deleteUnit(m_overlapCl[io]);
+            continue;
+          }
+          else{
+            //Use the average energy
+            double aveEn1 = 0.;
+            for(int i1d=0; i1d<m_clus1_front.size(); i1d++) aveEn1 += m_clus1_front[i1d]->getEnergy();
+            for(int i1d=0; i1d<m_clus1_back.size(); i1d++) aveEn1 += m_clus1_back[i1d]->getEnergy();
+            aveEn1 = aveEn1/(m_clus1_front.size()+m_clus1_back.size());
+
+            double aveEn2 = 0.;
+            for(int i1d=0; i1d<m_clus2_front.size(); i1d++) aveEn2 += m_clus2_front[i1d]->getEnergy();
+            for(int i1d=0; i1d<m_clus2_back.size(); i1d++) aveEn2 += m_clus2_back[i1d]->getEnergy();
+            aveEn2 = aveEn2/(m_clus2_front.size()+m_clus2_back.size());
+
+            std::shared_ptr<PandoraPlus::Calo1DCluster> m_splitCl1 = std::make_shared<PandoraPlus::Calo1DCluster>();
+            std::shared_ptr<PandoraPlus::Calo1DCluster> m_splitCl2 = std::make_shared<PandoraPlus::Calo1DCluster>();
+
+            for(int ib=0; ib<m_overlapCl[io]->getBars().size(); ib++){
+              auto bar1 = m_overlapCl[io]->getBars()[ib]->Clone();
+              bar1->setQ( bar1->getQ1()*aveEn1/(aveEn1+aveEn2), bar1->getQ2()*aveEn1/(aveEn1+aveEn2) );
+              m_splitCl1->addUnit(bar1.get());
+
+              auto bar2 = m_overlapCl[io]->getBars()[ib]->Clone();
+              bar2->setQ( bar2->getQ1()*aveEn2/(aveEn1+aveEn2), bar2->getQ2()*aveEn2/(aveEn1+aveEn2) );
+              m_splitCl2->addUnit(bar2.get());
+
+              m_bkCol.map_BarCol["bkBar"].push_back(bar1);
+              m_bkCol.map_BarCol["bkBar"].push_back(bar2);
+            }
+            m_splitCl1->setSeed();
+            m_splitCl1->setIDInfo();
+            m_splitCl1->getLinkedMCPfromUnit();
+            m_splitCl2->setSeed();
+            m_splitCl2->setIDInfo();
+            m_splitCl2->getLinkedMCPfromUnit();
+
+            m_HFClusCol[ic]->deleteUnit(m_overlapCl[io]);
+            m_HFClusCol[ic]->addUnit(m_splitCl1.get());
+
+            m_HFClusCol[jc]->deleteUnit(m_overlapCl[io]);
+            m_HFClusCol[jc]->addUnit(m_splitCl2.get());
+
+            m_bkCol.map_1DCluster["bk1DCluster"].push_back(m_splitCl1);
+            m_bkCol.map_1DCluster["bk1DCluster"].push_back(m_splitCl2);
+          }
+
+
+        }//end loop m_HFClusCol
+      }
+
+    }
+  }
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode EnergySplittingAlg::MergeToClosestCluster( PandoraPlus::Calo1DCluster* iclus, std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>>& clusvec ){
+
+  int cLedge = iclus->getLeftEdge();
+  int cRedge = iclus->getRightEdge();
+
+//printf("ClusterMerging: input cluster layer %d, energy %.4f, edge [%d, %d]\n", iclus->getDlayer(), iclus->getEnergy(), cLedge, cRedge);
+
+  //Find the closest cluster with iclus.
+  int minD = 99;
+  int index = -1;
+  for(int icl=0; icl<clusvec.size(); icl++){
+    if(clusvec[icl].get()->getNseeds()==0) continue;
+    if( iclus->getTowerID() != clusvec[icl].get()->getTowerID() ) continue;
+    if( iclus->getDlayer() != clusvec[icl].get()->getDlayer() ) continue;
+
+    int iLedge = clusvec[icl].get()->getLeftEdge();
+    int iRedge = clusvec[icl].get()->getRightEdge();
+
+    //int dis = (cLedge-iRedge>0 ? cLedge-iRedge : iLedge-cRedge );
+    int dis = min( abs(cLedge-iRedge), abs(iLedge-cRedge) );
+
+//printf("  Loop in clusterCol #%d: range [%d, %d], distance %d, energy ratio %.3f \n", icl, iLedge, iRedge, dis, iclus->getEnergy()/clusvec[icl]->getEnergy());
+    if(dis>10) continue; //Don't merge to a too far cluster.
+    if(dis<minD){ minD = dis; index=icl; }
+  }
+//cout<<"Selected closest cluster #"<<index<<endl;
+
+  if(index<0) return StatusCode::FAILURE;
+
+  //Merge to the selected cluster
+  for(int icl=0; icl<iclus->getBars().size(); icl++)  clusvec[index].get()->addUnit(iclus->getBars()[icl]);
+
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode EnergySplittingAlg::MergeToClosestCluster( const PandoraPlus::Calo1DCluster* m_shower, 
+                                                      std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_clusters )
+{
+  if(m_clusters.size()==0) return StatusCode::SUCCESS;
+
+
+  int minLayer = 99;
+  int maxLayer = -99;
+  for(int ic=0; ic<m_clusters.size(); ic++){
+    if(minLayer>m_clusters[ic].get()->getBeginningDlayer())  minLayer = m_clusters[ic].get()->getBeginningDlayer();
+    if(maxLayer<m_clusters[ic].get()->getEndDlayer())        maxLayer = m_clusters[ic].get()->getEndDlayer();
+  }
+  if(minLayer==99 || minLayer<0 || maxLayer<0) return StatusCode::SUCCESS;
+
+  int dlayer = m_shower->getDlayer();
+  TVector3 sh_pos = m_shower->getPos();
+
+//cout<<"  Cluster range: ("<<minLayer<<", "<<maxLayer<<") "<<endl;
+//cout<<"  Merging shower into cluster: Input shower ";
+//printf(" (%.3f, %.3f, %.3f, %.3f), towerID[%d, %d, %d], layer #%d \n", sh_pos.X(), sh_pos.Y(), sh_pos.Z(), m_shower->getEnergy(), m_shower->getTowerID()[0][0], m_shower->getTowerID()[0][1], m_shower->getTowerID()[0][2], dlayer);
+
+  double minR = 999;
+  int index_cluster = -1;
+  double m_distance = 0;
+  for(int ic=0; ic<m_clusters.size(); ic++ ){
+//printf("    Cluster #%d: pos (%.2f, %.2f, %.2f) \n", ic, m_clusters[ic]->getPos().x(), m_clusters[ic]->getPos().y(), m_clusters[ic]->getPos().z());
+    if(dlayer<=minLayer)
+      m_distance = (sh_pos-m_clusters[ic].get()->getClusterInLayer(m_clusters[ic].get()->getBeginningDlayer())[0]->getPos()).Mag();     
+    else if(dlayer>=maxLayer)
+      m_distance = (sh_pos-m_clusters[ic].get()->getClusterInLayer(m_clusters[ic].get()->getEndDlayer())[0]->getPos()).Mag();    
+    else
+      m_distance = (sh_pos-m_clusters[ic].get()->getPos()).Mag();
+
+//cout<<"    Cluster #"<<ic<<": distance with shower = "<<m_distance<<endl;
+    
+    if( m_distance<minR )  { minR=m_distance; index_cluster=ic; }
+  }
+
+//printf("  minR = %.3f, in #cl %d, cluster size %d \n", minR, index_cluster, m_clusters.size());
+
+  if(index_cluster>=0) m_clusters[index_cluster].get()->addUnit( m_shower );
+
+  return StatusCode::SUCCESS;
+}
+
+
+void EnergySplittingAlg::CalculateInitialEseed( const std::vector<const PandoraPlus::CaloUnit*>& Seeds, const TVector3* pos, double* Eseed){
+//Calculate Eseed by solving a linear function:
+// [ f(11) .. f(1mu)  .. ]   [E_seed 1 ]   [E_bar 1]
+// [  ..   ..   ..    .. ] * [...      ] = [...    ]
+// [ f(i1) .. f(imu)  .. ]   [E_seed mu]   [E_bar i]
+// [ f(N1) ..   ..  f(NN)]   [E_seed N ]   [E_bar N]
+
+  const int Nele = Seeds.size();
+  std::vector<double> Eratio;
+  std::vector<double> vec_Etot; //bar energy
+
+  TVector vecE(Nele);
+  TMatrix matrixR(Nele, Nele);
+
+  for(int i=0;i<Nele;i++){ //Loop bar
+    vecE[i] = Seeds[i]->getEnergy();
+    for(int j=0;j<Nele;j++) matrixR[i][j] = GetShowerProfile(Seeds[i]->getPosition(), pos[j]);
+  }
+
+
+  matrixR.Invert();
+  TVector sol = matrixR*vecE;
+
+  for(int i=0;i<Nele;i++) Eseed[i] = sol[i];
+
+//cout<<"Print initial Eseed: "<<endl;
+//for(int i=0;i<Nele;i++) cout<<Eseed[i]<<'\t';
+//cout<<endl;
+
+}
+
+
+double EnergySplittingAlg::GetShowerProfile(const TVector3& p_bar, const TVector3& p_seed ){
+  TVector3 rpos = p_bar-p_seed;
+  double dis = sqrt(rpos.Mag2());
+  double a1, a2, b1, b2, Rm;
+  a1=0.037; a2=0.265; b1=0.101; b2=0.437; Rm=1.868;
+
+  return a1*exp(-b1*dis/Rm)+a2*exp(-b2*dis/Rm);
+}
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/EnergyTimeMatchingAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/EnergyTimeMatchingAlg.cpp
new file mode 100644
index 00000000..2bfc0c2a
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/EnergyTimeMatchingAlg.cpp
@@ -0,0 +1,1445 @@
+#ifndef ETMATCHING_ALG_C
+#define ETMATCHING_ALG_C
+
+#include "Algorithm/EnergyTimeMatchingAlg.h"
+StatusCode EnergyTimeMatchingAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  //Set initial value
+  if(settings.map_floatPars.find("chi2Wi_E")==settings.map_floatPars.end())          settings.map_floatPars["chi2Wi_E"] = 1.;
+  if(settings.map_floatPars.find("chi2Wi_T")==settings.map_floatPars.end())          settings.map_floatPars["chi2Wi_T"] = 10.;
+  if(settings.map_floatPars.find("sigmaE")==settings.map_floatPars.end())            settings.map_floatPars["sigmaE"] = 0.10;
+  if(settings.map_floatPars.find("sigmaPos")==settings.map_floatPars.end())          settings.map_floatPars["sigmaPos"] = 34.89;
+  if(settings.map_floatPars.find("nMat")==settings.map_floatPars.end())              settings.map_floatPars["nMat"] = 2.15;
+  if(settings.map_floatPars.find("Eth_HFClus")==settings.map_floatPars.end())        settings.map_floatPars["Eth_HFClus"] = 0.05;
+  if(settings.map_floatPars.find("th_overlapE")==settings.map_floatPars.end())       settings.map_floatPars["th_overlapE"] = 0.3;
+  if(settings.map_floatPars.find("th_UVdeltaE")==settings.map_floatPars.end())       settings.map_floatPars["th_UVdeltaE"] = 0.3;
+  if(settings.map_intPars.find("compressLayer")==settings.map_intPars.end())       settings.map_intPars["compressLayer"] = 3;
+  if(settings.map_floatPars.find("th_UVdeltaEinLayer")==settings.map_floatPars.end()) settings.map_floatPars["th_UVdeltaEinLayer"] = 0.5;
+  if(settings.map_floatPars.find("th_ConeTheta")==settings.map_floatPars.end())      settings.map_floatPars["th_ConeTheta"] = TMath::Pi()/6.;
+  if(settings.map_floatPars.find("th_ConeR")==settings.map_floatPars.end())          settings.map_floatPars["th_ConeR"] = 30.;
+
+  if(settings.map_stringPars.find("ReadinHFClusterName")==settings.map_stringPars.end()) settings.map_stringPars["ReadinHFClusterName"] = "ESHalfCluster";
+  if(settings.map_stringPars.find("ReadinTowerName")==settings.map_stringPars.end()) settings.map_stringPars["ReadinTowerName"] = "ESTower";
+  if(settings.map_stringPars.find("OutputClusterName")==settings.map_stringPars.end()) settings.map_stringPars["OutputClusterName"] = "EcalCluster";
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode EnergyTimeMatchingAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  m_HFClusUCol.clear();
+  m_HFClusVCol.clear();
+  m_clusterCol.clear();
+  m_towerCol.clear();
+  m_bkCol.Clear();
+
+  int ntower = m_datacol.map_CaloCluster[settings.map_stringPars["ReadinTowerName"]].size(); 
+  for(int it=0; it<ntower; it++)
+    m_towerCol.push_back( m_datacol.map_CaloCluster[settings.map_stringPars["ReadinTowerName"]][it].get() );
+
+//cout<<"  EnergyTimeMatchingAlg: Readin tower size: "<<m_towerCol.size()<<endl;
+	return StatusCode::SUCCESS;
+};
+
+
+StatusCode EnergyTimeMatchingAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+
+
+  m_clusterCol.clear();
+  std::vector<const PandoraPlus::CaloHalfCluster*> m_leftHFClusterUCol; 
+  std::vector<const PandoraPlus::CaloHalfCluster*> m_leftHFClusterVCol; 
+
+  //Loop for towers:  
+  for(int it=0; it<m_towerCol.size(); it++){
+    m_HFClusUCol.clear(); m_HFClusVCol.clear();
+
+//cout<<"Check tower ID: ";
+//for(int i=0; i<m_towerCol[it]->getTowerID().size(); i++) printf("[%d, %d, %d], ", m_towerCol[it]->getTowerID()[i][0], m_towerCol[it]->getTowerID()[i][1], m_towerCol[it]->getTowerID()[i][2]);
+//cout<<endl;
+
+    m_HFClusUCol = m_towerCol.at(it)->getHalfClusterUCol(settings.map_stringPars["ReadinHFClusterName"]+"U");
+    m_HFClusVCol = m_towerCol.at(it)->getHalfClusterVCol(settings.map_stringPars["ReadinHFClusterName"]+"V");
+
+    Matching(m_HFClusUCol, m_HFClusVCol, m_clusterCol);
+
+  }//End loop towers
+
+  //Re-loop tower for empty half-clusters
+//cout<<"Match clusters without axis"<<endl;
+  for(int it=0; it<m_towerCol.size(); it++){
+    m_HFClusUCol.clear(); m_HFClusVCol.clear();
+
+    m_HFClusUCol = m_towerCol.at(it)->getHalfClusterUCol("emptyHalfClusterU");
+    m_HFClusVCol = m_towerCol.at(it)->getHalfClusterVCol("emptyHalfClusterV");
+
+    Matching(m_HFClusUCol, m_HFClusVCol, m_clusterCol);
+
+  }//End loop tower
+
+  ClusterReconnecting( m_clusterCol );
+  
+  m_datacol.map_CaloCluster[settings.map_stringPars["OutputClusterName"]] = m_clusterCol;
+
+//cout<<" Save backup collections in to main datacol. "<<endl;
+  //Save backup collections in to main datacol. 
+  m_datacol.map_CaloHit["bkHit"].insert( m_datacol.map_CaloHit["bkHit"].end(), m_bkCol.map_CaloHit["bkHit"].begin(), m_bkCol.map_CaloHit["bkHit"].end() );
+  m_datacol.map_BarCol["bkBar"].insert( m_datacol.map_BarCol["bkBar"].end(), m_bkCol.map_BarCol["bkBar"].begin(), m_bkCol.map_BarCol["bkBar"].end() );
+  m_datacol.map_1DCluster["bk1DCluster"].insert( m_datacol.map_1DCluster["bk1DCluster"].end(), m_bkCol.map_1DCluster["bk1DCluster"].begin(), m_bkCol.map_1DCluster["bk1DCluster"].end() );
+  m_datacol.map_2DCluster["bk2DCluster"].insert( m_datacol.map_2DCluster["bk2DCluster"].end(), m_bkCol.map_2DCluster["bk2DCluster"].begin(), m_bkCol.map_2DCluster["bk2DCluster"].end() );
+  m_datacol.map_HalfCluster["bkHalfCluster"].insert( m_datacol.map_HalfCluster["bkHalfCluster"].end(), m_bkCol.map_HalfCluster["bkHalfCluster"].begin(), m_bkCol.map_HalfCluster["bkHalfCluster"].end() );
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode EnergyTimeMatchingAlg::ClearAlgorithm(){
+  m_HFClusUCol.clear();
+  m_HFClusVCol.clear();
+  m_clusterCol.clear();
+  m_towerCol.clear();
+  m_bkCol.Clear();
+  
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode EnergyTimeMatchingAlg::Matching( std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusUCol,
+                                            std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusVCol,
+                                            std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_clusterCol )
+{
+
+  const int NclusU = m_HFClusUCol.size();
+  const int NclusV = m_HFClusVCol.size();
+
+//cout<<" cluster size ["<<NclusU<<", "<<NclusV<<"] "<<endl;
+//cout<<"ClusterU energy: ";
+//for(int i=0; i<NclusU; i++) cout<<m_HFClusUCol[i]->getEnergy()<<'\t';
+//cout<<endl;
+//cout<<"ClusterV energy: ";
+//for(int i=0; i<NclusV; i++) cout<<m_HFClusVCol[i]->getEnergy()<<'\t';
+//cout<<endl;
+
+
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> tmp_clusters; tmp_clusters.clear();
+  if(NclusU==0 || NclusV==0) return StatusCode::SUCCESS;
+
+  else if(NclusU==1 && NclusV==1){
+      std::shared_ptr<PandoraPlus::Calo3DCluster> tmp_clus = std::make_shared<PandoraPlus::Calo3DCluster>();
+      XYClusterMatchingL0(m_HFClusUCol[0], m_HFClusVCol[0], tmp_clus);
+      tmp_clusters.push_back(tmp_clus);
+  }  
+  else if(NclusU==1){
+    double sumE_V = 0.;
+    for(int icl=0; icl<NclusV; icl++) sumE_V += m_HFClusVCol[icl]->getEnergy();
+
+    //Split the HalfClusterU into NclusV part
+    for(int icl=0; icl<NclusV; icl++){
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> newClus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      double frac = m_HFClusVCol[icl]->getEnergy()/sumE_V;
+      for(int ish=0; ish<m_HFClusUCol[0]->getCluster().size(); ish++){
+        const PandoraPlus::Calo1DCluster* p_shower = m_HFClusUCol[0]->getCluster()[ish];
+
+        std::vector<const PandoraPlus::CaloUnit*> Bars; Bars.clear();
+        for(int ibar=0; ibar<p_shower->getCluster().size(); ibar++){
+          auto bar = p_shower->getCluster()[ibar]->Clone();
+          bar->setQ(bar->getQ1()*frac, bar->getQ2()*frac );
+          Bars.push_back(bar.get());
+          m_bkCol.map_BarCol["bkBar"].push_back(bar);
+        }
+
+        std::shared_ptr<PandoraPlus::Calo1DCluster> shower = std::make_shared<PandoraPlus::Calo1DCluster>();
+        shower->setBars(Bars);
+        shower->setSeed();
+        shower->setIDInfo();
+
+        newClus->addUnit(shower.get());
+        m_bkCol.map_1DCluster["bk1DCluster"].push_back( shower );
+      }
+      newClus->setHoughPars(m_HFClusUCol[0]->getHoughAlpha(), m_HFClusUCol[0]->getHoughRho());
+      newClus->setIntercept(m_HFClusUCol[0]->getHoughIntercept());
+      for(auto iter: m_HFClusUCol[0]->getHalfClusterMap()) newClus->setHalfClusters( iter.first, iter.second );
+      for(int itrk=0; itrk<m_HFClusUCol[0]->getAssociatedTracks().size(); itrk++) newClus->addAssociatedTrack(m_HFClusUCol[0]->getAssociatedTracks()[itrk]);
+      newClus->setLinkedMCP(m_HFClusUCol[0]->getLinkedMCP());
+      newClus->setType(m_HFClusUCol[0]->getType());
+      m_bkCol.map_HalfCluster["bkHalfCluster"].push_back(newClus);
+
+      //Match the splitted HalfClusterU and HalfClusterV[icl]
+      std::shared_ptr<PandoraPlus::Calo3DCluster> tmp_clus = std::make_shared<PandoraPlus::Calo3DCluster>();
+      XYClusterMatchingL0(newClus.get(), m_HFClusVCol[icl], tmp_clus);
+      tmp_clusters.push_back(tmp_clus);
+    }
+  }
+
+  else if(NclusV==1){
+    double sumE_U = 0.;
+    for(int icl=0; icl<NclusU; icl++) sumE_U += m_HFClusUCol[icl]->getEnergy();
+
+    //Split the HalfClusterV into NclusU part
+    for(int icl=0; icl<NclusU; icl++){
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> newClus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      double frac = m_HFClusUCol[icl]->getEnergy()/sumE_U;
+      for(int ish=0; ish<m_HFClusVCol[0]->getCluster().size(); ish++){
+        const PandoraPlus::Calo1DCluster* p_shower = m_HFClusVCol[0]->getCluster()[ish];
+
+        std::vector<const PandoraPlus::CaloUnit*> Bars; Bars.clear();
+        for(int ibar=0; ibar<p_shower->getCluster().size(); ibar++){
+          auto bar = p_shower->getCluster()[ibar]->Clone();
+          bar->setQ(bar->getQ1()*frac, bar->getQ2()*frac );
+          Bars.push_back(bar.get());
+          m_bkCol.map_BarCol["bkBar"].push_back(bar);
+        }
+
+        std::shared_ptr<PandoraPlus::Calo1DCluster> shower = std::make_shared<PandoraPlus::Calo1DCluster>();
+        shower->setBars(Bars);
+        shower->setSeed();
+        shower->setIDInfo();
+
+        newClus->addUnit(shower.get());
+        m_bkCol.map_1DCluster["bk1DCluster"].push_back( shower );
+      }
+      newClus->setHoughPars(m_HFClusVCol[0]->getHoughAlpha(), m_HFClusVCol[0]->getHoughRho());
+      newClus->setIntercept(m_HFClusVCol[0]->getHoughIntercept());
+      for(auto iter: m_HFClusVCol[0]->getHalfClusterMap()) newClus->setHalfClusters( iter.first, iter.second );
+      for(int itrk=0; itrk<m_HFClusVCol[0]->getAssociatedTracks().size(); itrk++) newClus->addAssociatedTrack(m_HFClusVCol[0]->getAssociatedTracks()[itrk]);
+      newClus->setLinkedMCP(m_HFClusVCol[0]->getLinkedMCP());
+      newClus->setType(m_HFClusVCol[0]->getType());
+      m_bkCol.map_HalfCluster["bkHalfCluster"].push_back(newClus);
+
+      //Match the splitted HalfClusterU and HalfClusterV[icl]
+      std::shared_ptr<PandoraPlus::Calo3DCluster> tmp_clus = std::make_shared<PandoraPlus::Calo3DCluster>();
+      XYClusterMatchingL0(m_HFClusUCol[icl], newClus.get(), tmp_clus);
+      tmp_clusters.push_back(tmp_clus);
+
+    }
+  }
+
+  else{
+    vector<vector<double>> Chi2Matrix(NclusV, vector<double>(NclusU, 0.));
+    std::vector< std::pair<int, int> > Chi2MatrixOrder; Chi2MatrixOrder.clear();
+    //Calculate the chi2 matrix with energy
+    Chi2MatrixCalculation(m_HFClusUCol, m_HFClusVCol, Chi2Matrix, Chi2MatrixOrder);
+
+//cout<<"Print chi2 matrix "<<endl;
+//for (const auto& row : Chi2Matrix) {
+//    for (const double& val : row) {
+//        std::cout << val << " ";
+//    }
+//    std::cout << std::endl;
+//}
+
+    //Calculate the initial pattern matrix with track + neighbor
+    vector<vector<int>> PatternMatrix(NclusV, vector<int>(NclusU, 0));
+    PatternMatrixCalculation(m_HFClusUCol, m_HFClusVCol, PatternMatrix);
+
+    //If the pattern is empty: use chi2 map to determine the first element.
+    bool isEmpty = true;
+    for(int i=0; i<NclusV && isEmpty; i++){
+      for(int j=0; j<NclusU; j++){
+        if(PatternMatrix[i][j]==1) isEmpty = false;
+        break;
+    }}
+    if(isEmpty){
+      for(int ipoint=0; ipoint<Chi2MatrixOrder.size(); ipoint++){
+        if(PatternMatrix[Chi2MatrixOrder[ipoint].first][Chi2MatrixOrder[ipoint].second]==0 ){
+          PatternMatrix[Chi2MatrixOrder[ipoint].first][Chi2MatrixOrder[ipoint].second] = 1;
+          break;
+        }
+      }
+    }
+
+//cout<<"Print initial pattern matrix"<<endl;
+//for (const auto& row : PatternMatrix) {
+//    for (const int& val : row) {
+//        std::cout << val << " ";
+//    }
+//    std::cout << std::endl;
+//}
+
+    //Initialize
+    double min_residual = 9999;
+    int index = -1;
+    std::vector<double> Eij(NclusU*NclusV, 0.);
+    std::vector<std::vector<int>> ParameterMatrix;
+    std::vector<double> En_clusters; En_clusters.clear();
+    std::vector<double> map_residual; map_residual.clear();
+    std::vector<vector<vector<int>> > map_pattern; map_pattern.clear();
+    std::vector<std::vector<double>> map_Eij; map_Eij.clear();
+    for(int icl=0; icl<NclusV; icl++) En_clusters.push_back( m_HFClusVCol[icl]->getEnergy() );
+    for(int icl=0; icl<NclusU; icl++) En_clusters.push_back( m_HFClusUCol[icl]->getEnergy() );
+
+    //Loop up to NclusV+NclusU
+    for(int iter=0; iter<NclusV+NclusU-1; iter++){
+      //Modify the chi2 map with pattern matrix
+      bool modified = false;
+      bool hasEmptyRow = false;
+      bool hasEmptyColumn = false; 
+      for (size_t i = 0; i < PatternMatrix.size(); ++i) {
+          int rowSum = 0;
+          for (size_t j = 0; j < PatternMatrix[i].size(); ++j) {
+              rowSum += PatternMatrix[i][j];
+          }
+          if(rowSum==0){
+            hasEmptyRow = true;
+            break;
+          }
+      }
+      for (size_t j = 0; j < PatternMatrix[0].size(); ++j) {
+          int colSum = 0;
+          for (size_t i = 0; i < PatternMatrix.size(); ++i) {
+              colSum += PatternMatrix[i][j];
+          }
+          if(colSum==0){
+            hasEmptyColumn = true;
+            break;
+          }
+      }
+
+      vector<vector<double>> Chi2Matrix_copy; 
+      if(hasEmptyRow && hasEmptyColumn){
+
+        Chi2Matrix_copy = Chi2Matrix;
+
+        for (size_t i = 0; i < PatternMatrix.size(); ++i) {
+            int rowSum = 0;
+            for (size_t j = 0; j < PatternMatrix[i].size(); ++j) {
+                rowSum += PatternMatrix[i][j];
+            }
+            if (rowSum != 0) {
+                Chi2Matrix_copy[i] = vector<double>(PatternMatrix[0].size(), 9999.);
+                modified = true;
+            }
+        }
+        for (size_t j = 0; j < PatternMatrix[0].size(); ++j) {
+            int colSum = 0;
+            for (size_t i = 0; i < PatternMatrix.size(); ++i) {
+                colSum += PatternMatrix[i][j];
+            }
+            if (colSum != 0) {
+                for (size_t i = 0; i < PatternMatrix.size(); ++i) {
+                    Chi2Matrix_copy[i][j] = 9999.;
+                }
+                modified = true;
+            }
+        }
+      }
+      else if(hasEmptyRow || hasEmptyColumn){
+        Chi2Matrix_copy = vector<vector<double>>(NclusV, vector<double>(NclusU, 9999.));
+        for (size_t i = 0; i < PatternMatrix.size(); ++i) {
+            int rowSum = 0;
+            for (size_t j = 0; j < PatternMatrix[i].size(); ++j) {
+                rowSum += PatternMatrix[i][j];
+            }
+            if (rowSum == 0) {
+                Chi2Matrix_copy[i] = Chi2Matrix[i];
+                modified = true;
+            }
+        }
+
+        for (size_t j = 0; j < PatternMatrix[0].size(); ++j) {
+            int colSum = 0;
+            for (size_t i = 0; i < PatternMatrix.size(); ++i) {
+                colSum += PatternMatrix[i][j];
+            }
+            if (colSum == 0) {
+                for (size_t i = 0; i < PatternMatrix.size(); ++i) {
+                    Chi2Matrix_copy[i][j] = Chi2Matrix[i][j];
+                }
+                modified = true;
+            }
+        }
+      }
+      else Chi2Matrix_copy = Chi2Matrix;
+    
+
+//cout<<"In iteration #"<<iter<<": Print updated chi2 matrix"<<endl;
+//for (const auto& row : Chi2Matrix_copy) {
+//    for (const double& val : row) {
+//        std::cout << val << " ";
+//    }
+//    std::cout << std::endl;
+//}
+
+      std::vector< std::pair<int, int> > Chi2MatrixOrder_copy;
+      if(modified){
+        std::vector<std::pair<double, int>> indices;
+        for(size_t i = 0; i < Chi2Matrix_copy.size(); ++i) {
+          for (size_t j = 0; j < Chi2Matrix_copy[i].size(); ++j) {
+            indices.push_back(std::make_pair(Chi2Matrix_copy[i][j], i * Chi2Matrix_copy[i].size() + j));
+          }
+        }
+        std::sort(indices.begin(), indices.end());
+        for(auto &iter1: indices)
+          Chi2MatrixOrder_copy.push_back( std::make_pair( iter1.second/Chi2Matrix_copy[0].size(), iter1.second%Chi2Matrix_copy[0].size() ) );
+      }
+      else
+        Chi2MatrixOrder_copy = Chi2MatrixOrder;
+
+
+      //Update the pattern matrix with chi2 map
+      bool updateFlag = false;
+      if(iter>0){
+        for(int ipoint=0; ipoint<Chi2MatrixOrder_copy.size(); ipoint++){
+          if(PatternMatrix[Chi2MatrixOrder_copy[ipoint].first][Chi2MatrixOrder_copy[ipoint].second]==0 ){
+            PatternMatrix[Chi2MatrixOrder_copy[ipoint].first][Chi2MatrixOrder_copy[ipoint].second] = 1;
+            updateFlag = true;
+            break;
+          }
+        }
+      }
+      if(iter>0 && !updateFlag){
+        //std::cout<<"ERROR: Already loop for all pattern element. Break. "<<std::endl;
+        break;
+      }
+
+//cout<<"  Print new pattern matrix "<<endl;
+//for (const auto& row : PatternMatrix) {
+//    for (const int& val : row) {
+//        std::cout << val << " ";
+//    }
+//    std::cout << std::endl;
+//}
+
+      //Re-calculate the parameter matrix, simplify it with pattern matrix
+      ParameterMatrix.clear(); Eij.clear();
+      Eij = std::vector<double>(NclusU*NclusV, 0.);
+      ParameterMatrixCalculation(NclusV, NclusU, ParameterMatrix);
+      SimplityMatrix(ParameterMatrix, Eij, PatternMatrix);
+
+      //Recalculate the residual
+      double residual = SolveMatrix(NclusU, NclusV, ParameterMatrix, Eij, En_clusters);
+//cout<<"  In iteration "<<iter<<": residual = "<<residual<<endl;
+
+      if(residual<min_residual){
+        min_residual = residual;
+        index = iter;
+      }
+      map_residual.push_back(residual);
+      map_Eij.push_back(Eij);
+      map_pattern.push_back(PatternMatrix);
+    }
+
+//cout<<"Minimal residual = "<<min_residual<<", index "<<index<<endl;
+//cout<<"Chosen pattern matrix "<<endl;
+//for (const auto& row : map_pattern[index]) {
+//    for (const int& val : row) {
+//        std::cout << val << " ";
+//    }
+//    std::cout << std::endl;
+//}
+
+    //Derive final energy matrix
+    vector<vector<double>> EnergyMatrix(NclusV, vector<double>(NclusU, 0));
+    int icount = 0;
+    for(int icl=0; icl<NclusV; icl++){
+      for(int jcl=0; jcl<NclusU; jcl++){
+        if(map_pattern[index][icl][jcl]==1){
+          EnergyMatrix[icl][jcl] = map_Eij[index][icount];
+          icount++;
+        }
+      }
+    }
+//cout<<"Calculated energy matrix"<<endl;
+//for (const auto& row : EnergyMatrix) {
+//    for (const double& val : row) {
+//        std::cout << val << " ";
+//    }
+//    std::cout << std::endl;
+//}
+
+    //Create Calo3DClusters with the result
+    ClusterBuilding(EnergyMatrix, m_HFClusUCol, m_HFClusVCol, tmp_clusters);
+  }
+
+  //Clean empty Calo3DClusters
+  for(int ic=0; ic<tmp_clusters.size(); ic++){
+    if( !tmp_clusters[ic].get() || tmp_clusters[ic]->getEnergy()==0 || isnan(tmp_clusters[ic]->getShowerCenter().x()) ){
+      tmp_clusters.erase(tmp_clusters.begin()+ic);
+      ic--;
+    }
+  }
+
+  m_clusterCol.insert( m_clusterCol.end(), tmp_clusters.begin(), tmp_clusters.end() );
+  tmp_clusters.clear();
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode EnergyTimeMatchingAlg::PatternMatrixCalculation( std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusUCol,
+                                                            std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusVCol,
+                                                            vector<vector<int>>& matrix )
+{
+
+  const int NclusU = m_HFClusUCol.size();
+  const int NclusV = m_HFClusVCol.size();  
+
+  if(NclusU==0 || NclusV==0){
+    std::cout<<"ERROR: empty HalfCluster input. Check! "<<std::endl;
+    matrix.clear();
+    return StatusCode::SUCCESS;
+  }
+  else if(NclusU<=1 || NclusV<=1){
+    for(int icl=0; icl<NclusV; icl++){
+      for(int jcl=0; jcl<NclusU; jcl++) matrix[icl][jcl] = 1;
+    }
+    return StatusCode::SUCCESS;
+  }
+  else{
+    //Fill the matrix with track info
+    std::vector<const PandoraPlus::Track*> m_linkedTrk; m_linkedTrk.clear();
+
+    for(int icl=0; icl<m_HFClusUCol.size(); ++icl){
+      std::vector<const PandoraPlus::Track*> tmp_linkedTrk = m_HFClusUCol[icl]->getAssociatedTracks();
+      if(tmp_linkedTrk.size()==0) continue;
+      m_linkedTrk.insert(m_linkedTrk.end(), tmp_linkedTrk.begin(), tmp_linkedTrk.end() );
+    }
+    for(int icl=0; icl<m_HFClusVCol.size(); ++icl){
+      std::vector<const PandoraPlus::Track*> tmp_linkedTrk = m_HFClusVCol[icl]->getAssociatedTracks();
+      if(tmp_linkedTrk.size()==0) continue;
+      m_linkedTrk.insert(m_linkedTrk.end(), tmp_linkedTrk.begin(), tmp_linkedTrk.end());
+    }
+
+    for(int itrk=0; itrk<m_linkedTrk.size(); itrk++){
+      int indexU = -1;
+      int indexV = -1;
+      for(int icl=0; icl<m_HFClusUCol.size(); ++icl){
+        std::vector<const PandoraPlus::Track*> m_linkedTrkU = m_HFClusUCol[icl]->getAssociatedTracks();
+        if(m_linkedTrkU.size()==0) continue;
+        if(find(m_linkedTrkU.begin(), m_linkedTrkU.end(), m_linkedTrk[itrk])!=m_linkedTrkU.end()){
+          indexU = icl;
+          break;
+        }
+      }
+
+      for(int icl=0; icl<m_HFClusVCol.size(); ++icl){
+        std::vector<const PandoraPlus::Track*> m_linkedTrkV = m_HFClusVCol[icl]->getAssociatedTracks();
+        if(m_linkedTrkV.size()==0) continue;
+        if(find(m_linkedTrkV.begin(), m_linkedTrkV.end(), m_linkedTrk[itrk])!=m_linkedTrkV.end()){
+          indexV = icl;
+          break;
+        }
+      }
+
+      if(indexU>=0 && indexV>=0) matrix[indexV][indexU] = 1;
+    }
+
+    //Fill the matrix with neighbor info
+    for(int icl=0; icl<m_HFClusUCol.size(); ++icl){
+      if(m_HFClusUCol[icl]->getHalfClusterCol("CousinCluster").size()==0) continue;
+      for(int jcl=0; jcl<m_HFClusVCol.size(); ++jcl){
+        if(m_HFClusVCol[jcl]->getHalfClusterCol("CousinCluster").size()==0) continue;
+
+        std::vector<const PandoraPlus::CaloHalfCluster*> m_cousinU = m_HFClusUCol[icl]->getHalfClusterCol("CousinCluster");
+        std::vector<const PandoraPlus::CaloHalfCluster*> m_cousinV = m_HFClusVCol[jcl]->getHalfClusterCol("CousinCluster");
+
+        //Make map: <towerID, (E_clU - E_clV)/mean(E_clU, E_clV)  >
+        std::map<std::vector<int>, pair<float, float>> map_pairE; map_pairE.clear();
+        map_pairE[m_HFClusUCol[icl]->getTowerID()[0]] = make_pair(m_HFClusUCol[icl]->getEnergy(), m_HFClusVCol[jcl]->getEnergy());
+        for(int ics=0; ics<m_cousinU.size(); ++ics){
+        for(int jcs=0; jcs<m_cousinV.size(); ++jcs){
+          if(m_cousinU[ics]->getTowerID()[0] == m_cousinV[jcs]->getTowerID()[0])
+            map_pairE[ m_cousinU[ics]->getTowerID()[0] ] = make_pair(m_cousinU[ics]->getEnergy(), m_cousinV[jcs]->getEnergy());
+
+        }}
+        if(map_pairE.size()<=1) continue; //No common tower cousin clusters.
+
+        bool isLink = true;
+        float totE_U = 0.;
+        float totE_V = 0.;
+        for(auto iter: map_pairE){
+          float deltaE = 2*fabs(iter.second.first - iter.second.second)/(iter.second.first + iter.second.second);
+          totE_U += iter.second.first;
+          totE_V += iter.second.second;
+          //In tower: if too large difference between U/V then do not link.
+          if(deltaE > settings.map_floatPars["th_UVdeltaE"]) { isLink = false; break; }
+          //if(iter.second.first>1 && iter.second.second>1 && deltaE > settings.map_floatPars["th_UVdeltaE1"]){ isLink = false; break; } //High energy: deltaE
+          //else if(iter.second.first/iter.second.second > 10 || iter.second.first/iter.second.second<0.1) { isLink = false; break; }  //Low energy: 0.1<E_u/E_v<10.
+        }
+        if( 2*fabs(totE_U-totE_V)/(totE_U+totE_V)>settings.map_floatPars["th_UVdeltaE"] ) isLink=false;
+
+        if(isLink) matrix[jcl][icl] = 1;
+      }
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode EnergyTimeMatchingAlg::Chi2MatrixCalculation( std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusUCol,
+                                                         std::vector<const PandoraPlus::CaloHalfCluster*> m_HFClusVCol,
+                                                         vector<vector<double>>& matrix,
+                                                         std::vector< std::pair<int, int> >& chi2order)
+{
+
+  const int NclusU = m_HFClusUCol.size();
+  const int NclusV = m_HFClusVCol.size();
+//cout<<"  Chi2MatrixCalculation: input cluster size "<<NclusU<<", "<<NclusV<<endl;
+
+  if(NclusU==0 || NclusV==0){
+    std::cout<<"ERROR: empty HalfCluster input. Check! "<<std::endl;
+    matrix.clear();
+    chi2order.clear();
+    return StatusCode::SUCCESS;
+  }
+  else if(NclusU<=1 || NclusV<=1){
+    for(int icl=0; icl<NclusV; icl++){
+      for(int jcl=0; jcl<NclusU; jcl++) matrix[icl][jcl] = 0.;
+    }
+    chi2order.clear();
+    return StatusCode::SUCCESS;
+  }
+  else{ 
+//cout<<" Print sum chi2 matrix"<<endl;
+//for (const auto& row : matrix) {
+//    for (const double& val : row) {
+//        std::cout << val << " ";
+//    }
+//    std::cout << std::endl;
+//}
+
+    int Nlayer = PandoraPlus::CaloUnit::Nlayer; 
+    for(int il=0; il<Nlayer; il++){
+      std::vector<std::vector<const PandoraPlus::Calo1DCluster*>> tmp_showerU(NclusU);
+      std::vector<std::vector<const PandoraPlus::Calo1DCluster*>> tmp_showerV(NclusV);
+      for(int icl=0; icl<NclusU; icl++){
+        for(int jcl=0; jcl<NclusV; jcl++){
+          tmp_showerU[icl] = m_HFClusUCol[icl]->getClusterInLayer(il+1);
+          tmp_showerV[jcl] = m_HFClusVCol[jcl]->getClusterInLayer(il+1);
+        }
+      }
+//cout<<"  In layer #"<<il<<": cluster size "<<tmp_showerU.size()<<", "<<tmp_showerV.size()<<endl;
+      vector<vector<double>> chi2Map;
+      chi2Map = GetClusterChi2Map(tmp_showerU, tmp_showerV);
+
+//cout<<" Print chi2 matrix"<<endl;
+//for (const auto& row : chi2Map) {
+//    for (const double& val : row) {
+//        std::cout << val << " ";
+//    }
+//    std::cout << std::endl;
+//}
+
+      for(int icl=0; icl<NclusU; icl++){
+        for(int jcl=0; jcl<NclusV; jcl++){
+          matrix[jcl][icl] += chi2Map[jcl][icl];
+          //Use 1/E as weight: 
+          //matrix[jcl][icl] += chi2Map[jcl][icl]*( 1./m_HFClusUCol[icl]->getEnergy() + 1./m_HFClusVCol[jcl]->getEnergy() );
+        }
+      }
+//cout<<" Print sum chi2 matrix"<<endl;
+//for (const auto& row : matrix) {
+//    for (const double& val : row) {
+//        std::cout << val << " ";
+//    }
+//    std::cout << std::endl;
+//}
+    }
+
+    
+    //Get the order of chi2 matrix map
+    std::vector<std::pair<double, int>> indices;
+    for(size_t i = 0; i < matrix.size(); ++i) {
+      for (size_t j = 0; j < matrix[i].size(); ++j) {
+        indices.push_back(std::make_pair(matrix[i][j], i * matrix[i].size() + j));
+      }
+    }
+    std::sort(indices.begin(), indices.end());
+//cout<<"  matrix size: "<<matrix.size()<<", "<<matrix[0].size()<<endl;
+    for(auto &iter: indices)
+      chi2order.push_back( std::make_pair( iter.second/matrix[0].size(), iter.second%matrix[0].size() ) );
+
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+vector<vector<double>> EnergyTimeMatchingAlg::GetClusterChi2Map( std::vector<std::vector<const PandoraPlus::Calo1DCluster*>>& barShowerUCol,
+                                                                 std::vector<std::vector<const PandoraPlus::Calo1DCluster*>>& barShowerVCol )
+{
+
+
+  const int NclusU = barShowerUCol.size();
+  const int NclusV = barShowerVCol.size();
+  vector<vector<double>> chi2map_E(NclusV, vector<double>(NclusU, 0.));
+  vector<vector<double>> chi2map_tx(NclusV, vector<double>(NclusU, 0.));
+  vector<vector<double>> chi2map_ty(NclusV, vector<double>(NclusU, 0.));
+  vector<vector<double>> chi2map(NclusV, vector<double>(NclusU, 0.));
+
+  if(NclusU==0 || NclusV==0) return chi2map;
+
+  double wi_E = settings.map_floatPars["chi2Wi_E"]/(settings.map_floatPars["chi2Wi_E"] + settings.map_floatPars["chi2Wi_T"]);
+  double wi_T = settings.map_floatPars["chi2Wi_T"]/(settings.map_floatPars["chi2Wi_E"] + settings.map_floatPars["chi2Wi_T"]);
+
+  TVector3 m_vec(0,0,0);
+  double rotAngle = -999;
+  TVector3 Ctower(0,0,0);
+  for(int ish=0; ish<barShowerUCol.size(); ish++){
+    if(barShowerUCol[ish].size()==0) continue;
+    rotAngle = -(barShowerUCol[ish][0]->getBars())[0]->getModule()*TMath::TwoPi()/PandoraPlus::CaloUnit::Nmodule;
+    Ctower.SetX( (barShowerUCol[ish][0]->getBars())[0]->getPosition().x() );
+    Ctower.SetY( (barShowerUCol[ish][0]->getBars())[0]->getPosition().y() );
+  }
+  for(int ish=0; ish<barShowerVCol.size(); ish++){
+    if(barShowerVCol[ish].size()==0) continue;
+    Ctower.SetZ( (barShowerVCol[ish][0]->getBars())[0]->getPosition().z() );
+  }
+  Ctower.RotateZ(rotAngle);
+
+  for(int ix=0;ix<NclusU;ix++){
+  for(int iy=0;iy<NclusV;iy++){
+    std::vector<const PandoraPlus::Calo1DCluster*> clusterU = barShowerUCol[ix];
+    std::vector<const PandoraPlus::Calo1DCluster*> clusterV = barShowerVCol[iy];
+
+    if(clusterU.size()==0){
+      double totE_V = 0;
+      for(int icy=0; icy<clusterV.size(); icy++) totE_V += clusterV[icy]->getEnergy();
+      chi2map[iy][ix] = pow(totE_V/settings.map_floatPars["sigmaE"], 2);
+      continue;
+    }
+    if(clusterV.size()==0){
+      double totE_U = 0;
+      for(int icy=0; icy<clusterU.size(); icy++) totE_U += clusterU[icy]->getEnergy();
+      chi2map[iy][ix] = pow(totE_U/settings.map_floatPars["sigmaE"], 2);
+      continue;
+    }
+
+    double min_chi2E = 999;
+    double min_chi2tx = 999;
+    double min_chi2ty = 999;
+
+    for(int icx=0; icx<clusterU.size(); icx++){
+    for(int icy=0; icy<clusterV.size(); icy++){
+      const PandoraPlus::Calo1DCluster* showerX = clusterU[icx];
+      const PandoraPlus::Calo1DCluster* showerY = clusterV[icy];
+
+      double Ex = showerX->getEnergy();
+      double Ey = showerY->getEnergy();
+      double chi2_E = pow(fabs(Ex-Ey)/settings.map_floatPars["sigmaE"], 2);
+      double PosTx = C*(showerY->getT1()-showerY->getT2())/(2*settings.map_floatPars["nMat"]) + showerY->getPos().z();
+      double chi2_tx = pow( fabs(PosTx-showerX->getPos().z())/settings.map_floatPars["sigmaPos"], 2 );
+
+      double PosTy = C*(showerX->getT1()-showerX->getT2())/(2*settings.map_floatPars["nMat"]);
+      m_vec = showerY->getPos();
+      m_vec.RotateZ(rotAngle);
+      double chi2_ty = pow( fabs(PosTy - (m_vec-Ctower).x() )/settings.map_floatPars["sigmaPos"], 2);
+
+      if(chi2_E<min_chi2E) min_chi2E=chi2_E;
+      if(chi2_tx<min_chi2tx) min_chi2tx=chi2_tx;
+      if(chi2_ty<min_chi2ty) min_chi2ty=chi2_ty;
+    }}
+
+    chi2map_E[iy][ix] = min_chi2E;
+    chi2map_tx[iy][ix] = min_chi2tx;
+    chi2map_ty[iy][ix] = min_chi2ty;
+    chi2map[iy][ix] = chi2map_E[iy][ix]*wi_E + (chi2map_tx[iy][ix]+chi2map_ty[iy][ix])*wi_T ;
+//cout<<"  chi2 value "<<min_chi2E<<endl;
+
+  }}
+
+  return chi2map;
+}
+
+
+StatusCode EnergyTimeMatchingAlg::ParameterMatrixCalculation(const int& M, const int& N, vector<vector<int>>& parMatrix){
+
+  parMatrix = vector<vector<int>>(M + N, std::vector<int>(M * N, 0));
+  //Fill first M raws
+  for (int i = 0; i < M; i++) {
+      for (int j = i * N; j < (i + 1) * N; j++) {
+          parMatrix[i][j] = 1;
+      }
+  }
+  //Fill second N raws
+  for(int i=M; i<N+M; i++){
+    for(int j=0; j<M; j++)
+      parMatrix[i][i-M+j*N] = 1;
+  }
+
+  return StatusCode::SUCCESS; 
+}
+
+
+StatusCode EnergyTimeMatchingAlg::SimplityMatrix(vector<vector<int>>& parMatrix, vector<double>& Eij, vector<vector<int>>& pattern){
+  int M = pattern.size();
+  int N = pattern[0].size();
+
+  int count = 0;
+  for(int i=0; i<M; i++){
+    for(int j=0; j<N; j++){
+      int k = i*N+j-count;
+      if(pattern[i][j]==0){
+          for (int a = 0; a < M + N; a++) {
+            parMatrix[a].erase(parMatrix[a].begin() + k);
+          }
+          Eij.erase(Eij.begin()+k);
+          count++;
+      }
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+double EnergyTimeMatchingAlg::SolveMatrix(const int& M, const int& N, vector<vector<int>>& parMatrix, vector<double>& Eij, vector<double>& En_clusters){
+
+
+  leastSquares(parMatrix, En_clusters, Eij);
+  vector<double> residual; residual.clear();
+
+  for (int i = 0; i < parMatrix.size(); i++) {
+      double r = 0;
+      for (int j = 0; j < parMatrix[i].size(); j++) {
+          r += parMatrix[i][j] * Eij[j];
+      }
+      r -= En_clusters[i];
+      residual.push_back(r);
+  }
+
+  double residualSum = 0;
+  for (int i = 0; i < residual.size(); i++) {
+      residualSum += residual[i] * residual[i];
+  }
+
+  return residualSum;
+}
+
+
+StatusCode EnergyTimeMatchingAlg::leastSquares(const vector<vector<int>>& A, const vector<double>& b, vector<double>& x){
+  int m = A.size();
+  int n = A[0].size();
+
+  vector<vector<double>> Q, R;
+  vector<vector<double>> doubleA(m, vector<double>(n));
+  for(int i=0; i<m; i++){
+    for(int j=0; j<n; j++)
+      doubleA[i][j] = (double)A[i][j];
+  }
+
+  //qrDecomposition(doubleA, Q, R);
+  Q = vector<vector<double>>(m, vector<double>(n, 0));
+  R = vector<vector<double>>(n, vector<double>(n, 0));
+  vector<vector<double>> A_copy = doubleA;
+  for (int j = 0; j < n; j++) {
+      double colNorm = 0.0;
+      for (int i = 0; i < m; i++) {
+          colNorm += A_copy[i][j] * A_copy[i][j];
+      }
+      colNorm = sqrt(colNorm);
+
+      R[j][j] = colNorm;
+
+      for (int i = 0; i < m; i++) {
+          Q[i][j] = A_copy[i][j] / colNorm;
+      }
+
+      for (int k = j + 1; k < n; k++) {
+          double dotProduct = 0.0;
+          for (int i = 0; i < m; i++) {
+              dotProduct += Q[i][j] * A_copy[i][k];
+          }
+          R[j][k] = dotProduct;
+          for (int i = 0; i < m; i++) {
+              A_copy[i][k] -= Q[i][j] * dotProduct;
+          }
+      }
+  }
+
+
+  vector<double> Qt_b(n, 0);
+  for (int i = 0; i < m; i++) {
+      for (int j = 0; j < n; j++) {
+          Qt_b[j] += Q[i][j] * b[i];
+      }
+  }
+
+  x = vector<double>(n, 0);
+  for (int i = n - 1; i >= 0; i--) {
+      double sum = 0.0;
+      for (int j = i + 1; j < n; j++) {
+          sum += R[i][j] * x[j];
+      }
+      if(R[i][i]!=0) x[i] = (Qt_b[i] - sum) / R[i][i];
+      if(x[i]<0) x[i] = 0.;
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode EnergyTimeMatchingAlg::ClusterBuilding( std::vector<std::vector<double>> Ematrix,
+                                                   std::vector<const PandoraPlus::CaloHalfCluster*>& m_HFClusUCol,
+                                                   std::vector<const PandoraPlus::CaloHalfCluster*>& m_HFClusVCol,
+                                                   std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_clusterCol)
+{
+
+  int NclusU = m_HFClusUCol.size();
+  int NclusV = m_HFClusVCol.size();
+  if(NclusU==0 || NclusV==0){
+    std::cout<<"ERROR: Empty readin HalfCluster! "<<NclusU<<", "<<NclusV<<endl;
+  }
+  else if(NclusU==1 && NclusV==1){
+    std::shared_ptr<PandoraPlus::Calo3DCluster> tmp_clus = std::make_shared<PandoraPlus::Calo3DCluster>();
+    XYClusterMatchingL0(m_HFClusUCol[0], m_HFClusVCol[0], tmp_clus);
+    m_clusterCol.push_back(tmp_clus);
+  }
+  else{
+    std::vector<double> sumE_U(NclusU, 0.); //column
+    std::vector<double> sumE_V(NclusV, 0.); //raw
+    for(int icl=0; icl<NclusV; icl++){
+      for(int jcl=0; jcl<NclusU; jcl++){
+        sumE_V[icl] += Ematrix[icl][jcl];
+        sumE_U[jcl] += Ematrix[icl][jcl];
+      }
+    }
+
+    for(int icl=0; icl<NclusV; icl++){
+      for(int jcl=0; jcl<NclusU; jcl++){
+
+        double fracU = Ematrix[icl][jcl]/sumE_U[jcl];
+        if(fracU==0) continue;
+        std::shared_ptr<PandoraPlus::CaloHalfCluster> newClusU = std::make_shared<PandoraPlus::CaloHalfCluster>();
+        if(fracU==1) newClusU = m_HFClusUCol[jcl]->Clone();
+        else{
+          for(int ish=0; ish<m_HFClusUCol[jcl]->getCluster().size(); ish++){
+            const PandoraPlus::Calo1DCluster* p_shower = m_HFClusUCol[jcl]->getCluster()[ish];
+   
+            std::vector<const PandoraPlus::CaloUnit*> Bars; Bars.clear();
+            for(int ibar=0; ibar<p_shower->getCluster().size(); ibar++){
+              auto bar = p_shower->getCluster()[ibar]->Clone();
+              bar->setQ(bar->getQ1()*fracU, bar->getQ2()*fracU );
+              Bars.push_back(bar.get());
+              m_bkCol.map_BarCol["bkBar"].push_back(bar);
+            }
+   
+            std::shared_ptr<PandoraPlus::Calo1DCluster> shower = std::make_shared<PandoraPlus::Calo1DCluster>();
+            shower->setBars(Bars);
+            shower->setSeed();
+            shower->setIDInfo();
+   
+            newClusU->addUnit(shower.get());
+            m_bkCol.map_1DCluster["bk1DCluster"].push_back( shower );
+          }
+          newClusU->setHoughPars(m_HFClusUCol[jcl]->getHoughAlpha(), m_HFClusUCol[jcl]->getHoughRho());
+          newClusU->setIntercept(m_HFClusUCol[jcl]->getHoughIntercept());
+          for(auto iter: m_HFClusUCol[jcl]->getHalfClusterMap()) newClusU->setHalfClusters( iter.first, iter.second );
+          for(int itrk=0; itrk<m_HFClusUCol[jcl]->getAssociatedTracks().size(); itrk++) newClusU->addAssociatedTrack(m_HFClusUCol[jcl]->getAssociatedTracks()[itrk]);
+          newClusU->setLinkedMCP(m_HFClusUCol[jcl]->getLinkedMCP());
+          newClusU->setType(m_HFClusUCol[jcl]->getType());
+        }
+
+        std::shared_ptr<PandoraPlus::CaloHalfCluster> newClusV = std::make_shared<PandoraPlus::CaloHalfCluster>();
+        double fracV = Ematrix[icl][jcl]/sumE_V[icl];
+        if(fracV==0) continue;
+        if(fracV==1) newClusV = m_HFClusVCol[icl]->Clone();
+        else{
+          for(int ish=0; ish<m_HFClusVCol[icl]->getCluster().size(); ish++){
+            const PandoraPlus::Calo1DCluster* p_shower = m_HFClusVCol[icl]->getCluster()[ish];
+   
+            std::vector<const PandoraPlus::CaloUnit*> Bars; Bars.clear();
+            for(int ibar=0; ibar<p_shower->getCluster().size(); ibar++){
+              auto bar = p_shower->getCluster()[ibar]->Clone();
+              bar->setQ(bar->getQ1()*fracV, bar->getQ2()*fracV );
+              Bars.push_back(bar.get());
+              m_bkCol.map_BarCol["bkBar"].push_back(bar);
+            }
+   
+            std::shared_ptr<PandoraPlus::Calo1DCluster> shower = std::make_shared<PandoraPlus::Calo1DCluster>();
+            shower->setBars(Bars);
+            shower->setSeed();
+            shower->setIDInfo();
+   
+            newClusV->addUnit(shower.get());
+            m_bkCol.map_1DCluster["bk1DCluster"].push_back( shower );
+          }
+          newClusV->setHoughPars(m_HFClusVCol[icl]->getHoughAlpha(), m_HFClusVCol[icl]->getHoughRho());
+          newClusV->setIntercept(m_HFClusVCol[icl]->getHoughIntercept());
+          for(auto iter: m_HFClusVCol[icl]->getHalfClusterMap()) newClusV->setHalfClusters( iter.first, iter.second );
+          for(int itrk=0; itrk<m_HFClusVCol[icl]->getAssociatedTracks().size(); itrk++) newClusV->addAssociatedTrack(m_HFClusVCol[icl]->getAssociatedTracks()[itrk]);
+          newClusV->setLinkedMCP(m_HFClusVCol[icl]->getLinkedMCP());
+          newClusV->setType(m_HFClusVCol[icl]->getType());
+        }
+        m_bkCol.map_HalfCluster["bkHalfCluster"].push_back(newClusU);
+        m_bkCol.map_HalfCluster["bkHalfCluster"].push_back(newClusV);
+
+        std::shared_ptr<PandoraPlus::Calo3DCluster> tmp_clus = std::make_shared<PandoraPlus::Calo3DCluster>();
+        XYClusterMatchingL0(newClusU.get(), newClusV.get(), tmp_clus);
+        m_clusterCol.push_back(tmp_clus);
+    }}
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+//Longitudinal cluster: 1*1
+StatusCode EnergyTimeMatchingAlg::XYClusterMatchingL0( const PandoraPlus::CaloHalfCluster* m_longiClU,
+                                                       const PandoraPlus::CaloHalfCluster* m_longiClV,
+                                                       std::shared_ptr<PandoraPlus::Calo3DCluster>& m_clus )
+{
+//cout<<"  Cluster matching for case: 1 * 1. Input HalfCluster En: "<<m_longiClU->getEnergy()<<", "<<m_longiClV->getEnergy()<<endl;
+//cout<<"  Print 1DShower En in HalfClusterU: "<<endl;
+//for(int i=0; i<m_longiClU->getCluster().size(); i++)
+//  cout<<m_longiClU->getCluster()[i]->getDlayer()<<'\t'<<m_longiClU->getCluster()[i]->getEnergy()<<endl;
+//cout<<"  Print 1DShower En in HalfClusterV: "<<endl;
+//for(int i=0; i<m_longiClV->getCluster().size(); i++)
+//  cout<<m_longiClV->getCluster()[i]->getDlayer()<<'\t'<<m_longiClV->getCluster()[i]->getEnergy()<<endl;
+
+
+  std::vector<int> layerindex; layerindex.clear();
+  std::map<int, std::vector<const PandoraPlus::Calo1DCluster*> > map_showersUinlayer; map_showersUinlayer.clear();
+  std::map<int, std::vector<const PandoraPlus::Calo1DCluster*> > map_showersVinlayer; map_showersVinlayer.clear();
+
+  for(int is=0; is<m_longiClU->getCluster().size(); is++){
+    int m_layer = m_longiClU->getCluster()[is]->getDlayer();
+    if( find( layerindex.begin(), layerindex.end(), m_layer )==layerindex.end() ) layerindex.push_back(m_layer);
+    map_showersUinlayer[m_layer].push_back(m_longiClU->getCluster()[is]);
+  }
+  for(int is=0; is<m_longiClV->getCluster().size(); is++){
+    int m_layer = m_longiClV->getCluster()[is]->getDlayer();
+    if( find( layerindex.begin(), layerindex.end(), m_layer )==layerindex.end() ) layerindex.push_back(m_layer);
+    map_showersVinlayer[m_layer].push_back(m_longiClV->getCluster()[is]);
+  }
+
+  for(int il=0; il<layerindex.size(); il++){
+    std::vector<const PandoraPlus::Calo1DCluster*> m_showerXcol = map_showersUinlayer[layerindex[il]];
+    std::vector<const PandoraPlus::Calo1DCluster*> m_showerYcol = map_showersVinlayer[layerindex[il]];
+
+//cout<<"  Print 1D showers in layer "<<layerindex[il]<<endl;
+//cout<<"  ShowerU size = "<<m_showerXcol.size()<<endl;
+//for(int a=0; a<m_showerXcol.size(); a++) printf("    #%d shower: En %.3f, address %p \n", a, m_showerXcol[a]->getEnergy(), m_showerXcol[a]);
+//cout<<"  ShowerV size = "<<m_showerYcol.size()<<endl;
+//for(int a=0; a<m_showerYcol.size(); a++) printf("    #%d shower: En %.3f, address %p \n", a, m_showerYcol[a]->getEnergy(), m_showerYcol[a]);
+
+    std::vector<PandoraPlus::Calo2DCluster*> m_showerinlayer; m_showerinlayer.clear();
+
+    if(m_showerXcol.size()==0 || m_showerYcol.size()==0) continue;
+    //else if(m_showerXcol.size()==0){
+    //  std::shared_ptr<PandoraPlus::Calo2DCluster> tmp_shower = std::make_shared<PandoraPlus::Calo2DCluster>();
+    //  GetMatchedShowerFromEmpty(m_showerYcol[0], m_longiClU, tmp_shower.get());
+    //  //m_showerinlayer.push_back(tmp_shower.get());
+    //  //m_bkCol.map_2DCluster["bk2DCluster"].push_back(tmp_shower);
+    //}
+    //else if(m_showerYcol.size()==0){
+    //  std::shared_ptr<PandoraPlus::Calo2DCluster> tmp_shower = std::make_shared<PandoraPlus::Calo2DCluster>();
+    //  GetMatchedShowerFromEmpty(m_showerXcol[0], m_longiClU, tmp_shower.get());
+    //}
+    else if(m_showerXcol.size()==1 && m_showerYcol.size()==1){
+      std::shared_ptr<PandoraPlus::Calo2DCluster> tmp_shower = std::make_shared<PandoraPlus::Calo2DCluster>();
+      GetMatchedShowersL0(m_showerXcol[0], m_showerYcol[0], tmp_shower.get());
+      m_showerinlayer.push_back(tmp_shower.get());
+      m_bkCol.map_2DCluster["bk2DCluster"].push_back(tmp_shower);
+    }
+    else if(m_showerXcol.size()==1) GetMatchedShowersL1(m_showerXcol[0], m_showerYcol, m_showerinlayer );
+    else if(m_showerYcol.size()==1) GetMatchedShowersL1(m_showerYcol[0], m_showerXcol, m_showerinlayer );
+    else{ std::cout<<"CAUSION in XYClusterMatchingL0: HFCluster has ["<<m_showerXcol.size()<<", "<<m_showerYcol.size()<<"] showers in layer "<<layerindex[il]<<std::endl; }
+
+//cout<<"    After matching: shower size = "<<m_showerinlayer.size()<<", Print showers: "<<endl;
+//for(int is=0; is<m_showerinlayer.size(); is++) printf("  Pos+E (%.3f, %.3f, %.3f, %.3f) \t", m_showerinlayer[is]->getPos().x(), m_showerinlayer[is]->getPos().y(), m_showerinlayer[is]->getPos().z(), m_showerinlayer[is]->getEnergy() );
+//cout<<endl;
+
+    for(int is=0; is<m_showerinlayer.size(); is++) m_clus->addUnit(m_showerinlayer[is]);
+  }
+
+  m_clus->setCaloHitsFrom2DCluster();
+  m_clus->addHalfClusterU( "LinkedLongiCluster", m_longiClU );
+  m_clus->addHalfClusterV( "LinkedLongiCluster", m_longiClV );
+  for(auto itrk : m_longiClU->getAssociatedTracks()){
+    for(auto jtrk : m_longiClV->getAssociatedTracks()){
+      if(itrk!=jtrk) continue;
+      if( find(m_clus->getAssociatedTracks().begin(), m_clus->getAssociatedTracks().end(), itrk)==m_clus->getAssociatedTracks().end() )
+        m_clus->addAssociatedTrack(itrk);
+  }}
+  m_clus->getLinkedMCPfromHFCluster("LinkedLongiCluster");
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode EnergyTimeMatchingAlg::GetMatchedShowersL0( const PandoraPlus::Calo1DCluster* barShowerU,
+                                                       const PandoraPlus::Calo1DCluster* barShowerV,
+                                                       PandoraPlus::Calo2DCluster* outsh )
+{
+
+  std::vector<const PandoraPlus::CaloHit*> m_digiCol; m_digiCol.clear();
+  int NbarsX = barShowerU->getBars().size();
+  int NbarsY = barShowerV->getBars().size();
+  if(NbarsX==0 || NbarsY==0){ std::cout<<"WARNING: empty DigiHitsCol returned!"<<std::endl; return StatusCode::SUCCESS; }
+  if(barShowerU->getTowerID().size()==0) { std::cout<<"WARNING:GetMatchedShowersL0  No TowerID in 1DCluster!"<<std::endl; return StatusCode::SUCCESS; }
+  //if(barShowerU->getTowerID().size()==0) { barShowerU->setIDInfo(); }
+
+  int _layer = barShowerU->getDlayer();
+  int _module = barShowerU->getTowerID()[0][0];
+  float rotAngle = -_module*TMath::TwoPi()/PandoraPlus::CaloUnit::Nmodule;
+
+  for(int ibar=0;ibar<NbarsX;ibar++){
+    double En_x = barShowerU->getBars()[ibar]->getEnergy();
+    TVector3 m_vecx = barShowerU->getBars()[ibar]->getPosition();
+    m_vecx.RotateZ(rotAngle);
+
+    for(int jbar=0;jbar<NbarsY;jbar++){
+      double En_y = barShowerV->getBars()[jbar]->getEnergy();
+      TVector3 m_vecy = barShowerV->getBars()[jbar]->getPosition();
+      m_vecy.RotateZ(rotAngle);
+
+      TVector3 p_hit(m_vecy.x(), (m_vecx.y()+m_vecy.y())/2., m_vecx.z() );
+      p_hit.RotateZ(-rotAngle);
+      double m_Ehit = En_x*En_y/barShowerV->getEnergy() + En_x*En_y/barShowerU->getEnergy();
+      //Create new CaloHit
+      std::shared_ptr<PandoraPlus::CaloHit> hit = std::make_shared<PandoraPlus::CaloHit>();
+      hit->setcellID(_module, _layer);
+      hit->setPosition(p_hit);
+      hit->setEnergy(m_Ehit);
+      m_digiCol.push_back(hit.get());
+      m_bkCol.map_CaloHit["bkHit"].push_back( hit );
+    }
+  }
+
+  outsh->addUnit( barShowerU );
+  outsh->addUnit( barShowerV );
+  outsh->setCaloHits( m_digiCol );
+//cout<<"    End output shower"<<endl;
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode EnergyTimeMatchingAlg::GetMatchedShowersL1( const PandoraPlus::Calo1DCluster* shower1,
+                                                       std::vector<const PandoraPlus::Calo1DCluster*>& showerNCol,
+                                                       std::vector<PandoraPlus::Calo2DCluster*>& outshCol )
+{
+//cout<<"  GetMatchedShowersL1: input shower size: 1 * "<<showerNCol.size()<<endl;
+  outshCol.clear();
+
+  int _slayer = shower1->getBars()[0]->getSlayer();
+
+  const int NshY = showerNCol.size();
+  double totE_shY = 0;
+  double EshY[NshY] = {0};
+  for(int is=0;is<NshY;is++){ EshY[is] = showerNCol[is]->getEnergy(); totE_shY += EshY[is]; }
+  for(int is=0;is<NshY;is++){
+    double wi_E = EshY[is]/totE_shY;
+    std::shared_ptr<PandoraPlus::Calo1DCluster> m_splitshower1 = std::make_shared<PandoraPlus::Calo1DCluster>();
+    m_bkCol.map_1DCluster["bk1DCluster"].push_back( m_splitshower1 );
+
+    std::shared_ptr<PandoraPlus::CaloUnit> m_wiseed = nullptr;
+    if(shower1->getSeeds().size()>0) m_wiseed = shower1->getSeeds()[0]->Clone();
+    else{ cout<<"ERROR: Input shower has no seed! Check! Use the most energitic bar as seed. bar size: "<<shower1->getBars().size()<<endl;
+      double m_maxE = -99;
+      int index = -1;
+      for(int ib=0; ib<shower1->getBars().size(); ib++){
+        if(shower1->getBars()[ib]->getEnergy()>m_maxE) { m_maxE=shower1->getBars()[ib]->getEnergy(); index=ib; }
+      }
+      if(index>=0) m_wiseed = shower1->getBars()[index]->Clone();
+    }
+    m_wiseed->setQ( wi_E*m_wiseed->getQ1(), wi_E*m_wiseed->getQ2() );
+    m_bkCol.map_BarCol["bkBar"].push_back( m_wiseed );
+
+    std::vector<const PandoraPlus::CaloUnit*> m_wibars; m_wibars.clear();
+    for(int ib=0;ib<shower1->getBars().size();ib++){
+      std::shared_ptr<PandoraPlus::CaloUnit> m_wibar = shower1->getBars()[ib]->Clone();
+      m_wibar->setQ(wi_E*m_wibar->getQ1(), wi_E*m_wibar->getQ2());
+      m_wibars.push_back(m_wibar.get());
+      m_bkCol.map_BarCol["bkBar"].push_back( m_wibar );
+    }
+    m_splitshower1->setBars( m_wibars );
+    m_splitshower1->addSeed( m_wiseed.get() );
+    m_splitshower1->setIDInfo();
+    std::shared_ptr<PandoraPlus::Calo2DCluster> m_shower = std::make_shared<PandoraPlus::Calo2DCluster>();
+    if(_slayer==0 ) GetMatchedShowersL0( m_splitshower1.get(), showerNCol[is], m_shower.get() );
+    else            GetMatchedShowersL0( showerNCol[is], m_splitshower1.get(), m_shower.get() );
+
+    outshCol.push_back( m_shower.get() );
+    m_bkCol.map_2DCluster["bk2DCluster"].push_back( m_shower );
+  }
+  return StatusCode::SUCCESS;
+}
+
+
+
+
+StatusCode EnergyTimeMatchingAlg::ClusterReconnecting( std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_clusterCol ){
+/*
+cout<<"Print 3DClusters"<<endl;
+for(int ic=0; ic<m_clusterCol.size(); ic++){
+  printf("  Cluster #%d pos+En [%.3f, %.3f, %.3f, %.3f], hit size %d \n ", ic, m_clusterCol[ic]->getShowerCenter().x(), 
+                                                          m_clusterCol[ic]->getShowerCenter().y(), 
+                                                          m_clusterCol[ic]->getShowerCenter().z(), 
+                                                          m_clusterCol[ic]->getEnergy(), 
+                                                          m_clusterCol[ic]->getCaloHits().size() );
+  std::vector<const CaloHalfCluster*> m_HFClusUInTower = m_clusterCol[ic]->getHalfClusterUCol("LinkedLongiCluster");
+  std::vector<const CaloHalfCluster*> m_HFClusVInTower = m_clusterCol[ic]->getHalfClusterVCol("LinkedLongiCluster");  
+  cout<<"  Linked half cluster size: "<<m_HFClusUInTower.size()<<", "<<m_HFClusVInTower.size()<<endl;
+  cout<<"    Loop print HalfClusterU: "<<endl;
+  for(int icl=0; icl<m_HFClusUInTower.size(); icl++){
+    cout<<"      In HFClusU #"<<icl<<": shower size = "<<m_HFClusUInTower[icl]->getCluster().size()<<", En = "<<m_HFClusUInTower[icl]->getEnergy()<<", type "<<m_HFClusUInTower[icl]->getType();
+    printf(", Position (%.3f, %.3f, %.3f), address %p ",m_HFClusUInTower[icl]->getPos().x(), m_HFClusUInTower[icl]->getPos().y(), m_HFClusUInTower[icl]->getPos().z(), m_HFClusUInTower[icl]);
+    printf(", cousin size %d, address: ", m_HFClusUInTower[icl]->getHalfClusterCol("CousinCluster").size());
+    for(int ics=0; ics<m_HFClusUInTower[icl]->getHalfClusterCol("CousinCluster").size(); ics++) printf("%p, ", m_HFClusUInTower[icl]->getHalfClusterCol("CousinCluster")[ics]);
+    printf(", track size %d, address: ", m_HFClusUInTower[icl]->getAssociatedTracks().size());
+    for(int itrk=0; itrk<m_HFClusUInTower[icl]->getAssociatedTracks().size(); itrk++) printf("%p, ", m_HFClusUInTower[icl]->getAssociatedTracks()[itrk]);
+    cout<<endl;
+    for(auto ish : m_HFClusUInTower[icl]->getCluster()){
+      printf("          Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Nbars %d, NSeed %d, Address %p \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish->getBars().size(),  ish->getNseeds(), ish );
+    }
+  }
+  cout<<endl;
+  cout<<"    Loop print HalfClusterV: "<<endl;
+  for(int icl=0; icl<m_HFClusVInTower.size(); icl++){
+    cout<<"      In HFClusV #"<<icl<<": shower size = "<<m_HFClusVInTower[icl]->getCluster().size()<<", En = "<<m_HFClusVInTower[icl]->getEnergy()<<", type "<<m_HFClusVInTower[icl]->getType();
+    printf(", Position (%.3f, %.3f, %.3f), address %p ",m_HFClusVInTower[icl]->getPos().x(), m_HFClusVInTower[icl]->getPos().y(), m_HFClusVInTower[icl]->getPos().z(), m_HFClusVInTower[icl]);
+    printf(", cousin size %d, address: ", m_HFClusVInTower[icl]->getHalfClusterCol("CousinCluster").size());
+    for(int ics=0; ics<m_HFClusVInTower[icl]->getHalfClusterCol("CousinCluster").size(); ics++) printf("%p, ", m_HFClusVInTower[icl]->getHalfClusterCol("CousinCluster")[ics]);
+    printf(", track size %d, address: ", m_HFClusVInTower[icl]->getAssociatedTracks().size());
+    for(int itrk=0; itrk<m_HFClusVInTower[icl]->getAssociatedTracks().size(); itrk++) printf("%p, ", m_HFClusVInTower[icl]->getAssociatedTracks()[itrk]);
+    cout<<endl;
+    for(auto ish : m_HFClusVInTower[icl]->getCluster()){
+      printf("          Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Nbars %d, NSeed %d, Address %p \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish->getBars().size(), ish->getNseeds(), ish );
+    }
+  }
+  cout<<endl;
+}
+*/
+  //Remove the same clusters
+  for(int ic=0; ic<m_clusterCol.size() && m_clusterCol.size()>1; ic++){
+    for(int jc=ic+1; jc<m_clusterCol.size(); jc++){
+      if(ic>m_clusterCol.size()) ic--;
+
+      //From pos+E. Can confirm from ChildCluster info.
+      float m_clusE_ic = m_clusterCol[ic].get()->getEnergy();
+      TVector3 m_pos_ic = m_clusterCol[ic].get()->getShowerCenter();
+      float m_clusE_jc = m_clusterCol[jc].get()->getEnergy();
+      TVector3 m_pos_jc = m_clusterCol[jc].get()->getShowerCenter();
+      if( fabs(m_clusE_ic-m_clusE_jc)<0.1 && (m_pos_ic-m_pos_jc).Mag()<10 ){
+        m_clusterCol.erase(m_clusterCol.begin()+jc);
+        jc--;
+        if(ic>jc+1) ic--;
+      }
+    }
+  }
+
+//cout<<"  ClusterReconnecting: Cluster size after double-counting check: "<<m_clusterCol.size()<<endl;
+
+  //  Type2: from longiCluster aspect
+  for(int ic=0; ic<m_clusterCol.size() && m_clusterCol.size()>1; ic++){
+    if( m_clusterCol[ic].get()->getHalfClusterUCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster").size()==0 ||
+        m_clusterCol[ic].get()->getHalfClusterVCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster").size()==0 ) continue;
+//cout<<"  Cluster ic "<<ic<<" has parent"<<endl;
+    for(int jc=ic+1; jc<m_clusterCol.size(); jc++){
+      if( m_clusterCol[jc].get()->getHalfClusterUCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster").size()==0 ||
+          m_clusterCol[jc].get()->getHalfClusterVCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster").size()==0 ) continue;
+//cout<<"  Cluster jc "<<jc<<" also has parent, start check"<<endl;
+/*
+      std::vector<const PandoraPlus::CaloHalfCluster*> m_cousinU = m_clusterCol[ic].get()->getHalfClusterUCol("LinkedLongiCluster")[0]->getHalfClusterCol("CousinCluster");
+      std::vector<const PandoraPlus::CaloHalfCluster*> m_cousinV = m_clusterCol[ic].get()->getHalfClusterVCol("LinkedLongiCluster")[0]->getHalfClusterCol("CousinCluster");
+      if( find(m_cousinU.begin(), m_cousinU.end(), m_clusterCol[jc].get()->getHalfClusterUCol("LinkedLongiCluster")[0] )!=m_cousinU.end() &&
+          find(m_cousinV.begin(), m_cousinV.end(), m_clusterCol[jc].get()->getHalfClusterVCol("LinkedLongiCluster")[0] )!=m_cousinV.end() &&
+          m_clusterCol[ic].get()->getShowerCenter().Angle( m_clusterCol[jc].get()->getShowerCenter() )<0.05 ){ //3deg, ~10cm in ECAL.
+        m_clusterCol[ic].get()->mergeCluster( m_clusterCol[jc].get() );
+        m_clusterCol.erase(m_clusterCol.begin()+jc);
+        jc--;
+        if(jc<ic) jc=ic;
+      }
+
+      m_cousinU.clear();
+      m_cousinV.clear();
+      m_cousinU = m_clusterCol[jc].get()->getHalfClusterUCol("LinkedLongiCluster")[0]->getHalfClusterCol("CousinCluster");
+      m_cousinV = m_clusterCol[jc].get()->getHalfClusterVCol("LinkedLongiCluster")[0]->getHalfClusterCol("CousinCluster");
+      if( find(m_cousinU.begin(), m_cousinU.end(), m_clusterCol[ic].get()->getHalfClusterUCol("LinkedLongiCluster")[0] )!=m_cousinU.end() &&
+          find(m_cousinV.begin(), m_cousinV.end(), m_clusterCol[ic].get()->getHalfClusterVCol("LinkedLongiCluster")[0] )!=m_cousinV.end() &&
+          m_clusterCol[ic].get()->getShowerCenter().Angle( m_clusterCol[jc].get()->getShowerCenter() )<0.05 ){ //3deg, ~10cm in ECAL.
+        m_clusterCol[ic].get()->mergeCluster( m_clusterCol[jc].get() );
+        m_clusterCol.erase(m_clusterCol.begin()+jc);
+        jc--;
+        if(jc<ic) jc=ic;
+      }
+*/
+      if( m_clusterCol[ic].get()->getHalfClusterUCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster").size()>1 || 
+          m_clusterCol[ic].get()->getHalfClusterVCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster").size()>1 ||
+          m_clusterCol[jc].get()->getHalfClusterUCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster").size()>1 ||
+          m_clusterCol[jc].get()->getHalfClusterVCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster").size()>1 ){
+
+        cout<<"  ERROR: more than 1 Parent Cluster!"<<endl; 
+        printf("    In #%d cluster: parent cluster size [%d, %d]. In  #%d cluster: parent cluster size [%d, %d] \n", ic, m_clusterCol[ic].get()->getHalfClusterUCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster").size(), m_clusterCol[ic].get()->getHalfClusterVCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster").size(), jc, m_clusterCol[jc].get()->getHalfClusterUCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster").size(), m_clusterCol[jc].get()->getHalfClusterVCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster").size() );
+        continue;
+      }      
+
+      const PandoraPlus::CaloHalfCluster* m_parentU1 = m_clusterCol[ic].get()->getHalfClusterUCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster")[0]; 
+      const PandoraPlus::CaloHalfCluster* m_parentV1 = m_clusterCol[ic].get()->getHalfClusterVCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster")[0]; 
+      const PandoraPlus::CaloHalfCluster* m_parentU2 = m_clusterCol[jc].get()->getHalfClusterUCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster")[0];
+      const PandoraPlus::CaloHalfCluster* m_parentV2 = m_clusterCol[jc].get()->getHalfClusterVCol("LinkedLongiCluster")[0]->getHalfClusterCol("ParentCluster")[0];
+      if(m_parentU1==m_parentU2 && m_parentV1==m_parentV2 && m_clusterCol[ic].get()->getShowerCenter().Angle( m_clusterCol[jc].get()->getShowerCenter() )<0.05){
+//cout<<"  In pair ("<<ic<<", "<<jc<<"): merge "<<endl;
+        m_clusterCol[ic].get()->mergeCluster( m_clusterCol[jc].get() );
+        m_clusterCol.erase(m_clusterCol.begin()+jc);
+        jc--;
+        if(jc<ic) jc=ic;
+      }
+  }}
+
+//cout<<"  ClusterReconnecting: Cluster size after neighbor check: "<<m_clusterCol.size()<<endl;
+
+  //  Type3: merge clusters linked to the same track.
+  for(int ic=0; ic<m_clusterCol.size() && m_clusterCol.size()>1; ic++){
+    if(m_clusterCol[ic].get()->getAssociatedTracks().size()==0) continue;
+    std::vector<const PandoraPlus::Track*> m_trkCol = m_clusterCol[ic].get()->getAssociatedTracks();
+
+    for(int jc=ic+1; jc<m_clusterCol.size(); jc++){
+      if(m_clusterCol[jc].get()->getAssociatedTracks().size()==0) continue;
+//cout<<"Check pair: ["<<ic<<", "<<jc<<"]. Both have tracks. "<<endl;
+
+      for(int itrk=0; itrk<m_clusterCol[jc].get()->getAssociatedTracks().size(); itrk++){
+        if( find(m_trkCol.begin(), m_trkCol.end(), m_clusterCol[jc].get()->getAssociatedTracks()[itrk])!= m_trkCol.end() ){
+//cout<<"  Merge cluster pair: ["<<ic<<", "<<jc<<"]. "<<endl;
+          m_clusterCol[ic].get()->mergeCluster( m_clusterCol[jc].get() );
+          m_clusterCol.erase(m_clusterCol.begin()+jc);
+          jc--;
+          if(jc<ic) jc=ic;
+        }
+//cout<<"  After merge: ic="<<ic<<", jc="<<jc<<endl;
+        break;
+      }
+    }
+  }
+  for(int ic=0; ic<m_clusterCol.size(); ic++) m_clusterCol[ic].get()->getLinkedMCPfromHFCluster("LinkedLongiCluster");
+
+//cout<<"  ClusterReconnecting: Cluster size after track check: "<<m_clusterCol.size()<<endl;
+
+/*  //Check the clusters with the same ChildHFCluster: re-matching. 
+  //std::multimap<const CaloHalfCluster*, std::shared_ptr<PandoraPlus::Calo3DCluster>> map_linkedClusters; map_linkedClusters.clear();
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> Cluster_needMatch; Cluster_needMatch.clear();
+  for(int ic=0; ic<m_clusterCol.size(); ic++){
+    std::vector<const CaloHalfCluster*> m_HFClusU = m_clusterCol[ic].get()->getHalfClusterUCol("LinkedLongiCluster");
+    std::vector<const CaloHalfCluster*> m_HFClusV = m_clusterCol[ic].get()->getHalfClusterVCol("LinkedLongiCluster");
+
+    if(m_HFClusU.size()!=1 || m_HFClusV.size()!=1){
+      std::cout<<"  ERROR: 3Dcluster has more than 1 LinkedLongiCluster: ("<<m_HFClusU.size()<<", "<<m_HFClusV.size()<<")! Check! "<<std::endl;
+      continue;
+    }
+    std::vector<const CaloHalfCluster*> m_ChildClU = m_HFClusU[0]->getHalfClusterCol("ChildCluster");
+    std::vector<const CaloHalfCluster*> m_ChildClV = m_HFClusV[0]->getHalfClusterCol("ChildCluster");
+    if(m_ChildClU.size()==0 && m_ChildClV.size()==0) continue; 
+
+    //for(int icl=0; icl<m_ChildClU.size(); icl++) map_linkedClusters.insert( {m_HFClusU[icl], m_clusterCol[ic]} );
+    //for(int icl=0; icl<m_ChildClV.size(); icl++) map_linkedClusters.insert( {m_HFClusV[icl], m_clusterCol[ic]} );
+    Cluster_needMatch.push_back( m_clusterCol[ic] );
+    m_clusterCol.erase( m_clusterCol.begin()+ic );
+    ic--;
+  }
+cout<<"  ClusterReconnecting: Cluster size after child check: "<<m_clusterCol.size()<<endl;
+cout<<"  Need-match cluster size: "<<Cluster_needMatch.size()<<endl;
+
+  std::vector<const CaloHalfCluster*> m_ChildClU; 
+  std::vector<const CaloHalfCluster*> m_ChildClV; 
+  for(int ic=0; ic<Cluster_needMatch.size(); ic++){
+    std::vector<const CaloHalfCluster*> tmp_ChildCl = Cluster_needMatch[ic].get()->getHalfClusterUCol("LinkedLongiCluster")[0]->getHalfClusterCol("ChildCluster");
+    m_ChildClU.insert( m_ChildClU.end(), tmp_ChildCl.begin(), tmp_ChildCl.end() );
+    tmp_ChildCl.clear();
+    tmp_ChildCl = Cluster_needMatch[ic].get()->getHalfClusterVCol("LinkedLongiCluster")[0]->getHalfClusterCol("ChildCluster");
+    m_ChildClV.insert( m_ChildClV.end(), tmp_ChildCl.begin(), tmp_ChildCl.end() );
+  }
+cout<<"  Total child cluster size: ("<<m_ChildClU.size()<<", "<<m_ChildClV.size()<<endl;
+
+  std::vector<const CaloHalfCluster*> m_mergedClusU; m_mergedClusU.clear();
+  std::vector<const CaloHalfCluster*> m_eraseCl; m_eraseCl.clear(); 
+  do{
+    for(int icl=0; icl<m_ChildClU.size(); icl++){
+      std::vector<const CaloHalfCluster*> tmp_cousin = m_ChildClU[icl]->getHalfClusterCol("CousinCluster");
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> merged_HFClU = std::make_shared<PandoraPlus::CaloHalfCluster>();
+
+      merged_HFClU->mergeHalfCluster(m_ChildClU[icl]);
+      merged_HFClU.get()->addHalfCluster("ChildCluster", m_ChildClU[icl]);
+      for(auto cl_U : tmp_cousin){
+        merged_HFClU.get()->mergeHalfCluster(cl_U);
+        merged_HFClU.get()->addHalfCluster("ChildCluster", cl_U);
+        m_eraseCl.push_back(cl_U);
+      }
+      m_eraseCl.push_back(m_ChildClU[icl]);
+
+      m_bkCol.map_HalfCluster["bkHalfCluster"].push_back(merged_HFClU);
+      m_mergedClusU.push_back(merged_HFClU.get());
+      break;
+    }
+cout<<"    waiting erasing cluster size: "<<m_eraseCl.size()<<endl;
+    for(int icl=0; icl<m_eraseCl.size(); icl++){
+      auto iter_find = find( m_ChildClU.begin(), m_ChildClU.end(), m_eraseCl[icl] );
+      if(iter_find!=m_ChildClU.end()) m_ChildClU.erase( iter_find );
+    }
+    m_eraseCl.clear();
+cout<<"    left child cluster size: "<<m_ChildClU.size()<<endl;
+  }while(m_ChildClU.size()!=0);
+
+  std::vector<const CaloHalfCluster*> m_mergedClusV; m_mergedClusV.clear();
+  m_eraseCl.clear();
+  do{
+    for(int icl=0; icl<m_ChildClV.size(); icl++){
+      std::vector<const CaloHalfCluster*> tmp_cousin = m_ChildClV[icl]->getHalfClusterCol("CousinCluster");
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> merged_HFClV = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      merged_HFClV->mergeHalfCluster(m_ChildClV[icl]);
+      merged_HFClV.get()->addHalfCluster("ChildCluster", m_ChildClV[icl]);
+      for(auto cl_V : tmp_cousin){
+        merged_HFClV.get()->mergeHalfCluster(cl_V);
+        merged_HFClV.get()->addHalfCluster("ChildCluster", cl_V);
+        m_eraseCl.push_back(cl_V);
+      }
+      m_eraseCl.push_back(m_ChildClV[icl]);
+
+      m_bkCol.map_HalfCluster["bkHalfCluster"].push_back(merged_HFClV);
+      m_mergedClusV.push_back(merged_HFClV.get());
+      break;
+    }
+    for(int icl=0; icl<m_eraseCl.size(); icl++){
+      auto iter_find = find( m_ChildClV.begin(), m_ChildClV.end(), m_eraseCl[icl] );
+      if(iter_find!=m_ChildClV.end()) m_ChildClV.erase( iter_find );
+    }
+    m_eraseCl.clear();
+
+  }while(m_ChildClV.size()!=0);
+cout<<"  Merged HFCluster size: ("<<m_mergedClusU.size()<<", "<<m_mergedClusV.size()<<endl;
+
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> newClusters; newClusters.clear();
+  if(m_mergedClusU.size()==0 || m_mergedClusV.size()==0) return StatusCode::SUCCESS;
+  else if( m_mergedClusU.size()==1 && m_mergedClusV.size()==1 ){
+    std::shared_ptr<PandoraPlus::Calo3DCluster> tmp_clus = std::make_shared<PandoraPlus::Calo3DCluster>();
+    XYClusterMatchingL0(m_mergedClusU[0], m_mergedClusV[0], tmp_clus);
+    newClusters.push_back(tmp_clus);
+  }
+  else if( m_mergedClusU.size()==1 ){
+    std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> emptyCol;
+    XYClusterMatchingL1(m_mergedClusU[0], m_mergedClusV, emptyCol);
+    newClusters.insert( newClusters.end(), emptyCol.begin(), emptyCol.end() );
+  }
+  else if( m_mergedClusV.size()==1 ){
+    std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> emptyCol;
+    XYClusterMatchingL1(m_mergedClusV[0], m_mergedClusU, emptyCol);
+    newClusters.insert( newClusters.end(), emptyCol.begin(), emptyCol.end() );
+  }
+  else if( m_mergedClusU.size()==m_mergedClusV.size() ){
+    std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> emptyCol;
+    XYClusterMatchingL2(m_mergedClusU, m_mergedClusV, emptyCol);
+    newClusters.insert( newClusters.end(), emptyCol.begin(), emptyCol.end() );
+  }
+  else{
+    std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> emptyCol;
+    XYClusterMatchingL3(m_mergedClusU, m_mergedClusV, emptyCol);
+    newClusters.insert( newClusters.end(), emptyCol.begin(), emptyCol.end() );
+  }
+cout<<"  Created new 3DCluster size: "<<newClusters.size()<<endl;
+
+  for(int ic=0; ic<newClusters.size(); ic++){
+    if( !newClusters[ic].get() ){
+      newClusters.erase(newClusters.begin()+ic);
+      ic--;
+  }}
+cout<<"  Created new 3DCluster size: "<<newClusters.size()<<endl;
+
+  m_clusterCol.insert(m_clusterCol.end(), newClusters.begin(), newClusters.end() );
+*/
+  return StatusCode::SUCCESS;
+}
+
+
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/ExampleAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/ExampleAlg.cpp
new file mode 100644
index 00000000..2a3c5b76
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/ExampleAlg.cpp
@@ -0,0 +1,50 @@
+#ifndef _EXAMPLE_ALG_C
+#define _EXAMPLE_ALG_C
+
+#include "Algorithm/ExampleAlg.h"
+
+StatusCode ExampleAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  //Initialize parameters
+  if(settings.map_stringPars.find("Par1")==settings.map_stringPars.end()) settings.map_stringPars["Par1"] = "HERE";
+  if(settings.map_floatPars.find("Par2")==settings.map_floatPars.end()) settings.map_floatPars["Par2"] = 0;
+  if(settings.map_boolPars.find("Par3")==settings.map_boolPars.end()) settings.map_boolPars["Par3"] = 0;
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode ExampleAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  std::cout<<"Initialize ExampleAlg"<<std::endl;
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode ExampleAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+  std::cout<<"Excute ExampleAlg"<<std::endl;
+  std::cout<<"  Run SelfAlg1"<<std::endl;
+  SelfAlg1();
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode ExampleAlg::ClearAlgorithm(){
+  std::cout<<"End run ExampleAlg. Clean it."<<std::endl;
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode ExampleAlg::SelfAlg1(){
+  std::cout<<"  Processing SelfAlg1: print Setting parameters"<<std::endl;
+
+  for(auto &iter : settings.map_stringPars) cout<<iter.first<<": "<<iter.second<<endl;
+  for(auto &iter : settings.map_floatPars) cout<<iter.first<<": "<<iter.second<<endl;
+  for(auto &iter : settings.map_boolPars) cout<<iter.first<<": "<<iter.second<<endl;
+
+
+  return StatusCode::SUCCESS;
+};
+
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/GlobalClusteringAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/GlobalClusteringAlg.cpp
new file mode 100644
index 00000000..4efcfae2
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/GlobalClusteringAlg.cpp
@@ -0,0 +1,146 @@
+#ifndef GLOBALCLUSTERING_ALG_C
+#define GLOBALCLUSTERING_ALG_C
+
+#include "Algorithm/GlobalClusteringAlg.h"
+using namespace PandoraPlus;
+
+StatusCode GlobalClusteringAlg::ReadSettings(PandoraPlus::Settings& m_settings){
+  settings = m_settings;
+  if(settings.map_floatPars.find("unit_threshold")==settings.map_floatPars.end())  settings.map_floatPars["unit_threshold"] = 0.001;
+  if(settings.map_stringPars.find("InputECALBars")==settings.map_stringPars.end()) settings.map_stringPars["InputECALBars"] = "BarCol";
+  if(settings.map_stringPars.find("OutputECAL1DClusters")==settings.map_stringPars.end()) settings.map_stringPars["OutputECAL1DClusters"] = "Cluster1DCol";
+  if(settings.map_stringPars.find("OutputECALHalfClusters")==settings.map_stringPars.end()) settings.map_stringPars["OutputECALHalfClusters"] = "HalfClusterCol";
+  return StatusCode::SUCCESS;
+};
+
+StatusCode GlobalClusteringAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  m_bars.clear();
+  m_processbars.clear(); 
+  m_restbars.clear();
+  m_1dclusters.clear();
+  m_halfclusters.clear();
+
+  //Readin data from DataCol: 
+  m_bars = m_datacol.map_BarCol[settings.map_stringPars["InputECALBars"]];
+
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode GlobalClusteringAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+  //system("/cefs/higgs/songwz/winter22/CEPCSW/workarea/memory/memory_test.sh cluster_begin");
+  //time_t time_cb;  
+  //time(&time_cb);  
+  //cout<<" When begin clustering: "<<ctime(&time_cb)<<endl;
+
+  //Threshold and scale factor (Todo) for bars. 
+  for(int ibar=0; ibar<m_bars.size(); ibar++)
+  {
+    if(m_bars.at(ibar)->getEnergy()>settings.map_floatPars["unit_threshold"])
+    {
+      m_processbars.push_back(m_bars.at(ibar));
+    }
+    else
+    {
+      m_restbars.push_back(m_bars.at(ibar));
+    }
+  }
+
+  //Clustering
+  Clustering(m_processbars, m_1dclusters);
+  Clustering(m_1dclusters, m_halfclusters);
+
+  //Store created objects to backup col. 
+  m_datacol.map_1DCluster["bk1DCluster"].insert(m_datacol.map_1DCluster["bk1DCluster"].end(), m_1dclusters.begin(), m_1dclusters.end());
+  m_datacol.map_HalfCluster["bkHalfCluster"].insert(m_datacol.map_HalfCluster["bkHalfCluster"].end(), m_halfclusters.begin(), m_halfclusters.end());
+
+
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_HalfClusterV; m_HalfClusterV.clear();
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_HalfClusterU; m_HalfClusterU.clear();
+  for(int i=0; i<m_halfclusters.size() && m_halfclusters[i]; i++){
+    m_halfclusters[i]->setType(0);
+    if(m_halfclusters[i]->getSlayer()==0 && m_halfclusters[i]->getCluster().size()>0)
+      m_HalfClusterU.push_back(m_halfclusters[i]);
+    else if(m_halfclusters[i]->getSlayer()==1 && m_halfclusters[i]->getCluster().size()>0)
+      m_HalfClusterV.push_back(m_halfclusters[i]);
+  }
+
+//printf("  GlobalClustering: RestBarCol size %d, 1DCluster size %d, HalfCluster size (%d, %d) \n", m_restbars.size(), m_1dclusters.size(), m_HalfClusterU.size(), m_HalfClusterV.size());
+//for(int ic=0; ic<m_HalfClusterU.size(); ic++) cout<<m_HalfClusterU[ic]->getEnergy()<<'\t';
+//cout<<endl;
+//for(int ic=0; ic<m_HalfClusterV.size(); ic++) cout<<m_HalfClusterV[ic]->getEnergy()<<'\t';
+//cout<<endl;
+
+
+  //Write results into DataCol.
+  //m_datacol.map_BarCol["RestBarCol"] = m_restbars;
+  m_datacol.map_1DCluster[settings.map_stringPars["OutputECAL1DClusters"]] = m_1dclusters;
+  m_datacol.map_HalfCluster[settings.map_stringPars["OutputECALHalfClusters"]+"U"] = m_HalfClusterU;
+  m_datacol.map_HalfCluster[settings.map_stringPars["OutputECALHalfClusters"]+"V"] = m_HalfClusterV;
+
+
+  //time_t time_ce;  
+  //time(&time_ce); 
+  //cout<<" When end clustering: "<<ctime(&time_ce)<<endl;
+  //system("/cefs/higgs/songwz/winter22/CEPCSW/workarea/memory/memory_test.sh cluster_end");
+  return StatusCode::SUCCESS;
+};
+
+StatusCode GlobalClusteringAlg::ClearAlgorithm(){
+
+  //Clear local memory
+  m_bars.clear();
+  m_processbars.clear();
+  m_restbars.clear();
+  m_1dclusters.clear();
+  m_halfclusters.clear();
+
+  return StatusCode::SUCCESS;
+};
+
+template<typename T1, typename T2> StatusCode GlobalClusteringAlg::Clustering(std::vector<std::shared_ptr<T1>> &m_input, std::vector<std::shared_ptr<T2>> &m_output) 
+{
+  std::vector<std::shared_ptr<T2>> record;
+  record.clear();
+
+  for(int i=0; i<m_input.size(); i++)
+  {
+    T1* lowlevelcluster = m_input.at(i).get();
+    for(int j=0; j<m_output.size(); j++)
+    {
+      if(m_output.at(j).get()->isNeighbor(lowlevelcluster)) // //m_output.at(j).isNeighbor(lowlevelcluster)
+        record.push_back(m_output.at(j));
+
+    }
+    if(record.size()>0)
+    {
+      record.at(0).get()->addUnit(lowlevelcluster);
+      for(int k=1; k<record.size(); k++)
+      {
+        for(int l=0; l<record.at(k).get()->getCluster().size(); l++)
+        {
+          record.at(0).get()->addUnit(record.at(k).get()->getCluster().at(l));
+        }
+      }
+      for(int m=1; m<record.size(); m++)
+      {
+        m_output.erase(find(m_output.begin(),m_output.end(),record.at(m)));
+        //delete record.at(m); 
+        record.at(m) = nullptr;
+      }
+      record.clear();
+      lowlevelcluster = nullptr;
+      continue;
+    }
+    //T2* highlevelcluster = new T2(); //first new
+    std::shared_ptr<T2> highlevelcluster = std::make_shared<T2>();
+
+    highlevelcluster.get()->addUnit(lowlevelcluster);
+    m_output.push_back(highlevelcluster);
+  }
+	return StatusCode::SUCCESS;
+	//cout<<"how many neighbors: "<<number<<endl;
+}
+
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/HcalClusteringAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/HcalClusteringAlg.cpp
new file mode 100644
index 00000000..31b1bcc5
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/HcalClusteringAlg.cpp
@@ -0,0 +1,175 @@
+#ifndef HCALCLUSTERING_ALG_C
+#define HCALCLUSTERING_ALG_C
+
+#include "Algorithm/HcalClusteringAlg.h"
+using namespace PandoraPlus;
+
+StatusCode HcalClusteringAlg::ReadSettings(PandoraPlus::Settings& m_settings){
+  settings = m_settings;
+
+  if(settings.map_stringPars.find("InputHCALHits")==settings.map_stringPars.end())         settings.map_stringPars["InputHCALHits"] = "HCALBarrel";
+  if(settings.map_stringPars.find("OutputHCALClusters")==settings.map_stringPars.end())    settings.map_stringPars["OutputHCALClusters"] = "HCALCluster";
+
+  //Set initial values
+  if(settings.map_floatPars.find("th_ConeTheta_l1")==settings.map_floatPars.end())    settings.map_floatPars["th_ConeTheta_l1"] = TMath::Pi()/2.;
+  if(settings.map_floatPars.find("th_ConeR_l1")==settings.map_floatPars.end())        settings.map_floatPars["th_ConeR_l1"] = 70.;
+  if(settings.map_floatPars.find("th_ConeTheta_l2")==settings.map_floatPars.end())    settings.map_floatPars["th_ConeTheta_l2"] = TMath::Pi()/3.;
+  if(settings.map_floatPars.find("th_ConeR_l2")==settings.map_floatPars.end())        settings.map_floatPars["th_ConeR_l2"] = 120.;
+  if(settings.map_floatPars.find("th_ClusChi2")==settings.map_floatPars.end())        settings.map_floatPars["th_ClusChi2"] = 10e17;
+  if(settings.map_floatPars.find("fl_GoodClusLevel")==settings.map_floatPars.end())   settings.map_floatPars["fl_GoodClusLevel"] = 10;
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode HcalClusteringAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  return StatusCode::SUCCESS;
+};
+
+StatusCode HcalClusteringAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+  //Readin data from DataCol: 
+//   std::vector<PandoraPlus::CaloHit*> m_hcalHits;
+//   m_hcalHits.clear();
+//   for(int ih=0; ih<m_datacol.map_CaloHit["HCALBarrel"].size(); ih++)
+//     m_hcalHits.push_back( m_datacol.map_CaloHit["HCALBarrel"][ih].get() );
+//   //ordered hits by layer
+//   std::map<int, std::vector<PandoraPlus::CaloHit*> > m_orderedHit;  
+//   m_orderedHit.clear();
+//   for(int ih=0;ih<m_hcalHits.size();ih++)
+//     m_orderedHit[m_hcalHits[ih]->getLayer()].push_back(m_hcalHits[ih]);
+
+  std::vector<PandoraPlus::CaloHit*> m_hcalHits;
+  m_hcalHits.clear();
+  for(int ih=0; ih<m_datacol.map_CaloHit[settings.map_stringPars["InputHCALHits"]].size(); ih++)
+    m_hcalHits.push_back( m_datacol.map_CaloHit[settings.map_stringPars["InputHCALHits"]][ih].get() );
+
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_clusterCol;  
+  m_clusterCol.clear();
+
+//   LongiConeLinking( m_orderedHit, m_clusterCol );
+  Clustering(m_hcalHits, m_clusterCol);
+//   m_datacol.bk_Cluster3DCol.insert(  m_datacol.bk_Cluster3DCol.end(), m_clusterCol.begin(), m_clusterCol.end() );
+  // cout<<"  Cluster size: "<<m_clusterCol.size()<<endl;
+  // for(int ic=0; ic<m_clusterCol.size(); ic++)
+  // {
+  //   cout<<"    Cluster "<<ic<<":"<<m_clusterCol[ic]->getCaloHits().size()<<endl;
+  // }
+
+//   m_datacol.map_CaloCluster[settings.map_stringPars["OutputCluster"]] = m_clusterCol;
+  m_datacol.map_CaloCluster[settings.map_stringPars["OutputHCALClusters"]]= m_clusterCol;
+  return StatusCode::SUCCESS;
+};
+
+StatusCode HcalClusteringAlg::ClearAlgorithm(){
+
+  //Clear local memory
+//   m_hcalHits.clear();
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode HcalClusteringAlg::LongiConeLinking(  const std::map<int, std::vector<PandoraPlus::CaloHit*> >& orderedHit, 
+                                                 std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster> >& ClusterCol)
+{
+
+  if(orderedHit.size()==0) return StatusCode::SUCCESS;
+
+  auto iter = orderedHit.begin();
+  //In first layer: initial clusters. All showers in the first layer are regarded as cluster seed.
+  //cluster initial direction = R.
+  std::vector<PandoraPlus::CaloHit*> HitsinFirstLayer = iter->second;
+  for(int i=0;i<HitsinFirstLayer.size(); i++){
+    std::shared_ptr<PandoraPlus::Calo3DCluster> m_clus = std::make_shared<PandoraPlus::Calo3DCluster>();
+    m_clus->addHit(HitsinFirstLayer[i]);
+    ClusterCol.push_back(m_clus);
+  }
+  iter++;
+
+//cout<<"    LongiConeLinking: Cluster seed in first layer: "<<ClusterCol.size()<<endl;
+
+  //Use different cone angle for 1->2/2->3 and 3->n case
+  //Loop later layers
+  for(iter; iter!=orderedHit.end(); iter++){
+    std::vector<PandoraPlus::CaloHit*> HitsinLayer = iter->second;
+//cout<<"    In Layer: "<<iter->first<<"  Hit size: "<<HitsinLayer.size()<<endl;
+
+    for(int is=0; is<HitsinLayer.size(); is++){
+      PandoraPlus::CaloHit* m_hit = HitsinLayer[is];
+//printf("     New Hit: (%.3f, %.3f, %.3f), Layer %d \n", m_hit->getPosition().x(), m_hit->getPosition().y(), m_hit->getPosition().z(),  m_hit->getLayer() );
+//cout<<"     Cluster size: "<<ClusterCol.size()<<endl;
+
+      for(int ic=0; ic<ClusterCol.size(); ic++ ){
+        int m_Nhits = ClusterCol[ic]->getCaloHits().size();
+        const PandoraPlus::CaloHit* hit_in_clus = ClusterCol[ic]->getCaloHits().back();
+        TVector3 relR_vec = m_hit->getPosition() - hit_in_clus->getPosition();
+//printf("      New hit: (%.2f, %.2f, %.2f), Cluster last: (%.2f, %.2f, %.2f, %d), Cluster axis: (%.2f, %.2f, %.2f) \n",
+//    m_hit->getPosition().x(), m_hit->getPosition().y(),m_hit->getPosition().z(),
+//    hit_in_clus->getPosition().x(), hit_in_clus->getPosition().y(), hit_in_clus->getPosition().z(), hit_in_clus->getLayer(), 
+//    ClusterCol[ic]->getAxis().x(), ClusterCol[ic]->getAxis().y(), ClusterCol[ic]->getAxis().z() );
+
+        if(  (m_Nhits<3 && m_Nhits>0 && relR_vec.Angle(ClusterCol[ic]->getAxis())< settings.map_floatPars["th_ConeTheta_l1"] && relR_vec.Mag()< settings.map_floatPars["th_ConeR_l1"]) ||
+             (m_Nhits>=3             && relR_vec.Angle(ClusterCol[ic]->getAxis())< settings.map_floatPars["th_ConeTheta_l2"] && relR_vec.Mag()< settings.map_floatPars["th_ConeR_l2"])  ){
+
+          ClusterCol[ic]->addHit(m_hit);
+          HitsinLayer.erase(HitsinLayer.begin()+is);
+          is--;
+          break;
+        }
+      }
+    }//end loop showers in layer.
+    if(HitsinLayer.size()>0){
+      for(int i=0;i<HitsinLayer.size(); i++){
+        std::shared_ptr<PandoraPlus::Calo3DCluster> m_clus = std::make_shared<PandoraPlus::Calo3DCluster>();
+        m_clus->addHit(HitsinLayer[i]);
+        ClusterCol.push_back(m_clus);
+    }}//end new cluster
+  }//end loop layers.
+
+
+  return StatusCode::SUCCESS;
+}
+
+template<typename T1, typename T2> StatusCode HcalClusteringAlg::Clustering(std::vector<T1*> &m_input, std::vector<std::shared_ptr<T2>> &m_output) 
+{
+  std::vector<std::shared_ptr<T2>> record;
+  record.clear();
+
+  for(int i=0; i<m_input.size(); i++)
+  {
+    T1* lowlevelcluster = m_input.at(i);
+    for(int j=0; j<m_output.size(); j++)
+    {
+      if(m_output.at(j).get()->isHCALNeighbor(lowlevelcluster)) // //m_output.at(j).isNeighbor(lowlevelcluster)
+        record.push_back(m_output.at(j));
+
+    }
+    if(record.size()>0)
+    {
+      record.at(0).get()->addHit(lowlevelcluster);
+      for(int k=1; k<record.size(); k++)
+      {
+        for(int l=0; l<record.at(k).get()->getCaloHits().size(); l++)
+        {
+          record.at(0).get()->addHit(record.at(k).get()->getCaloHits().at(l));
+        }
+      }
+      for(int m=1; m<record.size(); m++)
+      {
+        m_output.erase(find(m_output.begin(),m_output.end(),record.at(m)));
+        //delete record.at(m); 
+        record.at(m) = nullptr;
+      }
+      record.clear();
+      lowlevelcluster = nullptr;
+      continue;
+    }
+    //T2* highlevelcluster = new T2(); //first new
+    std::shared_ptr<T2> highlevelcluster = std::make_shared<T2>();
+
+    highlevelcluster.get()->addHit(lowlevelcluster);
+    m_output.push_back(highlevelcluster);
+  }
+  return StatusCode::SUCCESS;
+	//cout<<"how many neighbors: "<<number<<endl;
+}
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/HoughClusteringAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/HoughClusteringAlg.cpp
new file mode 100644
index 00000000..ba9bbde8
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/HoughClusteringAlg.cpp
@@ -0,0 +1,812 @@
+#ifndef HOUGHCLUSTERINGALG_C
+#define HOUGHCLUSTERINGALG_C
+
+#include "Algorithm/HoughClusteringAlg.h"
+#include <algorithm>
+#include <cmath>
+#include <set>
+#include "TCanvas.h"
+using namespace std;
+
+StatusCode HoughClusteringAlg::ReadSettings(PandoraPlus::Settings& m_settings){
+  settings = m_settings;
+
+  // ECAL geometry settings
+  //if(settings.map_floatPars.find("cell_size")==settings.map_floatPars.end())
+  //  settings.map_floatPars["cell_size"] = 10; // unit: mm
+  //if(settings.map_floatPars.find("ecal_inner_radius")==settings.map_floatPars.end())
+  //  settings.map_floatPars["ecal_inner_radius"] = 1860; // unit: mm
+
+  // Hough space settings
+  // alpha in V plane (bars parallel to z axis)
+  if(settings.map_floatPars.find("alpha_lowV")==settings.map_floatPars.end())
+    settings.map_floatPars["alpha_lowV"] = -0.1;  
+  if(settings.map_floatPars.find("alpha_highV")==settings.map_floatPars.end())
+    settings.map_floatPars["alpha_highV"] = 2.*TMath::Pi(); 
+  if(settings.map_intPars.find("Nbins_alphaV")==settings.map_intPars.end())
+    settings.map_intPars["Nbins_alphaV"] = 3000; 
+  if(settings.map_floatPars.find("bin_width_alphaV")==settings.map_floatPars.end())
+    settings.map_floatPars["bin_width_alphaV"] = ( settings.map_floatPars["alpha_highV"] - settings.map_floatPars["alpha_lowV"] ) / (double)settings.map_intPars["Nbins_alphaV"];
+  // double bin_width_alphaV = (alpha_highV - alpha_lowV) / (double)Nbins_alphaV;
+  
+  // alpha in U plane (bars perpendicular to z axis)
+  if(settings.map_floatPars.find("alpha_lowU")==settings.map_floatPars.end())
+    settings.map_floatPars["alpha_lowU"] = 0.;
+  if(settings.map_floatPars.find("alpha_highU")==settings.map_floatPars.end())
+    settings.map_floatPars["alpha_highU"] = TMath::Pi();  
+  if(settings.map_intPars.find("Nbins_alphaU")==settings.map_intPars.end())
+    settings.map_intPars["Nbins_alphaU"] = 5000;  
+  if(settings.map_floatPars.find("bin_width_alphaU")==settings.map_floatPars.end())
+    settings.map_floatPars["bin_width_alphaU"] = ( settings.map_floatPars["alpha_highU"] - settings.map_floatPars["alpha_lowU"] ) / (double)settings.map_intPars["Nbins_alphaU"];
+  // double bin_width_alphaU = (alpha_highU - alpha_lowU) / (double)Nbins_alphaU;
+
+  // rho
+  if(settings.map_floatPars.find("rho_low")==settings.map_floatPars.end())
+    settings.map_floatPars["rho_low"] = -50.;
+  if(settings.map_floatPars.find("rho_high")==settings.map_floatPars.end())
+    settings.map_floatPars["rho_high"] = 50.;
+  if(settings.map_intPars.find("Nbins_rho")==settings.map_intPars.end())
+    settings.map_intPars["Nbins_rho"] = 20;   // (rho_high - rho_low)/5
+  if(settings.map_floatPars.find("bin_width_rho")==settings.map_floatPars.end())
+    settings.map_floatPars["bin_width_rho"] = ( settings.map_floatPars["rho_high"] - settings.map_floatPars["rho_low"] ) / (double)settings.map_intPars["Nbins_rho"];
+
+  // Algorithm parameter settings
+  if(settings.map_intPars.find("th_Layers")==settings.map_intPars.end())
+    settings.map_intPars["th_Layers"] = 10;
+  if(settings.map_intPars.find("th_peak")==settings.map_intPars.end())
+    settings.map_intPars["th_peak"] = 3;
+  if(settings.map_intPars.find("th_continueN")==settings.map_intPars.end())
+    settings.map_intPars["th_continueN"] = 3;
+  if(settings.map_floatPars.find("th_AxisE")==settings.map_floatPars.end())
+    settings.map_floatPars["th_AxisE"] = 0.15; // unit: GeV
+  if(settings.map_floatPars.find("th_overlapE")==settings.map_floatPars.end())
+    settings.map_floatPars["th_overlapE"] = 0.5;
+  if(settings.map_floatPars.find("th_dAlpha1")==settings.map_floatPars.end())
+    settings.map_floatPars["th_dAlpha1"] = 0.1;
+  if(settings.map_floatPars.find("th_dAlpha2")==settings.map_floatPars.end())
+    settings.map_floatPars["th_dAlpha2"] = 0.05;
+  if(settings.map_floatPars.find("th_ERatio")==settings.map_floatPars.end())
+    settings.map_floatPars["th_ERatio"] = 0.04;
+
+  if(settings.map_stringPars.find("ReadinLocalMaxName")==settings.map_stringPars.end())  
+    settings.map_stringPars["ReadinLocalMaxName"] = "AllLocalMax";
+  if(settings.map_stringPars.find("LeftLocalMaxName")==settings.map_stringPars.end())    
+    settings.map_stringPars["LeftLocalMaxName"] = "LeftLocalMax";
+  if(settings.map_stringPars.find("OutputLongiClusName")==settings.map_stringPars.end()) 
+    settings.map_stringPars["OutputLongiClusName"] = "HoughAxis"; 
+
+  return StatusCode::SUCCESS;  
+}
+
+StatusCode HoughClusteringAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  p_HalfClusterU.clear(); 
+  p_HalfClusterV.clear(); 
+
+  for(int ih=0; ih<m_datacol.map_HalfCluster["HalfClusterColU"].size(); ih++)
+    p_HalfClusterU.push_back( m_datacol.map_HalfCluster["HalfClusterColU"][ih].get() );
+  for(int ih=0; ih<m_datacol.map_HalfCluster["HalfClusterColV"].size(); ih++)
+    p_HalfClusterV.push_back( m_datacol.map_HalfCluster["HalfClusterColV"][ih].get() );
+
+  //p_HalfClusterU = m_datacol.map_HalfCluster["HalfClusterColU"];
+  //p_HalfClusterV = m_datacol.map_HalfCluster["HalfClusterColV"];
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode HoughClusteringAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+
+  //if( (p_HalfClusterU.size()+p_HalfClusterV.size())<1 ){
+  //  std::cout << "HoughClusteringAlg: No HalfCluster input"<<std::endl;
+  //  return StatusCode::SUCCESS;
+  //}
+
+  
+  if(p_HalfClusterV.size()==0){ std::cout<<"  HoughClusteringAlg: No HalfClusterV in present data collection! "<<std::endl; }
+  if(p_HalfClusterU.size()==0){ std::cout<<"  HoughClusteringAlg: No HalfClusterU in present data collection! "<<std::endl; }
+
+//cout<<"Readin HalfCluster size: "<<p_HalfClusterV.size()<<", "<<p_HalfClusterU.size()<<endl;
+
+  //std::vector<const PandoraPlus::CaloHalfCluster*> m_refHFClusVCol; m_refHFClusVCol.clear();
+  // Processing V(xy) plane
+  for(int it=0; it<p_HalfClusterV.size(); it++){ // process each HalfCluster respectively
+    m_localMaxVCol.clear();
+    std::vector<const PandoraPlus::Calo1DCluster*> tmp_localMaxVCol = p_HalfClusterV[it]->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+
+    for(int il=0; il<tmp_localMaxVCol.size(); il++){
+      if(tmp_localMaxVCol[il]->getDlayer()<=settings.map_intPars["th_Layers"]) 
+        m_localMaxVCol.push_back(tmp_localMaxVCol[il]);
+    }
+
+    if(m_localMaxVCol.size()<settings.map_intPars["th_peak"]){
+      //std::cout << "    yyy: m_localMaxVCol.size()<th_peak, continue" << std::endl;
+      continue; 
+    } 
+
+//cout<<"  HoughClusteringAlg: Find Hough axis in HalfCluster "<<it<<". Local maximum size V = "<<m_localMaxVCol.size()<<endl;
+    
+    // cout<<"  HoughClusteringAlg: Creating m_HoughObjectsV"<<endl;
+    std::vector<PandoraPlus::HoughObject> m_HoughObjectsV; m_HoughObjectsV.clear(); 
+    for(int il=0; il<m_localMaxVCol.size(); il++){
+      PandoraPlus::HoughObject m_obj(m_localMaxVCol[il], PandoraPlus::CaloUnit::barsize, PandoraPlus::CaloUnit::ecal_innerR);
+      m_HoughObjectsV.push_back(m_obj);
+    }
+//cout<<"  HoughClusteringAlg: HoughObjectV size "<<m_HoughObjectsV.size()<<endl;
+
+    // cout<<"  HoughClusteringAlg: Hough transformation"<<endl;
+    HoughTransformation(m_HoughObjectsV);
+
+    // cout<<"  HoughClusteringAlg: Creating hough_spaceV"<<endl;
+    PandoraPlus::HoughSpace hough_spaceV(settings.map_floatPars["alpha_lowV"], settings.map_floatPars["alpha_highV"], 
+                                         settings.map_floatPars["bin_width_alphaV"], settings.map_intPars["Nbins_alphaV"], 
+                                         settings.map_floatPars["rho_low"], settings.map_floatPars["rho_high"], 
+                                         settings.map_floatPars["bin_width_rho"], settings.map_intPars["Nbins_rho"]);
+
+//cout<<"  HoughClusteringAlg: Filling hough_spaceV"<<endl;
+    FillHoughSpace(m_HoughObjectsV, hough_spaceV);
+
+//cout<<"  HoughClusteringAlg: Finding clusters from Hough space"<<endl;
+
+    //Create output HoughClusters
+    m_longiClusVCol.clear(); 
+    ClusterFinding(m_HoughObjectsV, hough_spaceV, m_longiClusVCol  );
+    CleanClusters(m_longiClusVCol);
+//cout << "  HoughClusteringAlg: final output m_longiClusVCol.size() = " << m_longiClusVCol.size() << endl;
+    m_datacol.map_HalfCluster["bkHalfCluster"].insert( m_datacol.map_HalfCluster["bkHalfCluster"].end(), m_longiClusVCol.begin(), m_longiClusVCol.end() );
+
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_constHoughCluster; m_constHoughCluster.clear();
+    std::vector<const PandoraPlus::Calo1DCluster*> left_localMaxVCol; left_localMaxVCol.clear();
+    std::vector<const PandoraPlus::Calo1DCluster*> m_houghMax; m_houghMax.clear(); 
+    for(int is=0; is<tmp_localMaxVCol.size(); is++){
+      bool fl_incluster = false; 
+      for(int ic=0; ic<m_longiClusVCol.size(); ic++){
+        std::vector<const PandoraPlus::Calo1DCluster*> p_showers = m_longiClusVCol[ic]->getCluster();
+        if( find(p_showers.begin(), p_showers.end(), tmp_localMaxVCol[is])!=p_showers.end() ) { fl_incluster = true; break; }
+      }
+      if(!fl_incluster && find(left_localMaxVCol.begin(), left_localMaxVCol.end(), tmp_localMaxVCol[is])==left_localMaxVCol.end() ) left_localMaxVCol.push_back(tmp_localMaxVCol[is]);
+      m_houghMax.push_back( tmp_localMaxVCol[is] );
+    }
+    for(int ic=0; ic<m_longiClusVCol.size(); ic++)
+      m_constHoughCluster.push_back(m_longiClusVCol[ic].get());
+
+    //m_refHFClusVCol.insert(m_refHFClusVCol.end(), m_constHoughCluster.begin(), m_constHoughCluster.end());
+
+    p_HalfClusterV[it]->setLocalMax("HoughLocalMax", m_houghMax);
+    p_HalfClusterV[it]->setLocalMax(settings.map_stringPars["LeftLocalMaxName"], left_localMaxVCol);
+    p_HalfClusterV[it]->setHalfClusters(settings.map_stringPars["OutputLongiClusName"], m_constHoughCluster);
+    m_houghMax.clear();
+    left_localMaxVCol.clear();
+
+  }  // end of V plane
+//cout<<"Finish Hough in V. Reference HFClusterV size "<<m_refHFClusVCol.size()<<endl;
+
+
+  // Processing U(r-phi) plane
+  for(int it=0; it<p_HalfClusterU.size(); it++){ // process each HalfCluster respectively
+    m_localMaxUCol.clear();
+    std::vector<const PandoraPlus::Calo1DCluster*> tmp_localMaxUCol = p_HalfClusterU[it]->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+
+    for(int il=0; il<tmp_localMaxUCol.size(); il++){
+      if(tmp_localMaxUCol[il]->getDlayer()<=settings.map_intPars["th_Layers"])
+        m_localMaxUCol.push_back(tmp_localMaxUCol[il]);
+    }
+
+    if(m_localMaxUCol.size()<settings.map_intPars["th_peak"]){
+      //std::cout << "    yyy: m_localMaxUCol.size()<th_peak, continue" << std::endl;
+      continue;
+    }
+
+//cout<<"  HoughClusteringAlg: Find Hough axis in HalfCluster "<<it<<". Local maximum size U = "<<m_localMaxUCol.size()<<endl;
+
+    // cout<<"  HoughClusteringAlg: Creating m_HoughObjectsU"<<endl;
+    std::vector<PandoraPlus::HoughObject> m_HoughObjectsU; m_HoughObjectsU.clear();
+    for(int il=0; il<m_localMaxUCol.size(); il++){
+      PandoraPlus::HoughObject m_obj(m_localMaxUCol[il], PandoraPlus::CaloUnit::barsize, PandoraPlus::CaloUnit::ecal_innerR);
+      m_HoughObjectsU.push_back(m_obj);
+    }
+//cout<<"  HoughClusteringAlg: HoughObjectU size "<<m_HoughObjectsU.size()<<endl;
+
+    // cout<<"  HoughClusteringAlg: Hough transformation"<<endl;
+    HoughTransformation(m_HoughObjectsU);
+
+    // cout<<"  HoughClusteringAlg: Creating hough_spaceU"<<endl;
+    PandoraPlus::HoughSpace hough_spaceU(settings.map_floatPars["alpha_lowU"], settings.map_floatPars["alpha_highU"],
+                                         settings.map_floatPars["bin_width_alphaU"], settings.map_intPars["Nbins_alphaU"],
+                                         settings.map_floatPars["rho_low"], settings.map_floatPars["rho_high"],
+                                         settings.map_floatPars["bin_width_rho"], settings.map_intPars["Nbins_rho"]);
+
+//cout<<"  HoughClusteringAlg: Filling hough_spaceU"<<endl;
+    FillHoughSpace(m_HoughObjectsU, hough_spaceU);
+
+//cout<<"  HoughClusteringAlg: Finding clusters from Hough space"<<endl;
+    //Create output HoughClusters
+    m_longiClusUCol.clear();
+    ClusterFinding(m_HoughObjectsU, hough_spaceU, m_longiClusUCol  );
+    CleanClusters(m_longiClusUCol);
+//cout << "  HoughClusteringAlg: final output m_longiClusUCol.size() = " << m_longiClusUCol.size() << endl;
+    m_datacol.map_HalfCluster["bkHalfCluster"].insert( m_datacol.map_HalfCluster["bkHalfCluster"].end(), m_longiClusUCol.begin(), m_longiClusUCol.end() );
+
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_constHoughCluster; m_constHoughCluster.clear();
+    std::vector<const PandoraPlus::Calo1DCluster*> left_localMaxUCol; left_localMaxUCol.clear();
+    std::vector<const PandoraPlus::Calo1DCluster*> m_houghMax; m_houghMax.clear();
+    for(int is=0; is<tmp_localMaxUCol.size(); is++){
+      bool fl_incluster = false;
+      for(int ic=0; ic<m_longiClusUCol.size(); ic++){
+        std::vector<const PandoraPlus::Calo1DCluster*> p_showers = m_longiClusUCol[ic]->getCluster();
+        if( find(p_showers.begin(), p_showers.end(), tmp_localMaxUCol[is])!=p_showers.end() ) { fl_incluster = true; break; }
+      }
+      if(!fl_incluster && find(left_localMaxUCol.begin(), left_localMaxUCol.end(), tmp_localMaxUCol[is])==left_localMaxUCol.end() ) left_localMaxUCol.push_back(tmp_localMaxUCol[is]);
+      m_houghMax.push_back( tmp_localMaxUCol[is] );
+    }
+    for(int ic=0; ic<m_longiClusUCol.size(); ic++)
+      m_constHoughCluster.push_back(m_longiClusUCol[ic].get());
+
+    p_HalfClusterU[it]->setLocalMax("HoughLocalMax", m_houghMax);
+    p_HalfClusterU[it]->setLocalMax(settings.map_stringPars["LeftLocalMaxName"], left_localMaxUCol);
+    p_HalfClusterU[it]->setHalfClusters(settings.map_stringPars["OutputLongiClusName"], m_constHoughCluster);
+    m_houghMax.clear();
+    left_localMaxUCol.clear();
+
+  }  // end of U plane
+
+
+
+
+
+
+/*
+  for(int it=0; it<p_HalfClusterU.size(); it++){
+    m_localMaxUCol.clear();
+    std::vector<const PandoraPlus::Calo1DCluster*> tmp_localMaxUCol = p_HalfClusterU[it]->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+
+    for(int il=0; il<tmp_localMaxUCol.size(); il++){
+      if(tmp_localMaxUCol[il]->getDlayer()<=settings.map_intPars["th_Layers"]) 
+        m_localMaxUCol.push_back(tmp_localMaxUCol[il]);
+    }
+
+    if(m_localMaxUCol.size()<settings.map_intPars["th_peak"]){
+      continue; 
+    } 
+//cout<<"  HoughClusteringAlg: Find Hough axis in HalfCluster "<<it<<". Local maximum size U = "<<m_localMaxUCol.size()<<endl;
+
+    std::map<int, std::vector<PandoraPlus::HoughObject> > map_HoughObjectsU_module; map_HoughObjectsU_module.clear();
+    std::map<int, std::vector<PandoraPlus::HoughObject> > map_HoughObjectsU_crack; map_HoughObjectsU_crack.clear();
+  
+    for(int il=0; il<m_localMaxUCol.size(); il++){
+      int module = m_localMaxUCol[il]->getTowerID()[0][0];
+      PandoraPlus::HoughObject m_obj(m_localMaxUCol[il], PandoraPlus::CaloUnit::barsize, PandoraPlus::CaloUnit::ecal_innerR);
+      map_HoughObjectsU_module[module].push_back(m_obj);
+    }
+    for(int iref=0; iref<m_refHFClusVCol.size(); iref++){
+      double tmp_phi = m_refHFClusVCol[iref]->getPos().Phi();  // yyy: tmp_phi ranges from -pi to pi
+//cout<<"    Ref HFCluster phi: "<<tmp_phi<<endl;
+      double intPart, fracPart; 
+      fracPart = modf((tmp_phi+TMath::Pi())/(TMath::Pi()/4.), &intPart);   // yyy: tmp_phi + TMath::Pi() ranges from 0 to 2pi
+//cout<<"    Int part "<<intPart<<", frac part "<<fracPart<<endl;
+      if(fracPart<0.489 || fracPart>0.711) continue;  //Not in crack region. 
+      
+      int iCrack = intPart+2;
+      if(iCrack>=8) iCrack = iCrack-8; 
+//cout<<"  Crack No: "<<iCrack<<endl;
+
+      for(int il=0; il<m_localMaxUCol.size(); il++){
+        if( (m_localMaxUCol[il]->getTowerID()[0][0]==iCrack && m_localMaxUCol[il]->getTowerID()[0][1]==4) ||
+            (iCrack!=7 && m_localMaxUCol[il]->getTowerID()[0][0]==iCrack+1 && m_localMaxUCol[il]->getTowerID()[0][1]==1) || 
+            (iCrack==7 && m_localMaxUCol[il]->getTowerID()[0][0]==0 && m_localMaxUCol[il]->getTowerID()[0][1]==1)){
+          PandoraPlus::HoughObject m_obj(m_localMaxUCol[il], PandoraPlus::CaloUnit::barsize, PandoraPlus::CaloUnit::ecal_innerR, tmp_phi);
+          map_HoughObjectsU_crack[iCrack].push_back(m_obj);
+        }
+      }
+    
+    }
+
+//cout<<"  Module HoughObject: "<<endl;
+//for(auto iter: map_HoughObjectsU_module)
+//printf("    Module #%d: object size %d \n", iter.first, iter.second.size());
+//cout<<"  Crack HoughObject: "<<endl;
+//for(auto iter: map_HoughObjectsU_crack)
+//printf("    Crack #%d: object size %d \n", iter.first, iter.second.size());
+
+
+    //Do hough transformation for HoughObjects 
+    for(auto &imodule: map_HoughObjectsU_module) HoughTransformation(imodule.second);
+    for(auto &icrack: map_HoughObjectsU_crack) HoughTransformation(icrack.second);
+    
+    //Fill Hough space
+    std::map<int, PandoraPlus::HoughSpace> hough_spacesU_module;
+    std::map<int, PandoraPlus::HoughSpace> hough_spacesU_crack;
+    for(auto &imodule: map_HoughObjectsU_module){
+      PandoraPlus::HoughSpace hspaceU(settings.map_floatPars["alpha_lowU"], settings.map_floatPars["alpha_highU"],
+                                      settings.map_floatPars["bin_width_alphaU"], settings.map_intPars["Nbins_alphaU"],
+                                      settings.map_floatPars["rho_low"], settings.map_floatPars["rho_high"],
+                                      settings.map_floatPars["bin_width_rho"], settings.map_intPars["Nbins_rho"]);
+      FillHoughSpace(imodule.second, hspaceU);
+      hough_spacesU_module[imodule.first] = hspaceU;
+    }
+    for(auto &icrack: map_HoughObjectsU_crack){
+      PandoraPlus::HoughSpace hspaceU(settings.map_floatPars["alpha_lowU"], settings.map_floatPars["alpha_highU"],
+                                      settings.map_floatPars["bin_width_alphaU"], settings.map_intPars["Nbins_alphaU"],
+                                      settings.map_floatPars["rho_low"], settings.map_floatPars["rho_high"],
+                                      settings.map_floatPars["bin_width_rho"], settings.map_intPars["Nbins_rho"]);
+      FillHoughSpace(icrack.second, hspaceU);
+      hough_spacesU_crack[icrack.first] = hspaceU;
+    }
+//cout<<"  Module Hough space size: "<<hough_spacesU_module.size()<<endl;
+//cout<<"  Crack Hough space size: "<<hough_spacesU_module.size()<<endl;
+
+    m_longiClusUCol.clear();
+    for(auto &imodule: map_HoughObjectsU_module)
+      ClusterFinding(imodule.second, hough_spacesU_module[imodule.first], m_longiClusUCol );
+    for(auto &icrack: map_HoughObjectsU_crack)
+      ClusterFinding(icrack.second, hough_spacesU_crack[icrack.first], m_longiClusUCol );
+
+//cout<<"  Hough axis size: "<<m_longiClusUCol.size()<<endl;
+    CleanClusters(m_longiClusUCol);
+    m_datacol.map_HalfCluster["bkHalfCluster"].insert( m_datacol.map_HalfCluster["bkHalfCluster"].end(), m_longiClusUCol.begin(), m_longiClusUCol.end() );
+//cout<<"  Hough axis size after cleaning: "<<m_longiClusUCol.size()<<endl;
+//cout<<"  Print axis "<<endl;
+//for(int i=0; i<m_longiClusUCol.size(); i++){
+//  printf("    Axis #%d: hit size %d, type %d, address %p \n", i, m_longiClusUCol[i]->getCluster().size(), m_longiClusUCol[i]->getType(), m_longiClusUCol[i].get() );
+//}
+
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_constHoughCluster; m_constHoughCluster.clear();
+    std::vector<const PandoraPlus::Calo1DCluster*> left_localMaxUCol; left_localMaxUCol.clear();
+    std::vector<const PandoraPlus::Calo1DCluster*> m_houghMax; m_houghMax.clear();
+    for(int is=0; is<tmp_localMaxUCol.size(); is++){
+      bool fl_incluster = false; 
+      for(int ic=0; ic<m_longiClusUCol.size(); ic++){
+        std::vector<const PandoraPlus::Calo1DCluster*> p_showers = m_longiClusUCol[ic]->getCluster();
+        if( find(p_showers.begin(), p_showers.end(), tmp_localMaxUCol[is])!=p_showers.end() ) { fl_incluster = true; break; }
+      }
+      if(!fl_incluster && find(left_localMaxUCol.begin(), left_localMaxUCol.end(), tmp_localMaxUCol[is])==left_localMaxUCol.end() ) left_localMaxUCol.push_back(tmp_localMaxUCol[is]);
+      else m_houghMax.push_back( tmp_localMaxUCol[is] );
+    }
+
+    for(int ic=0; ic<m_longiClusUCol.size(); ic++)
+      m_constHoughCluster.push_back(m_longiClusUCol[ic].get());
+
+    p_HalfClusterU[it]->setLocalMax("HoughLocalMax", m_houghMax);
+    p_HalfClusterU[it]->setLocalMax(settings.map_stringPars["LeftLocalMaxName"], left_localMaxUCol);
+    p_HalfClusterU[it]->setHalfClusters(settings.map_stringPars["OutputLongiClusName"], m_constHoughCluster);
+    m_houghMax.clear();
+    left_localMaxUCol.clear(); 
+
+  }  // end of U plane
+*/
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode HoughClusteringAlg::ClearAlgorithm(){
+  p_HalfClusterV.clear();
+  p_HalfClusterU.clear(); 
+  m_localMaxVCol.clear();
+  m_localMaxUCol.clear(); 
+  m_longiClusVCol.clear();
+  m_longiClusUCol.clear();
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode HoughClusteringAlg::HoughTransformation(std::vector<PandoraPlus::HoughObject>& Hobjects){
+  if(Hobjects.size()<settings.map_intPars["th_peak"]) return StatusCode::SUCCESS;
+
+  // range of alpha of different lines
+  double range12[2] = {0, 0};
+  double range34[2] = {0, 0};
+
+  for(int iobj=0; iobj<Hobjects.size(); iobj++){
+    int t_slayer = Hobjects[iobj].getSlayer();
+    //SetLineRange(t_module, t_slayer, range12, range34);
+    double point_Phi = Hobjects[iobj].getCenterPoint().Phi();
+    double alpha_min, alpha_max;
+
+    if(t_slayer==0){
+      if(point_Phi<TMath::PiOver2()){
+        alpha_min = TMath::PiOver2();
+        alpha_max = TMath::Pi();
+      }
+      else{   
+        alpha_min = 0;
+        alpha_max = TMath::PiOver2();
+      }
+    }
+    else{
+      if( point_Phi < 5*TMath::Pi()/8. && point_Phi >= TMath::PiOver2() ){
+        alpha_min = -0.1;
+        alpha_max = TMath::PiOver4();
+      }
+      else if(point_Phi>3*TMath::Pi()/8. && point_Phi<TMath::PiOver2()){
+        alpha_min = 7.*TMath::Pi()/4.;
+        alpha_max = 2*TMath::Pi();
+      }
+      else{
+        alpha_min = floor(4*point_Phi/TMath::Pi() - 1.5)*TMath::PiOver4() - TMath::PiOver4();
+        alpha_max = floor(4*point_Phi/TMath::Pi() - 1.5)*TMath::PiOver4() + TMath::PiOver4();
+      }
+   
+      if(alpha_min<=0 && alpha_max<=0){
+        alpha_min += 2*TMath::Pi();
+        alpha_max += 2*TMath::Pi();
+      }
+    }
+
+    TF1 line1("line1", "[0]*cos(x)+[1]*sin(x)", alpha_min, alpha_max);
+    TF1 line2("line2", "[0]*cos(x)+[1]*sin(x)", alpha_min, alpha_max);
+    //TF1 line3("line3", "[0]*cos(x)+[1]*sin(x)", range34[0], range34[1]);
+    //TF1 line4("line4", "[0]*cos(x)+[1]*sin(x)", range34[0], range34[1]);
+
+    if(t_slayer==0){
+      line1.SetParameters( Hobjects[iobj].getUpperPoint().X(), Hobjects[iobj].getUpperPoint().Y() );
+      line2.SetParameters( Hobjects[iobj].getLowerPoint().X(), Hobjects[iobj].getLowerPoint().Y() );
+      //line3.SetParameters( Hobjects[iobj].getPointUL().X(), Hobjects[iobj].getPointUL().Y() );
+      //line4.SetParameters( Hobjects[iobj].getPointDR().X(), Hobjects[iobj].getPointDR().Y() );
+    }
+    else if(t_slayer==1){
+      //if(t_module % 2 == 0){
+        line1.SetParameters( Hobjects[iobj].getUpperPoint().X(), Hobjects[iobj].getUpperPoint().Y() );
+        line2.SetParameters( Hobjects[iobj].getLowerPoint().X(), Hobjects[iobj].getLowerPoint().Y() );
+        //line3.SetParameters( Hobjects[iobj].getPointUL().X(), Hobjects[iobj].getPointUL().Y() );
+        //line4.SetParameters( Hobjects[iobj].getPointDR().X(), Hobjects[iobj].getPointDR().Y() );
+      //}else{
+      //  line1.SetParameters( Hobjects[iobj].getPointU().X(), Hobjects[iobj].getPointU().Y() );
+      //  line2.SetParameters( Hobjects[iobj].getPointD().X(), Hobjects[iobj].getPointD().Y() );
+      //  line3.SetParameters( Hobjects[iobj].getPointL().X(), Hobjects[iobj].getPointL().Y() );
+      //  line4.SetParameters( Hobjects[iobj].getPointR().X(), Hobjects[iobj].getPointR().Y() );
+      //}
+    }
+    
+    Hobjects[iobj].setHoughLine(line1, line2);  
+
+  }
+
+  return StatusCode::SUCCESS;
+}  // HoughTransformation() end
+
+/*
+StatusCode HoughClusteringAlg::SetLineRange(int module, int slayer, double *range12, double* range34){
+  // range12: ur, dl, u, d
+  // range34: ul, dr, l, r
+  if(slayer == 0){
+    range12[0] = 0.;
+    range12[1] = TMath::Pi()/2.;
+    range34[0] = TMath::Pi()/2.;
+    range34[1] = TMath::Pi();
+  }
+  else if(slayer == 1){
+    switch(module){
+      case 0:{
+        range12[0] = -0.1;
+        range12[1] = TMath::Pi()/4.;
+        range34[0] = 7.*TMath::Pi()/4.;
+        range34[1] = range34[0] + TMath::Pi()/4.;
+        break;
+      }
+      case 1:{
+        range12[0] = TMath::Pi()/4.;
+        range12[1] = range12[0] + TMath::Pi()/4.;
+        range34[0] = 0;
+        range34[1] = range34[0] + TMath::Pi()/4.;
+        break;
+      }
+      case 2:{
+        range12[0] = TMath::Pi()/4.;
+        range12[1] = range12[0] + TMath::Pi()/4.;
+        range34[0] = TMath::Pi()/2;
+        range34[1] = range34[0] + TMath::Pi()/4.;
+        break;
+      }
+      case 3:{
+        range12[0] = TMath::Pi()/2.;
+        range12[1] = range12[0] + TMath::Pi()/4.;
+        range34[0] = 3.*TMath::Pi()/4.;
+        range34[1] = range34[0] + TMath::Pi()/4.;
+        break;
+      }
+      case 4:{
+        range12[0] = TMath::Pi();
+        range12[1] = range12[0] + TMath::Pi()/4.;
+        range34[0] = 3.*TMath::Pi()/4.;
+        range34[1] = range34[0] + TMath::Pi()/4.;
+        break;
+      }
+      case 5:{
+        range12[0] = 5.*TMath::Pi()/4.;
+        range12[1] = range12[0] + TMath::Pi()/4.;
+        range34[0] = TMath::Pi();
+        range34[1] = range34[0] + TMath::Pi()/4.;
+        break;
+      }
+      case 6:{
+        range12[0] = 5.*TMath::Pi()/4.;
+        range12[1] = range12[0] + TMath::Pi()/4.;
+        range34[0] = 3.*TMath::Pi()/2.;
+        range34[1] = range34[0] + TMath::Pi()/4.;
+        break;
+      }
+      case 7:{
+        range12[0] = 3.*TMath::Pi()/2.;
+        range12[1] = range12[0] + TMath::Pi()/4.;
+        range34[0] = 7.*TMath::Pi()/4.;
+        range34[1] = range34[0] + TMath::Pi()/4.;
+        break;
+      }
+      default:{
+        cout << "Wrong module: module = " << module << endl;
+      }
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}  // SetLineRange() end
+*/
+
+StatusCode HoughClusteringAlg::FillHoughSpace(vector<PandoraPlus::HoughObject>& Hobjects, PandoraPlus::HoughSpace& Hspace){  
+
+  // Fill Hough space
+  // Loop Hough objects
+  for(int ih=0; ih<Hobjects.size(); ih++){
+    TF1 line1 = Hobjects[ih].getHoughLine1();
+    TF1 line2 = Hobjects[ih].getHoughLine2();
+    //TF1 line3 = Hobjects[ih].getHoughLine3();
+    //TF1 line4 = Hobjects[ih].getHoughLine4();
+
+    // line1 and line2 share the same range in alpha, so does line3 and line4
+    double range_min, range_max;
+    line1.GetRange(range_min, range_max);
+    
+    // Get bin num in alpha axis
+    int bin_min = Hspace.getAlphaBin(range_min);
+    int bin_max = Hspace.getAlphaBin(range_max);
+    //int bin_34_min = Hspace.getAlphaBin(range_34_min);
+    //int bin_34_max = Hspace.getAlphaBin(range_34_max);
+    //if (bin_12_max == bin_34_min) bin_34_min ++;
+    //if (bin_34_max == bin_12_min) bin_12_min ++;
+
+
+    // Loop for alpha bins, line1 and line2
+    for(int ialpha=bin_min; ialpha<=bin_max; ialpha++) {
+      // The lines should be monotone at this range
+      double line1_rho1 = line1.Eval( Hspace.getAlphaBinLowEdge(ialpha) );
+      double line1_rho2 = line1.Eval( Hspace.getAlphaBinUpEdge(ialpha)  );
+      double line2_rho1 = line2.Eval( Hspace.getAlphaBinLowEdge(ialpha) );
+      double line2_rho2 = line2.Eval( Hspace.getAlphaBinUpEdge(ialpha)  );
+
+      double line1_rho_min = TMath::Min(line1_rho1, line1_rho2);
+      double line1_rho_max = TMath::Max(line1_rho1, line1_rho2);
+      double line2_rho_min = TMath::Min(line2_rho1, line2_rho2);
+      double line2_rho_max = TMath::Max(line2_rho1, line2_rho2);
+
+      if(line1_rho_min>line1_rho_max || line2_rho_min>line2_rho_max){
+        cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl;
+      }
+
+      double rho_min = TMath::Min( line1_rho_min, line2_rho_min);
+      double rho_max = TMath::Max( line1_rho_max, line2_rho_max);
+
+      if(rho_max<settings.map_floatPars["rho_low"] || rho_min>settings.map_floatPars["rho_high"]) continue;
+
+      int nbin_rho_min = TMath::Max( int(ceil( (rho_min-settings.map_floatPars["rho_low"]) / settings.map_floatPars["bin_width_rho"] )), 1 );
+      int nbin_rho_max = TMath::Min( int(ceil( (rho_max-settings.map_floatPars["rho_low"]) / settings.map_floatPars["bin_width_rho"] )), settings.map_intPars["Nbins_rho"] );
+
+
+      for(int irho=nbin_rho_min; irho<=nbin_rho_max; irho++){
+        Hspace.AddBinHobj(ialpha, irho, ih);
+      }
+    }  // end loop alpha bin, line1 and line2
+/*    // Loop for alpha bins, line3 and line4
+    for(int ialpha=bin_34_min; ialpha<=bin_34_max; ialpha++) {
+      // The lines should be monotone at this range
+      double line3_rho1 = line3.Eval( Hspace.getAlphaBinLowEdge(ialpha) );
+      double line3_rho2 = line3.Eval( Hspace.getAlphaBinUpEdge(ialpha)  );
+      double line4_rho1 = line4.Eval( Hspace.getAlphaBinLowEdge(ialpha) );
+      double line4_rho2 = line4.Eval( Hspace.getAlphaBinUpEdge(ialpha)  );
+
+      double line3_rho_min = TMath::Min(line3_rho1, line3_rho2);
+      double line3_rho_max = TMath::Max(line3_rho1, line3_rho2);;
+      double line4_rho_min = TMath::Min(line4_rho1, line4_rho2);
+      double line4_rho_max = TMath::Max(line4_rho1, line4_rho2);
+
+      if(line3_rho_min>line3_rho_max || line4_rho_min>line4_rho_max){
+        cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << endl;
+      }
+
+      double rho_min = TMath::Min( line3_rho_min, line4_rho_min);
+      double rho_max = TMath::Max( line3_rho_max, line4_rho_max);
+
+      if(rho_max<settings.map_floatPars["rho_low"] || rho_min>settings.map_floatPars["rho_high"]) continue;
+
+      int nbin_rho_min = TMath::Max( int(ceil( (rho_min-settings.map_floatPars["rho_low"]) / settings.map_floatPars["bin_width_rho"] )), 1 );
+      int nbin_rho_max = TMath::Min( int(ceil( (rho_max-settings.map_floatPars["rho_low"]) / settings.map_floatPars["bin_width_rho"] )), settings.map_intPars["Nbins_rho"] );
+
+      for(int irho=nbin_rho_min; irho<=nbin_rho_max; irho++){
+        Hspace.AddBinHobj(ialpha, irho, ih);
+      }
+    }  // end loop alpha bin, line3 and line4
+*/
+  }  // End loop Hough objects
+
+  return StatusCode::SUCCESS;
+}  // FillHoughSpace() end
+
+
+StatusCode HoughClusteringAlg::ClusterFinding(vector<PandoraPlus::HoughObject>& Hobjects, PandoraPlus::HoughSpace& Hspace, 
+                                              std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_longiClusCol ){
+  if(Hobjects.size()==0) return StatusCode::SUCCESS;
+
+  map< pair<int, int>, set<int> > Hough_bins = Hspace.getHoughBins();
+  // transform candidate to longicluster
+  vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_clusCol; m_clusCol.clear();
+  for(auto ihb : Hough_bins){
+    if(ihb.second.size()<settings.map_intPars["th_peak"]) continue;
+    
+    std::shared_ptr<PandoraPlus::CaloHalfCluster> m_clus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+    for(auto it = (ihb.second).begin(); it!=(ihb.second).end(); it++){
+      m_clus->addUnit(Hobjects[*it].getLocalMax());
+    }
+
+    double t_alpha = Hspace.getAlphaBinCenter((ihb.first).first);
+    double t_rho = Hspace.getRhoBinCenter((ihb.first).second);
+    m_clus->setHoughPars(t_alpha, t_rho);
+
+    if( !m_clus->isContinueN(settings.map_intPars["th_continueN"]) ){
+      continue;
+    } 
+    m_clus->setType(100); //EM-type axis.     
+    m_clus->getLinkedMCPfromUnit();
+    m_clusCol.push_back(m_clus);
+  }
+  // Clean cluster
+  //CleanClusters(m_clusCol);
+
+  //bk_HFclus.insert( bk_HFclus.end(), m_clusCol.begin(), m_clusCol.end() );
+  m_longiClusCol.insert( m_longiClusCol.end(), m_clusCol.begin(), m_clusCol.end() );
+
+  return StatusCode::SUCCESS;
+}  // ClusterFinding() end
+
+
+StatusCode HoughClusteringAlg::CleanClusters( std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_longiClusCol){
+
+  if(m_longiClusCol.size()==0)  return StatusCode::SUCCESS;
+
+  // Remove repeated tracks
+  for(int ic=0; ic<m_longiClusCol.size(); ic++){
+  for(int jc=0; jc<m_longiClusCol.size(); jc++){
+    if(ic>=m_longiClusCol.size()) ic--;
+    if(ic==jc) continue;
+
+    if( m_longiClusCol[ic].get()->isSubset(m_longiClusCol[jc].get()) ){  //jc is the subset of ic. remove jc. 
+      //delete m_longiClusCol[jc]; m_longiClusCol[jc] = NULL;
+      m_longiClusCol.erase(m_longiClusCol.begin()+jc );
+      jc--;
+      if(ic>jc+1) ic--;
+    }
+  }}
+
+  //Depart the HoughCluster to 2 sub-clusters if it has blank in middle.
+  for(int ic=0; ic<m_longiClusCol.size(); ic++){
+    int m_nhit = m_longiClusCol[ic].get()->getCluster().size(); 
+    m_longiClusCol[ic].get()->sortBarShowersByLayer();
+
+    for(int ih=0; ih<m_nhit-1; ih++){
+      if(m_longiClusCol[ic].get()->getCluster()[ih+1]->getDlayer() - m_longiClusCol[ic].get()->getCluster()[ih]->getDlayer() > 2){
+        //PandoraPlus::CaloHalfCluster* clus_head = new PandoraPlus::CaloHalfCluster();
+        //PandoraPlus::CaloHalfCluster* clus_tail = new PandoraPlus::CaloHalfCluster();
+        std::shared_ptr<PandoraPlus::CaloHalfCluster> clus_head = std::make_shared<PandoraPlus::CaloHalfCluster>();
+        std::shared_ptr<PandoraPlus::CaloHalfCluster> clus_tail = std::make_shared<PandoraPlus::CaloHalfCluster>();
+
+        for(int jh=0; jh<=ih; jh++) 
+          clus_head->addUnit( m_longiClusCol[ic].get()->getCluster()[jh]);
+        for(int jh=ih+1; jh<m_nhit; jh++) 
+          clus_tail->addUnit( m_longiClusCol[ic].get()->getCluster()[jh]);
+        
+        if( clus_head->isContinueN(settings.map_intPars["th_continueN"]) ) {
+            clus_head->setType(100); 
+            clus_head->setHoughPars(m_longiClusCol[ic].get()->getHoughAlpha(), m_longiClusCol[ic].get()->getHoughRho());
+            clus_head->getLinkedMCPfromUnit();
+            m_longiClusCol.push_back(clus_head);
+        }
+        //else{
+        //  delete clus_head;
+        //}
+        if( clus_tail->isContinueN(settings.map_intPars["th_continueN"]) ) {
+          clus_tail->setHoughPars(m_longiClusCol[ic].get()->getHoughAlpha(), m_longiClusCol[ic].get()->getHoughRho());
+          clus_tail->setType(100);
+          clus_tail->getLinkedMCPfromUnit();
+          m_longiClusCol.push_back(clus_tail);
+        }
+        //else{
+        //  delete clus_tail;
+        //}
+            
+
+        //delete m_longiClusCol[ic]; m_longiClusCol[ic] = NULL;
+        m_longiClusCol.erase(m_longiClusCol.begin()+ic);
+        ic--;
+        break;
+      }
+    }
+  }
+
+  // Remove repeated tracks
+  for(int ic=0; ic<m_longiClusCol.size(); ic++){
+  for(int jc=0; jc<m_longiClusCol.size(); jc++){
+    if(ic>=m_longiClusCol.size()) ic--;
+    if(ic==jc) continue;
+
+    if( m_longiClusCol[ic].get()->isSubset(m_longiClusCol[jc].get()) ){  //jc is the subset of ic. remove jc. 
+      //delete m_longiClusCol[jc]; m_longiClusCol[jc] = NULL;
+      m_longiClusCol.erase(m_longiClusCol.begin()+jc );
+      jc--;
+      if(ic>jc+1) ic--;
+    }
+  }}
+
+  // Cut energy
+  for(int ic=0; ic<m_longiClusCol.size(); ic++){
+    if(m_longiClusCol[ic].get()->getEnergy()<settings.map_floatPars["th_AxisE"]){
+      //delete m_longiClusCol[ic]; m_longiClusCol[ic]=NULL;
+      m_longiClusCol.erase(m_longiClusCol.begin()+ic );
+      ic--;
+    }
+  }
+
+  // Overlap with other clusters: 
+    if(m_longiClusCol.size()>=2){
+      for(int ic=0; ic<m_longiClusCol.size()-1; ic++){
+      for(int jc=ic+1; jc<m_longiClusCol.size(); jc++){
+        if(ic>=m_longiClusCol.size()) ic--;
+
+        double delta_alpha = TMath::Abs(m_longiClusCol[ic].get()->getHoughAlpha() -  m_longiClusCol[jc].get()->getHoughAlpha());
+        if( (delta_alpha > settings.map_floatPars["th_dAlpha1"])
+            && (delta_alpha < 2*TMath::Pi()-settings.map_floatPars["th_dAlpha1"]) ) continue;
+
+        double m_ratio1 = m_longiClusCol[ic].get()->OverlapRatioE(m_longiClusCol[jc].get());
+        double m_ratio2 = m_longiClusCol[jc].get()->OverlapRatioE(m_longiClusCol[ic].get());
+
+        if(m_ratio1>settings.map_floatPars["th_overlapE"] && m_longiClusCol[ic].get()->getEnergy()<m_longiClusCol[jc].get()->getEnergy()){
+          //delete m_longiClusCol[ic]; m_longiClusCol[ic] = NULL;
+          m_longiClusCol.erase( m_longiClusCol.begin()+ic );
+          ic--;
+          break;
+        }
+
+        if(m_ratio2>settings.map_floatPars["th_overlapE"] && m_longiClusCol[jc].get()->getEnergy()<m_longiClusCol[ic].get()->getEnergy()){
+          //delete m_longiClusCol[jc]; m_longiClusCol[jc] = NULL;
+          m_longiClusCol.erase( m_longiClusCol.begin()+jc );
+          jc--;
+        }
+      }}
+  }
+
+  // If two cluster are close to each other, and E_small/E_large < threshold, delete the small ones
+  if(m_longiClusCol.size()>=2){
+    for(int ic=0; ic<m_longiClusCol.size()-1; ic++){
+    for(int jc=ic+1; jc<m_longiClusCol.size(); jc++){
+      if(ic>=m_longiClusCol.size()) ic--;
+
+      double delta_alpha = TMath::Abs(m_longiClusCol[ic].get()->getHoughAlpha() -  m_longiClusCol[jc].get()->getHoughAlpha());
+      if( delta_alpha > settings.map_floatPars["th_dAlpha2"] ) continue;
+
+      double E_ratio1 = m_longiClusCol[ic].get()->getEnergy() / m_longiClusCol[jc].get()->getEnergy();
+      double E_ratio2 = m_longiClusCol[jc].get()->getEnergy() / m_longiClusCol[ic].get()->getEnergy();
+
+      if( E_ratio1 < settings.map_floatPars["th_ERatio"] ){
+        //delete m_longiClusCol[ic]; m_longiClusCol[ic] = NULL;
+        m_longiClusCol.erase( m_longiClusCol.begin()+ic );
+        ic--;
+        break;
+      }
+      else if( E_ratio2 < settings.map_floatPars["th_ERatio"] ){
+        //delete m_longiClusCol[jc]; m_longiClusCol[jc] = NULL;
+        m_longiClusCol.erase( m_longiClusCol.begin()+jc );
+        jc--;
+      }
+    }}
+  }
+
+  return StatusCode::SUCCESS;
+}  // CleanClusters() end
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/LocalMaxFindingAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/LocalMaxFindingAlg.cpp
new file mode 100644
index 00000000..7d261a89
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/LocalMaxFindingAlg.cpp
@@ -0,0 +1,150 @@
+#ifndef _LOCALMAXFINDING_ALG_C
+#define _LOCALMAXFINDING_ALG_C
+
+#include "Algorithm/LocalMaxFindingAlg.h"
+using namespace PandoraPlus; 
+
+StatusCode LocalMaxFindingAlg::ReadSettings(PandoraPlus::Settings& m_settings){
+  settings = m_settings;
+
+  if(settings.map_floatPars.find("Eth_localMax")==settings.map_floatPars.end()) settings.map_floatPars["Eth_localMax"] = 0.005;
+  if(settings.map_floatPars.find("Eth_MaxWithNeigh")==settings.map_floatPars.end()) settings.map_floatPars["Eth_MaxWithNeigh"] = 0.;
+  if(settings.map_stringPars.find("OutputLocalMaxName")==settings.map_stringPars.end()) settings.map_stringPars["OutputLocalMaxName"] = "AllLocalMax";
+  return StatusCode::SUCCESS;
+}
+
+StatusCode LocalMaxFindingAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  p_HalfClusU = NULL;
+  p_HalfClusV = NULL; 
+
+  p_HalfClusU = &(m_datacol.map_HalfCluster["HalfClusterColU"]);
+  p_HalfClusV = &(m_datacol.map_HalfCluster["HalfClusterColV"]);
+
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode LocalMaxFindingAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol){
+
+  if(!p_HalfClusU || !p_HalfClusV) {std::cout<<"ERROR: No HalfClusters in present data collection! "<<std::endl; return StatusCode::FAILURE; }
+
+  for(int iu = 0; iu<p_HalfClusU->size(); iu++){
+    std::vector<const Calo1DCluster*> m_1dClusCol = p_HalfClusU->at(iu).get()->getCluster();
+
+    std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> ptr_localMax; ptr_localMax.clear();
+    for(int i1d=0; i1d<m_1dClusCol.size(); i1d++) GetLocalMax(m_1dClusCol[i1d], ptr_localMax);
+
+    std::vector<const PandoraPlus::Calo1DCluster*> tmp_localMax; tmp_localMax.clear();
+    for(auto iter : ptr_localMax){
+      iter->getLinkedMCPfromUnit();
+      tmp_localMax.push_back(iter.get());
+      m_datacol.map_1DCluster["bk1DCluster"].push_back(iter);
+    }
+    p_HalfClusU->at(iu).get()->setLocalMax(settings.map_stringPars["OutputLocalMaxName"], tmp_localMax);
+    m_1dClusCol.clear(); 
+    ptr_localMax.clear();
+  }
+
+  for(int iv=0; iv<p_HalfClusV->size(); iv++){
+    std::vector<const Calo1DCluster*> m_1dClusCol = p_HalfClusV->at(iv).get()->getCluster();
+
+    std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> ptr_localMax; ptr_localMax.clear();
+    for(int i1d=0; i1d<m_1dClusCol.size(); i1d++) GetLocalMax(m_1dClusCol[i1d], ptr_localMax);
+
+    std::vector<const PandoraPlus::Calo1DCluster*> tmp_localMax; tmp_localMax.clear();
+    for(auto iter : ptr_localMax){
+      iter->getLinkedMCPfromUnit();
+      tmp_localMax.push_back(iter.get());
+      m_datacol.map_1DCluster["bk1DCluster"].push_back(iter);
+    }
+    p_HalfClusV->at(iv).get()->setLocalMax(settings.map_stringPars["OutputLocalMaxName"], tmp_localMax);
+    m_1dClusCol.clear(); 
+    ptr_localMax.clear();
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode LocalMaxFindingAlg::ClearAlgorithm(){
+  p_HalfClusU = nullptr;
+  p_HalfClusV = nullptr;
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode LocalMaxFindingAlg::GetLocalMax( const PandoraPlus::Calo1DCluster* m_1dClus, 
+                                            std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>>& m_output){
+
+  if(m_1dClus->getBars().size()==0) return StatusCode::SUCCESS;
+
+  std::vector<const PandoraPlus::CaloUnit*> m_barCol = m_1dClus->getBars();
+
+//cout<<"  LocalMaxFindingAlg::GetLocalMax: Input bar collection size: "<<m_barCol.size()<<endl;
+
+  std::vector<const PandoraPlus::CaloUnit*> localMaxCol; localMaxCol.clear();
+
+  GetLocalMaxBar( m_barCol, localMaxCol );
+
+//cout<<"  LocalMaxFindingAlg::GetLocalMax: Found local max bar size: "<<localMaxCol.size()<<endl;
+//cout<<"  Transfer bar to barShower"<<endl;
+
+  for(int j=0; j<localMaxCol.size(); j++){
+    std::shared_ptr<PandoraPlus::Calo1DCluster> m_shower = std::make_shared<PandoraPlus::Calo1DCluster>();
+    m_shower->addUnit( localMaxCol[j] );
+    m_output.push_back(m_shower);
+  }
+
+//cout<<"  Output bar shower size: "<<m_output.size()<<endl;
+//cout<<endl;
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode LocalMaxFindingAlg::GetLocalMaxBar( std::vector<const PandoraPlus::CaloUnit*>& barCol, std::vector<const PandoraPlus::CaloUnit*>& localMaxCol ){
+  //std::sort( barCol.begin(), barCol.end(), compBar );
+  for(int ib=0; ib<barCol.size(); ib++){
+    std::vector<const PandoraPlus::CaloUnit*> m_neighbors = getNeighbors( barCol[ib], barCol );
+    if( m_neighbors.size()==0 && barCol[ib]->getEnergy()>settings.map_floatPars["Eth_localMax"] ) { 
+      localMaxCol.push_back( barCol[ib] ); continue; 
+    }
+
+    bool isLocalMax=true;
+    double Eneigh=0;
+    if(barCol[ib]->getEnergy()<settings.map_floatPars["Eth_localMax"]) isLocalMax = false;
+    for(int j=0;j<m_neighbors.size();j++){
+      if(m_neighbors[j]->getEnergy() > barCol[ib]->getEnergy()) isLocalMax=false;
+      Eneigh += m_neighbors[j]->getEnergy();
+    }
+    if( (barCol[ib]->getEnergy()/(barCol[ib]->getEnergy()+Eneigh))<settings.map_floatPars["Eth_MaxWithNeigh"] ) isLocalMax = false;
+    if(isLocalMax) localMaxCol.push_back( barCol[ib] );
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+std::vector<const PandoraPlus::CaloUnit*> LocalMaxFindingAlg::getNeighbors( const PandoraPlus::CaloUnit* seed, std::vector<const PandoraPlus::CaloUnit*>& barCol){
+  std::vector<const PandoraPlus::CaloUnit*> m_neighbor; m_neighbor.clear();
+  for(int i=0;i<barCol.size();i++){
+    bool fl_neighbor = false; 
+    if( seed->getModule()==barCol[i]->getModule() && 
+        seed->getStave()==barCol[i]->getStave() && 
+        seed->getDlayer()==barCol[i]->getDlayer() &&
+        seed->getSlayer()==barCol[i]->getSlayer() &&
+        abs( seed->getBar()-barCol[i]->getBar() )==1 ) fl_neighbor=true;
+    else if( seed->getStave()==barCol[i]->getStave() && 
+             ( ( seed->getModule()-barCol[i]->getModule()==1 && seed->isAtLowerEdgePhi() && barCol[i]->isAtUpperEdgePhi() ) ||
+               ( barCol[i]->getModule()-seed->getModule()==1 && seed->isAtUpperEdgePhi() && barCol[i]->isAtLowerEdgePhi() ) ) ) fl_neighbor=true;
+    else if( seed->getModule()==barCol[i]->getModule() && 
+             ( ( seed->getStave()-barCol[i]->getStave()==1 && seed->isAtLowerEdgeZ() && barCol[i]->isAtUpperEdgeZ() ) || 
+               ( barCol[i]->getStave()-seed->getStave()==1 && seed->isAtUpperEdgeZ() && barCol[i]->isAtLowerEdgeZ() ) ) ) fl_neighbor=true;
+
+    if(fl_neighbor) m_neighbor.push_back(barCol[i]);
+  }
+  if(m_neighbor.size()>2) std::cout<<"WARNING: more than 2 hits in neighborCol!!"<<std::endl;
+
+  return m_neighbor;
+}
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/PFOCreatingAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/PFOCreatingAlg.cpp
new file mode 100644
index 00000000..1d044542
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/PFOCreatingAlg.cpp
@@ -0,0 +1,278 @@
+#ifndef _PFOCREATING_ALG_C
+#define _PFOCREATING_ALG_C
+
+#include "Algorithm/PFOCreatingAlg.h"
+
+StatusCode PFOCreatingAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  if(settings.map_stringPars.find("ReadinECALClusters")==settings.map_stringPars.end()) settings.map_stringPars["ReadinECALClusters"] = "EcalCluster";
+  if(settings.map_stringPars.find("ReadinHCALClusters")==settings.map_stringPars.end()) settings.map_stringPars["ReadinHCALClusters"] = "HCALCluster";
+  if(settings.map_stringPars.find("OutputCombPFO")==settings.map_stringPars.end()) settings.map_stringPars["OutputCombPFO"] = "outputPFO";
+
+  if(settings.map_floatPars.find("delta_phi_cut")==settings.map_floatPars.end())
+    settings.map_floatPars["delta_phi_cut"] = 30./180.*TMath::Pi(); 
+  if(settings.map_floatPars.find("delta_cosTheta_cut")==settings.map_floatPars.end())
+    settings.map_floatPars["delta_cosTheta_cut"] = 0.25; 
+  if(settings.map_floatPars.find("extrPoint_HCALHit_distCut")==settings.map_floatPars.end())
+    settings.map_floatPars["extrPoint_HCALHit_distCut"] = 100; 
+  if(settings.map_floatPars.find("nearby_clus_angleCut")==settings.map_floatPars.end())
+    settings.map_floatPars["nearby_clus_angleCut"] = 0.15; 
+  return StatusCode::SUCCESS;
+};
+
+StatusCode PFOCreatingAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  m_tracks.clear();
+  m_ecal_clusters.clear();
+  m_hcal_clusters.clear();
+  m_pfobjects.clear();
+
+  for(int it=0; it<m_datacol.TrackCol.size(); it++){
+    m_tracks.push_back( m_datacol.TrackCol[it].get() );
+  }
+  for(int ie=0; ie<m_datacol.map_CaloCluster[settings.map_stringPars["ReadinECALClusters"]].size(); ie++){
+    m_ecal_clusters.push_back( m_datacol.map_CaloCluster[settings.map_stringPars["ReadinECALClusters"]][ie].get() );
+  }
+  for(int ih=0; ih<m_datacol.map_CaloCluster[settings.map_stringPars["ReadinHCALClusters"]].size(); ih++){
+    m_hcal_clusters.push_back( m_datacol.map_CaloCluster[settings.map_stringPars["ReadinHCALClusters"]][ih].get() );
+  }
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode PFOCreatingAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+  std::cout << "yyy: Running PFOCreatingAlg" << std::endl;
+
+  if(m_tracks.size()==0 && m_ecal_clusters.size()==0 && m_hcal_clusters.size()==0){
+    std::cout << "  yyy: No tracks, no clusters in ECAL and HCAL. End PFOCreatingAlg" << std::endl;
+    return StatusCode::SUCCESS;
+  }
+
+  // Create PFO with ECAl clusters. If a ECAL cluster is a charged cluster, connect HCAL clusters using extrapolated points
+  for(int ie=0; ie<m_ecal_clusters.size(); ie++){
+    std::vector<const PandoraPlus::Track*> ecal_cls_track = m_ecal_clusters[ie]->getAssociatedTracks();
+    if(ecal_cls_track.size()>1){
+      std::cout << "Error! " << ecal_cls_track.size() << " tracks associated to one ECAL cluster!" << std::endl;
+      continue;
+    }
+
+    // Charged cluster in ECAL (A cluster with a track)
+    if(ecal_cls_track.size()==1){  
+      std::vector<PandoraPlus::Calo3DCluster*> hcal_clus_candidate;
+      hcal_clus_candidate.clear();
+      GetChargedHCALCandidates(ecal_cls_track[0], m_hcal_clusters, hcal_clus_candidate);
+
+      std::shared_ptr<PandoraPlus::PFObject> tmp_pfo = std::make_shared<PandoraPlus::PFObject>();
+      tmp_pfo->addTrack(ecal_cls_track[0]);
+      tmp_pfo->addECALCluster(m_ecal_clusters[ie]);
+      for(int ic=0; ic<hcal_clus_candidate.size(); ic++){
+        tmp_pfo->addHCALCluster(hcal_clus_candidate[ic]);
+      }
+      m_pfobjects.push_back(tmp_pfo);
+
+      CleanUsedElements(hcal_clus_candidate, m_hcal_clusters);
+      CleanUsedElements(ecal_cls_track, m_tracks);
+    }
+
+    // Neutral cluster in ECAL (A cluster without track)
+    else if(ecal_cls_track.size()==0){
+      // Create PFO with only a ECAL cluster
+      std::shared_ptr<PandoraPlus::PFObject> tmp_pfo = std::make_shared<PandoraPlus::PFObject>();
+      tmp_pfo->addECALCluster(m_ecal_clusters[ie]);
+      m_pfobjects.push_back(tmp_pfo);
+      
+      
+    }
+    else{
+      cout << "  yyy: Error: Wrong number of tracks" << endl;
+    }
+  }
+
+  // Create PFO with only tracks
+  for(int it=0; it<m_tracks.size(); it++){
+    std::shared_ptr<PandoraPlus::PFObject> tmp_pfo = std::make_shared<PandoraPlus::PFObject>();
+    tmp_pfo->addTrack(m_tracks[it]);
+    m_pfobjects.push_back(tmp_pfo);
+  }
+
+  
+  // Connect left HCAL clusters to PFO. These PFO are made of ECAL clusters( and tracks) now
+  for(int ih=0; ih<m_hcal_clusters.size(); ih++){
+    TVector3 hcal_clus_pos = m_hcal_clusters[ih]->getHitCenter();
+    double min_angle = 999.0;
+    int pfo_index = -1;
+    
+    for(int ip=0; ip<m_pfobjects.size(); ip++){
+      std::vector<const PandoraPlus::Calo3DCluster*> pf_ecal_clus = m_pfobjects[ip].get()->getECALClusters();
+      for(int ie=0; ie<pf_ecal_clus.size(); ie++){
+        TVector3 ecal_clus_pos = pf_ecal_clus[ie]->getShowerCenter();
+
+        double angle = hcal_clus_pos.Angle(ecal_clus_pos);
+        if(angle<settings.map_floatPars["nearby_clus_angleCut"] && angle<min_angle){
+          min_angle=angle;
+          pfo_index = ip;
+        }
+      } 
+    }
+
+    if(pfo_index>=0){
+      m_pfobjects[pfo_index].get()->addHCALCluster(m_hcal_clusters[ih]);
+    }
+    else{
+      std::shared_ptr<PandoraPlus::PFObject> tmp_pfo = std::make_shared<PandoraPlus::PFObject>();
+      tmp_pfo->addHCALCluster(m_hcal_clusters[ih]);
+      m_pfobjects.push_back(tmp_pfo);
+    }
+
+  }  
+
+  m_datacol.map_PFObjects[settings.map_stringPars["OutputCombPFO"]] = m_pfobjects;
+
+  
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode PFOCreatingAlg::ClearAlgorithm(){
+  m_tracks.clear();
+  m_ecal_clusters.clear();
+  m_hcal_clusters.clear();
+  m_pfobjects.clear();
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode PFOCreatingAlg::GetChargedHCALCandidates(const PandoraPlus::Track* _track,
+                                      std::vector<PandoraPlus::Calo3DCluster*>& _hcal_clusters,
+                                      std::vector<PandoraPlus::Calo3DCluster*>& _hcal_clus_candidate)
+{
+  std::vector<TrackState> hcal_trk_states = _track->getTrackStates("Hcal");
+  if(hcal_trk_states.size()==0)
+    return StatusCode::SUCCESS;
+
+
+  std::vector<TVector3> extrpolated_points;
+  for(int it=0; it<hcal_trk_states.size(); it++){
+    extrpolated_points.push_back(hcal_trk_states[it].referencePoint);
+  }
+  
+  for(int ih=0; ih<_hcal_clusters.size(); ih++){
+    TVector3 clus_center = _hcal_clusters[ih]->getHitCenter();
+    TVector3 distance = clus_center - extrpolated_points[0];
+    if(distance.Mag()>1000) continue;  // yyy: harcode for 1000. If the cluster is too far away from the extrpolated points in HCAL, it is obviously not a candidate
+
+    bool is_candidate = false;
+    for(int ihit=0; ihit<_hcal_clusters[ih]->getCaloHits().size(); ihit++){
+      if(is_candidate) break;
+      TVector3 hit_pos = _hcal_clusters[ih]->getCaloHits()[ihit]->getPosition();
+      for(int ie=0; ie<extrpolated_points.size(); ie++){
+        TVector3 dist = hit_pos - extrpolated_points[ie];
+        if(dist.Mag()<settings.map_floatPars["extrPoint_HCALHit_distCut"]){
+          is_candidate = true;
+          break;
+        }
+      }
+    }
+
+    if(is_candidate){
+      _hcal_clus_candidate.push_back(_hcal_clusters[ih]);
+    }
+  }
+} 
+
+StatusCode PFOCreatingAlg::GetNearbyHCALCandidates( PandoraPlus::Calo3DCluster* _ecal_cluster,
+                                  std::vector<PandoraPlus::Calo3DCluster*>& _hcal_clusters,
+                                  std::vector<PandoraPlus::Calo3DCluster*>& _hcal_clus_candidate)
+{
+  TVector3 ecal_pos = _ecal_cluster->getShowerCenter();
+  for(int ih=0; ih<_hcal_clusters.size(); ih++){
+    TVector3 hcal_pos = _hcal_clusters[ih]->getHitCenter();
+
+    double angle = ecal_pos.Angle(hcal_pos);
+    if(angle<settings.map_floatPars["nearby_clus_angleCut"]){
+      _hcal_clus_candidate.push_back(_hcal_clusters[ih]);
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+bool PFOCreatingAlg::isReachOuterMostECAL(PandoraPlus::Calo3DCluster* _ecal_cluster)
+{
+  // A neutral cluster may deposits energy into HCAL if its cluster in ECAL reach the outermost boundary.
+  //   In this case, it hits the last layer of ECAL or hit the boundary of different modules.
+  const CaloHalfCluster* p_HFClusterU = _ecal_cluster->getHalfClusterUCol("LinkedLongiCluster")[0];
+  const CaloHalfCluster* p_HFClusterV = _ecal_cluster->getHalfClusterVCol("LinkedLongiCluster")[0];
+  int endLayer = max(p_HFClusterU->getEndDlayer(), p_HFClusterV->getEndDlayer());
+  if (endLayer>13){  // yyy: hardcode! number of layer in ECAL may be different from 14 for other design
+    cout << "    yyy: The ECAL cluster reach the outermost ECAL. endLayer=" <<  endLayer << endl;
+    return true;
+  }
+
+  std::vector< std::vector<int> > cluster_towers = _ecal_cluster->getTowerID();
+  set<int> towerID;
+  for(int it=0; it<cluster_towers.size(); it++){
+    towerID.insert(cluster_towers[it][0]);
+  }
+  if(towerID.size()>1){
+    return true;
+
+    cout << "    yyy: The ECAL cluster deposits in over one module. towerID = (";
+    for (int i : towerID) {
+      cout << i << ", ";
+    }cout << ")" << endl;
+  }
+
+  cout << "    yyy: The ECAL cluster does not reach the outermost ECAL. EndLayer=" <<  endLayer 
+        << ", towerID = (";
+  for (int i : towerID) {
+    cout << i << ", ";
+  }cout << ")" << endl;
+
+  return false;
+}
+
+template<typename T1, typename T2>
+StatusCode PFOCreatingAlg::CleanUsedElements(std::vector<T1>& _used_elements,
+                                                    std::vector<T2>& _left_elements)
+{
+  for(int i=0; i<_used_elements.size(); i++){
+    auto it = std::find(_left_elements.begin(), _left_elements.end(), _used_elements[i]);
+    if (it != _left_elements.end()) {
+      _left_elements.erase(it);
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+template<typename T1, typename T2> StatusCode CleanUsedElement(T1 _used_element,
+                                                    std::vector<T2>& _left_elements)
+{
+  auto it = std::find(_left_elements.begin(), _left_elements.end(), _used_element);
+
+  if (it != _left_elements.end()) {
+      _left_elements.erase(it);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode PFOCreatingAlg::CreateLeftPFO(std::vector<PandoraPlus::Track*>& _tracks,
+                            std::vector<PandoraPlus::Calo3DCluster*>& _hcal_clusters,
+                            std::vector<std::shared_ptr<PandoraPlus::PFObject>>& _pfobjects)
+{
+  for(int it=0; it<_tracks.size(); it++){
+    std::shared_ptr<PandoraPlus::PFObject> tmp_pfo = std::make_shared<PandoraPlus::PFObject>();
+    tmp_pfo->addTrack(_tracks[it]);
+    _pfobjects.push_back(tmp_pfo);
+  }
+
+  for(int ih=0; ih<_hcal_clusters.size(); ih++){
+    std::shared_ptr<PandoraPlus::PFObject> tmp_pfo = std::make_shared<PandoraPlus::PFObject>();
+    tmp_pfo->addHCALCluster(_hcal_clusters[ih]);
+    _pfobjects.push_back(tmp_pfo);
+  }
+  return StatusCode::SUCCESS;
+} 
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/PFOReclusteringAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/PFOReclusteringAlg.cpp
new file mode 100644
index 00000000..32a50d61
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/PFOReclusteringAlg.cpp
@@ -0,0 +1,579 @@
+#ifndef _PFORECLUSTERING_ALG_C
+#define _PFORECLUSTERING_ALG_C
+
+#include "Algorithm/PFOReclusteringAlg.h"
+
+StatusCode PFOReclusteringAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  //Initialize parameters
+  if(settings.map_stringPars.find("ReadinPFOName")==settings.map_stringPars.end()) settings.map_stringPars["ReadinPFOName"] = "outputPFO";
+  if(settings.map_floatPars.find("ECALCalib")==settings.map_floatPars.end()) settings.map_floatPars["ECALCalib"] = 1.02;
+  if(settings.map_floatPars.find("HCALCalib")==settings.map_floatPars.end()) settings.map_floatPars["HCALCalib"] = 65.;
+  
+  if(settings.map_floatPars.find("EnergyRes")==settings.map_floatPars.end()) settings.map_floatPars["EnergyRes"] = 0.4;
+  if(settings.map_floatPars.find("SplitSigma")==settings.map_floatPars.end()) settings.map_floatPars["SplitSigma"] = 0.;
+  if(settings.map_floatPars.find("NeutralMergeSigma")==settings.map_floatPars.end()) settings.map_floatPars["NeutralMergeSigma"] = 0.;
+  if(settings.map_floatPars.find("VirtualMergeSigma")==settings.map_floatPars.end()) settings.map_floatPars["VirtualMergeSigma"] = 0.6;
+  if(settings.map_floatPars.find("MinAngleForNeuMerge")==settings.map_floatPars.end()) settings.map_floatPars["MinAngleForNeuMerge"] = 0.18;
+  if(settings.map_floatPars.find("MinAngleForVirMerge")==settings.map_floatPars.end()) settings.map_floatPars["MinAngleForVirMerge"] = 0.12;
+
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode PFOReclusteringAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  p_PFObjects = nullptr;
+
+  p_PFObjects = &(m_datacol.map_PFObjects[settings.map_stringPars["ReadinPFOName"]]);
+  return StatusCode::SUCCESS;
+};
+
+StatusCode PFOReclusteringAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+
+  std::vector< std::shared_ptr<PandoraPlus::PFObject> > m_chargedPFOs; 
+  std::vector< std::shared_ptr<PandoraPlus::PFObject> > m_neutralPFOs; 
+
+  for(int ipfo=0; ipfo<p_PFObjects->size(); ipfo++){
+    if(p_PFObjects->at(ipfo)->getTracks().size()==0) m_neutralPFOs.push_back( p_PFObjects->at(ipfo) );
+    else m_chargedPFOs.push_back( p_PFObjects->at(ipfo) );
+  }
+
+  std::sort(m_chargedPFOs.begin(), m_chargedPFOs.end(), compTrkP);
+ double totE_Ecal = 0;
+ double totE_Hcal = 0;
+ cout<<"Readin PFO: "<<p_PFObjects->size()<<", charged "<<m_chargedPFOs.size()<<", neutral "<<m_neutralPFOs.size()<<endl;
+ for(int i=0; i<m_neutralPFOs.size(); i++){
+   cout<<"    PFO #"<<i<<": track size "<<m_neutralPFOs[i]->getTracks().size()<<", leading P "<<m_neutralPFOs[i]->getTrackMomentum();
+   cout<<", ECAL cluster size "<<m_neutralPFOs[i]->getECALClusters().size()<<", totE "<<m_neutralPFOs[i]->getECALClusterEnergy();
+   cout<<", HCAL cluster size "<<m_neutralPFOs[i]->getHCALClusters().size()<<", totE "<<m_neutralPFOs[i]->getHCALClusterEnergy()<<endl;
+   totE_Ecal += m_neutralPFOs[i]->getECALClusterEnergy();
+   totE_Hcal += m_neutralPFOs[i]->getHCALClusterEnergy();
+ }
+ cout<<"-----Neutral cluster Ecal total energy: "<<totE_Ecal<<", Hcal total energy: "<<totE_Hcal<<endl;
+ totE_Ecal = 0;
+ totE_Hcal = 0;
+ for(int i=0; i<m_chargedPFOs.size(); i++){
+   cout<<"    PFO #"<<i<<": track size "<<m_chargedPFOs[i]->getTracks().size()<<", leading P "<<m_chargedPFOs[i]->getTrackMomentum();
+   cout<<", ECAL cluster size "<<m_chargedPFOs[i]->getECALClusters().size()<<", totE "<<m_chargedPFOs[i]->getECALClusterEnergy();
+   cout<<", HCAL cluster size "<<m_chargedPFOs[i]->getHCALClusters().size()<<", totE "<<m_chargedPFOs[i]->getHCALClusterEnergy()<<endl;
+   totE_Ecal += m_chargedPFOs[i]->getECALClusterEnergy();
+   totE_Hcal += m_chargedPFOs[i]->getHCALClusterEnergy();
+ }
+ cout<<"-----Charged cluster Ecal total energy: "<<totE_Ecal<<", Hcal total energy: "<<totE_Hcal<<endl;
+
+
+  //If P_trk < E_cluster, create a virtual neutral PFO. 
+  ReCluster_SplitFromChg(m_chargedPFOs, m_neutralPFOs);
+
+ totE_Ecal = 0;
+ totE_Hcal = 0;
+ cout<<"After split from Ch: charged "<<m_chargedPFOs.size()<<", neutral "<<m_neutralPFOs.size()<<", total "<<p_PFObjects->size()<<endl;
+ for(int i=0; i<m_neutralPFOs.size(); i++){
+   cout<<"    PFO #"<<i<<": track size "<<m_neutralPFOs[i]->getTracks().size()<<", leading P "<<m_neutralPFOs[i]->getTrackMomentum();
+   cout<<", ECAL cluster size "<<m_neutralPFOs[i]->getECALClusters().size()<<", totE "<<m_neutralPFOs[i]->getECALClusterEnergy();
+   cout<<", HCAL cluster size "<<m_neutralPFOs[i]->getHCALClusters().size()<<", totE "<<m_neutralPFOs[i]->getHCALClusterEnergy()<<endl;
+   totE_Ecal += m_neutralPFOs[i]->getECALClusterEnergy();
+   totE_Hcal += m_neutralPFOs[i]->getHCALClusterEnergy();
+ }
+ cout<<"-----Neutral cluster Ecal total energy: "<<totE_Ecal<<", Hcal total energy: "<<totE_Hcal<<endl;
+ totE_Ecal = 0;
+ totE_Hcal = 0;
+ for(int i=0; i<m_chargedPFOs.size(); i++){
+   cout<<"    PFO #"<<i<<": track size "<<m_chargedPFOs[i]->getTracks().size()<<", leading P "<<m_chargedPFOs[i]->getTrackMomentum();
+   cout<<", ECAL cluster size "<<m_chargedPFOs[i]->getECALClusters().size()<<", totE "<<m_chargedPFOs[i]->getECALClusterEnergy();
+   cout<<", HCAL cluster size "<<m_chargedPFOs[i]->getHCALClusters().size()<<", totE "<<m_chargedPFOs[i]->getHCALClusterEnergy()<<endl;
+   totE_Ecal += m_chargedPFOs[i]->getECALClusterEnergy();
+   totE_Hcal += m_chargedPFOs[i]->getHCALClusterEnergy();
+ }
+ cout<<"-----Charged cluster Ecal total energy: "<<totE_Ecal<<", Hcal total energy: "<<totE_Hcal<<endl;
+
+
+  //If P_trk > E_cluster, merge nearby neutral PFO into the charged. 
+  ReCluster_MergeToChg(m_chargedPFOs, m_neutralPFOs);
+
+  m_datacol.map_CaloHit["bkHit"].insert( m_datacol.map_CaloHit["bkHit"].end(), m_bkCol.map_CaloHit["bkHit"].begin(), m_bkCol.map_CaloHit["bkHit"].end() );
+  m_datacol.map_CaloCluster["bk3DCluster"].insert( m_datacol.map_CaloCluster["bk3DCluster"].end(), m_bkCol.map_CaloCluster["bk3DCluster"].begin(), m_bkCol.map_CaloCluster["bk3DCluster"].end() );
+  m_datacol.map_PFObjects["bkPFO"].insert( m_datacol.map_PFObjects["bkPFO"].end(), m_bkCol.map_PFObjects["bkPFO"].begin(), m_bkCol.map_PFObjects["bkPFO"].end() );
+
+ totE_Ecal = 0;
+ totE_Hcal = 0;
+ cout<<"After merge all virtual to Ch: charged "<<m_chargedPFOs.size()<<", neutral "<<m_neutralPFOs.size()<<", total "<<p_PFObjects->size()<<endl;
+ for(int i=0; i<m_neutralPFOs.size(); i++){
+   cout<<"    PFO #"<<i<<": track size "<<m_neutralPFOs[i]->getTracks().size()<<", leading P "<<m_neutralPFOs[i]->getTrackMomentum();
+   cout<<", ECAL cluster size "<<m_neutralPFOs[i]->getECALClusters().size()<<", totE "<<m_neutralPFOs[i]->getECALClusterEnergy();
+   cout<<", HCAL cluster size "<<m_neutralPFOs[i]->getHCALClusters().size()<<", totE "<<m_neutralPFOs[i]->getHCALClusterEnergy()<<endl;
+   totE_Ecal += m_neutralPFOs[i]->getECALClusterEnergy();
+   totE_Hcal += m_neutralPFOs[i]->getHCALClusterEnergy();
+ }
+ cout<<"-----Neutral cluster Ecal total energy: "<<totE_Ecal<<", Hcal total energy: "<<totE_Hcal<<endl;
+ totE_Ecal = 0;
+ totE_Hcal = 0;
+ for(int i=0; i<m_chargedPFOs.size(); i++){
+   cout<<"    PFO #"<<i<<": track size "<<m_chargedPFOs[i]->getTracks().size()<<", leading P "<<m_chargedPFOs[i]->getTrackMomentum();
+   cout<<", ECAL cluster size "<<m_chargedPFOs[i]->getECALClusters().size()<<", totE "<<m_chargedPFOs[i]->getECALClusterEnergy();
+   cout<<", HCAL cluster size "<<m_chargedPFOs[i]->getHCALClusters().size()<<", totE "<<m_chargedPFOs[i]->getHCALClusterEnergy()<<endl;
+   totE_Ecal += m_chargedPFOs[i]->getECALClusterEnergy();
+   totE_Hcal += m_chargedPFOs[i]->getHCALClusterEnergy();
+ }
+ cout<<"-----Charged cluster Ecal total energy: "<<totE_Ecal<<", Hcal total energy: "<<totE_Hcal<<endl;
+
+
+  m_chargedPFOs.clear();
+  m_neutralPFOs.clear();
+  return StatusCode::SUCCESS;
+};
+
+StatusCode PFOReclusteringAlg::ClearAlgorithm(){
+  p_PFObjects = nullptr;
+  m_bkCol.Clear();
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode PFOReclusteringAlg::ReCluster_MergeToChg(std::vector< std::shared_ptr<PandoraPlus::PFObject> >& m_chargedPFOs, 
+                                                    std::vector< std::shared_ptr<PandoraPlus::PFObject> >& m_neutralPFOs ){
+
+  //Merge real neutral PFOs
+  for(int ic=0; ic<m_chargedPFOs.size(); ic++){
+    if(m_chargedPFOs[ic]->getECALClusters().size()==0 && m_chargedPFOs[ic]->getHCALClusters().size()==0) continue;
+
+    double track_energy = m_chargedPFOs[ic]->getTrackMomentum();
+    double ECAL_energy = settings.map_floatPars["ECALCalib"]*m_chargedPFOs[ic]->getECALClusterEnergy();
+    double HCAL_energy = settings.map_floatPars["HCALCalib"]*m_chargedPFOs[ic]->getHCALClusterEnergy();
+    if(track_energy<0 || ECAL_energy<0 || HCAL_energy<0){
+      std::cout<<"ERROR: Charged PFO info break. Ptrk "<<track_energy<<", E_ecal "<<ECAL_energy<<", E_hcal "<<HCAL_energy<<endl;
+      continue;
+    }
+
+    double delta_energy = ECAL_energy + HCAL_energy - track_energy;
+    double sigmaE = settings.map_floatPars["EnergyRes"] * sqrt(ECAL_energy + HCAL_energy);
+
+//cout<<"  ReCluster_MergeToChg: In ChPFO #"<<ic;
+//cout<<": ECAL cluster size "<<m_chargedPFOs[ic]->getECALClusters().size()<<", HCAL cluster size "<<m_chargedPFOs[ic]->getHCALClusters().size();
+//cout<<", Ptrk = "<<track_energy<<", Eecal = "<<ECAL_energy<<", Ehcal = "<<HCAL_energy<<", deltaE = "<<delta_energy<<", sigmaE = "<<sigmaE<<endl;
+
+    if(delta_energy >= settings.map_floatPars["NeutralMergeSigma"]*sigmaE) continue;
+//cout<<"    Do charged PFO merge. "<<endl;
+
+    // All HCAL clusters in the neutral PFO
+    std::vector<const PandoraPlus::Calo3DCluster*> all_neutral_HCAL_clus;
+    for(int ip=0; ip<m_neutralPFOs.size(); ip++){
+      std::vector<const PandoraPlus::Calo3DCluster*> tmp_HCAL_clus = m_neutralPFOs[ip]->getHCALClusters();
+      all_neutral_HCAL_clus.insert(all_neutral_HCAL_clus.end(), tmp_HCAL_clus.begin(), tmp_HCAL_clus.end());
+    }
+//cout<<"      Neutral HCAL cluster size "<<all_neutral_HCAL_clus.size()<<endl;
+
+    TVector3 trackclus_pos(0, 0, 0);
+    if(m_chargedPFOs[ic]->getECALClusters().size()>0){
+      trackclus_pos = m_chargedPFOs[ic]->getECALClusters()[0]->getShowerCenter();
+    }
+    else{
+      for(int jc=0; jc<m_chargedPFOs[ic]->getHCALClusters().size(); jc++)
+        trackclus_pos += m_chargedPFOs[ic]->getHCALClusters()[jc]->getHitCenter() * settings.map_floatPars["HCALCalib"]*m_chargedPFOs[ic]->getHCALClusters()[jc]->getHitsE();
+
+      trackclus_pos = trackclus_pos*(1./HCAL_energy);
+    }
+//printf("    charged cluster trk pos: [%.3f, %.3f, %.3f] \n", trackclus_pos.x(), trackclus_pos.y(), trackclus_pos.z());  
+
+    int loop_count = 0;
+    std::vector<const PandoraPlus::Calo3DCluster*> skip_clus;
+    while(delta_energy < 0){
+      loop_count++;
+      if(loop_count>all_neutral_HCAL_clus.size()+10){
+        break;
+      }
+      double min_angle = 999.0;
+      int clus_index = -1;
+
+      for(int in=0; in<all_neutral_HCAL_clus.size(); in++){
+        if(find(skip_clus.begin(), skip_clus.end(), all_neutral_HCAL_clus[in]) != skip_clus.end()) continue;
+
+        TVector3 neutral_clus_pos = all_neutral_HCAL_clus[in]->getHitCenter();
+        double pfo_angle = trackclus_pos.Angle(neutral_clus_pos);
+        if (pfo_angle<settings.map_floatPars["MinAngleForNeuMerge"] && pfo_angle<min_angle){
+          min_angle=pfo_angle;
+          clus_index = in;
+        }
+      }
+
+//cout<<"    In Loop "<<loop_count<<": current deltaE = "<<delta_energy<<", closest virtual cluster index "<<clus_index<<endl;
+      if(clus_index<0) break;  // No neutral Hcal cluster to be merged
+
+      double tmp_delta_E = delta_energy + settings.map_floatPars["HCALCalib"]*all_neutral_HCAL_clus[clus_index]->getHitsE();
+      if (TMath::Abs(tmp_delta_E) >= TMath::Abs(delta_energy)){
+        skip_clus.push_back(all_neutral_HCAL_clus[clus_index]);
+        continue;  // No need to merge this HCAL cluster
+      }
+
+      // Update delta_energy
+      delta_energy = tmp_delta_E;
+      // Add this HCAL cluster to charged PFO
+      m_chargedPFOs[ic]->addHCALCluster(all_neutral_HCAL_clus[clus_index]);
+
+      // Remove this HCAL cluster from neutral PFO
+      bool is_found = false;
+      for(int in=0; in<m_neutralPFOs.size(); in++){
+        std::vector<const PandoraPlus::Calo3DCluster*> neutral_cluster = m_neutralPFOs[in]->getHCALClusters();
+        int tmp_index=-1;
+        for(int ii=0; ii<neutral_cluster.size(); ii++){
+          if (all_neutral_HCAL_clus[clus_index]==neutral_cluster[ii]){
+            tmp_index = ii;
+            break;
+          }
+        }
+        if (tmp_index==-1) continue;
+//cout<<"  Remove a neutral cluster: En "<<settings.map_floatPars["HCALCalib"]*neutral_cluster[tmp_index]->getHitsE()<<endl;
+
+        neutral_cluster.erase(neutral_cluster.begin()+tmp_index);
+        m_neutralPFOs[in]->setHCALCluster(neutral_cluster);
+
+        if(m_neutralPFOs[in]->getTracks().size() + m_neutralPFOs[in]->getECALClusters().size() + m_neutralPFOs[in]->getHCALClusters().size()==0){
+//cout<<"  Remove a neutral PFO: ECAL En "<<m_neutralPFOs[in]->getECALClusterEnergy()<<", HCAL En "<<m_neutralPFOs[in]->getHCALClusterEnergy()<<endl;
+          auto iter = find(p_PFObjects->begin(), p_PFObjects->end(), m_neutralPFOs[in]);
+          if(iter==p_PFObjects->end()){
+            std::cout<<"ERROR: can not find this neutral PFO in p_PFObjects. "<<std::endl;
+          }
+          else{
+            m_neutralPFOs.erase(m_neutralPFOs.begin()+in);
+            p_PFObjects->erase(iter);
+          }
+        }
+        is_found = true;
+        break;
+      }
+      if(!is_found){
+        cout << "Error! Can not find the HCAL cluster in neutral PFO" << endl;
+      }
+
+      // Remove this HCAL cluster from all_neutral_HCAL_clus
+      all_neutral_HCAL_clus.erase(all_neutral_HCAL_clus.begin()+clus_index);
+    }
+  }
+
+
+ double totE_Ecal = 0;
+ double totE_Hcal = 0;
+ cout<<"After merge real neu to Ch: charged "<<m_chargedPFOs.size()<<", neutral "<<m_neutralPFOs.size()<<", total "<<p_PFObjects->size()<<endl;
+ for(int i=0; i<m_neutralPFOs.size(); i++){
+   cout<<"    PFO #"<<i<<": track size "<<m_neutralPFOs[i]->getTracks().size()<<", leading P "<<m_neutralPFOs[i]->getTrackMomentum();
+   cout<<", ECAL cluster size "<<m_neutralPFOs[i]->getECALClusters().size()<<", totE "<<m_neutralPFOs[i]->getECALClusterEnergy();
+   cout<<", HCAL cluster size "<<m_neutralPFOs[i]->getHCALClusters().size()<<", totE "<<m_neutralPFOs[i]->getHCALClusterEnergy()<<endl;
+   totE_Ecal += m_neutralPFOs[i]->getECALClusterEnergy();
+   totE_Hcal += m_neutralPFOs[i]->getHCALClusterEnergy();
+ }
+ cout<<"-----Neutral cluster Ecal total energy: "<<totE_Ecal<<", Hcal total energy: "<<totE_Hcal<<endl;
+ totE_Ecal = 0;
+ totE_Hcal = 0;
+ for(int i=0; i<m_chargedPFOs.size(); i++){
+   cout<<"    PFO #"<<i<<": track size "<<m_chargedPFOs[i]->getTracks().size()<<", leading P "<<m_chargedPFOs[i]->getTrackMomentum();
+   cout<<", ECAL cluster size "<<m_chargedPFOs[i]->getECALClusters().size()<<", totE "<<m_chargedPFOs[i]->getECALClusterEnergy();
+   cout<<", HCAL cluster size "<<m_chargedPFOs[i]->getHCALClusters().size()<<", totE "<<m_chargedPFOs[i]->getHCALClusterEnergy()<<endl;
+   totE_Ecal += m_chargedPFOs[i]->getECALClusterEnergy();
+   totE_Hcal += m_chargedPFOs[i]->getHCALClusterEnergy();
+ }
+ cout<<"-----Charged cluster Ecal total energy: "<<totE_Ecal<<", Hcal total energy: "<<totE_Hcal<<endl;
+
+
+
+  //Merge virtual neutral PFOs created from splitting. 
+  for(int ic=0; ic<m_chargedPFOs.size(); ic++){
+    if(m_chargedPFOs[ic]->getTracks().size()==0) continue;
+    if( m_chargedPFOs[ic]->getECALClusters().size()==0 && 
+        m_chargedPFOs[ic]->getHCALClusters().size()==0 && 
+        m_chargedPFOs[ic]->getTracks()[0]->getTrackStates("Hcal").size()==0) continue;
+
+    double track_energy = m_chargedPFOs[ic]->getTrackMomentum();
+    double ECAL_energy = settings.map_floatPars["ECALCalib"]*m_chargedPFOs[ic]->getECALClusterEnergy();
+    double HCAL_energy = settings.map_floatPars["HCALCalib"]*m_chargedPFOs[ic]->getHCALClusterEnergy();
+    if(track_energy<0 || ECAL_energy<0 || HCAL_energy<0){
+      std::cout<<"ERROR: Charged PFO info break. Ptrk "<<track_energy<<", E_ecal "<<ECAL_energy<<", E_hcal "<<HCAL_energy<<endl;
+      continue;
+    }
+
+    double sigmaE = settings.map_floatPars["EnergyRes"] * sqrt(track_energy);
+    double delta_energy = ECAL_energy + HCAL_energy - track_energy;
+
+cout<<"  ReCluster_MergeToChg: In ChPFO #"<<ic<<": Ptrk = "<<track_energy<<", Eecal = "<<ECAL_energy<<", Ehcal = "<<HCAL_energy<<", deltaE = "<<delta_energy<<", sigmaE = "<<sigmaE<<endl;
+
+    if(delta_energy > settings.map_floatPars["VirtualMergeSigma"]*sigmaE) continue;
+cout<<"    Do charged PFO merge. "<<endl;
+
+    // Virtual HCAL clusters in the neutral PFO
+    std::vector<const PandoraPlus::Calo3DCluster*> all_neutral_HCAL_clus; all_neutral_HCAL_clus.clear();
+    for(int ip=0; ip<m_neutralPFOs.size(); ip++){
+      std::vector<const PandoraPlus::Calo3DCluster*> tmp_HCAL_clus = m_neutralPFOs[ip]->getHCALClusters();
+      if(tmp_HCAL_clus.size()!=1) continue;
+      if(tmp_HCAL_clus[0]->getType()!=-1) continue;
+      all_neutral_HCAL_clus.push_back(tmp_HCAL_clus[0]);
+    }
+cout<<"    Virtual HCAL cluster size "<<all_neutral_HCAL_clus.size()<<", print them: "<<endl;
+for(int aa=0; aa<all_neutral_HCAL_clus.size(); aa++){
+  printf("      #%d: pos (%.3f, %.3f, %.3f), En %.3f, Nhit %d, type %d \n", aa, all_neutral_HCAL_clus[aa]->getHitCenter().x(), all_neutral_HCAL_clus[aa]->getHitCenter().y(), all_neutral_HCAL_clus[aa]->getHitCenter().z(), all_neutral_HCAL_clus[aa]->getHitsE()*settings.map_floatPars["HCALCalib"], all_neutral_HCAL_clus[aa]->getCaloHits().size(), all_neutral_HCAL_clus[aa]->getType() );
+}
+
+    TVector3 trackclus_pos(0, 0, 0);
+    if(m_chargedPFOs[ic]->getTracks()[0]->getTrackStates("Hcal").size()>0){
+      std::vector<TrackState> m_extTrkStats = m_chargedPFOs[ic]->getTracks()[0]->getTrackStates("Hcal");
+
+      int min_hit_index = -1;
+      double min_distance = 999999;
+      for(int i=0; i<m_extTrkStats.size(); i++){
+        double hit_distance = m_extTrkStats[i].referencePoint.Perp();
+        if(hit_distance<min_distance){
+          min_distance = hit_distance;
+          min_hit_index = i;
+        }
+      }
+      
+      if(min_hit_index>=0) trackclus_pos = m_extTrkStats[min_hit_index].referencePoint;
+    }
+    else if(m_chargedPFOs[ic]->getECALClusters().size()>0){
+      trackclus_pos = m_chargedPFOs[ic]->getECALClusters()[0]->getShowerCenter();
+    }
+    else{
+      for(int jc=0; jc<m_chargedPFOs[ic]->getHCALClusters().size(); jc++)
+        trackclus_pos += m_chargedPFOs[ic]->getHCALClusters()[jc]->getHitCenter() * settings.map_floatPars["HCALCalib"]*m_chargedPFOs[ic]->getHCALClusters()[jc]->getHitsE();
+
+      trackclus_pos = trackclus_pos*(1./HCAL_energy);
+    }
+ printf("    charged cluster trk pos: [%.3f, %.3f, %.3f] \n", trackclus_pos.x(), trackclus_pos.y(), trackclus_pos.z());
+
+    int loop_count = 0;
+    std::vector<const PandoraPlus::Calo3DCluster*> skip_clus;
+    while(delta_energy < sigmaE*settings.map_floatPars["VirtualMergeSigma"]-(1e-6)){
+      loop_count++;
+      if(loop_count>all_neutral_HCAL_clus.size()+10){
+        break;
+      }
+      double min_angle = 999.0;
+      int clus_index = -1;
+
+      for(int in=0; in<all_neutral_HCAL_clus.size(); in++){
+        if(find(skip_clus.begin(), skip_clus.end(), all_neutral_HCAL_clus[in]) != skip_clus.end()) continue;
+
+        TVector3 neutral_clus_pos = all_neutral_HCAL_clus[in]->getHitCenter();
+        double pfo_angle = trackclus_pos.Angle(neutral_clus_pos);
+        if (pfo_angle<settings.map_floatPars["MinAngleForVirMerge"] && pfo_angle<min_angle){
+          min_angle=pfo_angle;
+          clus_index = in;
+        }
+      }
+cout<<"    In Loop "<<loop_count<<": current deltaE = "<<delta_energy<<", closest virtual cluster index "<<clus_index<<endl;
+
+      if(clus_index<0) break;  // No neutral Hcal cluster to be merged
+
+      double tmp_delta_E = delta_energy + settings.map_floatPars["HCALCalib"]*all_neutral_HCAL_clus[clus_index]->getHitsE();
+cout<<"    If include this cluster: new deltaE "<<tmp_delta_E<<", merge = "<<(tmp_delta_E > sigmaE * settings.map_floatPars["VirtualMergeSigma"])<<endl;
+      if(tmp_delta_E > sigmaE * settings.map_floatPars["VirtualMergeSigma"]){
+        double absorbed_energy = sigmaE*settings.map_floatPars["VirtualMergeSigma"] - delta_energy;
+        delta_energy = delta_energy + absorbed_energy;
+
+
+        //Create a new virtual neutral cluster with energy = absorbed_energy. 
+
+        std::shared_ptr<PandoraPlus::CaloHit> m_hit = all_neutral_HCAL_clus[clus_index]->getCaloHits()[0]->Clone();
+        m_hit->setEnergy(absorbed_energy/settings.map_floatPars["HCALCalib"]);
+
+        std::shared_ptr<PandoraPlus::Calo3DCluster> m_clus = std::make_shared<PandoraPlus::Calo3DCluster>();
+        m_clus->addHit(m_hit.get());
+        m_clus->setType(-1);
+
+        m_bkCol.map_CaloHit["bkHit"].push_back( m_hit );
+        m_bkCol.map_CaloCluster["bk3DCluster"].push_back(m_clus);
+
+        m_chargedPFOs[ic]->addHCALCluster( m_clus.get() );
+
+
+        //Re-set neutral virtual cluster energy
+        bool is_found = false;
+        for(int ip=0; ip<m_neutralPFOs.size(); ip++){
+          auto tmp_HCAL_clus = m_neutralPFOs[ip]->getHCALClusters();
+
+          if(tmp_HCAL_clus.size()!=1) continue;
+          if(tmp_HCAL_clus[0]->getType()!=-1) continue;
+
+          if(tmp_HCAL_clus[0]==all_neutral_HCAL_clus[clus_index]){
+            std::shared_ptr<PandoraPlus::CaloHit> m_newhit = all_neutral_HCAL_clus[clus_index]->getCaloHits()[0]->Clone();
+            m_newhit->setEnergy(all_neutral_HCAL_clus[clus_index]->getHitsE() - absorbed_energy/settings.map_floatPars["HCALCalib"] );
+
+            std::shared_ptr<PandoraPlus::Calo3DCluster> m_newclus = std::make_shared<PandoraPlus::Calo3DCluster>();
+            m_newclus->addHit(m_newhit.get());
+            m_newclus->setType(-1);            
+
+            m_bkCol.map_CaloHit["bkHit"].push_back( m_newhit );
+            m_bkCol.map_CaloCluster["bk3DCluster"].push_back(m_newclus);
+
+            std::vector<const Calo3DCluster*> tmp_clusters; tmp_clusters.clear();
+            tmp_clusters.push_back(m_clus.get());
+            m_neutralPFOs[ip]->setHCALCluster( tmp_clusters );
+
+            is_found = true;
+            break;
+          }
+
+        }
+        if(!is_found){
+          cout << "Error! Can not find the HCAL cluster in neutral PFO to delete part of the energy" << endl;
+        }
+        //Reset the energy
+        all_neutral_HCAL_clus.erase(all_neutral_HCAL_clus.begin()+clus_index);
+        all_neutral_HCAL_clus.push_back(m_clus.get());
+        skip_clus.push_back(m_clus.get());
+      }
+      else{
+
+        if (TMath::Abs(tmp_delta_E) >= TMath::Abs(delta_energy)){
+          skip_clus.push_back(all_neutral_HCAL_clus[clus_index]);
+          continue;  // No need to merge this HCAL cluster
+        }
+   
+        // Update delta_energy
+        delta_energy = tmp_delta_E;
+        // Add this HCAL cluster to charged PFO
+        m_chargedPFOs[ic]->addHCALCluster(all_neutral_HCAL_clus[clus_index]);
+   
+        // Remove this HCAL cluster from neutral PFO
+        bool is_found = false;
+        for(int in=0; in<m_neutralPFOs.size(); in++){
+          std::vector<const PandoraPlus::Calo3DCluster*> neutral_cluster = m_neutralPFOs[in]->getHCALClusters();
+          int tmp_index=-1;
+          for(int ii=0; ii<neutral_cluster.size(); ii++){
+            if (all_neutral_HCAL_clus[clus_index]==neutral_cluster[ii]){
+              tmp_index = ii;
+              break;
+            }
+          }
+          if (tmp_index==-1) continue;
+cout<<"  Remove a neutral cluster: En "<<settings.map_floatPars["HCALCalib"]*neutral_cluster[tmp_index]->getHitsE()<<endl;
+
+          neutral_cluster.erase(neutral_cluster.begin()+tmp_index);
+          m_neutralPFOs[in]->setHCALCluster(neutral_cluster);
+   
+          if(m_neutralPFOs[in]->getTracks().size() + m_neutralPFOs[in]->getECALClusters().size() + m_neutralPFOs[in]->getHCALClusters().size()==0){
+cout<<"  Remove a neutral PFO: ECAL En "<<m_neutralPFOs[in]->getECALClusterEnergy()<<", HCAL En "<<m_neutralPFOs[in]->getHCALClusterEnergy()<<endl;
+            auto iter = find(p_PFObjects->begin(), p_PFObjects->end(), m_neutralPFOs[in]);
+            if(iter==p_PFObjects->end()){
+              std::cout<<"ERROR: can not find this neutral PFO in p_PFObjects. "<<std::endl;
+            }
+            else{
+              m_neutralPFOs.erase(m_neutralPFOs.begin()+in);
+              p_PFObjects->erase(iter);
+            }
+          }
+          is_found = true;
+          break;
+        }
+        if(!is_found){
+          cout << "Error! Can not find the HCAL cluster in neutral PFO" << endl;
+        }
+   
+        // Remove this HCAL cluster from all_neutral_HCAL_clus
+        all_neutral_HCAL_clus.erase(all_neutral_HCAL_clus.begin()+clus_index);
+
+      }
+
+    }
+  }
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode PFOReclusteringAlg::ReCluster_SplitFromChg( std::vector< std::shared_ptr<PandoraPlus::PFObject> >& m_chargedPFOs,
+                                                       std::vector< std::shared_ptr<PandoraPlus::PFObject> >& m_neutralPFOs ){
+
+  for(int ipfo=0; ipfo<m_chargedPFOs.size(); ipfo++){
+    if(m_chargedPFOs[ipfo]->getECALClusters().size()==0 && m_chargedPFOs[ipfo]->getHCALClusters().size()==0) continue;
+
+    double track_energy = m_chargedPFOs[ipfo]->getTrackMomentum();
+    double ECAL_energy = settings.map_floatPars["ECALCalib"]*m_chargedPFOs[ipfo]->getECALClusterEnergy();
+    double HCAL_energy = settings.map_floatPars["HCALCalib"]*m_chargedPFOs[ipfo]->getHCALClusterEnergy();
+    if(track_energy<0 || ECAL_energy<0 || HCAL_energy<0){
+      std::cout<<"ERROR: Charged PFO info break. Ptrk "<<track_energy<<", E_ecal "<<ECAL_energy<<", E_hcal "<<HCAL_energy<<endl;
+      continue;
+    }
+
+    double delta_energy = ECAL_energy + HCAL_energy - track_energy;
+    double sigmaE = settings.map_floatPars["EnergyRes"] * sqrt(ECAL_energy + HCAL_energy);
+    
+//cout<<"  ReCluster_MergeToChg: In ChPFO #"<<ipfo<<": Ptrk = "<<track_energy<<", Eecal = "<<ECAL_energy<<", Ehcal = "<<HCAL_energy<<", deltaE = "<<delta_energy<<", sigmaE = "<<sigmaE<<endl;
+
+    if(delta_energy <= settings.map_floatPars["SplitSigma"]*sigmaE) continue;
+//cout<<"    Do charged PFO splitting. "<<endl;
+
+    //Create a new hit and cluster
+    TVector3 tmp_pos(0,0,0); 
+    for(int ic=0; ic<m_chargedPFOs[ipfo]->getECALClusters().size(); ic++)
+      tmp_pos += settings.map_floatPars["ECALCalib"] * m_chargedPFOs[ipfo]->getECALClusters()[ic]->getLongiE() * m_chargedPFOs[ipfo]->getECALClusters()[ic]->getShowerCenter();
+    for(int ic=0; ic<m_chargedPFOs[ipfo]->getHCALClusters().size(); ic++) 
+      tmp_pos += settings.map_floatPars["HCALCalib"] * m_chargedPFOs[ipfo]->getHCALClusters()[ic]->getHitsE() * m_chargedPFOs[ipfo]->getHCALClusters()[ic]->getHitCenter();
+    tmp_pos = tmp_pos*(1./(ECAL_energy+HCAL_energy));
+
+    std::shared_ptr<PandoraPlus::CaloHit> m_hit = std::make_shared<PandoraPlus::CaloHit>();
+    m_hit->setPosition( tmp_pos );
+    m_hit->setEnergy( delta_energy/settings.map_floatPars["HCALCalib"] );
+
+    std::shared_ptr<PandoraPlus::Calo3DCluster> m_clus = std::make_shared<PandoraPlus::Calo3DCluster>();
+    m_clus->addHit(m_hit.get());
+    m_clus->setType(-1);
+
+    std::shared_ptr<PandoraPlus::PFObject> m_pfo = std::make_shared<PandoraPlus::PFObject>();
+    m_pfo->addHCALCluster( m_clus.get() );
+//cout<<"Create a new Neutral PFO: energy "<<m_pfo->getHCALClusterEnergy()<<endl;
+
+    m_neutralPFOs.push_back( m_pfo );   
+    p_PFObjects->push_back( m_pfo );
+
+    //For this charged PFO: reset HCAL energy. (negative HCAL energy is NOT allowed) 
+    if(HCAL_energy>0 && HCAL_energy-delta_energy>0){
+      double m_HcalEnScale = (HCAL_energy-delta_energy)/HCAL_energy;
+//cout<<"[FY debug] In PFO "<<ipfo<<": track P "<<m_chargedPFOs[ipfo]->getTrackMomentum()<<", HCAL cluster size "<<m_chargedPFOs[ipfo]->getHCALClusters().size()<<", first HCAL has Nhit "<<m_chargedPFOs[ipfo]->getHCALClusters()[0]->getCaloHits().size()<<endl;
+//cout<<"[FY debug] Hcal energy scale = "<<m_HcalEnScale<<endl;
+
+      //Create new HCAL cluster
+      std::shared_ptr<PandoraPlus::Calo3DCluster> m_newclus = std::make_shared<PandoraPlus::Calo3DCluster>();
+      for(int ic=0; ic<m_chargedPFOs[ipfo]->getHCALClusters().size(); ic++){
+        std::vector<const PandoraPlus::CaloHit*> tmp_hits = m_chargedPFOs[ipfo]->getHCALClusters()[ic]->getCaloHits();
+        for(int ih=0; ih<tmp_hits.size(); ih++){
+          std::shared_ptr<CaloHit> tmp_newhit = tmp_hits[ih]->Clone();
+          tmp_newhit->setEnergy( tmp_newhit->getEnergy()*m_HcalEnScale );
+          m_newclus->addHit(tmp_newhit.get());
+          m_bkCol.map_CaloHit["bkHit"].push_back( tmp_newhit );
+        }
+      }
+      m_bkCol.map_CaloCluster["bk3DCluster"].push_back(m_newclus);
+
+//cout<<"[FY debug] new HCAL cluster: hit size "<<m_newclus->getCaloHits().size()<<", total energy "<<m_newclus->getHitsE()<<endl;
+
+      //Create a new PFO
+      std::shared_ptr<PandoraPlus::PFObject> m_newpfo = m_chargedPFOs[ipfo]->Clone();
+      std::vector<const PandoraPlus::Calo3DCluster*> tmp_clusvec; tmp_clusvec.clear(); 
+      tmp_clusvec.push_back(m_newclus.get());
+      m_newpfo->setHCALCluster(tmp_clusvec);
+
+      m_bkCol.map_PFObjects["bkPFO"].push_back(m_newpfo);
+
+
+      auto iter = find(p_PFObjects->begin(), p_PFObjects->end(), m_chargedPFOs[ipfo]);
+      if(iter!=p_PFObjects->end())
+        *iter = m_newpfo;
+
+      m_chargedPFOs[ipfo] = m_newpfo;
+//cout<<"[FY debug] The splitted new charged PFO: track size "<<m_newpfo->getTracks().size()<<", leading P "<<m_newpfo->getTrackMomentum();
+//cout<<", ECAL cluster size "<<m_newpfo->getECALClusters().size()<<", totE "<<m_newpfo->getECALClusterEnergy();
+//cout<<", HCAL cluster size "<<m_newpfo->getHCALClusters().size()<<", totE "<<m_newpfo->getHCALClusterEnergy()<<endl;
+    }
+
+    m_bkCol.map_CaloHit["bkHit"].push_back( m_hit );
+    m_bkCol.map_CaloCluster["bk3DCluster"].push_back(m_clus);
+    m_bkCol.map_PFObjects["bkPFO"].push_back(m_pfo);
+  }
+
+
+
+  return StatusCode::SUCCESS;
+};
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/TrackClusterConnectingAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/TrackClusterConnectingAlg.cpp
new file mode 100644
index 00000000..324c9d18
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/TrackClusterConnectingAlg.cpp
@@ -0,0 +1,371 @@
+#ifndef _TRACKCLUSTERCONNECTING_ALG_C
+#define _TRACKCLUSTERCONNECTING_ALG_C
+
+#include "Algorithm/TrackClusterConnectingAlg.h"
+
+StatusCode TrackClusterConnectingAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  //Initialize parameters
+  if(settings.map_stringPars.find("ReadinECALClusterName")==settings.map_stringPars.end()) settings.map_stringPars["ReadinECALClusterName"] = "EcalCluster";
+  if(settings.map_stringPars.find("ReadinHCALClusterName")==settings.map_stringPars.end()) settings.map_stringPars["ReadinHCALClusterName"] = "HcalCluster";
+
+  if(settings.map_floatPars.find("th_ChFragEn")==settings.map_floatPars.end()) settings.map_floatPars["th_ChFragEn"] = 2.;
+  if(settings.map_floatPars.find("th_ChFragDepth")==settings.map_floatPars.end()) settings.map_floatPars["th_ChFragDepth"] = 100.;
+  if(settings.map_floatPars.find("th_ChFragMinR")==settings.map_floatPars.end()) settings.map_floatPars["th_ChFragMinR"] = 200.;
+  if(settings.map_floatPars.find("th_HcalMatchingaR")==settings.map_floatPars.end()) settings.map_floatPars["th_HcalMatchingR"] = 100.;
+
+  if(settings.map_floatPars.find("th_MIPEnergy")==settings.map_floatPars.end()) settings.map_floatPars["th_MIPEnergy"] = 0.5;
+  if(settings.map_floatPars.find("th_AbsorbCone")==settings.map_floatPars.end()) settings.map_floatPars["th_AbsorbCone"] = 0.8;
+
+  if(settings.map_stringPars.find("OutputMergedECALCluster")==settings.map_stringPars.end()) settings.map_stringPars["OutputMergedECALCluster"] = "TrkMergedECAL";
+  if(settings.map_stringPars.find("OutputCombPFO")==settings.map_stringPars.end()) settings.map_stringPars["OutputCombPFO"] = "outputPFO";
+
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TrackClusterConnectingAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  m_EcalClusters.clear();
+  m_HcalClusters.clear();
+  m_tracks.clear();
+  m_absorbedEcal.clear();
+  m_PFObjects.clear();
+  m_bkCol.Clear();
+
+  for(int ic=0; ic<m_datacol.map_CaloCluster[settings.map_stringPars["ReadinECALClusterName"]].size(); ic++){
+    m_EcalClusters.push_back( m_datacol.map_CaloCluster[settings.map_stringPars["ReadinECALClusterName"]][ic].get() );
+  }
+  for(int ic=0; ic<m_datacol.map_CaloCluster[settings.map_stringPars["ReadinHCALClusterName"]].size(); ic++){
+    m_HcalClusters.push_back( m_datacol.map_CaloCluster[settings.map_stringPars["ReadinHCALClusterName"]][ic].get() );
+  }
+  for(int itrk=0; itrk<m_datacol.TrackCol.size(); itrk++){
+    m_tracks.push_back( m_datacol.TrackCol[itrk].get() );
+  }
+
+//cout<<"Readin Track size: "<<m_tracks.size()<<", ECAL cluster size: "<<m_EcalClusters.size()<<", HCAL cluster size "<<m_HcalClusters.size()<<endl;
+//cout<<"Print all ECAL cluster "<<endl;
+//for(int ic=0; ic<m_EcalClusters.size(); ic++){
+//  cout<<"    ECAL Cluster #"<<ic<<": En = "<<m_EcalClusters[ic]->getLongiE()<<", track size "<<m_EcalClusters[ic]->getAssociatedTracks().size();
+//  if(m_EcalClusters[ic]->getAssociatedTracks().size()>0) cout<<", Leading track P = "<<m_EcalClusters[ic]->getAssociatedTracks()[0]->getMomentum()<<endl;
+//  else cout<<endl;
+//}
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TrackClusterConnectingAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+  //Readin: tracks, ECAL clusters and HCAL clusters. 
+  //Output: PFObject
+
+
+  //1. Merge ECAL clusters. 
+  //Possible to add some cluster ID and merge functions. 
+  m_absorbedEcal.clear();
+  EcalChFragAbsorption(m_EcalClusters, m_tracks, m_absorbedEcal);
+//cout<<"  TrackClusterConnectingAlg: After ECAL charged fragment absorption: cluster size "<<m_absorbedEcal.size()<<endl;
+
+  //2. Create PFObject with ECAL cluster and track
+  std::vector<const PandoraPlus::Calo3DCluster*> tmp_constClus; 
+  for(int ic=0; ic<m_absorbedEcal.size(); ic++) tmp_constClus.push_back(m_absorbedEcal[ic].get());
+  PFOCreating(tmp_constClus, m_tracks, m_PFObjects);
+
+//cout<<"  TrackClusterConnectingAlg: created PFO: "<<m_PFObjects.size()<<endl;
+//for(int i=0; i<m_PFObjects.size(); i++){
+//  cout<<"    PFO #"<<i<<": track size "<<m_PFObjects[i]->getTracks().size()<<", leading P "<<m_PFObjects[i]->getTrackMomentum();
+//  cout<<", ECAL cluster size "<<m_PFObjects[i]->getECALClusters().size()<<", totE "<<m_PFObjects[i]->getECALClusterEnergy();
+//  cout<<", HCAL cluster size "<<m_PFObjects[i]->getHCALClusters().size()<<", totE "<<m_PFObjects[i]->getHCALClusterEnergy()<<endl;
+//}
+
+  //3. Add HCAL clusters into the PFObject. 
+  std::sort(m_PFObjects.begin(), m_PFObjects.end(), compTrkP);
+  HcalExtrapolatingMatch(m_HcalClusters, m_PFObjects);
+//cout<<"  TrackClusterConnectingAlg: PFO size after HCAL matching: "<<m_PFObjects.size()<<endl;
+//for(int i=0; i<m_PFObjects.size(); i++){
+//  cout<<"    PFO #"<<i<<": track size "<<m_PFObjects[i]->getTracks().size()<<", leading P "<<m_PFObjects[i]->getTrackMomentum();
+//  cout<<", ECAL cluster size "<<m_PFObjects[i]->getECALClusters().size()<<", totE "<<m_PFObjects[i]->getECALClusterEnergy();
+//  cout<<", HCAL cluster size "<<m_PFObjects[i]->getHCALClusters().size()<<", totE "<<m_PFObjects[i]->getHCALClusterEnergy()<<endl;
+//} 
+
+  m_datacol.map_CaloCluster[ settings.map_stringPars["OutputMergedECALCluster"] ] = m_absorbedEcal;
+  m_datacol.map_PFObjects[settings.map_stringPars["OutputCombPFO"]] = m_PFObjects;
+
+  m_datacol.map_CaloCluster["bk3DCluster"].insert( m_datacol.map_CaloCluster["bk3DCluster"].end(), m_bkCol.map_CaloCluster["bk3DCluster"].begin(), m_bkCol.map_CaloCluster["bk3DCluster"].end() );
+  m_datacol.map_PFObjects["bkPFO"].insert( m_datacol.map_PFObjects["bkPFO"].end(), m_bkCol.map_PFObjects["bkPFO"].begin(), m_bkCol.map_PFObjects["bkPFO"].end() );
+  m_datacol.map_PFObjects["bkPFO"].insert( m_datacol.map_PFObjects["bkPFO"].end(), m_PFObjects.begin(), m_PFObjects.end() );
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TrackClusterConnectingAlg::ClearAlgorithm(){
+  m_EcalClusters.clear();
+  m_HcalClusters.clear();
+  m_tracks.clear();
+  m_absorbedEcal.clear();
+  m_PFObjects.clear();
+  m_bkCol.Clear();
+
+  return StatusCode::SUCCESS;
+};
+
+
+
+StatusCode TrackClusterConnectingAlg::PFOCreating( std::vector<const PandoraPlus::Calo3DCluster*>& m_clusters, 
+                                                   std::vector<const PandoraPlus::Track*>& m_trks,
+                                                   std::vector<std::shared_ptr<PandoraPlus::PFObject>>& m_PFOs ){
+
+//cout<<"  PFOCreating: Track size "<<m_trks.size()<<", Cluster size "<<m_clusters.size()<<endl;
+//for(int ic=0; ic<m_clusters.size(); ic++){
+//  cout<<"    ECAL Cluster #"<<ic<<": En = "<<m_clusters[ic]->getLongiE()<<", track size "<<m_clusters[ic]->getAssociatedTracks().size();
+//  if(m_clusters[ic]->getAssociatedTracks().size()>0) cout<<", Leading track P = "<<m_clusters[ic]->getAssociatedTracks()[0]->getMomentum()<<endl;
+//  else cout<<endl;
+//}
+
+  std::vector<const PandoraPlus::Track*> m_leftTrks = m_trks; 
+  for(int ic=0; ic<m_clusters.size(); ic++){
+    std::shared_ptr<PandoraPlus::PFObject> m_newPFO = std::make_shared<PandoraPlus::PFObject>();
+
+    m_newPFO->addECALCluster( m_clusters[ic] );
+
+    std::vector<const PandoraPlus::Track*> m_trkInClus = m_clusters[ic]->getAssociatedTracks();
+    if(m_trkInClus.size()!=0){
+      m_newPFO->addTrack( m_trkInClus[0] );
+      auto iter = find(m_leftTrks.begin(), m_leftTrks.end(), m_trkInClus[0]);
+      if( iter!=m_leftTrks.end() )
+        m_leftTrks.erase(iter);
+    }
+
+    m_PFOs.push_back(m_newPFO);
+    m_bkCol.map_PFObjects["bkPFO"].push_back(m_newPFO);
+  }
+
+  for(int itrk=0; itrk<m_leftTrks.size(); itrk++){
+    std::shared_ptr<PandoraPlus::PFObject> m_newPFO = std::make_shared<PandoraPlus::PFObject>();
+    m_newPFO->addTrack( m_leftTrks[itrk] );
+    m_PFOs.push_back(m_newPFO);
+    m_bkCol.map_PFObjects["bkPFO"].push_back(m_newPFO);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TrackClusterConnectingAlg::EcalChFragAbsorption( std::vector<const PandoraPlus::Calo3DCluster*>& m_clusters, 
+                                                            std::vector<const PandoraPlus::Track*>& m_trks, 
+                                                            std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_newclusCol){
+//cout<<"  In EcalChFragAbsorption: Input track size "<<m_trks.size()<<", cluster size "<<m_clusters.size()<<endl;
+//for(int ic=0; ic<m_clusters.size(); ic++){
+//  cout<<"    ECAL Cluster #"<<ic<<": En = "<<m_clusters[ic]->getLongiE()<<", track size "<<m_clusters[ic]->getAssociatedTracks().size()<<endl;
+//}
+
+
+  //1. Absorb neutral clusters to the nearby tracks
+  std::map<const PandoraPlus::Track*, int> m_matchedTrkMap; 
+  for(int ic=0; ic<m_clusters.size(); ic++){
+    for(int itrk=0; itrk<m_clusters[ic]->getAssociatedTracks().size(); itrk++){
+      if( find(m_trks.begin(), m_trks.end(), m_clusters[ic]->getAssociatedTracks()[itrk])!=m_trks.end() )
+        m_matchedTrkMap[m_clusters[ic]->getAssociatedTracks()[itrk]] = ic;
+    }
+  }
+//cout<<"  Matched track size: "<<m_matchedTrkMap.size()<<endl;
+
+  for(int ic=0; ic<m_clusters.size(); ic++){
+    if(m_clusters[ic]->getAssociatedTracks().size()!=0) continue; 
+
+    double clusDepth = m_clusters[ic]->getDepthToECALSurface();
+    double clusEn = m_clusters[ic]->getLongiE();
+
+    double minR2trk = 9999;
+    int index = -1;
+    for(int itrk=0; itrk<m_trks.size(); itrk++){
+      double tmp_minR = GetMinR2Trk( m_clusters[ic], m_trks[itrk]);
+      if(tmp_minR<minR2trk){
+        minR2trk = tmp_minR;
+        index = itrk;
+      }
+    }
+//cout<<"    Clus #"<<ic<<": depth "<<clusDepth<<", En "<<clusEn<<", minR "<<minR2trk<<", index "<<index<<endl;
+    //if(index<0){
+    //  std::cout<<"ERROR: can not find closest track "<<endl;
+    //  continue;
+    //}
+
+    if( clusEn<settings.map_floatPars["th_ChFragEn"] && clusDepth>settings.map_floatPars["th_ChFragDepth"] && minR2trk<settings.map_floatPars["th_ChFragMinR"]){
+      const PandoraPlus::Track* p_selTrk = m_trks[index]; //Closest track to this cluster. 
+
+      if( m_matchedTrkMap.find(p_selTrk)==m_matchedTrkMap.end() ){ //This track does not match to any existing charged cluster
+        std::shared_ptr<PandoraPlus::Calo3DCluster> m_newclus = m_clusters[ic]->Clone();
+        m_newclus->addAssociatedTrack(p_selTrk);
+        m_newclusCol.push_back( m_newclus );
+        m_bkCol.map_CaloCluster["bk3DCluster"].push_back(m_newclus);
+      }
+      else{ 
+        int tmp_index = m_matchedTrkMap[p_selTrk];
+        std::shared_ptr<PandoraPlus::Calo3DCluster> m_newclus = m_clusters[tmp_index]->Clone();
+        m_newclus->mergeCluster(m_clusters[ic]);
+        m_newclusCol.push_back( m_newclus );
+        m_matchedTrkMap.erase(p_selTrk);
+        m_bkCol.map_CaloCluster["bk3DCluster"].push_back(m_newclus);
+      }
+    }
+    else{
+      std::shared_ptr<PandoraPlus::Calo3DCluster> m_newclus = m_clusters[ic]->Clone();
+      m_newclusCol.push_back( m_newclus );
+      m_bkCol.map_CaloCluster["bk3DCluster"].push_back(m_newclus);
+    }
+
+  }
+
+  for(auto iter: m_matchedTrkMap){
+    std::shared_ptr<PandoraPlus::Calo3DCluster> m_newclus = m_clusters[iter.second]->Clone();
+    m_newclusCol.push_back( m_newclus );
+    m_bkCol.map_CaloCluster["bk3DCluster"].push_back(m_newclus);    
+  }
+
+
+  //Merge clusters if linked to the same track
+  for(int ic=0; ic<m_newclusCol.size() && m_newclusCol.size()>1; ic++){
+    if(m_newclusCol[ic].get()->getAssociatedTracks().size()==0) continue;
+    std::vector<const PandoraPlus::Track*> m_trkCol = m_newclusCol[ic].get()->getAssociatedTracks();
+
+    for(int jc=ic+1; jc<m_newclusCol.size(); jc++){
+      if(m_newclusCol[jc].get()->getAssociatedTracks().size()==0) continue;
+
+      for(int itrk=0; itrk<m_newclusCol[jc].get()->getAssociatedTracks().size(); itrk++){
+        if( find(m_trkCol.begin(), m_trkCol.end(), m_newclusCol[jc].get()->getAssociatedTracks()[itrk])!= m_trkCol.end() ){
+          m_newclusCol[ic].get()->mergeCluster( m_newclusCol[jc].get() );
+          m_newclusCol.erase(m_newclusCol.begin()+jc);
+          jc--;
+          if(jc<ic) jc=ic;
+        }
+        break;
+      }
+    }
+  }
+  for(int ic=0; ic<m_newclusCol.size(); ic++) m_newclusCol[ic].get()->getLinkedMCPfromHFCluster("LinkedLongiCluster");
+
+
+
+//cout<<"After nearby absorption: Print ECAL cluster "<<endl;
+//for(int ic=0; ic<m_newclusCol.size(); ic++){
+//  cout<<"    ECAL Cluster #"<<ic<<": En = "<<m_newclusCol[ic]->getLongiE()<<", track size "<<m_newclusCol[ic]->getAssociatedTracks().size();
+//  if(m_newclusCol[ic]->getAssociatedTracks().size()>0) cout<<", Leading track P = "<<m_newclusCol[ic]->getAssociatedTracks()[0]->getMomentum()<<endl;
+//  else cout<<endl;
+//}
+
+  //2. Find the shower vertex, absorb nearby neutral clusters (in a cone) into it. 
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_newChCluster;
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_newNeuCluster;
+  for(int icl=0; icl<m_newclusCol.size(); icl++){
+    if(m_newclusCol[icl]->getAssociatedTracks().size()==0 ) m_newNeuCluster.push_back(m_newclusCol[icl]);
+    else m_newChCluster.push_back(m_newclusCol[icl]);
+  }
+
+  for(int icl=0; icl<m_newChCluster.size(); icl++){
+    TVector3 cent = m_newChCluster[icl]->getShowerCenter();
+    double tmp_Ecl = m_newChCluster[icl]->getLongiE();
+
+    //Veto mip clusters and Eclus>Ptrk
+    if(tmp_Ecl<settings.map_floatPars["th_MIPEnergy"]) continue;
+    if(tmp_Ecl>m_newChCluster[icl]->getAssociatedTracks()[0]->getMomentum()) continue;
+
+    //Absorb neutral clusters in a cone angle
+    for(int jcl=0; jcl<m_newNeuCluster.size(); jcl++){
+      //Do not absorb: En, Nhit, start layer, 
+
+
+      TVector3 vec_pNeu = m_newNeuCluster[jcl]->getShowerCenter();
+      if( cent.Angle(vec_pNeu-cent)<settings.map_floatPars["th_AbsorbCone"] ){
+        m_newChCluster[icl]->mergeCluster(m_newNeuCluster[jcl].get());
+        auto iter = find(m_newclusCol.begin(), m_newclusCol.end(), m_newNeuCluster[jcl]);
+        m_newclusCol.erase(iter);
+        m_newNeuCluster.erase(m_newNeuCluster.begin()+jcl);
+        jcl--;
+      }
+    }
+  }
+//for(int ic=0; ic<m_newclusCol.size(); ic++){
+//  cout<<"    ECAL Cluster #"<<ic<<": En = "<<m_newclusCol[ic]->getLongiE()<<", track size "<<m_newclusCol[ic]->getAssociatedTracks().size()<<endl;
+//}
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode TrackClusterConnectingAlg::HcalExtrapolatingMatch(std::vector<const PandoraPlus::Calo3DCluster*>& m_clusters, std::vector<std::shared_ptr<PandoraPlus::PFObject>>& m_PFOs){
+
+  for(int ic=0; ic<m_clusters.size(); ic++){
+    std::vector<const PandoraPlus::CaloHit*> hcal_hits = m_clusters[ic]->getCaloHits();
+//cout<<"HCAL Cluster #"<<ic<<": Nhit "<<hcal_hits.size()<<", En "<<m_clusters[ic]->getHitsE()<<endl;
+
+    bool isInPfo = false; 
+    int index_selPfo = -1;
+    for(int ipfo=0; ipfo<m_PFOs.size(); ipfo++){
+      //Link HCAL cluster to charged PFO
+      if(m_PFOs[ipfo]->getTracks().size()!=0){ 
+        std::vector<TrackState> trk_points = m_PFOs[ipfo]->getTracks()[0]->getAllTrackStates();
+
+
+        bool is_candidate = false;
+        double minDistance = 99999;
+        for(int ihit=0; ihit<hcal_hits.size(); ihit++){
+          if(is_candidate) break;
+          TVector3 hit_pos = hcal_hits[ihit]->getPosition();
+   
+          for(int ipts=0; ipts<trk_points.size(); ipts++){
+            TVector3 hit_distance = hit_pos - trk_points[ipts].referencePoint;;
+            if(minDistance>hit_distance.Mag())  minDistance = hit_distance.Mag();
+            if(hit_distance.Mag()<settings.map_floatPars["th_HcalMatchingR"]){
+              is_candidate = true;
+              break;
+            }
+          }
+        }
+//cout<<"  Min distance "<<minDistance<<", is candidate "<<is_candidate<<endl;
+
+        if(is_candidate){
+//cout<<"  Pfo #"<<ipfo<<": Ntrk "<<m_PFOs[ipfo]->getTracks().size()<<", leading trk P "<<m_PFOs[ipfo]->getTrackMomentum()<<", trk state size "<<trk_points.size()<<endl;
+//cout<<"  Link cluster #"<<ic<<" to pfo #"<<ipfo<<endl;
+          m_PFOs[ipfo]->addHCALCluster( m_clusters[ic] );
+          isInPfo = true;
+          index_selPfo = ipfo;
+          break;
+        }        
+
+      }
+  
+      //Link HCAL cluster to neutral PFO
+
+    }//end loop pfos
+//if(isInPfo) cout<<"  Merged into PFO: Ptrk = "<<m_PFOs[index_selPfo]->getTrackMomentum()<<endl;
+
+    //If HCAL cluster is not linked to any existing PFO: create a new one. 
+    if(!isInPfo){
+//cout<<"  Create a new neutral PFO "<<endl;
+      std::shared_ptr<PandoraPlus::PFObject> m_newPFO = std::make_shared<PandoraPlus::PFObject>();
+      m_newPFO->addHCALCluster( m_clusters[ic] );     
+      m_PFOs.push_back(m_newPFO);
+      m_bkCol.map_PFObjects["bkPFO"].push_back(m_newPFO);
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+double TrackClusterConnectingAlg::GetMinR2Trk( const PandoraPlus::Calo3DCluster* p_clus, const PandoraPlus::Track* m_trk){
+  if(!p_clus || !m_trk) return 99999;
+
+  double minR = 99999;
+  int index = -1;
+  TVector3 clus_position = p_clus->getShowerCenter();
+  std::vector<TrackState> trk_points = m_trk->getAllTrackStates();
+  for(int i=0; i<trk_points.size(); i++){
+    TVector3 hit_distance = clus_position - trk_points[i].referencePoint;
+    if(hit_distance.Mag()<minR) minR = hit_distance.Mag();
+  }  
+
+  return minR;
+}
+#endif
+
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/TrackExtrapolatingAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/TrackExtrapolatingAlg.cpp
new file mode 100644
index 00000000..5473b9e1
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/TrackExtrapolatingAlg.cpp
@@ -0,0 +1,450 @@
+#ifndef _TRACKEXTRAPOLATING_ALG_C
+#define _TRACKEXTRAPOLATING_ALG_C
+
+#include "TVector2.h"
+
+#include "Algorithm/TrackExtrapolatingAlg.h"
+#include "Objects/Track.h"
+#include "Objects/TrackState.h"
+
+using namespace TMath;
+using namespace std;
+
+StatusCode TrackExtrapolatingAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  //Initialize parameters
+  // ECAL parameters
+  if(settings.map_floatPars.find("ECAL_innermost_distance")==settings.map_floatPars.end()) 
+    settings.map_floatPars["ECAL_innermost_distance"] = 1830;
+  if(settings.map_floatPars.find("ECAL_outermost_distance")==settings.map_floatPars.end()) 
+    settings.map_floatPars["ECAL_outermost_distance"] = 2130;
+  if(settings.map_intPars.find("ECAL_Nlayers")==settings.map_intPars.end()) 
+    settings.map_intPars["ECAL_Nlayers"] = 30;
+  if(settings.map_floatPars.find("ECAL_layer_width")==settings.map_floatPars.end())
+    settings.map_floatPars["ECAL_layer_width"] = 10;
+  if(settings.map_floatPars.find("ECAL_half_length")==settings.map_floatPars.end())
+    settings.map_floatPars["ECAL_half_length"] = 2900;
+  // HCAL parameters
+  if(settings.map_floatPars.find("HCAL_innermost_distance")==settings.map_floatPars.end()) 
+    settings.map_floatPars["HCAL_innermost_distance"] = 2140;
+  if(settings.map_floatPars.find("HCAL_outermost_distance")==settings.map_floatPars.end()) 
+    settings.map_floatPars["HCAL_outermost_distance"] = 3455;
+  if(settings.map_intPars.find("HCAL_Nlayers")==settings.map_intPars.end()) 
+    settings.map_intPars["HCAL_Nlayers"] = 48;
+  if(settings.map_floatPars.find("HCAL_layer_width")==settings.map_floatPars.end())
+    settings.map_floatPars["HCAL_layer_width"] = 30.5;
+  if(settings.map_floatPars.find("HCAL_sensitive_distance")==settings.map_floatPars.end())
+    settings.map_floatPars["HCAL_sensitive_distance"] = 9.9;  // distance between sensitive material and front face of each layer
+  if(settings.map_floatPars.find("HCAL_half_length")==settings.map_floatPars.end())
+    settings.map_floatPars["HCAL_half_length"] = 3300;
+
+  if(settings.map_intPars.find("Nmodule_ECAL")==settings.map_intPars.end()) 
+    settings.map_intPars["Nmodule_ECAL"] = 32;
+  if(settings.map_intPars.find("Nmodule_HCAL")==settings.map_intPars.end()) 
+    settings.map_intPars["Nmodule_HCAL"] = 16;
+
+  if(settings.map_floatPars.find("B_field")==settings.map_floatPars.end())
+    settings.map_floatPars["B_field"] = 3.0;
+
+  if(settings.map_intPars.find("Input_track")==settings.map_intPars.end())
+    settings.map_intPars["Input_track"] = 0;    // 0: reconstructed tracks.  1: MC particle track
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode TrackExtrapolatingAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  std::cout<<"Initialize TrackExtrapolatingAlg"<<std::endl;
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode TrackExtrapolatingAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+//std::cout<<"---oooOO0OOooo--- Excuting TrackExtrapolatingAlg ---oooOO0OOooo---"<<std::endl;
+
+  std::vector<std::shared_ptr<PandoraPlus::Track>>* p_tracks = &(m_datacol.TrackCol);
+//std::cout<<"  Track size: "<<p_tracks->size()<<std::endl;
+
+//std::cout<<"  GetPlaneNormalVector() "<<std::endl;
+  std::vector<TVector2> normal_vectors_Ecal, normal_vectors_Hcal;
+  GetPlaneNormalVector(normal_vectors_Ecal, normal_vectors_Hcal);
+
+//std::cout<<"  GetLayerPoints() "<<std::endl;
+  std::vector<std::vector<TVector2>> ECAL_layer_points;    // 32 modules, 30 layer points in each modules
+  std::vector<std::vector<TVector2>> HCAL_layer_points;    // 16 modules, 48 layer points in each modules
+  GetLayerPoints(normal_vectors_Ecal, normal_vectors_Hcal, ECAL_layer_points, HCAL_layer_points);
+
+  for(int itrk=0; itrk<p_tracks->size(); itrk++){
+    // Only tracks that reach ECAL should be processed.
+    if(!IsReachECAL( p_tracks->at(itrk).get() )) continue;
+
+//std::cout<<"  GetTrackStateAtCalo() "<<std::endl;
+    // get track state at calorimeter
+    PandoraPlus::TrackState CALO_trk_state;
+    GetTrackStateAtCalo(p_tracks->at(itrk).get(), CALO_trk_state);
+    
+//std::cout<<"  ExtrapolateByLayer() "<<std::endl;
+    ExtrapolateByLayer(normal_vectors_Ecal, normal_vectors_Hcal, ECAL_layer_points, HCAL_layer_points, CALO_trk_state, p_tracks->at(itrk).get());
+  } // end loop tracks
+
+
+  p_tracks = nullptr;
+  return StatusCode::SUCCESS;
+}; // RunAlgorithm end
+
+
+StatusCode TrackExtrapolatingAlg::ClearAlgorithm(){
+  std::cout<<"End run TrackExtrapolatingAlg. Clean it."<<std::endl;
+
+  return StatusCode::SUCCESS;
+};
+
+
+// StatusCode TrackExtrapolatingAlg::SelfAlg1(){
+//   std::cout<<"  Processing SelfAlg1: print Par1 = "<<settings.map_floatPars["Par1"]<<std::endl;
+
+//   return StatusCode::SUCCESS;
+// };
+
+
+
+// ...oooOO0OOooo......oooOO0OOooo......oooOO0OOooo...
+StatusCode TrackExtrapolatingAlg::GetPlaneNormalVector(std::vector<TVector2> & normal_vectors_Ecal, std::vector<TVector2> & normal_vectors_Hcal){
+  normal_vectors_Ecal.clear();
+  normal_vectors_Hcal.clear();
+
+  for(int im=0; im<settings.map_intPars["Nmodule_ECAL"]; im++){    
+    TVector2 t_vec(0, 1);
+    t_vec = t_vec.Rotate(1.*im/settings.map_intPars["Nmodule_ECAL"]*2*Pi());
+    normal_vectors_Ecal.push_back(t_vec);
+  }
+
+  for(int im=0; im<settings.map_intPars["Nmodule_HCAL"]; im++){
+    TVector2 t_vec(0, 1);
+    t_vec = t_vec.Rotate(1.*im/settings.map_intPars["Nmodule_HCAL"]*2*Pi());
+    normal_vectors_Hcal.push_back(t_vec);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+// ...oooOO0OOooo......oooOO0OOooo......oooOO0OOooo...
+StatusCode TrackExtrapolatingAlg::GetLayerPoints(const std::vector<TVector2> & normal_vectors_Ecal, const std::vector<TVector2> & normal_vectors_Hcal, 
+                          std::vector<std::vector<TVector2>> & ECAL_layer_points,
+                          std::vector<std::vector<TVector2>> & HCAL_layer_points){
+  // ECAL
+  for(int im=0; im< normal_vectors_Ecal.size(); im++){
+    std::vector<TVector2> t_points;
+    for(int il=0; il<settings.map_intPars["ECAL_Nlayers"]; il++){
+      TVector2 t_p;
+      float dist = settings.map_floatPars["ECAL_innermost_distance"] + 
+                   settings.map_floatPars["ECAL_layer_width"]*il + settings.map_floatPars["ECAL_layer_width"]/2;
+      float xx = (normal_vectors_Ecal[im].X()/normal_vectors_Ecal[im].Mod()) * dist;
+      float yy = (normal_vectors_Ecal[im].Y()/normal_vectors_Ecal[im].Mod()) * dist;
+      t_p.SetX(xx); t_p.SetY(yy);
+      t_points.push_back(t_p);
+    }
+    ECAL_layer_points.push_back(t_points);
+  }
+
+  // HCAL
+  for(int im=0; im< normal_vectors_Hcal.size(); im++){
+    std::vector<TVector2> t_points;
+    for(int il=0; il<settings.map_intPars["HCAL_Nlayers"]; il++){
+      TVector2 t_p;
+      float dist = settings.map_floatPars["HCAL_innermost_distance"] + 
+                   settings.map_floatPars["HCAL_layer_width"]*il + settings.map_floatPars["HCAL_sensitive_distance"];
+      float xx = (normal_vectors_Hcal[im].X()/normal_vectors_Hcal[im].Mod()) * dist;
+      float yy = (normal_vectors_Hcal[im].Y()/normal_vectors_Hcal[im].Mod()) * dist;
+      t_p.SetX(xx); t_p.SetY(yy);
+      t_points.push_back(t_p);
+    }
+    HCAL_layer_points.push_back(t_points);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+// ...oooOO0OOooo......oooOO0OOooo......oooOO0OOooo...
+bool TrackExtrapolatingAlg::IsReachECAL(PandoraPlus::Track * track){
+  if(settings.map_intPars["Input_track"] == 0){
+    // The track is reconstructed in ECAL. If the track reach ECAL, it should have track state at calorimeter
+    std::vector<TrackState> input_trackstates = track->getTrackStates("Input");
+    int count=0;
+    TVector3 t_vec;
+    for(int i=0; i<input_trackstates.size(); i++){
+      if(input_trackstates[i].location==PandoraPlus::TrackState::AtCalorimeter){
+        count++;
+        t_vec = input_trackstates[i].referencePoint;
+        break;
+      }
+    }
+    if(count==0){
+  std::cout<<"      the track has no track state at calorimeter"<<std::endl;
+      return false;
+    } 
+    if( Abs(Abs(t_vec.Z())-settings.map_floatPars["ECAL_half_length"]) < 1e-6 ){
+  std::cout<<"      the track escape from endcap"<<std::endl;
+      return false;
+    } 
+    
+
+    return true;
+  }
+  else if(settings.map_intPars["Input_track"] == 1){
+    // The track is from MC particle as ideal helix. 
+    // The pT should large enough to reach ECAL. The pz should not be so large that it escape from endcap
+    std::vector<TrackState> input_trackstates = track->getTrackStates("Input");
+    if(input_trackstates.size()==0){
+      std::cout << "Error! No track state!" << std::endl;
+      return false;
+    }
+
+    TrackState IP_trk_state;
+    for(int i=0; i<input_trackstates.size(); i++){
+      if(input_trackstates[i].location==PandoraPlus::TrackState::AtIP)
+        IP_trk_state = input_trackstates[i];
+      break;
+    }
+
+    TVector3 ref_point = IP_trk_state.referencePoint;
+    double rho = GetRho(IP_trk_state);
+    double r_max = TMath::Sqrt(ref_point.X()*ref_point.X() + ref_point.Y()*ref_point.Y()) + rho*2;
+
+    if(r_max<settings.map_floatPars["ECAL_innermost_distance"]){ return false; }
+
+    return true;
+  }
+  else{
+    std::cout << "Error, wrong source of input tracks for TrackExtrapolatingAlg!" << std:: endl;
+    return false;
+  }
+  
+}
+
+
+// ...oooOO0OOooo......oooOO0OOooo......oooOO0OOooo...
+StatusCode TrackExtrapolatingAlg::GetTrackStateAtCalo(PandoraPlus::Track * track, 
+                                                PandoraPlus::TrackState & trk_state_at_calo){
+  std::vector<TrackState> input_trackstates = track->getTrackStates("Input");
+
+  if(settings.map_intPars["Input_track"] == 0){
+    for(int its=0; its<input_trackstates.size(); its++){
+      if(input_trackstates[its].location==PandoraPlus::TrackState::AtCalorimeter){
+        trk_state_at_calo=input_trackstates[its];
+        break;
+      }
+    }
+  }
+  else if((settings.map_intPars["Input_track"] == 1)){
+    for(int its=0; its<input_trackstates.size(); its++){
+      if(input_trackstates[its].location==PandoraPlus::TrackState::AtIP){
+        trk_state_at_calo=input_trackstates[its];
+        break;
+      }
+    }
+  }
+  else{
+    std::cout << "Error, wrong source of input tracks for TrackExtrapolatingAlg!" << std:: endl;
+  }
+  
+  return StatusCode::SUCCESS;
+  
+}
+
+
+// ...oooOO0OOooo......oooOO0OOooo......oooOO0OOooo...
+StatusCode TrackExtrapolatingAlg::ExtrapolateByLayer(const std::vector<TVector2> & normal_vectors_Ecal,
+                              const std::vector<TVector2> & normal_vectors_Hcal,
+                              const std::vector<std::vector<TVector2>> & ECAL_layer_points, 
+                              const std::vector<std::vector<TVector2>> & HCAL_layer_points, 
+                              const PandoraPlus::TrackState & CALO_trk_state, 
+                              PandoraPlus::Track* p_track){
+  float rho = GetRho(CALO_trk_state);
+  TVector2 center = GetCenterOfCircle(CALO_trk_state, rho);
+  float alpha0 = GetRefAlpha0(CALO_trk_state, center); 
+  // bool is_rtbk = IsReturn(rho, center);
+  // Evaluate delta_phi
+  std::vector<std::vector<float>> ECAL_delta_phi = GetDeltaPhi(rho, center, alpha0, normal_vectors_Ecal, ECAL_layer_points, CALO_trk_state);
+  std::vector<std::vector<float>> HCAL_delta_phi = GetDeltaPhi(rho, center, alpha0, normal_vectors_Hcal, HCAL_layer_points, CALO_trk_state);
+  
+  // extrpolated track points. Will be stored as reference point in TrackState
+  std::vector<TVector3> ECAL_ext_points = GetExtrapoPoints("ECAL", rho, center, alpha0, CALO_trk_state, ECAL_delta_phi);
+  std::vector<TVector3> HCAL_ext_points = GetExtrapoPoints("HCAL", rho, center, alpha0, CALO_trk_state, HCAL_delta_phi);
+  
+  // Sort Extrapolated points
+  std::vector<TrackState> t_ECAL_states;
+  for(int ip=0; ip<ECAL_ext_points.size(); ip++){
+    TrackState t_state = CALO_trk_state;
+    t_state.location = PandoraPlus::TrackState::AtOther;
+    t_state.referencePoint = ECAL_ext_points[ip];
+    // Note GetExtrapolatedPhi0 is not same as the definition of phi0 in TrackState
+    t_state.phi0 = GetExtrapolatedPhi0(CALO_trk_state.Kappa, CALO_trk_state.phi0, center, ECAL_ext_points[ip]);
+    t_ECAL_states.push_back(t_state);
+  }
+  std::sort(t_ECAL_states.begin(), t_ECAL_states.end(), SortByPhi0);
+  p_track->setTrackStates("Ecal", t_ECAL_states);
+
+  std::vector<TrackState> t_HCAL_states;
+  for(int ip=0; ip<HCAL_ext_points.size(); ip++){
+    TrackState t_state = CALO_trk_state;
+    t_state.location = PandoraPlus::TrackState::AtOther;
+    t_state.referencePoint = HCAL_ext_points[ip];
+    // Note GetExtrapolatedPhi0 is not same as the definition of phi0 in TrackState
+    t_state.phi0 = GetExtrapolatedPhi0(CALO_trk_state.Kappa, CALO_trk_state.phi0, center, HCAL_ext_points[ip]);
+    t_HCAL_states.push_back(t_state);
+  }
+  std::sort(t_HCAL_states.begin(), t_HCAL_states.end(), SortByPhi0);
+  p_track->setTrackStates("Hcal", t_HCAL_states);
+  
+  return StatusCode::SUCCESS;
+}
+
+
+// ...oooOO0OOooo......oooOO0OOooo......oooOO0OOooo...
+float TrackExtrapolatingAlg::GetRho(const PandoraPlus::TrackState & trk_state){
+  float rho = Abs(1000. / (0.3*settings.map_floatPars["B_field"]*trk_state.Kappa));
+  return rho;
+}
+
+
+// ...oooOO0OOooo......oooOO0OOooo......oooOO0OOooo...
+TVector2 TrackExtrapolatingAlg::GetCenterOfCircle(const PandoraPlus::TrackState & trk_state, const float & rho){
+  float phi;
+  if(trk_state.Kappa>=0) phi = trk_state.phi0 - Pi()/2;
+  else phi = trk_state.phi0 + Pi()/2;
+
+  float xc = trk_state.referencePoint.X() + ((rho+trk_state.D0)*Cos(phi));
+  float yc = trk_state.referencePoint.Y() + ((rho+trk_state.D0)*Sin(phi));
+  TVector2 center(xc, yc);
+  return center;
+}
+
+
+// ...oooOO0OOooo......oooOO0OOooo......oooOO0OOooo...
+float TrackExtrapolatingAlg::GetRefAlpha0(const PandoraPlus::TrackState & trk_state, const TVector2 & center){
+  float deltaX = trk_state.referencePoint.X() - center.X();
+  float deltaY = trk_state.referencePoint.Y() - center.Y();
+  float alpha0 = ATan2(deltaY, deltaX);
+  return alpha0;
+}
+
+
+// ...oooOO0OOooo......oooOO0OOooo......oooOO0OOooo...
+std::vector<std::vector<float>> TrackExtrapolatingAlg::GetDeltaPhi(float rho, TVector2 center, float alpha0,
+                              const std::vector<TVector2> & normal_vectors,
+                              const std::vector<std::vector<TVector2>> & layer_points, 
+                              const PandoraPlus::TrackState & CALO_trk_state){
+  std::vector<std::vector<float>> delta_phi;
+  for(int im=0; im<normal_vectors.size(); im++){  // for each module
+    std::vector<float> t_delta_phi;
+    float beta = ATan2(normal_vectors[im].X(), normal_vectors[im].Y());
+    float denominator = rho * Sqrt( (normal_vectors[im].X()*normal_vectors[im].X()) +
+                                    (normal_vectors[im].Y()*normal_vectors[im].Y()) );
+    for(int il=0; il<layer_points[im].size(); il++){  // for each layer
+      float numerator = (layer_points[im][il].X()*normal_vectors[im].X()) -
+                        (center.X()*normal_vectors[im].X()) + 
+                        (layer_points[im][il].Y()*normal_vectors[im].Y()) -
+                        (center.Y()*normal_vectors[im].Y()) ;
+      if(Abs(numerator/denominator)>1) continue;
+      float t_as = ASin(numerator/denominator);
+      float t_ab = ASin(Sin(alpha0+beta));
+
+      float t_dphi1 = t_as - t_ab;
+      // float t_dphi2 = Pi()-t_as - t_ab;
+      if(CALO_trk_state.Kappa < 0){ 
+        t_delta_phi.push_back(t_dphi1); 
+        // if(is_rtbk){
+        //   t_delta_phi.push_back(t_dphi2);
+        // }
+      }
+      else{
+        t_delta_phi.push_back(-t_dphi1); 
+        // if(is_rtbk){
+        //   t_delta_phi.push_back(-t_dphi2);
+        // }
+      }
+    }
+    delta_phi.push_back(t_delta_phi);
+  }
+
+  return delta_phi;
+}
+
+
+// ...oooOO0OOooo......oooOO0OOooo......oooOO0OOooo...
+std::vector<TVector3> TrackExtrapolatingAlg::GetExtrapoPoints(std::string calo_name, 
+                                         float rho, TVector2 center, float alpha0, 
+                                         const PandoraPlus::TrackState & CALO_trk_state,
+                                         const std::vector<std::vector<float>>& delta_phi){
+  std::vector<TVector3> ext_points;
+  for(int im=0; im<delta_phi.size(); im++){
+    for(int ip=0; ip<delta_phi[im].size(); ip++){
+      float x = center.X() + (rho*Cos(alpha0+delta_phi[im][ip]));
+      float y = center.Y() + (rho*Sin(alpha0+delta_phi[im][ip]));
+      float z;
+      if(CALO_trk_state.Kappa > 0){
+        z = CALO_trk_state.referencePoint.Z() + CALO_trk_state.Z0 - 
+                (delta_phi[im][ip]*rho*CALO_trk_state.tanLambda);
+      }else{
+        z = CALO_trk_state.referencePoint.Z() + CALO_trk_state.Z0 + 
+                (delta_phi[im][ip]*rho*CALO_trk_state.tanLambda);
+      }
+
+      TVector3 t_vec3(x,y,z);
+      float rotate_angle = (6-im)*Pi()/4;
+      t_vec3.RotateZ(rotate_angle);
+
+      if(calo_name=="ECAL"){
+        if(Abs(z)>settings.map_floatPars["ECAL_half_length"]) continue;
+        if(Sqrt(x*x+y*y) > settings.map_floatPars["ECAL_outermost_distance"]/Cos(Pi()/settings.map_intPars["Nmodule_ECAL"])+100) continue;
+        if(Sqrt(x*x+y*y) < settings.map_floatPars["ECAL_innermost_distance"]) continue;
+        if(settings.map_floatPars["ECAL_outermost_distance"]*Sqrt(2)-t_vec3.X() < t_vec3.Y()) continue;
+        if(t_vec3.X()-settings.map_floatPars["ECAL_innermost_distance"]*Sqrt(2) > t_vec3.Y()) continue;
+      }
+      else if(calo_name=="HCAL"){
+        if(Abs(z)>settings.map_floatPars["HCAL_half_length"]) continue;
+        if(Sqrt(x*x+y*y) > settings.map_floatPars["HCAL_outermost_distance"]) continue;
+        if(Sqrt(x*x+y*y) < settings.map_floatPars["HCAL_innermost_distance"]) continue;
+        if(t_vec3.X()-settings.map_floatPars["HCAL_innermost_distance"]*Sqrt(2) > t_vec3.Y()) continue;
+      }
+      else continue;
+      
+      TVector3 extp(x,y,z);
+      ext_points.push_back(extp);
+    }
+  }          
+
+  return ext_points;
+}
+
+
+// ...oooOO0OOooo......oooOO0OOooo......oooOO0OOooo...
+bool TrackExtrapolatingAlg::IsReturn(float rho, TVector2 & center){
+  float farest = rho + center.Mod();
+  if (farest < settings.map_floatPars["ECAL_outermost_distance"]/Cos(Pi()/settings.map_intPars["Nmodule_ECAL"])+100) {return true;}
+  else{return false;}
+}
+
+
+// ...oooOO0OOooo......oooOO0OOooo......oooOO0OOooo...
+float TrackExtrapolatingAlg::GetExtrapolatedPhi0(float Kappa, float ECAL_phi0, TVector2 center, TVector3 ext_point){
+  // Note: phi0 of extrapolated points is (phi of velocity at extrapolated point) - (phi of velocity at ECAL front face)
+  TVector2 ext_point_xy(ext_point.X(), ext_point.Y());
+  TVector2 ext2center = center - ext_point_xy;
+  float ext_phi0;
+  if(Kappa>=0) ext_phi0 =  ext2center.Phi() + TMath::Pi()/2.;
+  else ext_phi0 = ext2center.Phi() - TMath::Pi()/2.;
+
+  float phi0 = ext_phi0 - ECAL_phi0;
+  while(phi0 < -Pi())  phi0 = phi0 + 2*Pi();
+  while(phi0 >  Pi())  phi0 = phi0 - 2*Pi();
+  return phi0;
+}
+
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/TrackMatchingAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/TrackMatchingAlg.cpp
new file mode 100644
index 00000000..b5c93e61
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/TrackMatchingAlg.cpp
@@ -0,0 +1,604 @@
+#ifndef TRACKMATCHING_C
+#define TRACKMATCHING_C
+
+#include "Algorithm/TrackMatchingAlg.h"
+
+
+StatusCode TrackMatchingAlg::ReadSettings(PandoraPlus::Settings& m_settings){
+  settings = m_settings;
+  // ECAL geometry settings
+  // Note: Bar half length is also geometry parameter, but obtained from the function GetBarHalfLength()
+  if(settings.map_floatPars.find("localmax_area")==settings.map_floatPars.end())
+    settings.map_floatPars["localmax_area"] = 10; // unit: mm
+  if(settings.map_floatPars.find("ConeNearByDistance")==settings.map_floatPars.end())
+    settings.map_floatPars["ConeNearByDistance"] = 100;
+  if(settings.map_floatPars.find("ConeMatchingCut_pT")==settings.map_floatPars.end())
+    settings.map_floatPars["ConeMatchingCut_pT"] = 2.0; // GeV. If pT of a track < ConeMatchingCut_pT, use Cone matching
+  if(settings.map_intPars.find("Max_Seed_Point")==settings.map_intPars.end())
+    settings.map_intPars["Max_Seed_Point"] = 4;
+  if(settings.map_floatPars.find("ConeSeedDistance")==settings.map_floatPars.end())
+    settings.map_floatPars["ConeSeedDistance"] = 20;
+  if(settings.map_floatPars.find("th_ConeTheta")==settings.map_floatPars.end())    
+    settings.map_floatPars["th_ConeTheta"] = TMath::Pi()/4.;
+  if(settings.map_floatPars.find("th_ConeR")==settings.map_floatPars.end())        
+    settings.map_floatPars["th_ConeR"] = 50;
+  if(settings.map_stringPars.find("ReadinLocalMaxName")==settings.map_stringPars.end())
+    settings.map_stringPars["ReadinLocalMaxName"] = "AllLocalMax";
+  if(settings.map_stringPars.find("OutputLongiClusName")==settings.map_stringPars.end()) 
+    settings.map_stringPars["OutputLongiClusName"] = "TrackAxis"; 
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TrackMatchingAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  m_TrackCol.clear();
+  p_HalfClusterV = nullptr;
+  p_HalfClusterU = nullptr;
+  // m_trackAxisVCol.clear();
+  // m_trackAxisUCol.clear();
+
+  for(int itrk=0; itrk<m_datacol.TrackCol.size(); itrk++ ) m_TrackCol.push_back(m_datacol.TrackCol[itrk].get());
+  p_HalfClusterU = &(m_datacol.map_HalfCluster["HalfClusterColU"]);
+  p_HalfClusterV = &(m_datacol.map_HalfCluster["HalfClusterColV"]);
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TrackMatchingAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+  std::cout << "---oooOO0OOooo---Excuting TrackMatchingAlg---oooOO0OOooo---"<<std::endl;
+  // Associate tracks to HalfClusters.
+  // This association is a many-to-many relationship: 
+  //    One HalCluster may have multiple tracks; 
+  //    One track may pass through multiple HalfClusters.
+//cout<<"track size: "<<m_TrackCol.size()<<", HFClusterU size "<<p_HalfClusterU->size()<<", HFClusterV size "<<p_HalfClusterV->size()<<endl;
+
+  for(int itrk=0; itrk<m_TrackCol.size(); itrk++){  // loop tracks
+//printf("  In track %d: Ptrack %.3f, track state size %d \n", itrk, m_TrackCol[itrk]->getMomentum(), m_TrackCol[itrk]->getTrackStates("Ecal").size());
+    if(m_TrackCol[itrk]->getTrackStates("Ecal").size()==0) continue;
+
+    // Get extrapolated points of the track. These points are sorted by the track
+    std::vector<TVector3> extrapo_points;
+    GetExtrpoECALPoints(m_TrackCol[itrk], extrapo_points);
+//printf("  In track %d: extrapolated point size %d \n", itrk, extrapo_points.size());
+    if(extrapo_points.size()==0) continue;
+
+    double pT = TMath::Abs(1. / m_TrackCol[itrk]->getTrackStates("Ecal")[0].Kappa);
+    if (pT >= settings.map_floatPars["ConeMatchingCut_pT"]){
+//std::cout << "For track " << itrk << ", pT = " << pT <<", match directly" << std::endl;
+
+      for(int ihc=0; ihc<p_HalfClusterV->size(); ihc++){  // loop HalfClusterV
+        // Get local max of the HalfCluster
+        std::vector<const PandoraPlus::Calo1DCluster*> localMaxColV = p_HalfClusterV->at(ihc).get()->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+//cout<<"    In HalfClusterV #"<<ihc<<": localMax size "<<localMaxColV.size()<<endl;
+        // Track axis candidate.
+        std::shared_ptr<PandoraPlus::CaloHalfCluster> t_track_axis = std::make_shared<PandoraPlus::CaloHalfCluster>();
+        CreateTrackAxis(extrapo_points, localMaxColV, t_track_axis.get());
+
+        // If the track does not match the Halfcluster, the track axis candidate will have no 1DCluster
+        if(t_track_axis->getCluster().size()==0)
+          continue;
+//cout<<"      Created a track axis"<<endl;
+        t_track_axis->addAssociatedTrack(m_TrackCol[itrk]);
+        t_track_axis->setType(10000); //Track-type axis. 
+        m_TrackCol[itrk]->addAssociatedHalfClusterV( p_HalfClusterV->at(ihc).get() );
+        m_datacol.map_HalfCluster["bkHalfCluster"].push_back(t_track_axis);
+        p_HalfClusterV->at(ihc).get()->addHalfCluster(settings.map_stringPars["OutputLongiClusName"], t_track_axis.get());
+      }  // end loop HalfClusterV
+
+      for(int ihc=0; ihc<p_HalfClusterU->size(); ihc++){  // loop HalfClusterU
+        // Get local max of the HalfCluster
+        std::vector<const PandoraPlus::Calo1DCluster*> localMaxColU = p_HalfClusterU->at(ihc).get()->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+//cout<<"    In HalfClusterU #"<<ihc<<": localMax size "<<localMaxColU.size()<<endl;
+
+        // Track axis candidate.
+        std::shared_ptr<PandoraPlus::CaloHalfCluster> t_track_axis = std::make_shared<PandoraPlus::CaloHalfCluster>();
+        CreateTrackAxis(extrapo_points, localMaxColU, t_track_axis.get());
+
+        // If the track do not match the Halfcluster, the track axis candidate will have no 1DCluster
+        if(t_track_axis->getCluster().size()==0)
+          continue;
+        
+//cout<<"      Created a track axis"<<endl;
+        t_track_axis->addAssociatedTrack(m_TrackCol[itrk]);
+        t_track_axis->setType(10000); //Track-type axis. 
+        m_TrackCol[itrk]->addAssociatedHalfClusterU( p_HalfClusterU->at(ihc).get() );
+        m_datacol.map_HalfCluster["bkHalfCluster"].push_back(t_track_axis);
+        p_HalfClusterU->at(ihc).get()->addHalfCluster(settings.map_stringPars["OutputLongiClusName"], t_track_axis.get());
+      }  // end loop HalfClusterU
+    }
+    else{  // pT < settings.map_floatPars["ConeMatchingCut_pT"]
+//std::cout << "For track " << itrk << ", pT = " << pT <<", using Cone method" << std::endl;
+
+      // Get local max and HalfCluster near the extrapolated points
+      std::vector<PandoraPlus::CaloHalfCluster*> t_nearbyHalfClustersV;  t_nearbyHalfClustersV.clear();
+      std::vector<PandoraPlus::CaloHalfCluster*> t_nearbyHalfClustersU;  t_nearbyHalfClustersU.clear();
+      std::vector<const PandoraPlus::Calo1DCluster*> t_nearbyLocalMaxV;     t_nearbyLocalMaxV.clear();
+      std::vector<const PandoraPlus::Calo1DCluster*> t_nearbyLocalMaxU;     t_nearbyLocalMaxU.clear();
+      GetNearby(p_HalfClusterV, extrapo_points, t_nearbyHalfClustersV, t_nearbyLocalMaxV);
+      GetNearby(p_HalfClusterU, extrapo_points, t_nearbyHalfClustersU, t_nearbyLocalMaxU);
+
+      // V plane
+      std::vector<const PandoraPlus::Calo1DCluster*> t_cone_axisV; t_cone_axisV.clear();
+      LongiConeLinking(extrapo_points, t_nearbyLocalMaxV, t_cone_axisV);
+      CreatConeAxis(m_datacol, m_TrackCol[itrk], t_nearbyHalfClustersV, t_cone_axisV);
+
+      // U plane
+      // Sort local max by their modules
+      //std::map<int, std::vector<const PandoraPlus::Calo1DCluster*> > m_orderedLocalMaxU;  // key: module of the bar
+      //m_orderedLocalMaxU.clear();
+      //for(int is=0; is<t_nearbyLocalMaxU.size(); is++)
+      //  m_orderedLocalMaxU[t_nearbyLocalMaxU[is]->getTowerID()[0][0]].push_back(t_nearbyLocalMaxU[is]);
+      //// linking 
+      //std::vector<const PandoraPlus::Calo1DCluster*> merged_cone_axisU; merged_cone_axisU.clear();
+      //for (auto it = m_orderedLocalMaxU.begin(); it != m_orderedLocalMaxU.end(); ++it){
+      //  std::vector<const PandoraPlus::Calo1DCluster*> moduled_localMaxU = it->second;
+      //  std::vector<const PandoraPlus::Calo1DCluster*> t_cone_axisU; t_cone_axisU.clear();
+      //  LongiConeLinking(extrapo_points, moduled_localMaxU, t_cone_axisU);
+      //  merged_cone_axisU.insert(merged_cone_axisU.end(), t_cone_axisU.begin(), t_cone_axisU.end());
+      //}
+      //CreatConeAxis(m_datacol, m_TrackCol[itrk], t_nearbyHalfClustersU, merged_cone_axisU);
+
+      // U plane
+      std::vector<const PandoraPlus::Calo1DCluster*> t_cone_axisU; t_cone_axisU.clear();
+      LongiConeLinking(extrapo_points, t_nearbyLocalMaxU, t_cone_axisU);
+      CreatConeAxis(m_datacol, m_TrackCol[itrk], t_nearbyHalfClustersU, t_cone_axisU);
+
+    }
+
+  
+
+  }  // end loop tracks
+
+  //Loop track to check the associated cluster: merge clusters if they are associated to the same track.
+  std::vector<PandoraPlus::CaloHalfCluster*> tmp_deleteClus; tmp_deleteClus.clear();
+  for(auto &itrk : m_TrackCol){
+    std::vector<PandoraPlus::CaloHalfCluster*> m_matchedUCol = itrk->getAssociatedHalfClustersU();
+    std::vector<PandoraPlus::CaloHalfCluster*> m_matchedVCol = itrk->getAssociatedHalfClustersV();
+
+    // std::cout << "yyy: Before merge, m_matchedVCol.size() = " << m_matchedVCol.size() << std::endl;
+    for(int imc=0; imc<m_matchedVCol.size(); imc++){
+      // std::cout<<"  yyy: m_matchedVCol["<<imc<<"]->getCluster().size()="<<m_matchedVCol[imc]->getCluster().size()<<std::endl;
+      // std::cout<<"  yyy: m_matchedVCol["<<imc<<"]->getAssociatedTracks().size()=" << m_matchedVCol[imc]->getAssociatedTracks().size() << std::endl;
+      int N_trk_axis = m_matchedVCol[imc]->getHalfClusterMap()[settings.map_stringPars["OutputLongiClusName"]].size() ;
+      // std::cout<<"  yyy: m_matchedVCol["<<imc<<"]->getHalfClusterMap()[TrackAxis].size()="<< N_trk_axis << std::endl;
+      for(int itk=0; itk<N_trk_axis; itk++){
+        // std::cout<<"    yyy: for TrackAxis " << itk 
+        // << ", Nlm = " << m_matchedVCol[imc]->getHalfClusterMap()["TrackAxis"][itk]->getCluster().size()
+        // << ", N trk = " << m_matchedVCol[imc]->getHalfClusterMap()["TrackAxis"][itk]->getAssociatedTracks().size() << std::endl;
+      }
+    }
+
+    if( m_matchedUCol.size()>1 ){
+      for(int i=1; i<m_matchedUCol.size(); i++){ 
+        m_matchedUCol[0]->mergeHalfCluster( m_matchedUCol[i] );
+        tmp_deleteClus.push_back(m_matchedUCol[i]);
+      }
+    }
+    if( m_matchedVCol.size()>1 ){
+      for(int i=1; i<m_matchedVCol.size(); i++){
+        m_matchedVCol[0]->mergeHalfCluster( m_matchedVCol[i] );
+        tmp_deleteClus.push_back(m_matchedVCol[i]);
+      }
+    }
+
+    /////////////////////////////////////////////
+    // if(m_matchedVCol.size()>0){
+    //   // std::cout<<"yyy: After merge, m_matchedVCol[0]->getCluster().size()=" << m_matchedVCol[0]->getCluster().size() << std::endl;
+    //   // std::cout<<"yyy: After merge, m_matchedVCol[0]->getAssociatedTracks().size()=" << m_matchedVCol[0]->getAssociatedTracks().size() << std::endl;
+    //   int N_trk_axis = m_matchedVCol[0]->getHalfClusterMap()["TrackAxis"].size() ;
+    //   // std::cout<<"yyy: After merge, m_matchedVCol[0]->getHalfClusterMap()[TrackAxis].size()="<< N_trk_axis << std::endl;
+    //   for(int nn=0; nn<N_trk_axis; nn++){
+    //     std::cout<<"yyy: for TrackAxis " << nn 
+    //     << ", Nlm = " << m_matchedVCol[0]->getHalfClusterMap()["TrackAxis"][nn]->getCluster().size()
+    //     << ", N trk = " << m_matchedVCol[0]->getHalfClusterMap()["TrackAxis"][nn]->getAssociatedTracks().size() << std::endl;
+
+    //   }
+    // }
+    /////////////////////////////////////////////
+  }
+
+  //Check vector: clean the merged clusters
+  for(int ihc=0; ihc<p_HalfClusterU->size(); ihc++){
+    if( find(tmp_deleteClus.begin(), tmp_deleteClus.end(), p_HalfClusterU->at(ihc).get())!=tmp_deleteClus.end() ){
+      p_HalfClusterU->erase(p_HalfClusterU->begin()+ihc);
+      ihc--;
+    }
+  }
+
+  for(int ihc=0; ihc<p_HalfClusterV->size(); ihc++){
+    if( find(tmp_deleteClus.begin(), tmp_deleteClus.end(), p_HalfClusterV->at(ihc).get())!=tmp_deleteClus.end() ){
+      p_HalfClusterV->erase(p_HalfClusterV->begin()+ihc);
+      ihc--;
+    }
+  }
+
+
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TrackMatchingAlg::ClearAlgorithm(){
+  m_TrackCol.clear();
+  p_HalfClusterV = nullptr;
+  p_HalfClusterU = nullptr;
+  // m_trackAxisVCol.clear();
+  // m_trackAxisUCol.clear();
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TrackMatchingAlg::GetExtrpoECALPoints(const PandoraPlus::Track* track, std::vector<TVector3>& extrapo_points){
+  std::vector<PandoraPlus::TrackState> ecal_track_states = track->getTrackStates("Ecal");
+  for(int its=0; its<ecal_track_states.size(); its++){
+    extrapo_points.push_back(ecal_track_states[its].referencePoint);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TrackMatchingAlg::CreateTrackAxis(vector<TVector3>& extrapo_points, std::vector<const PandoraPlus::Calo1DCluster*>& localMaxCol,
+                             PandoraPlus::CaloHalfCluster* t_track_axis){                           
+  if(localMaxCol.size()==0 || extrapo_points.size()==0)
+    return StatusCode::SUCCESS;
+  int t_slayer = localMaxCol[0]->getSlayer();
+  
+  if(t_slayer==1){  // V plane (xy plane)
+    for(int ipt=0; ipt<extrapo_points.size(); ipt++){
+    for(int ilm=0; ilm<localMaxCol.size(); ilm++){
+      // distance from the extrpolated point to the center of the local max bar
+      TVector3 distance = extrapo_points[ipt] - localMaxCol[ilm]->getPos();
+      if( TMath::Abs(distance.Z()) < (localMaxCol[ilm]->getBars()[0]->getBarLength())/2. &&
+          distance.Perp() < PandoraPlus::CaloUnit::barsize ) { 
+        t_track_axis->addUnit(localMaxCol[ilm]);
+      }
+      else { continue; }
+    }}
+  }
+  else{  // U plane (r-phi plane)
+    for(int ipt=0; ipt<extrapo_points.size(); ipt++){
+    for(int ilm=0; ilm<localMaxCol.size(); ilm++){
+      TVector3 lm_pos = localMaxCol[ilm]->getPos();
+      float barLength = localMaxCol[ilm]->getBars()[0]->getBarLength();
+      if( fabs(extrapo_points[ipt].z()-lm_pos.z()) < PandoraPlus::CaloUnit::barsize &&  
+          fabs(extrapo_points[ipt].Phi()-lm_pos.Phi()) < barLength/2./PandoraPlus::CaloUnit::ecal_innerR &&  
+          fabs(extrapo_points[ipt].Perp()-lm_pos.Perp()) < PandoraPlus::CaloUnit::barsize ){
+        t_track_axis->addUnit(localMaxCol[ilm]);
+      }
+      else { continue; }
+    }}
+  }
+
+//std::cout << "end calling CreateTrackAxis()" << std::endl;
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TrackMatchingAlg::GetNearby(const std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>* p_HalfCluster, 
+                                       const std::vector<TVector3>& extrapo_points, 
+                                       std::vector<PandoraPlus::CaloHalfCluster*>& t_nearbyHalfClusters, 
+                                       std::vector<const PandoraPlus::Calo1DCluster*>& t_nearbyLocalMax){
+  // std::cout << "calling TrackMatchingAlg::GetNearby()" << std::endl;
+
+  if(p_HalfCluster->size()==0 || extrapo_points.size()==0)  return StatusCode::SUCCESS; 
+
+  std::set<PandoraPlus::CaloHalfCluster*> set_nearbyHalfClusters;
+  int slayer = p_HalfCluster->at(0).get()->getSlayer();
+  if(slayer==1){  // V plane
+    for(int ihc=0; ihc<p_HalfCluster->size(); ihc++){
+      std::vector<const PandoraPlus::Calo1DCluster*> localMaxCol = p_HalfCluster->at(ihc).get()->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+      for(int ilm=0; ilm<localMaxCol.size(); ilm++){
+      for(int ipt=0; ipt<extrapo_points.size(); ipt++){
+        TVector3 distance(extrapo_points[ipt] - localMaxCol[ilm]->getPos());
+        if(TMath::Abs(distance.Z()) < (localMaxCol[ilm]->getBars()[0]->getBarLength())/2. && 
+            distance.Perp() < settings.map_floatPars["ConeNearByDistance"] ){  
+          t_nearbyLocalMax.push_back(localMaxCol[ilm]);
+          set_nearbyHalfClusters.insert(p_HalfCluster->at(ihc).get());
+          break;
+        }
+      }}
+      
+    }
+  }else{  // U plane
+    for(int ihc=0; ihc<p_HalfCluster->size(); ihc++){
+      std::vector<const PandoraPlus::Calo1DCluster*> localMaxCol = p_HalfCluster->at(ihc).get()->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+      for(int ilm=0; ilm<localMaxCol.size(); ilm++){
+      for(int ipt=0; ipt<extrapo_points.size(); ipt++){
+        TVector3 lm_pos = localMaxCol[ilm]->getPos();
+        float barLength = localMaxCol[ilm]->getBars()[0]->getBarLength();
+        if( fabs(extrapo_points[ipt].z()-lm_pos.z()) < settings.map_floatPars["ConeNearByDistance"] &&
+            fabs(extrapo_points[ipt].Phi()-lm_pos.Phi()) < barLength/2./PandoraPlus::CaloUnit::ecal_innerR &&
+            fabs(extrapo_points[ipt].Perp()-lm_pos.Perp()) < settings.map_floatPars["ConeNearByDistance"] ){
+          t_nearbyLocalMax.push_back(localMaxCol[ilm]);
+          set_nearbyHalfClusters.insert(p_HalfCluster->at(ihc).get());
+          break;
+        }
+      }}
+    }
+  }
+  t_nearbyHalfClusters.assign(set_nearbyHalfClusters.begin(), set_nearbyHalfClusters.end());
+
+  return StatusCode::SUCCESS; 
+}
+
+
+StatusCode TrackMatchingAlg::LongiConeLinking(const std::vector<TVector3>& extrapo_points,  
+                                              std::vector<const PandoraPlus::Calo1DCluster*>& nearbyLocalMax, 
+                                              std::vector<const PandoraPlus::Calo1DCluster*>& cone_axis){
+  // std::cout<<"yyy: calling longiConeLinking()"<<std::endl;
+  if(nearbyLocalMax.size()==0 || extrapo_points.size()==0) return StatusCode::SUCCESS;
+
+  // Seed finding
+  int slayer = nearbyLocalMax[0]->getSlayer();
+  
+  // int min_point = settings.map_intPars["Max_Seed_Point"];
+  // if (extrapo_points.size()<min_point) min_point = extrapo_points.size();
+  
+  if(slayer==1){  // If V plane
+    // for(int ip=0; ip<min_point; ip++){
+    for(int ip=0; ip<extrapo_points.size(); ip++){
+      double min_distance = 99999;
+      int seed_candidate_index = -1;
+
+      for(int il=0;il<nearbyLocalMax.size(); il++){
+        TVector3 distance = extrapo_points[ip] - nearbyLocalMax[il]->getPos();
+        double distance_2d = distance.Perp();
+        if(TMath::Abs(distance.Z()) < (nearbyLocalMax[il]->getBars()[0]->getBarLength())/2.
+           && distance_2d < settings.map_floatPars["ConeSeedDistance"]
+           && distance_2d < min_distance)
+        {
+          seed_candidate_index = il;
+          min_distance = distance_2d;
+        }
+      }
+      if (seed_candidate_index<0) continue;
+
+      cone_axis.push_back(nearbyLocalMax[seed_candidate_index]);
+      nearbyLocalMax.erase(nearbyLocalMax.begin() + seed_candidate_index);
+      break;
+    }
+  }
+  else{  // If U plane
+    // for(int ip=0; ip<min_point; ip++){
+    for(int ip=0; ip<extrapo_points.size(); ip++){
+      double min_distance = 99999;
+      int seed_candidate_index = -1;
+
+      for(int il=0;il<nearbyLocalMax.size(); il++){
+        TVector3 lm_pos = nearbyLocalMax[il]->getPos();
+        float barLength = nearbyLocalMax[il]->getBars()[0]->getBarLength();
+        float distance_2d = sqrt( pow(extrapo_points[ip].z()-lm_pos.z(), 2) + pow(extrapo_points[ip].Perp()-lm_pos.Perp(), 2) );
+        if( fabs(extrapo_points[ip].Phi()-lm_pos.Phi()) < barLength/2./PandoraPlus::CaloUnit::ecal_innerR && 
+            distance_2d < settings.map_floatPars["ConeSeedDistance"] &&
+            distance_2d < min_distance)
+        {
+          seed_candidate_index = il;
+          min_distance = distance_2d;
+        }
+      }
+      if (seed_candidate_index<0) continue;
+
+      cone_axis.push_back(nearbyLocalMax[seed_candidate_index]);
+      nearbyLocalMax.erase(nearbyLocalMax.begin() + seed_candidate_index);
+      break;
+    }
+  }
+
+  if (cone_axis.size() == 0) return StatusCode::SUCCESS;
+
+  // std::cout<<"  yyy: after seed-finding, cone_axis.size()="<<cone_axis.size()<<endl;
+
+  // Linking
+  while(nearbyLocalMax.size()>0){
+    // std::cout<<"  yyy: nearbyLocalMax.size()="<<nearbyLocalMax.size()<<", linking it!"<<std::endl;
+    const PandoraPlus::Calo1DCluster* shower_in_axis = cone_axis.back();
+    if(!shower_in_axis) break; 
+    if(isStopLinking(extrapo_points, shower_in_axis)) break;
+
+    // std::cout<<"  yyy: looking for a lm to link"<<std::endl;
+
+    double min_delta = 9999;
+    int shower_candidate_index = -1;
+
+    for(int il=0; il<nearbyLocalMax.size(); il++){
+      TVector2 relR = GetProjectedRelR(shower_in_axis, nearbyLocalMax[il]);  //Return vec: 1->2.
+      TVector2 clusaxis = GetProjectedAxis(extrapo_points, shower_in_axis);
+
+      double delta_phi = relR.DeltaPhi(clusaxis);
+      double delta_distance = (relR - (clusaxis*2)).Mod();
+
+      // std::cout<<"    yyy: for nearbyLocalMax["<<il<<"], "<<std::endl
+      //          <<"         shower_in_axis=("<<shower_in_axis->getPos().x()<<", "
+      //          <<shower_in_axis->getPos().y()<<", "
+      //          <<shower_in_axis->getPos().z()<<")"<<std::endl
+      //          <<"         nearbyLocalMax=("<<nearbyLocalMax[il]->getPos().x()<<", "
+      //          <<nearbyLocalMax[il]->getPos().y()<<", "
+      //          <<nearbyLocalMax[il]->getPos().z()<<")"<<std::endl
+      //          <<"         relR = ("<<relR.X()<<", "<<relR.Y()<<")"<<std::endl
+      //          <<"         clusaxis = ("<<clusaxis.X()<<", "<<clusaxis.Y()<<")"<<std::endl
+      //          <<"         delta_phi = "<<delta_phi<<", delta_distance="<<delta_distance<<std::endl;
+      
+      if( delta_phi<settings.map_floatPars["th_ConeTheta"] 
+          && relR.Mod()<settings.map_floatPars["th_ConeR"] 
+          && delta_distance<min_delta)
+      {
+        // std::cout<<"    yyy: nearbyLocalMax["<<il<<"] renewed"<<std::endl;
+        shower_candidate_index = il;
+        min_delta = delta_distance;
+      }
+    }
+    if (shower_candidate_index<0) break;
+
+    cone_axis.push_back(nearbyLocalMax[shower_candidate_index]);
+    nearbyLocalMax.erase(nearbyLocalMax.begin() + shower_candidate_index);
+  }
+  
+  return StatusCode::SUCCESS;
+}
+
+
+bool TrackMatchingAlg::isStopLinking( const std::vector<TVector3>& extrapo_points, 
+                                      const PandoraPlus::Calo1DCluster* final_cone_hit){
+
+  double slayer = final_cone_hit->getSlayer();
+  if(slayer==1){
+    TVector3 f_distance = extrapo_points.back() - final_cone_hit->getPos();
+    double f_distance_2d = f_distance.Perp(); 
+    for(int i=0; i<extrapo_points.size(); i++){
+      TVector3 distance = extrapo_points[i] - final_cone_hit->getPos();
+      double distance_2d = distance.Perp(); 
+      if (distance_2d < f_distance_2d) return false;
+    }
+  }
+  else{
+    
+
+    TVector3 lm_pos = final_cone_hit->getPos();
+    float barLength = final_cone_hit->getBars()[0]->getBarLength();
+    float f_distance_2d = sqrt( pow(extrapo_points.back().z()-lm_pos.z(), 2) + pow(extrapo_points.back().Perp()-lm_pos.Perp(), 2) );
+    for(int i=0; i<extrapo_points.size(); i++){
+      float distance_2d = sqrt( pow(extrapo_points[i].z()-lm_pos.z(), 2) + pow(extrapo_points[i].Perp()-lm_pos.Perp(), 2) );
+      if (distance_2d < f_distance_2d) return false;
+    }
+  }
+  // std::cout<<"  yyy: calling isStopLinking(): stop!"<<std::endl;
+  return true;   
+}
+
+
+TVector2 TrackMatchingAlg::GetProjectedRelR( const PandoraPlus::Calo1DCluster* m_shower1, const PandoraPlus::Calo1DCluster* m_shower2 ){
+  TVector2 paxis1, paxis2;
+  if(m_shower1->getSlayer()==1){ //For V-bars
+    paxis1.Set(m_shower1->getPos().x(), m_shower1->getPos().y());
+    paxis2.Set(m_shower2->getPos().x(), m_shower2->getPos().y());
+    return paxis2 - paxis1;
+  }
+  else{  //For U-bars
+    //if (m_shower1->getTowerID()[0][0] != m_shower2->getTowerID()[0][0])
+    //  std::cout << "warning: In GetProjectedRelR(), modules are different!" << std::endl;
+    TVector3 vec = m_shower2->getPos() - m_shower1->getPos();
+
+    TVector2 vec2d(vec.Perp(), vec.z());
+
+    return vec2d;
+  }
+
+  
+}
+
+
+TVector2 TrackMatchingAlg::GetProjectedAxis(const std::vector<TVector3>& extrapo_points, const PandoraPlus::Calo1DCluster* m_shower){
+  int min_index=0;
+  TVector2 distance(999., 999.);
+  if( m_shower->getSlayer()==1 ){  // V plane
+    for(int i=0; i<extrapo_points.size(); i++){
+      TVector2 t_distance(m_shower->getPos().x()-extrapo_points[i].x(), m_shower->getPos().y()-extrapo_points[i].y());
+      if(t_distance.Mod()<distance.Mod()){
+        distance = t_distance;
+        min_index = i;
+      }
+    }
+
+    if(min_index < extrapo_points.size()-1){
+      TVector2 axis(extrapo_points[min_index+1].x()-extrapo_points[min_index].x(), extrapo_points[min_index+1].y()-extrapo_points[min_index].y());
+      return axis;
+    }else{
+      TVector2 axis(extrapo_points[min_index].x()-extrapo_points[min_index-1].x(), extrapo_points[min_index].y()-extrapo_points[min_index-1].y());
+      return axis;
+    }
+
+  }else{  // U plane
+    for(int i=0; i<extrapo_points.size(); i++){
+      TVector3 dist3d = m_shower->getPos() - extrapo_points[i];
+      //dist3d.RotateZ( TMath::Pi()/4.*(6-m_shower->getTowerID()[0][0]) );
+      TVector2 t_distance(dist3d.Perp(), dist3d.z());
+      if(t_distance.Mod()<distance.Mod()){
+        distance = t_distance;
+        min_index = i;
+      }
+    }
+
+    if(min_index < extrapo_points.size()-1){
+      double dx = extrapo_points[min_index+1].x() - extrapo_points[min_index].x();
+      double dy = extrapo_points[min_index+1].y() - extrapo_points[min_index].y();
+      double dz = extrapo_points[min_index+1].z() - extrapo_points[min_index].z();
+      TVector3 vec(dx, dy, dz);
+      //vec.RotateZ( TMath::Pi()/4.*(6-m_shower->getTowerID()[0][0]) );
+      TVector2 axis(vec.Perp(), vec.z());
+      return axis;
+    }else{
+      double dx = extrapo_points[min_index].x() - extrapo_points[min_index-1].x();
+      double dy = extrapo_points[min_index].y() - extrapo_points[min_index-1].y();
+      double dz = extrapo_points[min_index].z() - extrapo_points[min_index-1].z();
+      TVector3 vec(dx, dy, dz);
+      //vec.RotateZ( TMath::Pi()/4.*(6-m_shower->getTowerID()[0][0]) );
+      TVector2 axis(vec.Perp(), vec.z());
+      return axis;
+    }
+  }
+
+  
+}
+
+
+StatusCode TrackMatchingAlg::CreatConeAxis(PandoraPlusDataCol& m_datacol, PandoraPlus::Track* track, std::vector<PandoraPlus::CaloHalfCluster*>& nearbyHalfClusters, 
+                                           std::vector<const PandoraPlus::Calo1DCluster*>& cone_axis){
+  // std::cout<<"yyy: Calling CreateConeAxis()"<<std::endl;
+  if(nearbyHalfClusters.size()==0 || cone_axis.size()==0) return StatusCode::SUCCESS; 
+
+  for(int ihc=0; ihc<nearbyHalfClusters.size(); ihc++){
+    std::vector<const PandoraPlus::Calo1DCluster*> localMaxCol = nearbyHalfClusters[ihc]->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+    // std::cout<<"    yyy: for nearbyHalfClusters["<<ihc<<"], localMax are:"
+    // Track axis candidate.
+    // PandoraPlus::CaloHalfCluster* t_track_axis = new PandoraPlus::CaloHalfCluster();
+    std::shared_ptr<PandoraPlus::CaloHalfCluster> t_track_axis = std::make_shared<PandoraPlus::CaloHalfCluster>();
+    for(int ica=0; ica<cone_axis.size(); ica++){
+      if( find(localMaxCol.begin(), localMaxCol.end(), cone_axis[ica]) != localMaxCol.end()){
+        t_track_axis->addUnit(cone_axis[ica]);
+        // std::cout<<"    add Unit from cone_axis to track_axis: " << cone_axis[ica]->getPos().x() << ", "
+        //          << cone_axis[ica]->getPos().y() << ", " << cone_axis[ica]->getPos().z() << std::endl;
+      }
+    }
+
+    // // If the track does not match the Halfcluster, the track axis candidate will have no 1DCluster
+    if(t_track_axis->getCluster().size()==0)
+      continue;
+    
+    t_track_axis->addAssociatedTrack(track);
+    t_track_axis->setType(10000); //Track-type axis. 
+    
+    if(nearbyHalfClusters[ihc]->getSlayer()==1){
+      track->addAssociatedHalfClusterV( nearbyHalfClusters[ihc] );
+      // std::cout<<"    yyy: track->addAssociatedHalfClusterV"<<std::endl;
+    }
+    else{
+      track->addAssociatedHalfClusterU( nearbyHalfClusters[ihc] );
+      // std::cout<<"    yyy: track->addAssociatedHalfClusterU"<<std::endl;
+    }
+    m_datacol.map_HalfCluster["bkHalfCluster"].push_back(t_track_axis);
+    nearbyHalfClusters[ihc]->addHalfCluster(settings.map_stringPars["OutputLongiClusName"], t_track_axis.get());
+    // std::cout<<"    yyy: nearbyHalfClusters["<<ihc<<"]->addHalfCluster, bars in the nearbyHalfClusters:"<<std::endl;
+    // for(int ii=0; ii<nearbyHalfClusters[ihc]->getCluster().size(); ii++){
+    //   std::cout<<"         "<<nearbyHalfClusters[ihc]->getCluster()[ii]->getPos().x()<<", "
+    //                         <<nearbyHalfClusters[ihc]->getCluster()[ii]->getPos().y()<<", "
+    //                         <<nearbyHalfClusters[ihc]->getCluster()[ii]->getPos().z()<<std::endl;
+    // }
+    // std::cout<<"          bars in the t_track_axis:"<<std::endl;
+    // for(int ii=0; ii<t_track_axis->getCluster().size(); ii++){
+    //   std::cout<<"         "<<t_track_axis->getCluster()[ii]->getPos().x()<<", "
+    //                         <<t_track_axis->getCluster()[ii]->getPos().y()<<", "
+    //                         <<t_track_axis->getCluster()[ii]->getPos().z()<<std::endl;
+    // }
+    
+    
+    
+  }
+
+  return StatusCode::SUCCESS; 
+}
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/TruthClusterMergingAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/TruthClusterMergingAlg.cpp
new file mode 100644
index 00000000..47abdaa1
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/TruthClusterMergingAlg.cpp
@@ -0,0 +1,200 @@
+#ifndef _TRUTHCLUSTERMERGING_ALG_C
+#define _TRUTHCLUSTERMERGING_ALG_C
+
+#include "Algorithm/TruthClusterMergingAlg.h"
+
+StatusCode TruthClusterMergingAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  //Initialize parameters
+  if(settings.map_boolPars.find("DoECALMerge")==settings.map_boolPars.end()) settings.map_boolPars["DoECALMerge"] = 1;
+  if(settings.map_boolPars.find("DoHCALMerge")==settings.map_boolPars.end()) settings.map_boolPars["DoHCALMerge"] = 1;
+  if(settings.map_boolPars.find("DoECALHCALConnection")==settings.map_boolPars.end()) settings.map_boolPars["DoECALHCALConnection"] = 1;
+
+  if(settings.map_stringPars.find("ReadinECALClusters")==settings.map_stringPars.end()) settings.map_stringPars["ReadinECALClusters"] = "TruthEcalCluster";
+  if(settings.map_stringPars.find("ReadinHCALClusters")==settings.map_stringPars.end()) settings.map_stringPars["ReadinHCALClusters"] = "TruthHcalCluster";
+  if(settings.map_stringPars.find("OutputECALCluster")==settings.map_stringPars.end()) settings.map_stringPars["OutputECALCluster"] = "TruthMergedEcalCluster";
+  if(settings.map_stringPars.find("OutputHCALCluster")==settings.map_stringPars.end()) settings.map_stringPars["OutputHCALCluster"] = "TruthMergedHcalCluster";
+  if(settings.map_stringPars.find("OutputCombPFO")==settings.map_stringPars.end()) settings.map_stringPars["OutputCombPFO"] = "TruthCombPFO";
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthClusterMergingAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+
+  for(int icl=0; icl<m_datacol.map_CaloCluster[settings.map_stringPars["ReadinECALClusters"]].size(); icl++)
+    m_EcalClusterCol.push_back(m_datacol.map_CaloCluster[settings.map_stringPars["ReadinECALClusters"]][icl].get()); 
+  for(int icl=0; icl<m_datacol.map_CaloCluster[settings.map_stringPars["ReadinHCALClusters"]].size(); icl++)
+    m_HcalClusterCol.push_back(m_datacol.map_CaloCluster[settings.map_stringPars["ReadinHCALClusters"]][icl].get()); 
+
+
+//cout<<"Print input ECAL cluster"<<endl;
+//for(int ic=0; ic<m_EcalClusterCol.size(); ic++){
+//  printf("  Pos+E (%.3f, %.3f, %.3f, %.3f), linked leading MCP [%d, %.3f], linked track size %d \n", m_EcalClusterCol[ic]->getShowerCenter().x(), m_EcalClusterCol[ic]->getShowerCenter().y(), m_EcalClusterCol[ic]->getShowerCenter().z(), m_EcalClusterCol[ic]->getLongiE(), m_EcalClusterCol[ic]->getLeadingMCP().getPDG(), m_EcalClusterCol[ic]->getLeadingMCPweight(), m_EcalClusterCol[ic]->getAssociatedTracks().size() );
+//  if(m_EcalClusterCol[ic]->getAssociatedTracks().size()>0) printf("    Track MCP: %d \n", m_EcalClusterCol[ic]->getAssociatedTracks()[0]->getLeadingMCP().getPDG());  
+//}
+//cout<<"Print input HCAL cluster"<<endl;
+//for(int ic=0; ic<m_HcalClusterCol.size(); ic++){
+//  printf("  Pos+E (%.3f, %.3f, %.3f, %.3f), linked leading MCP [%d, %.3f], linked track size %d \n", m_HcalClusterCol[ic]->getHitCenter().x(), m_HcalClusterCol[ic]->getHitCenter().y(), m_HcalClusterCol[ic]->getHitCenter().z(), m_HcalClusterCol[ic]->getHitsE(), m_HcalClusterCol[ic]->getLeadingMCP().getPDG(), m_HcalClusterCol[ic]->getLeadingMCPweight(), m_HcalClusterCol[ic]->getAssociatedTracks().size() );
+//  if(m_HcalClusterCol[ic]->getAssociatedTracks().size()>0) printf("    Track MCP: %d \n", m_HcalClusterCol[ic]->getAssociatedTracks()[0]->getLeadingMCP().getPDG());
+//}
+
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthClusterMergingAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+
+//cout<<"  TruthClusterMergingAlg: input ECAL cluster size "<<m_EcalClusterCol.size()<<", HCAL cluster size "<<m_HcalClusterCol.size()<<endl;
+//cout<<"  Print ECAL cluster energy: "<<endl;
+//for(int ic=0; ic<m_EcalClusterCol.size(); ic++)
+//  cout<<"    #"<<ic<<" En = "<<m_EcalClusterCol[ic]->getLongiE()<<endl;
+//cout<<"  Print HCAL cluster energy: "<<endl;
+//for(int ic=0; ic<m_HcalClusterCol.size(); ic++)
+//  cout<<"    #"<<ic<<" En = "<<m_HcalClusterCol[ic]->getHitsE()<<endl;
+
+  std::map<edm4hep::MCParticle, std::vector<const PandoraPlus::Calo3DCluster*>> map_ClusterCol_Ecal; 
+  std::map<edm4hep::MCParticle, std::vector<const PandoraPlus::Calo3DCluster*>> map_ClusterCol_Hcal; 
+  for(int ic=0; ic<m_EcalClusterCol.size(); ic++){
+    auto mcp = m_EcalClusterCol[ic]->getLeadingMCP();
+    map_ClusterCol_Ecal[mcp].push_back( m_EcalClusterCol[ic] );
+  }
+  for(int ic=0; ic<m_HcalClusterCol.size(); ic++){
+    auto mcp = m_HcalClusterCol[ic]->getLeadingMCP();
+    map_ClusterCol_Hcal[mcp].push_back( m_HcalClusterCol[ic] );
+  }
+
+  if(settings.map_boolPars["DoECALMerge"]){
+    for(auto& iter: map_ClusterCol_Ecal){
+      if(iter.second.size()==0) continue;
+      auto tmp_newcluster = iter.second[0]->Clone();
+      for(int icl=1; icl<iter.second.size(); icl++){
+        tmp_newcluster->mergeCluster( iter.second[icl] );
+      }
+      //tmp_newcluster->getLinkedMCPfromUnit();
+      merged_EcalClusterCol.push_back(tmp_newcluster);
+      m_datacol.map_CaloCluster["bk3DCluster"].push_back(tmp_newcluster);
+    }
+  }
+
+  if(settings.map_boolPars["DoHCALMerge"]){
+    for(auto& iter: map_ClusterCol_Hcal){
+      if(iter.second.size()==0) continue;
+      auto tmp_newcluster = iter.second[0]->Clone();
+      for(int icl=1; icl<iter.second.size(); icl++){
+        tmp_newcluster->mergeCluster( iter.second[icl] );
+      }
+      //tmp_newcluster->getLinkedMCPfromUnit();
+      merged_HcalClusterCol.push_back(tmp_newcluster);
+      m_datacol.map_CaloCluster["bk3DCluster"].push_back(tmp_newcluster);
+    }
+  }
+
+
+  if(settings.map_boolPars["DoECALHCALConnection"]){
+    map_ClusterCol_Ecal.clear();
+    map_ClusterCol_Hcal.clear();
+
+    if(settings.map_boolPars["DoECALMerge"]){
+      for(int ic=0; ic<merged_EcalClusterCol.size(); ic++){
+        auto mcp = merged_EcalClusterCol[ic]->getLeadingMCP();
+        map_ClusterCol_Ecal[mcp].push_back( merged_EcalClusterCol[ic].get() );
+      }
+    }
+    else{
+      for(int ic=0; ic<m_EcalClusterCol.size(); ic++){
+        auto mcp = m_EcalClusterCol[ic]->getLeadingMCP();
+        map_ClusterCol_Ecal[mcp].push_back( m_EcalClusterCol[ic] );
+      }
+    }
+
+    if(settings.map_boolPars["DoHCALMerge"]){
+      for(int ic=0; ic<merged_HcalClusterCol.size(); ic++){
+        auto mcp = merged_HcalClusterCol[ic]->getLeadingMCP();
+        map_ClusterCol_Hcal[mcp].push_back( merged_HcalClusterCol[ic].get() );
+      }
+    }
+    else{
+      for(int ic=0; ic<m_HcalClusterCol.size(); ic++){
+        auto mcp = m_HcalClusterCol[ic]->getLeadingMCP();
+        map_ClusterCol_Hcal[mcp].push_back( m_HcalClusterCol[ic] );
+      }
+    }
+
+
+    for(auto& iter: map_ClusterCol_Ecal){
+      auto mcp = iter.first;
+      std::vector<const PandoraPlus::Calo3DCluster*> m_linkedEcalClus = iter.second;
+      std::vector<const PandoraPlus::Calo3DCluster*> m_linkedHcalClus = map_ClusterCol_Hcal[mcp];   
+
+      if(m_linkedEcalClus.size()==0 && m_linkedHcalClus.size()==0) continue;
+
+      std::shared_ptr<PandoraPlus::PFObject> tmp_newpfo = std::make_shared<PandoraPlus::PFObject>();
+      double Emax = -99;
+      int index = -1;
+      for(int ic=0; ic<m_linkedEcalClus.size(); ic++){ 
+        tmp_newpfo->addECALCluster(m_linkedEcalClus[ic]);
+        if(m_linkedEcalClus[ic]->getLongiE()>Emax){
+          Emax = m_linkedEcalClus[ic]->getLongiE();
+          index = ic;
+        }
+      }
+      const PandoraPlus::Track* p_EcalLeadingTrk = nullptr;
+      if(index>=0 && m_linkedEcalClus[index]->getAssociatedTracks().size()>0) p_EcalLeadingTrk = m_linkedEcalClus[index]->getAssociatedTracks()[0];
+
+      Emax = -99; index = -1;
+      for(int ic=0; ic<m_linkedHcalClus.size(); ic++){
+        tmp_newpfo->addHCALCluster(m_linkedHcalClus[ic]);
+        if(m_linkedHcalClus[ic]->getHitsE()>Emax){
+          Emax = m_linkedHcalClus[ic]->getHitsE();
+          index = ic;
+        }
+      }
+      const PandoraPlus::Track* p_HcalLeadingTrk = nullptr;
+      if(index>=0 && m_linkedHcalClus[index]->getAssociatedTracks().size()>0) p_HcalLeadingTrk = m_linkedHcalClus[index]->getAssociatedTracks()[0];
+
+      if(!p_EcalLeadingTrk && p_HcalLeadingTrk) tmp_newpfo->addTrack(p_HcalLeadingTrk);
+      else if(p_EcalLeadingTrk) tmp_newpfo->addTrack(p_EcalLeadingTrk);
+
+      merged_CombClusterCol.push_back(tmp_newpfo);
+      m_datacol.map_PFObjects["bkPFO"].push_back(tmp_newpfo);
+    }
+
+  }
+ /* 
+cout<<"Print PFO"<<endl;
+for(int ip=0; ip<merged_CombClusterCol.size(); ip++){
+  printf("  PFO #%d: track size %d, ECAL cluster size %d, HCAL cluster size %d \n", ip, merged_CombClusterCol[ip]->getTracks().size(), merged_CombClusterCol[ip]->getECALClusters().size(), merged_CombClusterCol[ip]->getHCALClusters().size() );
+  cout<<"    Track: "<<endl;
+  for(int itrk=0; itrk<merged_CombClusterCol[ip]->getTracks().size(); itrk++) 
+    printf("      Trk #%d: momentum %.3f, linked MCP %d \n", itrk, merged_CombClusterCol[ip]->getTracks()[itrk]->getMomentum(), merged_CombClusterCol[ip]->getTracks()[itrk]->getLeadingMCP().getPDG() );
+  cout<<"    ECAL cluster: "<<endl;
+  for(int icl=0; icl<merged_CombClusterCol[ip]->getECALClusters().size(); icl++)
+    printf("      ECAL cluster #%d: energy %.3f, linked MCP %d \n", icl, merged_CombClusterCol[ip]->getECALClusters()[icl]->getLongiE(), merged_CombClusterCol[ip]->getECALClusters()[icl]->getLeadingMCP().getPDG() );
+  cout<<"    HCAL cluster: "<<endl;
+  for(int icl=0; icl<merged_CombClusterCol[ip]->getHCALClusters().size(); icl++)
+    printf("      HCAL cluster #%d: energy %.3f, linked MCP %d \n", icl, merged_CombClusterCol[ip]->getHCALClusters()[icl]->getHitsE(), merged_CombClusterCol[ip]->getHCALClusters()[icl]->getLeadingMCP().getPDG() );
+  cout<<endl;
+}
+*/
+
+  m_datacol.map_CaloCluster[settings.map_stringPars["OutputECALCluster"]] = merged_EcalClusterCol;
+  m_datacol.map_CaloCluster[settings.map_stringPars["OutputHCALCluster"]] = merged_HcalClusterCol;
+  m_datacol.map_PFObjects[settings.map_stringPars["OutputCombPFO"]] = merged_CombClusterCol;
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthClusterMergingAlg::ClearAlgorithm(){
+
+  m_EcalClusterCol.clear();
+  m_HcalClusterCol.clear();
+  merged_EcalClusterCol.clear();
+  merged_HcalClusterCol.clear();
+  merged_CombClusterCol.clear();
+
+  return StatusCode::SUCCESS;
+};
+
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/TruthClusteringAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/TruthClusteringAlg.cpp
new file mode 100644
index 00000000..cff2ea5e
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/TruthClusteringAlg.cpp
@@ -0,0 +1,496 @@
+#ifndef _TRUTHCLUS_ALG_C
+#define _TRUTHCLUS_ALG_C
+
+#include "Algorithm/TruthClusteringAlg.h"
+
+StatusCode TruthClusteringAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  if(settings.map_boolPars.find("UseSplit")==settings.map_boolPars.end()) 
+    settings.map_boolPars["UseSplit"] = 1;
+  if(settings.map_boolPars.find("DoECALClustering")==settings.map_boolPars.end()) 
+    settings.map_boolPars["DoECALClustering"] = 1;
+  if(settings.map_boolPars.find("DoHCALClustering")==settings.map_boolPars.end()) 
+    settings.map_boolPars["DoHCALClustering"] = 1;
+
+  if(settings.map_stringPars.find("InputECALBars")==settings.map_stringPars.end()) 
+    settings.map_stringPars["InputECALBars"] = "BarCol";
+  if(settings.map_stringPars.find("InputHCALHits")==settings.map_stringPars.end()) 
+    settings.map_stringPars["InputHCALHits"] = "HCALBarrel";
+  if(settings.map_stringPars.find("OutputECAL1DClusters")==settings.map_stringPars.end()) 
+    settings.map_stringPars["OutputECAL1DClusters"] = "TruthCluster1DCol";
+  if(settings.map_stringPars.find("OutputECALHalfClusters")==settings.map_stringPars.end()) 
+    settings.map_stringPars["OutputECALHalfClusters"] = "TruthESCluster";
+  if(settings.map_stringPars.find("OutputECALTower")==settings.map_stringPars.end()) 
+    settings.map_stringPars["OutputECALTower"] = "TruthESTower";
+  if(settings.map_stringPars.find("OutputHCALClusters")==settings.map_stringPars.end()) 
+    settings.map_stringPars["OutputHCALClusters"] = "TruthHcalCluster";
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthClusteringAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  m_TrackCol.clear();
+  m_bars.clear();
+  m_1dclusterUCol.clear();
+  m_1dclusterVCol.clear();
+  m_halfclusterU.clear();
+  m_halfclusterV.clear();
+  m_towers.clear();
+
+  m_hits.clear();
+  m_clusters.clear();
+  m_bkCol.Clear();
+
+  m_TrackCol = m_datacol.TrackCol; 
+  m_bars = m_datacol.map_BarCol[settings.map_stringPars["InputECALBars"]];
+  m_hits = m_datacol.map_CaloHit[settings.map_stringPars["InputHCALHits"]];
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthClusteringAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+
+  //ECAL clustering
+  if(settings.map_boolPars["DoECALClustering"]){
+//cout<<"Input bar size: "<<m_bars.size()<<endl;
+
+    std::map<edm4hep::MCParticle, std::vector<std::shared_ptr<PandoraPlus::CaloUnit>> > map_barCol; map_barCol.clear();
+    for(int ibar=0; ibar<m_bars.size(); ibar++){
+      if(settings.map_boolPars["UseSplit"]){
+        std::vector< std::pair<edm4hep::MCParticle, float> > m_linkVec = m_bars[ibar]->getLinkedMCP();
+//printf("  Bar #%d: link size %d: ", ibar, m_linkVec.size());
+//for(int il=0; il<m_linkVec.size(); il++) printf("(%d, %.2f), ", m_linkVec[il].first.getPDG(), m_linkVec[il].second);
+//cout<<endl;
+
+        if(m_linkVec.size()==0){
+          std::cout<<"ERROR: No truth info in EcalBar #"<<ibar<<std::endl;
+          continue;
+        }
+        if(m_linkVec.size()==1){
+          edm4hep::MCParticle mcp = m_linkVec[0].first;
+          map_barCol[mcp].push_back(m_bars[ibar]);
+          continue;
+        }
+        for(int ilink=0; ilink<m_linkVec.size(); ilink++){
+          auto tmp_newbar = m_bars[ibar]->Clone();
+          tmp_newbar->setQ(tmp_newbar->getQ1()*m_linkVec[ilink].second, tmp_newbar->getQ2()*m_linkVec[ilink].second);
+          map_barCol[m_linkVec[ilink].first].push_back(tmp_newbar);
+          tmp_newbar->setLinkedMCP( std::vector<std::pair<edm4hep::MCParticle, float>>{std::make_pair(m_linkVec[ilink].first, 1.)} );
+          m_datacol.map_BarCol["bkBars"].push_back(tmp_newbar); 
+        }
+      }
+      else{
+        edm4hep::MCParticle mcp = m_bars[ibar]->getLeadingMCP();
+        map_barCol[mcp].push_back(m_bars[ibar]);
+      }
+    }
+//cout<<"truth map size "<<map_barCol.size()<<endl;
+//for(auto& iter: map_barCol){ 
+//  double totE = 0.;
+//  for(int ibar=0; ibar<iter.second.size(); ibar++)
+//    totE += iter.second[ibar]->getEnergy();
+//  printf("  Truth MCP pdgid %d, bar size %d, totE %.3f \n", iter.first.getPDG(), iter.second.size(), totE);
+//}   
+
+
+    for(auto& iter: map_barCol){
+      std::map<int, std::vector<std::shared_ptr<PandoraPlus::CaloUnit>> > m_orderedBars; m_orderedBars.clear();
+      for(int ibar=0; ibar<iter.second.size(); ibar++)
+        m_orderedBars[iter.second[ibar]->getDlayer()].push_back(iter.second[ibar]);
+//cout<<"  MCP "<<iter.first.getPDG()<<" covers "<<m_orderedBars.size()<<"layers "<<endl;
+
+      std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> m_new1DClusUCol; m_new1DClusUCol.clear();
+      std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> m_new1DClusVCol; m_new1DClusVCol.clear();
+   
+      for(auto& iter_bar: m_orderedBars){
+        std::shared_ptr<PandoraPlus::Calo1DCluster> tmp_new1dclusterU = std::make_shared<PandoraPlus::Calo1DCluster>();
+        std::shared_ptr<PandoraPlus::Calo1DCluster> tmp_new1dclusterV = std::make_shared<PandoraPlus::Calo1DCluster>();
+        for(int ibar=0; ibar<iter_bar.second.size(); ibar++){ 
+          if(iter_bar.second[ibar]->getSlayer()==0) tmp_new1dclusterU->addUnit( iter_bar.second[ibar].get() );
+          if(iter_bar.second[ibar]->getSlayer()==1) tmp_new1dclusterV->addUnit( iter_bar.second[ibar].get() );
+        }
+        if(tmp_new1dclusterU  && tmp_new1dclusterU->getBars().size()>0){
+          tmp_new1dclusterU->getLinkedMCPfromUnit();
+          tmp_new1dclusterU->setSeed();
+          tmp_new1dclusterU->setIDInfo();
+          m_new1DClusUCol.push_back(tmp_new1dclusterU);
+          m_datacol.map_1DCluster["bk1DCluster"].push_back(tmp_new1dclusterU);
+        }
+        if(tmp_new1dclusterV  && tmp_new1dclusterV->getBars().size()>0){
+          tmp_new1dclusterV->getLinkedMCPfromUnit();
+          tmp_new1dclusterV->setSeed();
+          tmp_new1dclusterV->setIDInfo();
+          m_new1DClusVCol.push_back(tmp_new1dclusterV);
+          m_datacol.map_1DCluster["bk1DCluster"].push_back(tmp_new1dclusterV);
+        }
+      }
+//cout<<"  1D cluster size "<<m_new1DClusUCol.size()<<", "<<m_new1DClusVCol.size()<<endl;
+//for(int icl=0; icl<m_new1DClusUCol.size(); icl++){
+//  printf("    Dlayer %d, Slayer %d, En %.3f, bar size %d, seed size %d, MCP link size %d \n", m_new1DClusUCol[icl]->getDlayer(), m_new1DClusUCol[icl]->getSlayer(), m_new1DClusUCol[icl]->getEnergy(), m_new1DClusUCol[icl]->getBars().size(), m_new1DClusUCol[icl]->getNseeds(), m_new1DClusUCol[icl]->getLinkedMCP().size());
+//}
+//for(int icl=0; icl<m_new1DClusVCol.size(); icl++){
+//  printf("    Dlayer %d, Slayer %d, En %.3f, bar size %d, seed size %d, MCP link size %d \n", m_new1DClusVCol[icl]->getDlayer(), m_new1DClusVCol[icl]->getSlayer(), m_new1DClusVCol[icl]->getEnergy(), m_new1DClusVCol[icl]->getBars().size(), m_new1DClusVCol[icl]->getNseeds(), m_new1DClusVCol[icl]->getLinkedMCP().size());
+//}
+
+      m_1dclusterUCol.insert(m_1dclusterUCol.end(), m_new1DClusUCol.begin(), m_new1DClusUCol.end());
+      m_1dclusterVCol.insert(m_1dclusterVCol.end(), m_new1DClusVCol.begin(), m_new1DClusVCol.end());
+
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> m_newHFClusterU = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> m_newHFClusterV = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      for(int i1d=0; i1d<m_new1DClusUCol.size(); i1d++)
+        m_newHFClusterU->addUnit(m_new1DClusUCol[i1d].get());
+      for(int i1d=0; i1d<m_new1DClusVCol.size(); i1d++)
+        m_newHFClusterV->addUnit(m_new1DClusVCol[i1d].get());
+      
+      m_newHFClusterU->getLinkedMCPfromUnit();
+      m_newHFClusterV->getLinkedMCPfromUnit();
+      m_halfclusterU.push_back(m_newHFClusterU);
+      m_halfclusterV.push_back(m_newHFClusterV);
+      m_datacol.map_HalfCluster["bkHalfCluster"].push_back(m_newHFClusterU);
+      m_datacol.map_HalfCluster["bkHalfCluster"].push_back(m_newHFClusterV);
+    }
+
+    //Track match
+    for(int itrk=0; itrk<m_TrackCol.size(); itrk++){
+      edm4hep::MCParticle mcp_trk = m_TrackCol[itrk]->getLeadingMCP();
+      for(int ihf=0; ihf<m_halfclusterU.size(); ihf++){
+        if(m_halfclusterU[ihf]->getLeadingMCP()==mcp_trk){
+          m_halfclusterU[ihf]->addAssociatedTrack(m_TrackCol[itrk].get());
+          m_TrackCol[itrk]->addAssociatedHalfClusterU(m_halfclusterU[ihf].get());
+        }
+      }
+      for(int ihf=0; ihf<m_halfclusterV.size(); ihf++){
+        if(m_halfclusterV[ihf]->getLeadingMCP()==mcp_trk){
+          m_halfclusterV[ihf]->addAssociatedTrack(m_TrackCol[itrk].get());
+          m_TrackCol[itrk]->addAssociatedHalfClusterV(m_halfclusterV[ihf].get());
+        }
+      }
+    }
+
+/*
+cout<<"Check tower ID: ";
+cout<<endl;
+  printf("    HalfCluster size: (%d, %d) \n", m_halfclusterU.size(), m_halfclusterV.size() );
+  cout<<"    Loop print HalfClusterU: "<<endl;
+  for(int icl=0; icl<m_halfclusterU.size(); icl++){
+    cout<<"      In HFClusU #"<<icl<<": shower size = "<<m_halfclusterU[icl]->getCluster().size()<<", En = "<<m_halfclusterU[icl]->getEnergy();
+    printf(", Position (%.3f, %.3f, %.3f), address %p ",m_halfclusterU[icl]->getPos().x(), m_halfclusterU[icl]->getPos().y(), m_halfclusterU[icl]->getPos().z(), m_halfclusterU[icl]);
+    printf(", cousin size %d, address: ", m_halfclusterU[icl]->getHalfClusterCol("CousinCluster").size());
+    for(int ics=0; ics<m_halfclusterU[icl]->getHalfClusterCol("CousinCluster").size(); ics++) printf("%p, ", m_halfclusterU[icl]->getHalfClusterCol("CousinCluster")[ics]);
+    printf(", track size %d, address: ", m_halfclusterU[icl]->getAssociatedTracks().size());
+    for(int itrk=0; itrk<m_halfclusterU[icl]->getAssociatedTracks().size(); itrk++) printf("%p, ", m_halfclusterU[icl]->getAssociatedTracks()[itrk]);
+    printf(", MCP link size %d, pid, Pz and weight: ", m_halfclusterU[icl]->getLinkedMCP().size());
+    for(int imc=0; imc<m_halfclusterU[icl]->getLinkedMCP().size(); imc++) printf("(%d, %.3f, %.3f), ", m_halfclusterU[icl]->getLinkedMCP()[imc].first.getPDG(), m_halfclusterU[icl]->getLinkedMCP()[imc].first.getMomentum().z, m_halfclusterU[icl]->getLinkedMCP()[imc].second);
+    cout<<endl;
+    for(auto ish : m_halfclusterU[icl]->getCluster()){
+      printf("          Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Nbars %d, NSeed %d, Address %p \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish->getBars().size(),  ish->getNseeds(), ish );
+    }
+  }
+  cout<<endl;
+
+  cout<<"    Loop print HalfClusterV: "<<endl;
+  for(int icl=0; icl<m_halfclusterV.size(); icl++){
+    cout<<"      In HFClusV #"<<icl<<": shower size = "<<m_halfclusterV[icl]->getCluster().size()<<", En = "<<m_halfclusterV[icl]->getEnergy();
+    printf(", Position (%.3f, %.3f, %.3f), address %p ",m_halfclusterV[icl]->getPos().x(), m_halfclusterV[icl]->getPos().y(), m_halfclusterV[icl]->getPos().z(), m_halfclusterV[icl]);
+    printf(", cousin size %d, address: ", m_halfclusterV[icl]->getHalfClusterCol("CousinCluster").size());
+    for(int ics=0; ics<m_halfclusterV[icl]->getHalfClusterCol("CousinCluster").size(); ics++) printf("%p, ", m_halfclusterV[icl]->getHalfClusterCol("CousinCluster")[ics]);
+    printf(", track size %d, address: ", m_halfclusterV[icl]->getAssociatedTracks().size());
+    for(int itrk=0; itrk<m_halfclusterV[icl]->getAssociatedTracks().size(); itrk++) printf("%p, ", m_halfclusterV[icl]->getAssociatedTracks()[itrk]);
+    printf(", MCP link size %d, pid, Pz and weight: ", m_halfclusterV[icl]->getLinkedMCP().size());
+    for(int imc=0; imc<m_halfclusterV[icl]->getLinkedMCP().size(); imc++) printf("(%d, %.3f, %.3f), ", m_halfclusterV[icl]->getLinkedMCP()[imc].first.getPDG(), m_halfclusterV[icl]->getLinkedMCP()[imc].first.getMomentum().z, m_halfclusterV[icl]->getLinkedMCP()[imc].second);
+    cout<<endl;
+    for(auto ish : m_halfclusterV[icl]->getCluster()){
+      printf("          Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Nbars %d, NSeed %d, Address %p \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish->getBars().size(), ish->getNseeds(), ish );
+    }
+  }
+  cout<<endl;
+*/
+
+    //Create tower
+    HalfClusterToTowers(m_halfclusterU, m_halfclusterV, m_towers);
+//cout<<"  Ecal halfcluster size "<<m_halfclusterU.size()<<", "<<m_halfclusterV.size()<<", tower size "<<m_towers.size()<<endl;
+
+    m_datacol.map_1DCluster[settings.map_stringPars["OutputECAL1DClusters"]+"U"] = m_1dclusterUCol;
+    m_datacol.map_1DCluster[settings.map_stringPars["OutputECAL1DClusters"]+"V"] = m_1dclusterVCol;
+    m_datacol.map_HalfCluster[settings.map_stringPars["OutputECALHalfClusters"]+"U"] = m_halfclusterU;
+    m_datacol.map_HalfCluster[settings.map_stringPars["OutputECALHalfClusters"]+"V"] = m_halfclusterV;  
+    m_datacol.map_CaloCluster[settings.map_stringPars["OutputECALTower"]] = m_towers;
+  }
+
+
+
+
+  //HCAL clustering
+  if(settings.map_boolPars["DoHCALClustering"]){
+//cout<<"Input HCAL hit size "<<m_hits.size()<<endl;
+    std::map<edm4hep::MCParticle, std::vector<std::shared_ptr<PandoraPlus::CaloHit>> > map_hitCol; map_hitCol.clear();
+    for(int ihit=0; ihit<m_hits.size(); ihit++){
+      if(settings.map_boolPars["UseSplit"]){
+        std::vector< std::pair<edm4hep::MCParticle, float> > m_linkVec = m_hits[ihit]->getLinkedMCP();
+//printf("  Hit #%d: link size %d: ", ihit, m_linkVec.size());
+//for(int il=0; il<m_linkVec.size(); il++) printf("(%d, %.2f), ", m_linkVec[il].first.getPDG(), m_linkVec[il].second);
+//cout<<endl;
+
+        if(m_linkVec.size()==0){
+          std::cout<<"ERROR: No truth info in hit #"<<ihit<<std::endl;
+          continue;
+        }
+        if(m_linkVec.size()==1){
+          edm4hep::MCParticle mcp = m_linkVec[0].first;
+          map_hitCol[mcp].push_back(m_hits[ihit]);
+          continue;
+        }
+   
+        for(int ilink=0; ilink<m_linkVec.size(); ilink++){
+          auto tmp_newhit = m_hits[ihit]->Clone();
+          tmp_newhit->setEnergy(tmp_newhit->getEnergy()*m_linkVec[ilink].second);
+          tmp_newhit->setLinkedMCP(std::vector<std::pair<edm4hep::MCParticle, float>>{std::make_pair(m_linkVec[ilink].first, 1.)});
+          map_hitCol[m_linkVec[ilink].first].push_back(tmp_newhit);
+          m_datacol.map_CaloHit["bkHit"].push_back(tmp_newhit);
+        }
+      }
+   
+      else{
+        edm4hep::MCParticle mcp = m_hits[ihit]->getLeadingMCP();
+        map_hitCol[mcp].push_back(m_hits[ihit]);
+      }
+   
+    }//End loop hits
+
+//cout<<"truth map size "<<map_hitCol.size()<<endl;
+//for(auto& iter: map_hitCol){
+//  double totE = 0.;
+//  for(int ibar=0; ibar<iter.second.size(); ibar++)
+//    totE += iter.second[ibar]->getEnergy();
+//  printf("  Truth MCP pdgid %d, bar size %d, totE %.3f \n", iter.first.getPDG(), iter.second.size(), totE);
+//} 
+  
+    for(auto& iter: map_hitCol){
+      std::shared_ptr<PandoraPlus::Calo3DCluster> m_newCluster = std::make_shared<PandoraPlus::Calo3DCluster>();
+      for(int ihit=0; ihit<iter.second.size(); ihit++) m_newCluster->addHit( iter.second[ihit].get() );    
+      m_newCluster->getLinkedMCPfromHit();
+      m_clusters.push_back(m_newCluster);
+      m_datacol.map_CaloCluster["bk3DCluster"].push_back(m_newCluster);
+    }
+
+    for(int icl=0; icl<m_clusters.size(); icl++){
+      for(int itrk=0; itrk<m_TrackCol.size(); itrk++){
+        edm4hep::MCParticle mcp_trk = m_TrackCol[itrk]->getLeadingMCP();
+        if(m_clusters[icl]->getLeadingMCP()==mcp_trk){
+          m_clusters[icl]->addAssociatedTrack(m_TrackCol[itrk].get());
+          break;
+        }
+      }
+    }
+
+    m_datacol.map_CaloCluster[settings.map_stringPars["OutputHCALClusters"]] = m_clusters;
+  }
+
+  m_datacol.map_1DCluster["bk1DCluster"].insert( m_datacol.map_1DCluster["bk1DCluster"].end(), m_bkCol.map_1DCluster["bk1DCluster"].begin(), m_bkCol.map_1DCluster["bk1DCluster"].end() );
+  m_datacol.map_2DCluster["bk2DCluster"].insert( m_datacol.map_2DCluster["bk2DCluster"].end(), m_bkCol.map_2DCluster["bk2DCluster"].begin(), m_bkCol.map_2DCluster["bk2DCluster"].end() );
+  m_datacol.map_HalfCluster["bkHalfCluster"].insert( m_datacol.map_HalfCluster["bkHalfCluster"].end(), m_bkCol.map_HalfCluster["bkHalfCluster"].begin(), m_bkCol.map_HalfCluster["bkHalfCluster"].end() );
+
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthClusteringAlg::ClearAlgorithm(){
+  m_bars.clear();
+  m_1dclusterUCol.clear();
+  m_halfclusterU.clear();
+  m_halfclusterV.clear();
+
+  m_hits.clear();
+  m_clusters.clear();
+  m_towers.clear();
+
+  m_bkCol.Clear();
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthClusteringAlg::HalfClusterToTowers( std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_halfClusU,
+                                                    std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_halfClusV,
+                                                    std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_towers ){
+  m_towers.clear();
+
+  std::map<std::vector<int>, std::vector<const PandoraPlus::CaloUnit*>> map_barCol; map_barCol.clear(); 
+  for(int il=0; il<m_halfClusU.size(); il++){
+    for(int is=0; is<m_halfClusU[il]->getCluster().size(); is++){
+      const PandoraPlus::Calo1DCluster* p_shower = m_halfClusU[il]->getCluster()[is];
+      for(int ibar=0; ibar<p_shower->getBars().size(); ibar++){
+        std::vector<int> barID(2);
+        barID[0] = p_shower->getBars()[ibar]->getModule();
+        barID[1] = p_shower->getBars()[ibar]->getStave();
+        map_barCol[barID].push_back(p_shower->getBars()[ibar]);
+      }
+    }
+  }
+  for(int il=0; il<m_halfClusV.size(); il++){
+    for(int is=0; is<m_halfClusV[il]->getCluster().size(); is++){
+      const PandoraPlus::Calo1DCluster* p_shower = m_halfClusV[il]->getCluster()[is];
+      for(int ibar=0; ibar<p_shower->getBars().size(); ibar++){
+        std::vector<int> barID(2);
+        barID[0] = p_shower->getBars()[ibar]->getModule();
+        barID[1] = p_shower->getBars()[ibar]->getStave();
+        map_barCol[barID].push_back(p_shower->getBars()[ibar]);
+      }
+    }
+  }
+
+//cout<<"  tower size: "<<map_barCol.size()<<endl;
+//cout<<"  Print tower info "<<endl;
+//for(auto& iter: map_barCol){
+//printf("    TowerID [%d, %d, %d], bar size %d \n", iter.first[0], iter.first[1], iter.first[2], iter.second.size());
+//}
+
+  //Re-build the objects in tower
+  for(auto& itower: map_barCol){
+    std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_newHFClusUCol;
+    std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_newHFClusVCol;
+    std::vector<std::shared_ptr<PandoraPlus::Calo2DCluster>> m_new2DClusCol;
+
+    //Get map for MCP-bars
+    std::map<edm4hep::MCParticle, std::vector<const PandoraPlus::CaloUnit*> > map_matchBar; 
+    for(int ibar=0; ibar<itower.second.size(); ibar++){
+      edm4hep::MCParticle mcp = itower.second[ibar]->getLeadingMCP();
+      map_matchBar[mcp].push_back(itower.second[ibar]);
+    }
+//printf("    TowerID [%d, %d, %d], MCP size %d, bar size %d \n", itower.first[0], itower.first[1], itower.first[2], map_matchBar.size(), itower.second.size());
+
+    //Build objects for MCP
+    for(auto& iter: map_matchBar){
+//cout<<"    Print bar info"<<endl;
+//for(int ibar=0; ibar<iter.second.size(); ibar++){
+//printf("    cellID (%d, %d, %d, %d, %d, %d), En %.3f \n", iter.second[ibar]->getModule(), iter.second[ibar]->getPart(), iter.second[ibar]->getStave(), iter.second[ibar]->getDlayer(), iter.second[ibar]->getSlayer(), iter.second[ibar]->getBar(), iter.second[ibar]->getEnergy());
+//}
+      std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> m_new1DClusUCol;
+      std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> m_new1DClusVCol;
+
+      //Ordered bars
+      std::map<int, std::vector<const PandoraPlus::CaloUnit*> > m_orderedBars; m_orderedBars.clear();
+      for(int ibar=0; ibar<iter.second.size(); ibar++)
+        m_orderedBars[iter.second[ibar]->getDlayer()].push_back(iter.second[ibar]);
+
+//cout<<"    Layer size "<<m_orderedBars.size()<<endl;
+
+      //1D&2D cluster
+      for(auto& iter_bar: m_orderedBars){
+//cout<<"    In layer #"<<iter_bar.first<<": bar size "<<iter_bar.second.size()<<endl;
+
+        std::shared_ptr<PandoraPlus::Calo1DCluster> tmp_new1dclusterU = std::make_shared<PandoraPlus::Calo1DCluster>();
+        std::shared_ptr<PandoraPlus::Calo1DCluster> tmp_new1dclusterV = std::make_shared<PandoraPlus::Calo1DCluster>();
+        for(int ibar=0; ibar<iter_bar.second.size(); ibar++){
+          if(iter_bar.second[ibar]->getSlayer()==0) tmp_new1dclusterU->addUnit( iter_bar.second[ibar] );
+          if(iter_bar.second[ibar]->getSlayer()==1) tmp_new1dclusterV->addUnit( iter_bar.second[ibar] );
+        }
+        if(tmp_new1dclusterU && tmp_new1dclusterU->getBars().size()>0){
+          tmp_new1dclusterU->getLinkedMCPfromUnit();
+          tmp_new1dclusterU->setSeed();
+          tmp_new1dclusterU->setIDInfo();
+          m_new1DClusUCol.push_back(tmp_new1dclusterU);
+          m_bkCol.map_1DCluster["bk1DCluster"].push_back(tmp_new1dclusterU);
+        }
+        if(tmp_new1dclusterV && tmp_new1dclusterV->getBars().size()>0){
+          tmp_new1dclusterV->getLinkedMCPfromUnit();
+          tmp_new1dclusterV->setSeed();
+          tmp_new1dclusterV->setIDInfo();
+          m_new1DClusVCol.push_back(tmp_new1dclusterV);
+          m_bkCol.map_1DCluster["bk1DCluster"].push_back(tmp_new1dclusterV);
+        }
+        if(tmp_new1dclusterU && tmp_new1dclusterV && tmp_new1dclusterU->getBars().size()>0 && tmp_new1dclusterV->getBars().size()>0){
+          std::shared_ptr<PandoraPlus::Calo2DCluster> tmp_block = std::make_shared<PandoraPlus::Calo2DCluster>();
+          for(int ibar=0; ibar<iter_bar.second.size(); ibar++) tmp_block->addBar(iter_bar.second[ibar]);
+          tmp_block->addUnit(tmp_new1dclusterU.get());
+          tmp_block->addUnit(tmp_new1dclusterV.get());
+          tmp_block->setTowerID( itower.first );
+          m_new2DClusCol.push_back(tmp_block);
+          m_bkCol.map_2DCluster["bk2DCluster"].push_back(tmp_block);
+        }
+      }
+//cout<<"    1DClusU size "<<m_new1DClusUCol.size()<<", 1DClusV size "<<m_new1DClusVCol.size();
+//cout<<", 2DClus size "<<m_new2DClusCol.size()<<endl;
+
+      //Half cluster
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> m_newHFClusterU = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> m_newHFClusterV = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      for(int i1d=0; i1d<m_new1DClusUCol.size(); i1d++)
+        m_newHFClusterU->addUnit(m_new1DClusUCol[i1d].get());
+      for(int i1d=0; i1d<m_new1DClusVCol.size(); i1d++)
+        m_newHFClusterV->addUnit(m_new1DClusVCol[i1d].get());
+
+      m_newHFClusterU->getLinkedMCPfromUnit();
+      m_newHFClusterV->getLinkedMCPfromUnit();
+      m_newHFClusUCol.push_back(m_newHFClusterU);
+      m_newHFClusVCol.push_back(m_newHFClusterV);
+      m_bkCol.map_HalfCluster["bkHalfCluster"].push_back(m_newHFClusterU);
+      m_bkCol.map_HalfCluster["bkHalfCluster"].push_back(m_newHFClusterV);
+    }
+//printf("    In tower [%d, %d, %d] HFClus size (%d, %d) \n", itower.first[0], itower.first[1], itower.first[2], m_newHFClusUCol.size(), m_newHFClusVCol.size());
+    //Form a tower
+    std::vector<const PandoraPlus::CaloHalfCluster*> const_HFClusU; const_HFClusU.clear();
+    std::vector<const PandoraPlus::CaloHalfCluster*> const_HFClusV; const_HFClusV.clear();
+    for(int ics=0; ics<m_newHFClusUCol.size(); ics++){ const_HFClusU.push_back(m_newHFClusUCol[ics].get()); }
+    for(int ics=0; ics<m_newHFClusVCol.size(); ics++){ const_HFClusV.push_back(m_newHFClusVCol[ics].get()); }
+    
+    std::shared_ptr<PandoraPlus::Calo3DCluster> m_tower = std::make_shared<PandoraPlus::Calo3DCluster>();
+    m_tower->addTowerID(itower.first);
+    for(int i2d=0; i2d<m_new2DClusCol.size(); i2d++) m_tower->addUnit(m_new2DClusCol[i2d].get());
+    m_tower->setHalfClusters( settings.map_stringPars["OutputECALHalfClusters"]+"U", const_HFClusU,
+                              settings.map_stringPars["OutputECALHalfClusters"]+"V", const_HFClusV );
+    m_towers.push_back(m_tower);
+  }
+
+/*
+cout<<"  After splitting: tower size "<<m_towers.size()<<". Print Tower: "<<endl;
+for(auto it : m_towers){
+  std::vector<const CaloHalfCluster*> m_HFClusUInTower = it->getHalfClusterUCol(settings.map_stringPars["OutputECALHalfClusters"]+"U");
+  std::vector<const CaloHalfCluster*> m_HFClusVInTower = it->getHalfClusterVCol(settings.map_stringPars["OutputECALHalfClusters"]+"V");
+
+cout<<"Check tower ID: ";
+for(int i=0; i<it->getTowerID().size(); i++) printf("[%d, %d, %d], ", it->getTowerID()[i][0], it->getTowerID()[i][1], it->getTowerID()[i][2]);
+cout<<endl;
+  printf("    In Tower [%d, %d, %d], ", it->getTowerID()[0][0], it->getTowerID()[0][1], it->getTowerID()[0][2] );
+  printf("    HalfCluster size: (%d, %d) \n", m_HFClusUInTower.size(), m_HFClusVInTower.size() );
+  cout<<"    Loop print HalfClusterU: "<<endl;
+  for(int icl=0; icl<m_HFClusUInTower.size(); icl++){
+    cout<<"      In HFClusU #"<<icl<<": shower size = "<<m_HFClusUInTower[icl]->getCluster().size()<<", En = "<<m_HFClusUInTower[icl]->getEnergy();
+    printf(", Position (%.3f, %.3f, %.3f), address %p ",m_HFClusUInTower[icl]->getPos().x(), m_HFClusUInTower[icl]->getPos().y(), m_HFClusUInTower[icl]->getPos().z(), m_HFClusUInTower[icl]);
+    printf(", cousin size %d, address: ", m_HFClusUInTower[icl]->getHalfClusterCol("CousinCluster").size());
+    for(int ics=0; ics<m_HFClusUInTower[icl]->getHalfClusterCol("CousinCluster").size(); ics++) printf("%p, ", m_HFClusUInTower[icl]->getHalfClusterCol("CousinCluster")[ics]);
+    printf(", track size %d, address: ", m_HFClusUInTower[icl]->getAssociatedTracks().size());
+    for(int itrk=0; itrk<m_HFClusUInTower[icl]->getAssociatedTracks().size(); itrk++) printf("%p, ", m_HFClusUInTower[icl]->getAssociatedTracks()[itrk]);
+    printf(", MCP link size %d, pid, Pz and weight: ", m_HFClusUInTower[icl]->getLinkedMCP().size());
+    for(int imc=0; imc<m_HFClusUInTower[icl]->getLinkedMCP().size(); imc++) printf("(%d, %.3f, %.3f), ", m_HFClusUInTower[icl]->getLinkedMCP()[imc].first.getPDG(), m_HFClusUInTower[icl]->getLinkedMCP()[imc].first.getMomentum().z, m_HFClusUInTower[icl]->getLinkedMCP()[imc].second);
+    cout<<endl;
+    for(auto ish : m_HFClusUInTower[icl]->getCluster()){
+      printf("          Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Nbars %d, NSeed %d, Address %p \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish->getBars().size(),  ish->getNseeds(), ish );
+    }
+  }
+  cout<<endl;
+
+  cout<<"    Loop print HalfClusterV: "<<endl;
+  for(int icl=0; icl<m_HFClusVInTower.size(); icl++){
+    cout<<"      In HFClusV #"<<icl<<": shower size = "<<m_HFClusVInTower[icl]->getCluster().size()<<", En = "<<m_HFClusVInTower[icl]->getEnergy();
+    printf(", Position (%.3f, %.3f, %.3f), address %p ",m_HFClusVInTower[icl]->getPos().x(), m_HFClusVInTower[icl]->getPos().y(), m_HFClusVInTower[icl]->getPos().z(), m_HFClusVInTower[icl]);
+    printf(", cousin size %d, address: ", m_HFClusVInTower[icl]->getHalfClusterCol("CousinCluster").size());
+    for(int ics=0; ics<m_HFClusVInTower[icl]->getHalfClusterCol("CousinCluster").size(); ics++) printf("%p, ", m_HFClusVInTower[icl]->getHalfClusterCol("CousinCluster")[ics]);
+    printf(", track size %d, address: ", m_HFClusVInTower[icl]->getAssociatedTracks().size());
+    for(int itrk=0; itrk<m_HFClusVInTower[icl]->getAssociatedTracks().size(); itrk++) printf("%p, ", m_HFClusVInTower[icl]->getAssociatedTracks()[itrk]);
+    printf(", MCP link size %d, pid, Pz and weight: ", m_HFClusVInTower[icl]->getLinkedMCP().size());
+    for(int imc=0; imc<m_HFClusVInTower[icl]->getLinkedMCP().size(); imc++) printf("(%d, %.3f, %.3f), ", m_HFClusVInTower[icl]->getLinkedMCP()[imc].first.getPDG(), m_HFClusVInTower[icl]->getLinkedMCP()[imc].first.getMomentum().z, m_HFClusVInTower[icl]->getLinkedMCP()[imc].second);
+    cout<<endl;
+    for(auto ish : m_HFClusVInTower[icl]->getCluster()){
+      printf("          Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), Nbars %d, NSeed %d, Address %p \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish->getBars().size(), ish->getNseeds(), ish );
+    }
+  }
+  cout<<endl;
+}
+*/
+
+
+  return StatusCode::SUCCESS;
+}
+
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/TruthEnergySplittingAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/TruthEnergySplittingAlg.cpp
new file mode 100644
index 00000000..c2f10d5f
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/TruthEnergySplittingAlg.cpp
@@ -0,0 +1,625 @@
+#ifndef _TRUTHENERGYSPLITTING_ALG_C
+#define _TRUTHENERGYSPLITTING_ALG_C
+
+#include "Algorithm/TruthEnergySplittingAlg.h"
+
+StatusCode TruthEnergySplittingAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  //Initialize parameters
+  if(settings.map_stringPars.find("ReadinAxisName")==settings.map_stringPars.end())  settings.map_stringPars["ReadinAxisName"] = "TruthMergedAxis";
+  if(settings.map_stringPars.find("OutputClusName")==settings.map_stringPars.end())  settings.map_stringPars["OutputClusName"] = "TruthESCluster";
+  if(settings.map_stringPars.find("OutputTowerName")==settings.map_stringPars.end()) settings.map_stringPars["OutputTowerName"] = "TruthESTower";
+
+  if(settings.map_floatPars.find("Eth_HFClus")==settings.map_floatPars.end())        settings.map_floatPars["Eth_HFClus"] = 0.05;
+  if(settings.map_intPars.find("th_Nhit")==settings.map_intPars.end())               settings.map_intPars["th_Nhit"] = 2;
+  if(settings.map_boolPars.find("CompactHFCluster")==settings.map_boolPars.end())    settings.map_boolPars["CompactHFCluster"] = 1;
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthEnergySplittingAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+
+  p_HalfClusterU.clear();
+  p_HalfClusterV.clear();
+  m_newClusUCol.clear();
+  m_newClusVCol.clear();
+  m_bkCol.Clear();
+
+  for(int ih=0; ih<m_datacol.map_HalfCluster["HalfClusterColU"].size(); ih++)
+    p_HalfClusterU.push_back( m_datacol.map_HalfCluster["HalfClusterColU"][ih].get() );
+  for(int ih=0; ih<m_datacol.map_HalfCluster["HalfClusterColV"].size(); ih++)
+    p_HalfClusterV.push_back( m_datacol.map_HalfCluster["HalfClusterColV"][ih].get() );
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthEnergySplittingAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+//cout<<"  TruthEnergySplittingAlg: readin HFCluster size "<<p_HalfClusterU.size()<<", "<<p_HalfClusterV.size()<<endl;
+
+  for(int ih=0; ih<p_HalfClusterU.size(); ih++){
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_axisUCol;
+    if( settings.map_stringPars["ReadinAxisName"] == "AllAxis" ) m_axisUCol = p_HalfClusterU[ih]->getAllHalfClusterCol();
+    else m_axisUCol = p_HalfClusterU[ih]->getHalfClusterCol(settings.map_stringPars["ReadinAxisName"]);    
+
+    std::vector<const PandoraPlus::Calo1DCluster*> m_1dclusCol = p_HalfClusterU[ih]->getCluster();
+//cout<<"    In HFU #"<<ih<<": axis size "<<m_axisUCol.size()<<", 1DCluster size "<<m_1dclusCol.size()<<endl;
+    
+    for(int iax=0; iax<m_axisUCol.size(); iax++){
+      edm4hep::MCParticle truthMCP_axis = m_axisUCol[iax]->getLeadingMCP();
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> m_newClus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+//cout<<"      Axis #"<<iax<<": track size "<<m_axisUCol[iax]->getAssociatedTracks().size()<<", truth MC pid "<<truthMCP_axis.getPDG()<<endl;
+
+      for(int ish=0; ish<m_1dclusCol.size(); ish++){
+        std::shared_ptr<PandoraPlus::Calo1DCluster> m_shower = std::make_shared<PandoraPlus::Calo1DCluster>(); 
+//cout<<"        1D cluster #"<<ish<<": bar size "<<m_1dclusCol[ish]->getBars().size();
+        for(int ibar=0; ibar<m_1dclusCol[ish]->getBars().size(); ibar++){
+          auto truthMap = m_1dclusCol[ish]->getBars()[ibar]->getLinkedMCP();
+          for(auto& iter: truthMap){
+            if(!(iter.first==truthMCP_axis)) continue;
+            if(iter.second<0.05) continue;
+
+            //This bar has contribution from MCP. Split it. 
+            auto m_bar = m_1dclusCol[ish]->getBars()[ibar]->Clone();
+            m_bar->setQ(m_bar->getQ1()*iter.second, m_bar->getQ2()*iter.second);
+            m_bar->setLinkedMCP( std::vector<std::pair<edm4hep::MCParticle, float>>{std::make_pair(iter.first, 1)} );
+
+            m_shower->addUnit(m_bar.get());
+            m_datacol.map_BarCol["bkBars"].push_back(m_bar);
+          }
+        }
+
+        if(m_shower && m_shower->getBars().size()>0){
+//cout<<". Make a new shower. "<<endl;
+          m_shower->getLinkedMCPfromUnit();
+          m_shower->setSeed();
+          m_shower->setIDInfo();
+          m_newClus->addUnit(m_shower.get());
+          m_datacol.map_1DCluster["bk1DCluster"].push_back(m_shower);
+        }
+      }//End loop 1DClusters in HFCluster
+
+      if(m_newClus && m_newClus->getCluster().size()!=0){ 
+        for(int itrk=0; itrk<m_axisUCol[iax]->getAssociatedTracks().size(); itrk++) m_newClus->addAssociatedTrack(m_axisUCol[iax]->getAssociatedTracks()[itrk]);
+        m_newClus->getLinkedMCPfromUnit();
+        m_newClus->fitAxis("");
+        m_newClus->mergeClusterInLayer();
+        m_newClusUCol.push_back(m_newClus);
+        m_datacol.map_HalfCluster["bkHalfCluster"].push_back(m_newClus);
+      }
+    }//End loop axis
+  }//End loop HalfClusters.
+//cout<<"  Splitted HFClusterU size "<<m_newClusUCol.size()<<endl;
+
+  //Merge new HFClusters linked to the same MCP
+  for(int iax=0; iax<m_newClusUCol.size(); iax++){
+    for(int jax=iax+1; jax<m_newClusUCol.size(); jax++){
+      if(m_newClusUCol[iax]->getLeadingMCP() == m_newClusUCol[jax]->getLeadingMCP()){
+        m_newClusUCol[iax]->mergeHalfCluster( m_newClusUCol[jax].get() );
+        m_newClusUCol.erase(m_newClusUCol.begin()+jax);
+        jax--;
+        if(iax>jax+1) iax--;
+      }
+    }
+  }
+//cout<<"  Splitted HFClusterU size after merging "<<m_newClusUCol.size()<<endl;
+
+  for(int ih=0; ih<p_HalfClusterV.size(); ih++){
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_axisVCol;
+    if( settings.map_stringPars["ReadinAxisName"] == "AllAxis" ) m_axisVCol = p_HalfClusterV[ih]->getAllHalfClusterCol();
+    else m_axisVCol = p_HalfClusterV[ih]->getHalfClusterCol(settings.map_stringPars["ReadinAxisName"]);
+
+    std::vector<const PandoraPlus::Calo1DCluster*> m_1dclusCol = p_HalfClusterV[ih]->getCluster();
+//cout<<"    In HFV #"<<ih<<": axis size "<<m_axisVCol.size()<<", 1DCluster size "<<m_1dclusCol.size()<<endl;
+
+    for(int iax=0; iax<m_axisVCol.size(); iax++){
+      edm4hep::MCParticle truthMCP_axis = m_axisVCol[iax]->getLeadingMCP();
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> m_newClus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+//cout<<"      Axis #"<<iax<<": track size "<<m_axisVCol[iax]->getAssociatedTracks().size()<<", truth MC pid "<<truthMCP_axis.getPDG()<<endl;
+
+      for(int ish=0; ish<m_1dclusCol.size(); ish++){
+        std::shared_ptr<PandoraPlus::Calo1DCluster> m_shower = std::make_shared<PandoraPlus::Calo1DCluster>();
+        for(int ibar=0; ibar<m_1dclusCol[ish]->getBars().size(); ibar++){
+          auto truthMap = m_1dclusCol[ish]->getBars()[ibar]->getLinkedMCP();
+          for(auto& iter: truthMap){
+            if(!(iter.first==truthMCP_axis)) continue;
+            if(iter.second<0.05) continue;
+
+            //This bar has contribution from MCP. Split it.
+            auto m_bar = m_1dclusCol[ish]->getBars()[ibar]->Clone();
+            m_bar->setQ(m_bar->getQ1()*iter.second, m_bar->getQ2()*iter.second);
+            m_bar->setLinkedMCP( std::vector<std::pair<edm4hep::MCParticle, float>>{std::make_pair(iter.first, 1)} );
+
+            m_shower->addUnit(m_bar.get());
+            m_datacol.map_BarCol["bkBars"].push_back(m_bar);
+          }
+        }
+        if(m_shower && m_shower->getBars().size()>0){
+          m_shower->getLinkedMCPfromUnit();
+          m_shower->setSeed();
+          m_shower->setIDInfo();
+          m_newClus->addUnit(m_shower.get());
+          m_datacol.map_1DCluster["bk1DCluster"].push_back(m_shower);
+        }
+      }//End loop 1DClusters in HFCluster
+
+      if(m_newClus && m_newClus->getCluster().size()!=0){
+        for(int itrk=0; itrk<m_axisVCol[iax]->getAssociatedTracks().size(); itrk++) m_newClus->addAssociatedTrack(m_axisVCol[iax]->getAssociatedTracks()[itrk]);
+        m_newClus->getLinkedMCPfromUnit();
+        m_newClus->fitAxis("");
+        m_newClus->mergeClusterInLayer();
+        m_newClusVCol.push_back(m_newClus);
+        m_datacol.map_HalfCluster["bkHalfCluster"].push_back(m_newClus);
+      }
+    }//End loop axis
+  }//End loop HalfClusters.
+//cout<<"  Splitted HFClusterV size "<<m_newClusVCol.size()<<endl;
+
+  //Merge new HFClusters linked to the same MCP
+  for(int iax=0; iax<m_newClusVCol.size(); iax++){
+    for(int jax=iax+1; jax<m_newClusVCol.size(); jax++){
+      if(m_newClusVCol[iax]->getLeadingMCP() == m_newClusVCol[jax]->getLeadingMCP()){
+        m_newClusVCol[iax]->mergeHalfCluster( m_newClusVCol[jax].get() );
+        m_newClusVCol.erase(m_newClusVCol.begin()+jax);
+        jax--;
+        if(iax>jax+1) iax--;
+      }
+    }
+  }
+//cout<<"  Splitted HFClusterU size after merging "<<m_newClusVCol.size()<<endl;
+
+  m_datacol.map_HalfCluster[settings.map_stringPars["OutputClusName"]+"U"] = m_newClusUCol;
+  m_datacol.map_HalfCluster[settings.map_stringPars["OutputClusName"]+"V"] = m_newClusVCol;  
+
+  //Make tower
+  m_towerCol.clear();
+  HalfClusterToTowers(m_newClusUCol, m_newClusVCol, m_towerCol);
+  m_datacol.map_CaloCluster[settings.map_stringPars["OutputTowerName"]] = m_towerCol;
+
+/*
+cout<<"  After splitting: tower size "<<m_towerCol.size()<<". Print Tower: "<<endl;
+for(auto it : m_towerCol){
+  std::vector<const CaloHalfCluster*> m_HFClusUInTower = it->getHalfClusterUCol(settings.map_stringPars["OutputClusName"]+"U");
+  std::vector<const CaloHalfCluster*> m_HFClusVInTower = it->getHalfClusterVCol(settings.map_stringPars["OutputClusName"]+"V");
+  //std::vector<const CaloHalfCluster*> m_HFClusUInTower, m_HFClusVInTower; 
+  //for(int i=0; i<m_newClusUCol.size(); i++) m_HFClusUInTower.push_back(m_newClusUCol[i].get());
+  //for(int i=0; i<m_newClusVCol.size(); i++) m_HFClusVInTower.push_back(m_newClusVCol[i].get());
+
+
+cout<<"Check tower ID: ";
+for(int i=0; i<it->getTowerID().size(); i++) printf("[%d, %d, %d], ", it->getTowerID()[i][0], it->getTowerID()[i][1], it->getTowerID()[i][2]);
+cout<<endl;
+  printf("    In Tower [%d, %d, %d], ", it->getTowerID()[0][0], it->getTowerID()[0][1], it->getTowerID()[0][2] );
+  printf("    HalfCluster size: (%d, %d) \n", m_HFClusUInTower.size(), m_HFClusVInTower.size() );
+  cout<<"    Loop print HalfClusterU: "<<endl;
+  for(int icl=0; icl<m_HFClusUInTower.size(); icl++){
+    cout<<"      In HFClusU #"<<icl<<": shower size = "<<m_HFClusUInTower[icl]->getCluster().size()<<", En = "<<m_HFClusUInTower[icl]->getEnergy()<<", linked MC size "<<m_HFClusUInTower[icl]->getLinkedMCP().size();
+    for(int imc=0; imc<m_HFClusUInTower[icl]->getLinkedMCP().size(); imc++) printf("[%d, %.3f], ", m_HFClusUInTower[icl]->getLinkedMCP()[imc].first.getPDG(), m_HFClusUInTower[icl]->getLinkedMCP()[imc].second);
+    printf(", Position (%.3f, %.3f, %.3f), address %p ",m_HFClusUInTower[icl]->getPos().x(), m_HFClusUInTower[icl]->getPos().y(), m_HFClusUInTower[icl]->getPos().z(), m_HFClusUInTower[icl]);
+    printf(", cousin size %d, address: ", m_HFClusUInTower[icl]->getHalfClusterCol("CousinCluster").size());
+    for(int ics=0; ics<m_HFClusUInTower[icl]->getHalfClusterCol("CousinCluster").size(); ics++) printf("%p, ", m_HFClusUInTower[icl]->getHalfClusterCol("CousinCluster")[ics]);
+    printf(", track size %d, address: ", m_HFClusUInTower[icl]->getAssociatedTracks().size());
+    for(int itrk=0; itrk<m_HFClusUInTower[icl]->getAssociatedTracks().size(); itrk++) printf("%p, ", m_HFClusUInTower[icl]->getAssociatedTracks()[itrk]);
+    cout<<endl;
+    for(auto ish : m_HFClusUInTower[icl]->getCluster()){
+      printf("          Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), leading MCP [%d, %.3f], Nbars %d, NSeed %d, Address %p \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish->getLeadingMCP().getPDG(), ish->getLeadingMCPweight(),  ish->getBars().size(),  ish->getNseeds(), ish );
+    }
+  }
+  cout<<endl;
+
+  cout<<"    Loop print HalfClusterV: "<<endl;
+  for(int icl=0; icl<m_HFClusVInTower.size(); icl++){
+    cout<<"      In HFClusV #"<<icl<<": shower size = "<<m_HFClusVInTower[icl]->getCluster().size()<<", En = "<<m_HFClusVInTower[icl]->getEnergy()<<", linked MC size "<<m_HFClusVInTower[icl]->getLinkedMCP().size();
+    for(int imc=0; imc<m_HFClusVInTower[icl]->getLinkedMCP().size(); imc++) printf("[%d, %.3f], ", m_HFClusVInTower[icl]->getLinkedMCP()[imc].first.getPDG(), m_HFClusVInTower[icl]->getLinkedMCP()[imc].second);
+    printf(", Position (%.3f, %.3f, %.3f), address %p ",m_HFClusVInTower[icl]->getPos().x(), m_HFClusVInTower[icl]->getPos().y(), m_HFClusVInTower[icl]->getPos().z(), m_HFClusVInTower[icl]);
+    printf(", cousin size %d, address: ", m_HFClusVInTower[icl]->getHalfClusterCol("CousinCluster").size());
+    for(int ics=0; ics<m_HFClusVInTower[icl]->getHalfClusterCol("CousinCluster").size(); ics++) printf("%p, ", m_HFClusVInTower[icl]->getHalfClusterCol("CousinCluster")[ics]);
+    printf(", track size %d, address: ", m_HFClusVInTower[icl]->getAssociatedTracks().size());
+    for(int itrk=0; itrk<m_HFClusVInTower[icl]->getAssociatedTracks().size(); itrk++) printf("%p, ", m_HFClusVInTower[icl]->getAssociatedTracks()[itrk]);
+    cout<<endl;
+    for(auto ish : m_HFClusVInTower[icl]->getCluster()){
+      printf("          Shower layer %d, Pos+E (%.3f, %.3f, %.3f, %.3f), leading MCP [%d, %.3f], Nbars %d, NSeed %d, Address %p \n", ish->getDlayer(), ish->getPos().x(), ish->getPos().y(), ish->getPos().z(), ish->getEnergy(), ish->getLeadingMCP().getPDG(), ish->getLeadingMCPweight(), ish->getBars().size(), ish->getNseeds(), ish );
+    }
+  }
+  cout<<endl;
+}
+*/
+
+  m_datacol.map_1DCluster["bk1DCluster"].insert( m_datacol.map_1DCluster["bk1DCluster"].end(), m_bkCol.map_1DCluster["bk1DCluster"].begin(), m_bkCol.map_1DCluster["bk1DCluster"].end() );
+  m_datacol.map_2DCluster["bk2DCluster"].insert( m_datacol.map_2DCluster["bk2DCluster"].end(), m_bkCol.map_2DCluster["bk2DCluster"].begin(), m_bkCol.map_2DCluster["bk2DCluster"].end() );
+  m_datacol.map_HalfCluster["bkHalfCluster"].insert( m_datacol.map_HalfCluster["bkHalfCluster"].end(), m_bkCol.map_HalfCluster["bkHalfCluster"].begin(), m_bkCol.map_HalfCluster["bkHalfCluster"].end() );
+
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthEnergySplittingAlg::ClearAlgorithm(){
+  p_HalfClusterU.clear();
+  p_HalfClusterV.clear();
+  m_newClusUCol.clear();
+  m_newClusVCol.clear();
+
+  m_bkCol.Clear();
+  return StatusCode::SUCCESS;
+};
+
+
+
+StatusCode TruthEnergySplittingAlg::HalfClusterToTowers( std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_halfClusU,
+                                                         std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_halfClusV,
+                                                         std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_towers )
+{
+
+  m_towers.clear();
+
+  std::map<std::vector<int>, std::vector<const PandoraPlus::Calo2DCluster*> > map_2DCluster;
+  std::map<std::vector<int>, std::vector<PandoraPlus::CaloHalfCluster*> > map_HalfClusterU;
+  std::map<std::vector<int>, std::vector<PandoraPlus::CaloHalfCluster*> > map_HalfClusterV;
+
+  //Split CaloHalfClusterU
+  for(int il=0; il<m_halfClusU.size(); il++){
+    if(m_halfClusU[il]->getCluster().size()==0) {std::cout<<"WARNING: Have an empty CaloHalfCluster! Skip it! "<<std::endl; continue;}
+
+    //HalfCluster does not cover tower:
+    if( m_halfClusU[il]->getTowerID().size()==1 && m_halfClusU[il]->getCluster().size()>=settings.map_intPars["th_Nhit"]){
+      std::vector<int> cl_towerID =  m_halfClusU[il]->getTowerID()[0];
+      if(settings.map_boolPars["CompactHFCluster"]) m_halfClusU[il]->mergeClusterInLayer();
+      map_HalfClusterU[cl_towerID].push_back(m_halfClusU[il].get());
+      continue;
+    }
+
+    //CaloHalfCluster covers towers: Loop check showers.
+    std::map<std::vector<int>, PandoraPlus::CaloHalfCluster* > tmp_LongiClusMaps; tmp_LongiClusMaps.clear();
+    for(int is=0; is<m_halfClusU[il]->getCluster().size(); is++){
+      const PandoraPlus::Calo1DCluster* p_shower = m_halfClusU[il]->getCluster()[is];
+
+      if(p_shower->getSeeds().size()==0){
+        std::cout<<"  HalfClusterToTowers ERROR: No Seed in 1DShower, Check! "<<std::endl;
+        continue;
+      }
+      std::vector<int> seedID(2);
+      seedID[0] = p_shower->getSeeds()[0]->getModule();
+      seedID[1] = p_shower->getSeeds()[0]->getStave();
+
+      if( tmp_LongiClusMaps.find( seedID )!=tmp_LongiClusMaps.end() ){
+        tmp_LongiClusMaps[seedID]->addUnit( p_shower );
+        tmp_LongiClusMaps[seedID]->setTowerID( seedID );
+      }
+      else{
+        std::shared_ptr<PandoraPlus::CaloHalfCluster> tmp_clus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+        tmp_clus->addUnit( p_shower );
+        tmp_clus->setTowerID( seedID );
+        tmp_LongiClusMaps[seedID] = tmp_clus.get();
+        m_bkCol.map_HalfCluster["bkHalfCluster"].push_back( tmp_clus );
+      }
+      p_shower = nullptr;
+
+    }
+
+    //Connect cousins
+    if(tmp_LongiClusMaps.size()>1){
+      for(auto &iter : tmp_LongiClusMaps){
+        for(auto &iter1 : tmp_LongiClusMaps){
+          if(iter!= iter1 &&
+             iter.second->getEnergy()>settings.map_floatPars["Eth_HFClus"] &&
+             iter1.second->getEnergy()>settings.map_floatPars["Eth_HFClus"] &&
+             iter.second->getCluster().size()>=settings.map_intPars["th_Nhit"] &&
+             iter1.second->getCluster().size()>=settings.map_intPars["th_Nhit"] ){ iter.second->addCousinCluster(iter1.second); }
+        }
+      }
+    }
+    for(auto &iter : tmp_LongiClusMaps){
+      if(iter.second->getEnergy()<settings.map_floatPars["Eth_HFClus"]) continue;
+      iter.second->addHalfCluster("ParentCluster", m_halfClusU[il].get());
+      for(int itrk=0; itrk<m_halfClusU[il]->getAssociatedTracks().size(); itrk++)
+        iter.second->addAssociatedTrack( m_halfClusU[il]->getAssociatedTracks()[itrk] );
+      if(iter.second->getCluster().size()>=settings.map_intPars["th_Nhit"]){
+        if(settings.map_boolPars["CompactHFCluster"]){
+          iter.second->mergeClusterInLayer();
+          iter.second->setTowerID(iter.first);
+        }
+        map_HalfClusterU[iter.first].push_back(iter.second);
+      }
+    }
+
+  }
+
+  //Split CaloHalfClusterV
+  for(int il=0; il<m_halfClusV.size(); il++){
+    if(m_halfClusV[il]->getCluster().size()==0) {std::cout<<"WARNING: Have an empty CaloHalfCluster! Skip it! "<<std::endl; continue;}
+
+    //HalfCluster does not cover tower:
+    if( m_halfClusV[il]->getTowerID().size()==1 && m_halfClusV[il]->getCluster().size()>=settings.map_intPars["th_Nhit"]){
+      std::vector<int> cl_towerID =  m_halfClusV[il]->getTowerID()[0];
+      if(settings.map_boolPars["CompactHFCluster"]) m_halfClusV[il]->mergeClusterInLayer();
+      map_HalfClusterV[cl_towerID].push_back(m_halfClusV[il].get());
+      continue;
+    }
+
+    //CaloHalfCluster covers towers: Loop check showers.
+    std::map<std::vector<int>, PandoraPlus::CaloHalfCluster* > tmp_LongiClusMaps; tmp_LongiClusMaps.clear();
+    for(int is=0; is<m_halfClusV[il]->getCluster().size(); is++){
+      const PandoraPlus::Calo1DCluster* p_shower = m_halfClusV[il]->getCluster()[is];
+      if(p_shower->getSeeds().size()==0){
+        std::cout<<"  HalfClusterToTowers ERROR: No Seed in 1DShower, Check! "<<std::endl;
+        continue;
+      }
+      std::vector<int> seedID(2);
+      seedID[0] = p_shower->getSeeds()[0]->getModule();
+      seedID[1] = p_shower->getSeeds()[0]->getStave();
+
+      if( tmp_LongiClusMaps.find( seedID )!=tmp_LongiClusMaps.end() ){
+        tmp_LongiClusMaps[seedID]->addUnit( p_shower );
+        tmp_LongiClusMaps[seedID]->setTowerID( seedID );
+      }
+      else{
+        std::shared_ptr<PandoraPlus::CaloHalfCluster> tmp_clus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+        tmp_clus->addUnit( p_shower );
+        tmp_clus->setTowerID( seedID );
+        tmp_LongiClusMaps[seedID] = tmp_clus.get();
+        m_bkCol.map_HalfCluster["bkHalfCluster"].push_back( tmp_clus );
+      }
+      p_shower = nullptr;
+
+    }
+
+    //Connect cousins
+//cout<<"  LongiClusVMap size: "<<tmp_LongiClusMaps.size()<<endl;
+    if(tmp_LongiClusMaps.size()>1){
+      for(auto &iter : tmp_LongiClusMaps){
+        for(auto &iter1 : tmp_LongiClusMaps){
+          if(iter!= iter1 &&
+             iter.second->getEnergy()>settings.map_floatPars["Eth_HFClus"] &&
+             iter1.second->getEnergy()>settings.map_floatPars["Eth_HFClus"] &&
+             iter.second->getCluster().size()>=settings.map_intPars["th_Nhit"] &&
+             iter1.second->getCluster().size()>=settings.map_intPars["th_Nhit"] ){ iter.second->addCousinCluster(iter1.second); }
+        }
+      }
+    }
+    for(auto &iter : tmp_LongiClusMaps){
+      if(iter.second->getEnergy()<settings.map_floatPars["Eth_HFClus"]) continue;
+      iter.second->addHalfCluster("ParentCluster", m_halfClusV[il].get());
+      for(int itrk=0; itrk<m_halfClusV[il]->getAssociatedTracks().size(); itrk++)
+        iter.second->addAssociatedTrack( m_halfClusV[il]->getAssociatedTracks()[itrk] );
+      if(iter.second->getCluster().size()>=settings.map_intPars["th_Nhit"]){
+        if(settings.map_boolPars["CompactHFCluster"]){
+          iter.second->mergeClusterInLayer();
+          iter.second->setTowerID(iter.first);
+        }
+        map_HalfClusterV[iter.first].push_back(iter.second);
+      }
+    }
+
+  }
+
+  //Build 2DCluster
+  for(auto &iterU : map_HalfClusterU){
+    if( map_HalfClusterV.find(iterU.first)==map_HalfClusterV.end() ){
+      iterU.second.clear();
+      continue;
+    }
+
+    std::vector<PandoraPlus::CaloHalfCluster*> p_halfClusU = iterU.second;
+    std::vector<PandoraPlus::CaloHalfCluster*> p_halfClusV = map_HalfClusterV[iterU.first];
+
+    //Get ordered showers for looping in layers.
+    std::map<int, std::vector<const PandoraPlus::Calo1DCluster*>> m_orderedShowerU; m_orderedShowerU.clear();
+    std::map<int, std::vector<const PandoraPlus::Calo1DCluster*>> m_orderedShowerV; m_orderedShowerV.clear();
+
+    for(int ic=0; ic<p_halfClusU.size(); ic++){
+      for(int is=0; is<p_halfClusU.at(ic)->getCluster().size(); is++)
+        m_orderedShowerU[p_halfClusU.at(ic)->getCluster()[is]->getDlayer()].push_back( p_halfClusU.at(ic)->getCluster()[is] );
+    }
+    for(int ic=0; ic<p_halfClusV.size(); ic++){
+      for(int is=0; is<p_halfClusV.at(ic)->getCluster().size(); is++)
+        m_orderedShowerV[p_halfClusV.at(ic)->getCluster()[is]->getDlayer()].push_back( p_halfClusV.at(ic)->getCluster()[is] );
+    }
+    p_halfClusU.clear(); p_halfClusV.clear();
+
+
+    //Create super-layers (block)
+    std::vector<const PandoraPlus::Calo2DCluster*> m_blocks; m_blocks.clear();
+    for(auto &iter1 : m_orderedShowerU){
+      if( m_orderedShowerV.find( iter1.first )==m_orderedShowerV.end() ) continue;
+      std::shared_ptr<PandoraPlus::Calo2DCluster> tmp_block = std::make_shared<PandoraPlus::Calo2DCluster>();
+      for(int is=0; is<iter1.second.size(); is++) tmp_block->addUnit( iter1.second.at(is) );
+      for(int is=0; is<m_orderedShowerV[iter1.first].size(); is++) tmp_block->addUnit( m_orderedShowerV[iter1.first].at(is) );
+      tmp_block->setTowerID( iterU.first );
+      m_blocks.push_back( tmp_block.get() );
+      m_bkCol.map_2DCluster["bk2DCluster"].push_back( tmp_block );
+    }
+    map_2DCluster[iterU.first] = m_blocks;
+  }
+  for(auto &iterV : map_HalfClusterV){
+    if( map_HalfClusterU.find(iterV.first)==map_HalfClusterU.end() ){
+      iterV.second.clear();
+    }
+  }
+
+  //Form a tower:
+  for(auto &iter : map_2DCluster){
+    std::vector<int> m_towerID = iter.first;
+//printf("  In tower: [%d, %d, %d] \n", m_towerID[0], m_towerID[1], m_towerID[2]);
+    //Check cousin clusters:
+    std::vector<PandoraPlus::CaloHalfCluster*> m_HFClusUInTower = map_HalfClusterU[m_towerID];
+    for(auto &m_HFclus : m_HFClusUInTower){
+      std::vector<const CaloHalfCluster*> tmp_delClus; tmp_delClus.clear();
+//printf("    Check the cousin of HFClus %p: cousin size %d \n",m_HFclus, m_HFclus->getHalfClusterCol("CousinCluster").size());
+      for(int ics=0; ics<m_HFclus->getHalfClusterCol("CousinCluster").size(); ics++){
+        std::vector<int> tmp_towerID = m_HFclus->getHalfClusterCol("CousinCluster")[ics]->getTowerID()[0];
+//printf("      Cousin #%d: address %p, it's in tower [%d, %d, %d]. \n", ics,  m_HFclus->getHalfClusterCol("CousinCluster")[ics],  tmp_towerID[0], tmp_towerID[1], tmp_towerID[2]);
+//if(m_HFclus->getHalfClusterCol("CousinCluster")[ics]->getTowerID().size()!=1) cout<<"ERROR: cousin cluster covers >1 towers. Check here! "<<endl;
+
+        if( map_2DCluster.find( tmp_towerID )==map_2DCluster.end() )
+          tmp_delClus.push_back( m_HFclus->getHalfClusterCol("CousinCluster")[ics] );
+      }
+//cout<<"Need to delete "<<tmp_delClus.size()<<" cousins"<<endl;
+      for(int ics=0; ics<tmp_delClus.size(); ics++) m_HFclus->deleteCousinCluster( tmp_delClus[ics] );
+    }
+
+    std::vector<PandoraPlus::CaloHalfCluster*> m_HFClusVInTower = map_HalfClusterV[m_towerID];
+    for(auto &m_HFclus : m_HFClusVInTower){
+      std::vector<const CaloHalfCluster*> tmp_delClus; tmp_delClus.clear();
+//printf("    Check the cousin of HFClus %p: cousin size %d \n",m_HFclus, m_HFclus->getHalfClusterCol("CousinCluster").size());
+      for(int ics=0; ics<m_HFclus->getHalfClusterCol("CousinCluster").size(); ics++){
+        std::vector<int> tmp_towerID = m_HFclus->getHalfClusterCol("CousinCluster")[ics]->getTowerID()[0];
+//printf("      Cousin #%d: address %p, it's in tower [%d, %d, %d]. \n", ics,  m_HFclus->getHalfClusterCol("CousinCluster")[ics],  tmp_towerID[0], tmp_towerID[1], tmp_towerID[2]);
+//if(m_HFclus->getHalfClusterCol("CousinCluster")[ics]->getTowerID().size()!=1) cout<<"ERROR: cousin cluster covers >1 towers. Check here! "<<endl;
+
+        if( map_2DCluster.find( tmp_towerID )==map_2DCluster.end() )
+          tmp_delClus.push_back( m_HFclus->getHalfClusterCol("CousinCluster")[ics] );
+      }
+//cout<<"Need to delete "<<tmp_delClus.size()<<" cousins"<<endl;
+      for(int ics=0; ics<tmp_delClus.size(); ics++) m_HFclus->deleteCousinCluster( tmp_delClus[ics] );
+    }
+
+    //Convert to const
+    std::vector<const PandoraPlus::CaloHalfCluster*> const_HFClusU; const_HFClusU.clear();
+    std::vector<const PandoraPlus::CaloHalfCluster*> const_HFClusV; const_HFClusV.clear();
+    for(int ics=0; ics<m_HFClusUInTower.size(); ics++){ m_HFClusUInTower[ics]->getLinkedMCPfromUnit(); const_HFClusU.push_back(m_HFClusUInTower[ics]); }
+    for(int ics=0; ics<m_HFClusVInTower.size(); ics++){ m_HFClusVInTower[ics]->getLinkedMCPfromUnit(); const_HFClusV.push_back(m_HFClusVInTower[ics]); }
+
+    std::shared_ptr<PandoraPlus::Calo3DCluster> m_tower = std::make_shared<PandoraPlus::Calo3DCluster>();
+//printf("Creating tower: [%d, %d, %d] \n", m_towerID[0], m_towerID[1], m_towerID[2]);
+    m_tower->addTowerID( m_towerID );
+    for(int i2d=0; i2d<map_2DCluster[m_towerID].size(); i2d++) m_tower->addUnit(map_2DCluster[m_towerID][i2d]);
+    m_tower->setHalfClusters( settings.map_stringPars["OutputClusName"]+"U", const_HFClusU,
+                              settings.map_stringPars["OutputClusName"]+"V", const_HFClusV );
+    m_towers.push_back(m_tower);
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+
+
+/*
+StatusCode TruthEnergySplittingAlg::HalfClusterToTowers( std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_halfClusU,
+                                                         std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_halfClusV,
+                                                         std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_towers ){
+  m_towers.clear();
+
+  std::map<std::vector<int>, std::vector<const PandoraPlus::CaloUnit*>> map_barCol; map_barCol.clear();
+  for(int il=0; il<m_halfClusU.size(); il++){
+    for(int is=0; is<m_halfClusU[il]->getCluster().size(); is++){
+      const PandoraPlus::Calo1DCluster* p_shower = m_halfClusU[il]->getCluster()[is];
+      for(int ibar=0; ibar<p_shower->getBars().size(); ibar++){
+        std::vector<int> barID(3);
+        barID[0] = p_shower->getBars()[ibar]->getModule();
+        barID[1] = p_shower->getBars()[ibar]->getPart();
+        barID[2] = p_shower->getBars()[ibar]->getStave();
+        map_barCol[barID].push_back(p_shower->getBars()[ibar]);
+      }
+    }
+  }
+  for(int il=0; il<m_halfClusV.size(); il++){
+    for(int is=0; is<m_halfClusV[il]->getCluster().size(); is++){
+      const PandoraPlus::Calo1DCluster* p_shower = m_halfClusV[il]->getCluster()[is];
+      for(int ibar=0; ibar<p_shower->getBars().size(); ibar++){
+        std::vector<int> barID(3);
+        barID[0] = p_shower->getBars()[ibar]->getModule();
+        barID[1] = p_shower->getBars()[ibar]->getPart();
+        barID[2] = p_shower->getBars()[ibar]->getStave();
+        map_barCol[barID].push_back(p_shower->getBars()[ibar]);
+      }
+    }
+  }
+
+  //Re-build the objects in tower
+  for(auto& itower: map_barCol){
+    std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_newHFClusUCol;
+    std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_newHFClusVCol;
+    std::vector<std::shared_ptr<PandoraPlus::Calo2DCluster>> m_new2DClusCol;
+
+    //Get map for MCP-bars
+    std::map<edm4hep::MCParticle, std::vector<const PandoraPlus::CaloUnit*> > map_matchBar;
+    for(int ibar=0; ibar<itower.second.size(); ibar++){
+      edm4hep::MCParticle mcp = itower.second[ibar]->getLeadingMCP();
+      map_matchBar[mcp].push_back(itower.second[ibar]);
+    }
+
+    //Build objects for MCP
+    for(auto& iter: map_matchBar){
+//cout<<"    Print bar info"<<endl;
+//for(int ibar=0; ibar<iter.second.size(); ibar++){
+//printf("    cellID (%d, %d, %d, %d, %d, %d), En %.3f \n", iter.second[ibar]->getModule(), iter.second[ibar]->getPart(), iter.second[ibar]->getStave(), iter.second[ibar]->getDlayer(), iter.second[ibar]->getSlayer(), iter.second[ibar]->getBar(), iter.second[ibar]->getEnergy());
+//}
+      std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> m_new1DClusUCol;
+      std::vector<std::shared_ptr<PandoraPlus::Calo1DCluster>> m_new1DClusVCol;
+
+      //Ordered bars
+      std::map<int, std::vector<const PandoraPlus::CaloUnit*> > m_orderedBars; m_orderedBars.clear();
+      for(int ibar=0; ibar<iter.second.size(); ibar++)
+        m_orderedBars[iter.second[ibar]->getDlayer()].push_back(iter.second[ibar]);
+      //1D&2D cluster
+      for(auto& iter_bar: m_orderedBars){
+//cout<<"    In layer #"<<iter_bar.first<<": bar size "<<iter_bar.second.size()<<endl;
+
+        std::shared_ptr<PandoraPlus::Calo1DCluster> tmp_new1dclusterU = std::make_shared<PandoraPlus::Calo1DCluster>();
+        std::shared_ptr<PandoraPlus::Calo1DCluster> tmp_new1dclusterV = std::make_shared<PandoraPlus::Calo1DCluster>();
+        for(int ibar=0; ibar<iter_bar.second.size(); ibar++){
+          if(iter_bar.second[ibar]->getSlayer()==0) tmp_new1dclusterU->addUnit( iter_bar.second[ibar] );
+          if(iter_bar.second[ibar]->getSlayer()==1) tmp_new1dclusterV->addUnit( iter_bar.second[ibar] );
+        }
+        if(tmp_new1dclusterU && tmp_new1dclusterU->getBars().size()>0){
+          tmp_new1dclusterU->getLinkedMCPfromUnit();
+          tmp_new1dclusterU->setSeed();
+          tmp_new1dclusterU->setIDInfo();
+          m_new1DClusUCol.push_back(tmp_new1dclusterU);
+          m_bkCol.map_1DCluster["bk1DCluster"].push_back(tmp_new1dclusterU);
+        }
+        if(tmp_new1dclusterV && tmp_new1dclusterV->getBars().size()>0){
+          tmp_new1dclusterV->getLinkedMCPfromUnit();
+          tmp_new1dclusterV->setSeed();
+          tmp_new1dclusterV->setIDInfo();
+          m_new1DClusVCol.push_back(tmp_new1dclusterV);
+          m_bkCol.map_1DCluster["bk1DCluster"].push_back(tmp_new1dclusterV);
+        }
+        if(tmp_new1dclusterU && tmp_new1dclusterV && tmp_new1dclusterU->getBars().size()>0 && tmp_new1dclusterV->getBars().size()>0){
+          std::shared_ptr<PandoraPlus::Calo2DCluster> tmp_block = std::make_shared<PandoraPlus::Calo2DCluster>();
+          for(int ibar=0; ibar<iter_bar.second.size(); ibar++) tmp_block->addBar(iter_bar.second[ibar]);
+          tmp_block->addUnit(tmp_new1dclusterU.get());
+          tmp_block->addUnit(tmp_new1dclusterV.get());
+          tmp_block->setTowerID( itower.first );
+          m_new2DClusCol.push_back(tmp_block);
+          m_bkCol.map_2DCluster["bk2DCluster"].push_back(tmp_block);
+        }
+      }
+//cout<<"    1DClusU size "<<m_new1DClusUCol.size()<<", 1DClusV size "<<m_new1DClusVCol.size();
+//cout<<", 2DClus size "<<m_new2DClusCol.size()<<endl;
+      //Half cluster
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> m_newHFClusterU = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> m_newHFClusterV = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      for(int i1d=0; i1d<m_new1DClusUCol.size(); i1d++)
+        m_newHFClusterU->addUnit(m_new1DClusUCol[i1d].get());
+      for(int i1d=0; i1d<m_new1DClusVCol.size(); i1d++)
+        m_newHFClusterV->addUnit(m_new1DClusVCol[i1d].get());
+
+      m_newHFClusterU->getLinkedMCPfromUnit();
+      m_newHFClusterV->getLinkedMCPfromUnit();
+      m_newHFClusUCol.push_back(m_newHFClusterU);
+      m_newHFClusVCol.push_back(m_newHFClusterV);
+      m_bkCol.map_HalfCluster["bkHalfCluster"].push_back(m_newHFClusterU);
+      m_bkCol.map_HalfCluster["bkHalfCluster"].push_back(m_newHFClusterV);
+    }
+
+    //Form a tower
+    std::vector<const PandoraPlus::CaloHalfCluster*> const_HFClusU; const_HFClusU.clear();
+    std::vector<const PandoraPlus::CaloHalfCluster*> const_HFClusV; const_HFClusV.clear();
+    for(int ics=0; ics<m_newHFClusUCol.size(); ics++){ const_HFClusU.push_back(m_newHFClusUCol[ics].get()); }
+    for(int ics=0; ics<m_newHFClusVCol.size(); ics++){ const_HFClusV.push_back(m_newHFClusVCol[ics].get()); }
+
+    std::shared_ptr<PandoraPlus::Calo3DCluster> m_tower = std::make_shared<PandoraPlus::Calo3DCluster>();
+    m_tower->addTowerID(itower.first);
+    for(int i2d=0; i2d<m_new2DClusCol.size(); i2d++) m_tower->addUnit(m_new2DClusCol[i2d].get());
+    m_tower->setHalfClusters( settings.map_stringPars["OutputClusName"]+"U", const_HFClusU,
+                              settings.map_stringPars["OutputClusName"]+"V", const_HFClusV );
+    m_towers.push_back(m_tower);
+  }
+
+  return StatusCode::SUCCESS;
+}
+*/
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/TruthMatchingAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/TruthMatchingAlg.cpp
new file mode 100644
index 00000000..ee4f5064
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/TruthMatchingAlg.cpp
@@ -0,0 +1,444 @@
+#ifndef _TRUTHMATCHING_ALG_C
+#define _TRUTHMATCHING_ALG_C
+
+#include "Algorithm/TruthMatchingAlg.h"
+
+StatusCode TruthMatchingAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  //Initialize parameters
+  if(settings.map_stringPars.find("ReadinHFClusterName")==settings.map_stringPars.end()) settings.map_stringPars["ReadinHFClusterName"] = "TruthESCluster";
+  if(settings.map_stringPars.find("ReadinTowerName")==settings.map_stringPars.end()) settings.map_stringPars["ReadinTowerName"] = "TruthESTower";
+  if(settings.map_stringPars.find("OutputClusterName")==settings.map_stringPars.end()) settings.map_stringPars["OutputClusterName"] = "TruthEcalCluster";
+  
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthMatchingAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  std::cout<<"Initialize TruthMatchingAlg"<<std::endl;
+
+  m_HFClusUCol.clear();
+  m_HFClusVCol.clear();
+  m_clusterCol.clear();
+  m_towerCol.clear();
+  m_bkCol.Clear();
+
+  int ntower = m_datacol.map_CaloCluster[settings.map_stringPars["ReadinTowerName"]].size();
+  for(int it=0; it<ntower; it++)
+    m_towerCol.push_back( m_datacol.map_CaloCluster[settings.map_stringPars["ReadinTowerName"]][it].get() );
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthMatchingAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+//cout<<"TruthMatchingAlg: readin tower size "<<m_towerCol.size()<<endl;
+
+  for(int it=0; it<m_towerCol.size(); it++){
+
+    std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> tmp_clusters; tmp_clusters.clear();
+    m_HFClusUCol = m_towerCol.at(it)->getHalfClusterUCol(settings.map_stringPars["ReadinHFClusterName"]+"U");
+    m_HFClusVCol = m_towerCol.at(it)->getHalfClusterVCol(settings.map_stringPars["ReadinHFClusterName"]+"V");
+//cout<<"  In tower #"<<it<<": HFCluster size "<<m_HFClusUCol.size()<<", "<<m_HFClusVCol.size()<<endl;
+
+
+    TruthMatching(m_HFClusUCol, m_HFClusVCol, tmp_clusters);
+//cout<<"  After matching: 3DCluster size "<<tmp_clusters.size()<<endl;
+    m_clusterCol.insert(m_clusterCol.end(), tmp_clusters.begin(), tmp_clusters.end());
+  }
+
+  //Merge clusters linked to the same MCP.
+  for(int ic=0; ic<m_clusterCol.size() && m_clusterCol.size()>1; ic++){
+    edm4hep::MCParticle mcp1 = m_clusterCol[ic].get()->getLeadingMCP();
+    for(int jc=ic+1; jc<m_clusterCol.size(); jc++){
+      if(ic>m_clusterCol.size()) ic--;
+      edm4hep::MCParticle mcp2 = m_clusterCol[jc].get()->getLeadingMCP();
+      if(mcp1==mcp2){
+        m_clusterCol[ic].get()->mergeCluster( m_clusterCol[jc].get() );
+        m_clusterCol.erase(m_clusterCol.begin()+jc);
+        jc--;
+        if(jc<ic) jc=ic;
+      }
+    }
+  }
+//cout<<"After cluster merging: size "<<m_clusterCol.size()<<endl;
+
+  m_datacol.map_CaloCluster[settings.map_stringPars["OutputClusterName"]] = m_clusterCol;
+
+  m_datacol.map_BarCol["bkBar"].insert( m_datacol.map_BarCol["bkBar"].end(), m_bkCol.map_BarCol["bkBar"].begin(), m_bkCol.map_BarCol["bkBar"].end() );
+  m_datacol.map_1DCluster["bk1DCluster"].insert( m_datacol.map_1DCluster["bk1DCluster"].end(), m_bkCol.map_1DCluster["bk1DCluster"].begin(), m_bkCol.map_1DCluster["bk1DCluster"].end() );
+  m_datacol.map_2DCluster["bk2DCluster"].insert( m_datacol.map_2DCluster["bk2DCluster"].end(), m_bkCol.map_2DCluster["bk2DCluster"].begin(), m_bkCol.map_2DCluster["bk2DCluster"].end() );
+  m_datacol.map_HalfCluster["bkHalfCluster"].insert( m_datacol.map_HalfCluster["bkHalfCluster"].end(), m_bkCol.map_HalfCluster["bkHalfCluster"].begin(), m_bkCol.map_HalfCluster["bkHalfCluster"].end() );
+
+
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthMatchingAlg::ClearAlgorithm(){
+  std::cout<<"End run TruthMatchingAlg. Clean it."<<std::endl;
+
+  m_HFClusUCol.clear();
+  m_HFClusVCol.clear();
+  m_clusterCol.clear();
+  m_towerCol.clear();
+  m_bkCol.Clear();
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode TruthMatchingAlg::TruthMatching( std::vector<const PandoraPlus::CaloHalfCluster*>& m_ClUCol,
+                                            std::vector<const PandoraPlus::CaloHalfCluster*>& m_ClVCol,
+                                            std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>>& m_clusters )
+{
+  if(m_ClUCol.size()==0 || m_ClVCol.size()==0) return StatusCode::SUCCESS;
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_truthESClUCol; m_truthESClUCol.clear();
+  std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_truthESClVCol; m_truthESClVCol.clear();
+
+//cout<<"  Input HFCluster size "<<m_ClUCol.size()<<", "<<m_ClVCol.size()<<endl;
+
+  //Truth split in HFClusterU
+  for(int icl=0; icl<m_ClUCol.size(); icl++){
+    auto truthMap = m_ClUCol[icl]->getLinkedMCP();
+//cout<<"    In HFClusU #"<<icl<<": energy "<<m_ClUCol[icl]->getEnergy()<<", 1Dcluster size "<<m_ClUCol[icl]->getCluster().size()<<", truth link size "<<truthMap.size()<<endl;
+//for(auto& iter: truthMap)
+//  printf("    MC pid %d, weight %.3f \n", iter.first.getPDG(), iter.second);
+
+    if(truthMap.size()==1){
+      auto newClus = m_ClUCol[icl]->Clone();
+      m_truthESClUCol.push_back(newClus);
+      continue;
+    }
+
+    for(auto& iter: truthMap ){
+      if(iter.second<0.05) continue;
+
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> newClus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      //Copy and reweight 1D showers in HFCluster
+      for(int ish=0; ish<m_ClUCol[icl]->getCluster().size(); ish++){
+        const PandoraPlus::Calo1DCluster* p_shower = m_ClUCol[icl]->getCluster()[ish];
+
+        std::vector<const PandoraPlus::CaloUnit*> Bars; Bars.clear();
+        for(int ibar=0; ibar<p_shower->getCluster().size(); ibar++){
+          auto bar = p_shower->getCluster()[ibar]->Clone();
+          bar->setQ(bar->getQ1()*iter.second, bar->getQ2()*iter.second );
+
+          bar->setLinkedMCP(std::vector<std::pair<edm4hep::MCParticle, float>>{std::make_pair(iter.first, 1.)});
+          Bars.push_back(bar.get());
+          m_bkCol.map_BarCol["bkBar"].push_back(bar);
+        }
+
+        std::shared_ptr<PandoraPlus::Calo1DCluster> shower = std::make_shared<PandoraPlus::Calo1DCluster>();
+        shower->setBars(Bars);
+        shower->setSeed();
+        shower->setIDInfo();
+        shower->getLinkedMCPfromUnit();
+        newClus->addUnit(shower.get());
+        m_bkCol.map_1DCluster["bk1DCluster"].push_back( shower );
+      }
+      for(int itrk=0; itrk<m_ClUCol[icl]->getAssociatedTracks().size(); itrk++)  newClus->addAssociatedTrack( m_ClUCol[icl]->getAssociatedTracks()[itrk] );
+      newClus->setHoughPars( m_ClUCol[icl]->getHoughAlpha(), m_ClUCol[icl]->getHoughRho() );
+      newClus->setIntercept( m_ClUCol[icl]->getHoughIntercept() );
+      newClus->fitAxis("");
+      newClus->getLinkedMCPfromUnit();
+      newClus->mergeClusterInLayer();
+      m_truthESClUCol.push_back(newClus);
+    }
+  }
+//cout<<"    New HFClusterU size "<<m_truthESClUCol.size()<<endl;
+
+  //Merge HFClusters linked to the same MCP
+  for(int icl=0; icl<m_truthESClUCol.size() && m_truthESClUCol.size()>1; icl++){
+    for(int jcl=icl+1; jcl<m_truthESClUCol.size(); jcl++){
+      if(icl>m_truthESClUCol.size()) icl--;
+
+      if(m_truthESClUCol[icl]->getLeadingMCP()==m_truthESClUCol[jcl]->getLeadingMCP()){
+        m_truthESClUCol[icl].get()->mergeHalfCluster(m_truthESClUCol[jcl].get());
+        m_truthESClUCol.erase(m_truthESClUCol.begin()+jcl);
+        jcl--;
+        if(jcl<icl) jcl=icl;
+      }
+    }
+  }
+//cout<<"    HFClusterU size after merge "<<m_truthESClUCol.size()<<endl;
+  for(int icl=0; icl<m_truthESClUCol.size(); icl++) m_bkCol.map_HalfCluster["bkHalfCluster"].push_back(m_truthESClUCol[icl]);
+
+
+  //Truth split in HFClusterV
+  for(int icl=0; icl<m_ClVCol.size(); icl++){
+    auto truthMap = m_ClVCol[icl]->getLinkedMCP();
+//cout<<"    In HFClusV #"<<icl<<": energy "<<m_ClVCol[icl]->getEnergy()<<", 1Dcluster size "<<m_ClVCol[icl]->getCluster().size()<<", truth link size "<<truthMap.size()<<endl;
+//for(auto& iter: truthMap)
+//  printf("    MC pid %d, weight %.3f \n", iter.first.getPDG(), iter.second);
+
+    if(truthMap.size()==1){
+      auto newClus = m_ClVCol[icl]->Clone();
+      m_truthESClVCol.push_back(newClus);
+      continue;
+    }
+
+    for(auto iter: truthMap ){
+      if(iter.second<0.05) continue;
+
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> newClus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      //Copy and reweight 1D showers in HFCluster
+      for(int ish=0; ish<m_ClVCol[icl]->getCluster().size(); ish++){
+        const PandoraPlus::Calo1DCluster* p_shower = m_ClVCol[icl]->getCluster()[ish];
+
+        std::vector<const PandoraPlus::CaloUnit*> Bars; Bars.clear();
+        for(int ibar=0; ibar<p_shower->getCluster().size(); ibar++){
+          auto bar = p_shower->getCluster()[ibar]->Clone();
+          bar->setQ(bar->getQ1()*iter.second, bar->getQ2()*iter.second );
+
+          bar->setLinkedMCP(std::vector<std::pair<edm4hep::MCParticle, float>>{std::make_pair(iter.first, 1.)});
+          Bars.push_back(bar.get());
+          m_bkCol.map_BarCol["bkBar"].push_back(bar);
+        }
+        std::shared_ptr<PandoraPlus::Calo1DCluster> shower = std::make_shared<PandoraPlus::Calo1DCluster>();
+        shower->setBars(Bars);
+        shower->setSeed();
+        shower->setIDInfo();
+        shower->getLinkedMCPfromUnit();
+        newClus->addUnit(shower.get());
+        m_bkCol.map_1DCluster["bk1DCluster"].push_back( shower );
+      }
+      for(int itrk=0; itrk<m_ClVCol[icl]->getAssociatedTracks().size(); itrk++)  newClus->addAssociatedTrack( m_ClVCol[icl]->getAssociatedTracks()[itrk] );
+      newClus->setHoughPars( m_ClVCol[icl]->getHoughAlpha(), m_ClVCol[icl]->getHoughRho() );
+      newClus->setIntercept( m_ClVCol[icl]->getHoughIntercept() );
+      newClus->fitAxis("");
+      newClus->getLinkedMCPfromUnit();
+      newClus->mergeClusterInLayer();
+      m_truthESClVCol.push_back(newClus);
+    }
+  }
+//cout<<"    New HFClusterV size "<<m_truthESClVCol.size()<<endl;
+
+  //Merge HFClusters linked to the same MCP
+  for(int icl=0; icl<m_truthESClVCol.size() && m_truthESClVCol.size()>1; icl++){
+    for(int jcl=icl+1; jcl<m_truthESClVCol.size(); jcl++){
+      if(icl>m_truthESClVCol.size()) icl--;
+
+      if(m_truthESClVCol[icl]->getLeadingMCP()==m_truthESClVCol[jcl]->getLeadingMCP()){
+        m_truthESClVCol[icl].get()->mergeHalfCluster(m_truthESClVCol[jcl].get());
+        m_truthESClVCol.erase(m_truthESClVCol.begin()+jcl);
+        jcl--;
+        if(jcl<icl) jcl=icl;
+      }
+    }
+  }
+  for(int icl=0; icl<m_truthESClVCol.size(); icl++) m_bkCol.map_HalfCluster["bkHalfCluster"].push_back(m_truthESClVCol[icl]);
+//cout<<"    HFClusterV size after merge "<<m_truthESClVCol.size()<<endl;
+
+
+  //Doing matching.
+  for(int icl=0; icl<m_truthESClUCol.size(); icl++){
+    for(int jcl=0; jcl<m_truthESClVCol.size(); jcl++){
+      if(m_truthESClUCol[icl]->getLeadingMCP() == m_truthESClVCol[jcl]->getLeadingMCP()){
+        std::shared_ptr<PandoraPlus::Calo3DCluster> tmp_clus = std::make_shared<PandoraPlus::Calo3DCluster>();
+        XYClusterMatchingL0(m_truthESClUCol[icl].get(), m_truthESClVCol[jcl].get(), tmp_clus);
+        m_clusters.push_back(tmp_clus);
+      }
+    }
+  }
+
+  return StatusCode::SUCCESS;
+}
+
+
+//Longitudinal cluster: 1*1
+StatusCode TruthMatchingAlg::XYClusterMatchingL0( const PandoraPlus::CaloHalfCluster* m_longiClU,
+                                                  const PandoraPlus::CaloHalfCluster* m_longiClV,
+                                                  std::shared_ptr<PandoraPlus::Calo3DCluster>& m_clus )
+{
+/*
+cout<<"  XYClusterMatchingL0: print input HFClusterU. ";
+printf("cluster size %d, linked MC pid %d, track size %d, Cover tower %d: ", m_longiClU->getCluster().size(), m_longiClU->getLeadingMCP().getPDG(), m_longiClU->getAssociatedTracks().size(), m_longiClU->getTowerID().size());
+for(int it=0; it<m_longiClU->getTowerID().size(); it++) printf(" [%d, %d, %d] ", m_longiClU->getTowerID()[it][0], m_longiClU->getTowerID()[it][1], m_longiClU->getTowerID()[it][2]);
+cout<<endl;
+for(int ic=0; ic<m_longiClU->getCluster().size(); ic++){
+  printf("    Cluster #%d: towerID [%d, %d, %d], layer %d, pos+E (%.3f, %.3f, %.3f, %.3f), bar size %d, seed size %d, MCP link size %d: ", 
+    ic, m_longiClU->getCluster()[ic]->getTowerID()[0][0], m_longiClU->getCluster()[ic]->getTowerID()[0][1], m_longiClU->getCluster()[ic]->getTowerID()[0][2], m_longiClU->getCluster()[ic]->getDlayer(), 
+    m_longiClU->getCluster()[ic]->getPos().x(), m_longiClU->getCluster()[ic]->getPos().y(), m_longiClU->getCluster()[ic]->getPos().z(),  m_longiClU->getCluster()[ic]->getEnergy(), 
+    m_longiClU->getCluster()[ic]->getBars().size(), m_longiClU->getCluster()[ic]->getNseeds(), m_longiClU->getCluster()[ic]->getLinkedMCP().size() );
+  for(int imc=0; imc<m_longiClU->getCluster()[ic]->getLinkedMCP().size(); imc++) cout<<m_longiClU->getCluster()[ic]->getLinkedMCP()[imc].first.getPDG()<<", ";
+  cout<<endl;
+}
+cout<<"  XYClusterMatchingL0: print input HFClusterV. ";
+printf("cluster size %d, linked MC pid %d, track size %d, Cover tower %d: ", m_longiClV->getCluster().size(), m_longiClV->getLeadingMCP().getPDG(),  m_longiClV->getAssociatedTracks().size(), m_longiClV->getTowerID().size());
+for(int it=0; it<m_longiClV->getTowerID().size(); it++) printf(" [%d, %d, %d] ", m_longiClV->getTowerID()[it][0], m_longiClV->getTowerID()[it][1], m_longiClV->getTowerID()[it][2]);
+cout<<endl;
+for(int ic=0; ic<m_longiClV->getCluster().size(); ic++){
+  printf("    Cluster #%d: towerID [%d, %d, %d], layer %d, pos+E (%.3f, %.3f, %.3f, %.3f), bar size %d, seed size %d, MCP link size %d: ", 
+    ic, m_longiClV->getCluster()[ic]->getTowerID()[0][0], m_longiClV->getCluster()[ic]->getTowerID()[0][1], m_longiClV->getCluster()[ic]->getTowerID()[0][2], m_longiClV->getCluster()[ic]->getDlayer(), 
+    m_longiClV->getCluster()[ic]->getPos().x(), m_longiClV->getCluster()[ic]->getPos().y(), m_longiClV->getCluster()[ic]->getPos().z(), m_longiClV->getCluster()[ic]->getEnergy(), 
+    m_longiClV->getCluster()[ic]->getBars().size(), m_longiClV->getCluster()[ic]->getNseeds(), m_longiClV->getCluster()[ic]->getLinkedMCP().size() );
+  for(int imc=0; imc<m_longiClV->getCluster()[ic]->getLinkedMCP().size(); imc++) cout<<m_longiClV->getCluster()[ic]->getLinkedMCP()[imc].first.getPDG()<<", ";
+  cout<<endl;
+}
+cout<<endl;
+*/
+
+  std::vector<int> layerindex; layerindex.clear();
+  std::map<int, std::vector<const PandoraPlus::Calo1DCluster*> > map_showersUinlayer; map_showersUinlayer.clear();
+  std::map<int, std::vector<const PandoraPlus::Calo1DCluster*> > map_showersVinlayer; map_showersVinlayer.clear();
+
+  for(int is=0; is<m_longiClU->getCluster().size(); is++){
+    int m_layer = m_longiClU->getCluster()[is]->getDlayer();
+    if( find( layerindex.begin(), layerindex.end(), m_layer )==layerindex.end() ) layerindex.push_back(m_layer);
+    map_showersUinlayer[m_layer].push_back(m_longiClU->getCluster()[is]);
+  }
+  for(int is=0; is<m_longiClV->getCluster().size(); is++){
+    int m_layer = m_longiClV->getCluster()[is]->getDlayer();
+    if( find( layerindex.begin(), layerindex.end(), m_layer )==layerindex.end() ) layerindex.push_back(m_layer);
+    map_showersVinlayer[m_layer].push_back(m_longiClV->getCluster()[is]);
+  }
+
+//cout<<"    in XYClusterMatchingL0: "<<layerindex.size()<<" layer need matching"<<endl;
+
+  for(int il=0; il<layerindex.size(); il++){
+    std::vector<const PandoraPlus::Calo1DCluster*> m_showerXcol = map_showersUinlayer[layerindex[il]];
+    std::vector<const PandoraPlus::Calo1DCluster*> m_showerYcol = map_showersVinlayer[layerindex[il]];
+
+    std::vector<PandoraPlus::Calo2DCluster*> m_showerinlayer; m_showerinlayer.clear();
+
+//cout<<"    in layer "<<layerindex[il]<<": 1D shower size ("<<m_showerXcol.size()<<", "<<m_showerYcol.size()<<"). "<<endl;
+    if(m_showerXcol.size()==0 || m_showerYcol.size()==0) continue;
+    //else if(m_showerXcol.size()==0){
+    //  std::shared_ptr<PandoraPlus::Calo2DCluster> tmp_shower = std::make_shared<PandoraPlus::Calo2DCluster>();
+    //  GetMatchedShowerFromEmpty(m_showerYcol[0], m_longiClU, tmp_shower.get());
+    //  //m_showerinlayer.push_back(tmp_shower.get());
+    //  //m_bkCol.map_2DCluster["bk2DCluster"].push_back(tmp_shower);
+    //}
+    //else if(m_showerYcol.size()==0){
+    //  std::shared_ptr<PandoraPlus::Calo2DCluster> tmp_shower = std::make_shared<PandoraPlus::Calo2DCluster>();
+    //  GetMatchedShowerFromEmpty(m_showerXcol[0], m_longiClU, tmp_shower.get());
+    //}
+    else if(m_showerXcol.size()==1 && m_showerYcol.size()==1){
+      std::shared_ptr<PandoraPlus::Calo2DCluster> tmp_shower = std::make_shared<PandoraPlus::Calo2DCluster>();
+      GetMatchedShowersL0(m_showerXcol[0], m_showerYcol[0], tmp_shower.get());
+      m_showerinlayer.push_back(tmp_shower.get());
+      m_bkCol.map_2DCluster["bk2DCluster"].push_back(tmp_shower);
+    }
+    else if(m_showerXcol.size()==1) GetMatchedShowersL1(m_showerXcol[0], m_showerYcol, m_showerinlayer );
+    else if(m_showerYcol.size()==1) GetMatchedShowersL1(m_showerYcol[0], m_showerXcol, m_showerinlayer );
+    else{ std::cout<<"CAUSION in XYClusterMatchingL0: HFCluster has ["<<m_showerXcol.size()<<", "<<m_showerYcol.size()<<"] showers in layer "<<layerindex[il]<<std::endl; }
+
+    for(int is=0; is<m_showerinlayer.size(); is++) m_clus->addUnit(m_showerinlayer[is]);
+//cout<<"    after matching: 2D shower size "<<m_showerinlayer.size()<<endl;
+  }
+
+
+  m_clus->addHalfClusterU( "LinkedLongiCluster", m_longiClU );
+  m_clus->addHalfClusterV( "LinkedLongiCluster", m_longiClV );
+  for(auto itrk : m_longiClU->getAssociatedTracks()){
+    for(auto jtrk : m_longiClV->getAssociatedTracks()){
+      if(itrk!=jtrk) continue;
+      if( find(m_clus->getAssociatedTracks().begin(), m_clus->getAssociatedTracks().end(), itrk)==m_clus->getAssociatedTracks().end() )
+        m_clus->addAssociatedTrack(itrk);
+  }}
+  m_clus->getLinkedMCPfromHFCluster("LinkedLongiCluster");
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TruthMatchingAlg::GetMatchedShowersL0( const PandoraPlus::Calo1DCluster* barShowerU,
+                                                  const PandoraPlus::Calo1DCluster* barShowerV,
+                                                  PandoraPlus::Calo2DCluster* outsh )
+{
+//cout<<"  In GetMatchedShowersL0"<<endl;
+
+  std::vector<const PandoraPlus::CaloHit*> m_digiCol; m_digiCol.clear();
+  int NbarsX = barShowerU->getBars().size();
+  int NbarsY = barShowerV->getBars().size();
+  if(NbarsX==0 || NbarsY==0){ std::cout<<"WARNING: empty DigiHitsCol returned!"<<std::endl; return StatusCode::SUCCESS; }
+  if(barShowerU->getTowerID().size()==0) { std::cout<<"WARNING:GetMatchedShowersL0  No TowerID in 1DCluster!"<<std::endl; return StatusCode::SUCCESS; }
+  //if(barShowerU->getTowerID().size()==0) { barShowerU->setIDInfo(); }
+
+  int _module = barShowerU->getTowerID()[0][0];
+  float rotAngle = -_module*TMath::TwoPi()/PandoraPlus::CaloUnit::Nmodule;
+
+  for(int ibar=0;ibar<NbarsX;ibar++){
+    double En_x = barShowerU->getBars()[ibar]->getEnergy();
+    TVector3 m_vecx = barShowerU->getBars()[ibar]->getPosition();
+    m_vecx.RotateZ(rotAngle);
+
+    for(int jbar=0;jbar<NbarsY;jbar++){
+      double En_y = barShowerV->getBars()[jbar]->getEnergy();
+      TVector3 m_vecy = barShowerV->getBars()[jbar]->getPosition();
+      m_vecy.RotateZ(rotAngle);
+
+      TVector3 p_hit(m_vecy.x(), (m_vecx.y()+m_vecy.y())/2., m_vecx.z() );
+      p_hit.RotateZ(-rotAngle);
+      double m_Ehit = En_x*En_y/barShowerV->getEnergy() + En_x*En_y/barShowerU->getEnergy();
+      //Create new CaloHit
+      std::shared_ptr<PandoraPlus::CaloHit> hit = std::make_shared<PandoraPlus::CaloHit>();
+      //hit.setCellID(0);
+      hit->setPosition(p_hit);
+      hit->setEnergy(m_Ehit);
+      m_digiCol.push_back(hit.get());
+      m_bkCol.map_CaloHit["bkHit"].push_back( hit );
+    }
+  }
+
+  outsh->addUnit( barShowerU );
+  outsh->addUnit( barShowerV );
+  outsh->setCaloHits( m_digiCol );
+//cout<<"    End output shower"<<endl;
+
+  return StatusCode::SUCCESS;
+}
+
+
+StatusCode TruthMatchingAlg::GetMatchedShowersL1( const PandoraPlus::Calo1DCluster* shower1,
+                                                       std::vector<const PandoraPlus::Calo1DCluster*>& showerNCol,
+                                                       std::vector<PandoraPlus::Calo2DCluster*>& outshCol )
+{
+//cout<<"  GetMatchedShowersL1: input shower size: 1 * "<<showerNCol.size()<<endl;
+  outshCol.clear();
+
+  int _slayer = shower1->getBars()[0]->getSlayer();
+
+  const int NshY = showerNCol.size();
+  double totE_shY = 0;
+  double EshY[NshY] = {0};
+  for(int is=0;is<NshY;is++){ EshY[is] = showerNCol[is]->getEnergy(); totE_shY += EshY[is]; }
+  for(int is=0;is<NshY;is++){
+    double wi_E = EshY[is]/totE_shY;
+    std::shared_ptr<PandoraPlus::Calo1DCluster> m_splitshower1 = std::make_shared<PandoraPlus::Calo1DCluster>();
+    m_bkCol.map_1DCluster["bk1DCluster"].push_back( m_splitshower1 );
+
+    std::shared_ptr<PandoraPlus::CaloUnit> m_wiseed = nullptr;
+    if(shower1->getSeeds().size()>0) m_wiseed = shower1->getSeeds()[0]->Clone();
+    else{ cout<<"ERROR: Input shower has no seed! Check! Use the most energitic bar as seed. bar size: "<<shower1->getBars().size()<<endl;
+      double m_maxE = -99;
+      int index = -1;
+      for(int ib=0; ib<shower1->getBars().size(); ib++){
+        if(shower1->getBars()[ib]->getEnergy()>m_maxE) { m_maxE=shower1->getBars()[ib]->getEnergy(); index=ib; }
+      }
+      if(index>=0) m_wiseed = shower1->getBars()[index]->Clone();
+    }
+    m_wiseed->setQ( wi_E*m_wiseed->getQ1(), wi_E*m_wiseed->getQ2() );
+    m_bkCol.map_BarCol["bkBar"].push_back( m_wiseed );
+
+    std::vector<const PandoraPlus::CaloUnit*> m_wibars; m_wibars.clear();
+    for(int ib=0;ib<shower1->getBars().size();ib++){
+      std::shared_ptr<PandoraPlus::CaloUnit> m_wibar = shower1->getBars()[ib]->Clone();
+      m_wibar->setQ(wi_E*m_wibar->getQ1(), wi_E*m_wibar->getQ2());
+      m_wibars.push_back(m_wibar.get());
+      m_bkCol.map_BarCol["bkBar"].push_back( m_wibar );
+    }
+    m_splitshower1->setBars( m_wibars );
+    m_splitshower1->addSeed( m_wiseed.get() );
+    m_splitshower1->setIDInfo();
+    std::shared_ptr<PandoraPlus::Calo2DCluster> m_shower = std::make_shared<PandoraPlus::Calo2DCluster>();
+    if(_slayer==0 ) GetMatchedShowersL0( m_splitshower1.get(), showerNCol[is], m_shower.get() );
+    else            GetMatchedShowersL0( showerNCol[is], m_splitshower1.get(), m_shower.get() );
+
+    outshCol.push_back( m_shower.get() );
+    m_bkCol.map_2DCluster["bk2DCluster"].push_back( m_shower );
+  }
+  return StatusCode::SUCCESS;
+}
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/TruthPatternRecAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/TruthPatternRecAlg.cpp
new file mode 100644
index 00000000..6e13132c
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/TruthPatternRecAlg.cpp
@@ -0,0 +1,213 @@
+#ifndef _TRUTHPATTERNREC_ALG_C
+#define _TRUTHPATTERNREC_ALG_C
+
+#include "Algorithm/TruthPatternRecAlg.h"
+
+StatusCode TruthPatternRecAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  //Initialize parameters
+  if(settings.map_stringPars.find("ReadinLocalMaxName")==settings.map_stringPars.end())
+    settings.map_stringPars["ReadinLocalMaxName"] = "AllLocalMax";
+  if(settings.map_stringPars.find("OutputLongiClusName")==settings.map_stringPars.end())
+    settings.map_stringPars["OutputLongiClusName"] = "TruthAxis";    
+
+  if(settings.map_boolPars.find("DoAxisMerging")==settings.map_boolPars.end())
+    settings.map_boolPars["DoAxisMerging"] = 1;
+  if(settings.map_stringPars.find("ReadinAxisName")==settings.map_stringPars.end())
+    settings.map_stringPars["ReadinAxisName"] = "TruthTrackAxis";
+  if(settings.map_stringPars.find("OutputMergedAxisName")==settings.map_stringPars.end())
+    settings.map_stringPars["OutputMergedAxisName"] = "TruthMergedAxis";
+  if(settings.map_floatPars.find("th_overlap")==settings.map_floatPars.end()) 
+    settings.map_floatPars["th_overlap"] = 0.8;
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthPatternRecAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  p_HalfClusterU.clear();
+  p_HalfClusterV.clear();
+
+  for(int ih=0; ih<m_datacol.map_HalfCluster["HalfClusterColU"].size(); ih++)
+    p_HalfClusterU.push_back( m_datacol.map_HalfCluster["HalfClusterColU"][ih].get() );
+  for(int ih=0; ih<m_datacol.map_HalfCluster["HalfClusterColV"].size(); ih++)
+    p_HalfClusterV.push_back( m_datacol.map_HalfCluster["HalfClusterColV"][ih].get() );
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode TruthPatternRecAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+//cout<<"  TruthPatternRecAlg: Input HFCluster size ("<<p_HalfClusterU.size()<<", "<<p_HalfClusterV.size()<<") "<<endl;
+
+  for(int ihc=0; ihc<p_HalfClusterU.size(); ihc++){
+    std::map<edm4hep::MCParticle, std::vector<const Calo1DCluster*>> TruthAxesMap; 
+
+    std::vector<const PandoraPlus::Calo1DCluster*> tmp_localMaxUCol = p_HalfClusterU[ihc]->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+    for(int ilm=0; ilm<tmp_localMaxUCol.size(); ilm++){
+      edm4hep::MCParticle mcp_lm = tmp_localMaxUCol[ilm]->getLeadingMCP();
+      TruthAxesMap[mcp_lm].push_back(tmp_localMaxUCol[ilm]);
+    }
+
+//cout<<"    In HFClusterU #"<<ihc<<": localMax size "<<tmp_localMaxUCol.size()<<", print truth axes map"<<endl;
+//for(auto iter : TruthAxesMap)
+//  printf("      MCP id %d: localMax size %d \n", iter.first.getPDG(), iter.second.size());
+
+    //Create axes from MCPMap. 
+    std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> axisCol; axisCol.clear(); 
+    for(auto& iter : TruthAxesMap){
+      if(iter.second.size()==0) continue;
+  
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> t_axis = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      for(int icl=0; icl<iter.second.size(); icl++) t_axis->addUnit(iter.second[icl]);
+      t_axis->setType(100);
+      t_axis->getLinkedMCPfromUnit();
+      axisCol.push_back(t_axis);
+    }
+//cout<<"    Axes size: "<<axisCol.size()<<endl;
+
+    for(int iax=0; iax<axisCol.size(); iax++) p_HalfClusterU[ihc]->addHalfCluster(settings.map_stringPars["OutputLongiClusName"], axisCol[iax].get());
+    m_datacol.map_HalfCluster["bkHalfCluster"].insert(m_datacol.map_HalfCluster["bkHalfCluster"].end(), axisCol.begin(), axisCol.end() );
+  }
+
+
+  //Get the linked axes in V
+  for(int ihc=0; ihc<p_HalfClusterV.size(); ihc++){
+    std::map<edm4hep::MCParticle, std::vector<const Calo1DCluster*>> TruthAxesMap; 
+
+    std::vector<const PandoraPlus::Calo1DCluster*> tmp_localMaxVCol = p_HalfClusterV[ihc]->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+    for(int ilm=0; ilm<tmp_localMaxVCol.size(); ilm++){
+      edm4hep::MCParticle mcp_lm = tmp_localMaxVCol[ilm]->getLeadingMCP();
+      TruthAxesMap[mcp_lm].push_back(tmp_localMaxVCol[ilm]);
+    }
+
+//cout<<"    In HFClusterV #"<<ihc<<": localMax size "<<tmp_localMaxVCol.size()<<", print truth axes map"<<endl;
+//for(auto iter : TruthAxesMap)
+//  printf("      MCP id %d: localMax size %d \n", iter.first.getPDG(), iter.second.size());
+
+    //Create axes from MCPMap.
+    std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> axisCol; axisCol.clear(); 
+    for(auto& iter : TruthAxesMap){
+      if(iter.second.size()==0) continue;
+   
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> t_axis = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      for(int icl=0; icl<iter.second.size(); icl++) t_axis->addUnit(iter.second[icl]);
+      t_axis->setType(100);
+      t_axis->getLinkedMCPfromUnit();
+      axisCol.push_back(t_axis);
+    }
+
+//cout<<"    Axes size: "<<axisCol.size()<<endl;
+
+    for(int iax=0; iax<axisCol.size(); iax++) p_HalfClusterV[ihc]->addHalfCluster(settings.map_stringPars["OutputLongiClusName"], axisCol[iax].get());
+    m_datacol.map_HalfCluster["bkHalfCluster"].insert(m_datacol.map_HalfCluster["bkHalfCluster"].end(), axisCol.begin(), axisCol.end() );
+  }
+
+  if(settings.map_boolPars["DoAxisMerging"]){
+
+    for(int ihc=0; ihc<p_HalfClusterU.size(); ihc++){
+      std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_newAxisCol; m_newAxisCol.clear();
+
+      std::vector<const CaloHalfCluster*> m_recAxis = p_HalfClusterU[ihc]->getHalfClusterCol(settings.map_stringPars["OutputLongiClusName"]);
+      std::vector<const CaloHalfCluster*> m_trkAxis = p_HalfClusterU[ihc]->getHalfClusterCol(settings.map_stringPars["ReadinAxisName"]);
+
+      for(int iax=0; iax<m_recAxis.size(); iax++) m_newAxisCol.push_back(m_recAxis[iax]->Clone());
+      for(int iax=0; iax<m_trkAxis.size(); iax++) m_newAxisCol.push_back(m_trkAxis[iax]->Clone());
+
+//printf("  In HFClusterU #%d: rec axis %d, track axis %d. Print \n", ihc, m_recAxis.size(), m_trkAxis.size());
+//for(int i=0; i<m_recAxis.size(); i++)
+//  printf("    Rec axis %d: localMax size %d, track size %d, linked MC pid %d \n", i, m_recAxis[i]->getCluster().size(), m_recAxis[i]->getAssociatedTracks().size(), m_recAxis[i]->getLeadingMCP().getPDG());
+//for(int i=0; i<m_trkAxis.size(); i++)
+//  printf("    Trk axis %d: localMax size %d, track size %d, linked MC pid %d \n", i, m_trkAxis[i]->getCluster().size(), m_trkAxis[i]->getAssociatedTracks().size(), m_trkAxis[i]->getLeadingMCP().getPDG());
+
+      if(m_newAxisCol.size()>1) OverlapMerging(m_newAxisCol);
+//cout<<"  After merge: axis size "<<m_newAxisCol.size()<<endl;
+//for(int i=0; i<m_newAxisCol.size(); i++)
+//  printf("    Trk axis %d: localMax size %d, track size %d, linked MC pid %d \n", i, m_newAxisCol[i]->getCluster().size(), m_newAxisCol[i]->getAssociatedTracks().size(), m_newAxisCol[i]->getLeadingMCP().getPDG());
+
+      for(int iax=0; iax<m_newAxisCol.size(); iax++) p_HalfClusterU[ihc]->addHalfCluster(settings.map_stringPars["OutputMergedAxisName"], m_newAxisCol[iax].get());
+      m_datacol.map_HalfCluster["bkHalfCluster"].insert(m_datacol.map_HalfCluster["bkHalfCluster"].end(), m_newAxisCol.begin(), m_newAxisCol.end());
+    }
+
+    for(int ihc=0; ihc<p_HalfClusterV.size(); ihc++){
+      std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>> m_newAxisCol; m_newAxisCol.clear();
+
+      std::vector<const CaloHalfCluster*> m_recAxis = p_HalfClusterV[ihc]->getHalfClusterCol(settings.map_stringPars["OutputLongiClusName"]);
+      std::vector<const CaloHalfCluster*> m_trkAxis = p_HalfClusterV[ihc]->getHalfClusterCol(settings.map_stringPars["ReadinAxisName"]);
+
+      for(int iax=0; iax<m_recAxis.size(); iax++) m_newAxisCol.push_back(m_recAxis[iax]->Clone());
+      for(int iax=0; iax<m_trkAxis.size(); iax++) m_newAxisCol.push_back(m_trkAxis[iax]->Clone());
+
+
+//printf("  In HFClusterV #%d: rec axis %d, track axis %d. Print \n", ihc, m_recAxis.size(), m_trkAxis.size());
+//for(int i=0; i<m_recAxis.size(); i++)
+//  printf("    Rec axis %d: localMax size %d, track size %d, linked MC pid %d \n", i, m_recAxis[i]->getCluster().size(), m_recAxis[i]->getAssociatedTracks().size(), m_recAxis[i]->getLeadingMCP().getPDG());
+//for(int i=0; i<m_trkAxis.size(); i++)
+//  printf("    Trk axis %d: localMax size %d, track size %d, linked MC pid %d \n", i, m_trkAxis[i]->getCluster().size(), m_trkAxis[i]->getAssociatedTracks().size(), m_trkAxis[i]->getLeadingMCP().getPDG());
+
+      if(m_newAxisCol.size()>1) OverlapMerging(m_newAxisCol);
+
+
+//cout<<"  After merge: axis size "<<m_newAxisCol.size()<<endl;
+//for(int i=0; i<m_newAxisCol.size(); i++)
+//  printf("    Trk axis %d: localMax size %d, track size %d, linked MC pid %d \n", i, m_newAxisCol[i]->getCluster().size(), m_newAxisCol[i]->getAssociatedTracks().size(), m_newAxisCol[i]->getLeadingMCP().getPDG());
+      for(int iax=0; iax<m_newAxisCol.size(); iax++) p_HalfClusterV[ihc]->addHalfCluster(settings.map_stringPars["OutputMergedAxisName"], m_newAxisCol[iax].get());
+      m_datacol.map_HalfCluster["bkHalfCluster"].insert(m_datacol.map_HalfCluster["bkHalfCluster"].end(), m_newAxisCol.begin(), m_newAxisCol.end());
+    }
+
+  }
+
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode TruthPatternRecAlg::ClearAlgorithm(){
+  p_HalfClusterV.clear();
+  p_HalfClusterU.clear();  
+
+  return StatusCode::SUCCESS;
+};
+
+
+StatusCode TruthPatternRecAlg::OverlapMerging( std::vector<std::shared_ptr<PandoraPlus::CaloHalfCluster>>& m_axisCol ){
+  if(m_axisCol.size()<2) return StatusCode::SUCCESS;
+
+  for(int iax=0; iax<m_axisCol.size(); iax++){
+    const PandoraPlus::CaloHalfCluster* m_axis = m_axisCol[iax].get();
+    for(int jax=iax+1; jax<m_axisCol.size(); jax++){
+      const PandoraPlus::CaloHalfCluster* p_axis = m_axisCol[jax].get();
+
+      std::vector<const Calo1DCluster*> tmp_localMax = p_axis->getCluster();
+
+      int nsharedHits = 0;
+      for(int ihit=0; ihit<m_axis->getCluster().size(); ihit++)
+        if( find(tmp_localMax.begin(), tmp_localMax.end(), m_axis->getCluster()[ihit])!=tmp_localMax.end() ) nsharedHits++;
+
+
+      if( (m_axis->getCluster().size()<=p_axis->getCluster().size() && (float)nsharedHits/m_axis->getCluster().size()>settings.map_floatPars["th_overlap"] ) ||
+          (p_axis->getCluster().size()<m_axis->getCluster().size() && (float)nsharedHits/p_axis->getCluster().size()>settings.map_floatPars["th_overlap"] ) ){
+
+        m_axisCol[iax]->mergeHalfCluster( m_axisCol[jax].get() );
+
+        // track axis + neutral axis = track axis
+        int axis_type = m_axisCol[iax]->getType() + m_axisCol[jax]->getType();
+        m_axisCol[iax]->setType(axis_type);
+
+        //delete m_axisCol[jax]; m_axisCol[jax]=nullptr;
+        m_axisCol.erase(m_axisCol.begin()+jax);
+        jax--;
+        if(iax>jax+1) iax--;
+
+      }
+
+      p_axis=nullptr;
+    }
+    m_axis=nullptr;
+  }
+
+
+  return StatusCode::SUCCESS;
+}
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Algorithm/TruthTrackMatchingAlg.cpp b/Reconstruction/CrystalCaloRec/src/Algorithm/TruthTrackMatchingAlg.cpp
new file mode 100644
index 00000000..09a7d046
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Algorithm/TruthTrackMatchingAlg.cpp
@@ -0,0 +1,271 @@
+#ifndef _TRUTHTRKMATCHING_ALG_C
+#define _TRUTHTRKMATCHING_ALG_C
+
+#include "Algorithm/TruthTrackMatchingAlg.h"
+
+StatusCode TruthTrackMatchingAlg::ReadSettings(Settings& m_settings){
+  settings = m_settings;
+
+  //Initialize parameters
+  if(settings.map_stringPars.find("MatchType")==settings.map_stringPars.end())
+    settings.map_stringPars["MatchType"] = "LocalMax";  //LocalMax or Bar. TODO: implatement this option. 
+  if(settings.map_stringPars.find("ReadinLocalMaxName")==settings.map_stringPars.end())
+    settings.map_stringPars["ReadinLocalMaxName"] = "AllLocalMax";
+  if(settings.map_stringPars.find("OutputLongiClusName")==settings.map_stringPars.end())
+    settings.map_stringPars["OutputLongiClusName"] = "TruthTrackAxis";
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthTrackMatchingAlg::Initialize( PandoraPlusDataCol& m_datacol ){
+  m_TrackCol.clear();
+  p_HalfClusterV = nullptr;
+  p_HalfClusterU = nullptr;
+
+  for(int itrk=0; itrk<m_datacol.TrackCol.size(); itrk++ ) m_TrackCol.push_back(m_datacol.TrackCol[itrk].get());
+  p_HalfClusterU = &(m_datacol.map_HalfCluster["HalfClusterColU"]);
+  p_HalfClusterV = &(m_datacol.map_HalfCluster["HalfClusterColV"]);
+
+cout<<"TruthTrackMatchingAlg: Input track size "<<m_TrackCol.size()<<", HFClusterU size "<<p_HalfClusterU->size()<<", HFClusterV size "<<p_HalfClusterV->size()<<endl;
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthTrackMatchingAlg::RunAlgorithm( PandoraPlusDataCol& m_datacol ){
+  if( m_TrackCol.size()==0) return StatusCode::SUCCESS;
+
+  for(int itrk=0; itrk<m_TrackCol.size(); itrk++){
+
+    //Get MCP linked to the track
+    edm4hep::MCParticle mcp_trk = m_TrackCol[itrk]->getLeadingMCP();
+//cout<<"  Track #"<<itrk<<": MC pid "<<mcp_trk.getPDG()<<endl;
+ 
+    //Loop in HFCluster U
+    for(int ihf=0; ihf<p_HalfClusterU->size(); ihf++){
+      //Get Local max of the HalfCluster
+      std::vector<const PandoraPlus::Calo1DCluster*> localMaxColU = p_HalfClusterU->at(ihf).get()->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+
+      //Loop for the localMax: 
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> t_axis = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      for(int ilm=0; ilm<localMaxColU.size(); ilm++){
+        edm4hep::MCParticle mcp_lm = localMaxColU[ilm]->getLeadingMCP(); 
+        if(mcp_lm==mcp_trk)
+          t_axis->addUnit(localMaxColU[ilm]);
+        
+      }
+
+      // If the track does not match the Halfcluster, the track axis candidate will have no 1DCluster
+      if(!t_axis || t_axis->getCluster().size()==0)
+        continue;
+
+//cout<<"    HFCluster #"<<ihf<<" match with track. New axis localMax size "<<t_axis->getCluster().size()<<endl;
+      t_axis->getLinkedMCPfromUnit();
+      t_axis->addAssociatedTrack(m_TrackCol[itrk]);
+      t_axis->setType(10000); //Track-type axis.
+      m_TrackCol[itrk]->addAssociatedHalfClusterU( p_HalfClusterU->at(ihf).get() );
+      m_datacol.map_HalfCluster["bkHalfCluster"].push_back(t_axis);
+      p_HalfClusterU->at(ihf).get()->addHalfCluster(settings.map_stringPars["OutputLongiClusName"], t_axis.get());
+    }
+
+    //Loop in HFCluster V
+    for(int ihf=0; ihf<p_HalfClusterV->size(); ihf++){
+      //Get Local max of the HalfCluster
+      std::vector<const PandoraPlus::Calo1DCluster*> localMaxColV = p_HalfClusterV->at(ihf).get()->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+
+      //Loop for the localMax:
+      std::shared_ptr<PandoraPlus::CaloHalfCluster> t_axis = std::make_shared<PandoraPlus::CaloHalfCluster>();
+      for(int ilm=0; ilm<localMaxColV.size(); ilm++){
+        edm4hep::MCParticle mcp_lm = localMaxColV[ilm]->getLeadingMCP();
+        if(mcp_lm==mcp_trk)
+          t_axis->addUnit(localMaxColV[ilm]);
+        
+      }
+
+      // If the track does not match the Halfcluster, the track axis candidate will have no 1DCluster
+      if(!t_axis || t_axis->getCluster().size()==0)
+        continue;
+
+//cout<<"    HFCluster #"<<ihf<<" match with track. New axis localMax size "<<t_axis->getCluster().size()<<endl;
+      t_axis->getLinkedMCPfromUnit();
+      t_axis->addAssociatedTrack(m_TrackCol[itrk]);
+      t_axis->setType(10000); //Track-type axis.
+      m_TrackCol[itrk]->addAssociatedHalfClusterV( p_HalfClusterV->at(ihf).get() );
+      m_datacol.map_HalfCluster["bkHalfCluster"].push_back(t_axis);
+      p_HalfClusterV->at(ihf).get()->addHalfCluster(settings.map_stringPars["OutputLongiClusName"], t_axis.get());
+    }
+
+  }//End loop track
+
+//cout<<"Check HFClusters linked to the same track"<<endl;
+  //Loop track to check the associated cluster: merge clusters if they are associated to the same track.
+  std::vector<PandoraPlus::CaloHalfCluster*> tmp_deleteClus; tmp_deleteClus.clear();
+  for(auto &itrk : m_TrackCol){
+    std::vector<PandoraPlus::CaloHalfCluster*> m_matchedUCol = itrk->getAssociatedHalfClustersU();
+    std::vector<PandoraPlus::CaloHalfCluster*> m_matchedVCol = itrk->getAssociatedHalfClustersV();
+//cout<<"  In track "<<itrk<<": linked HFU size "<<m_matchedUCol.size()<<", linked HFV size "<<m_matchedVCol.size()<<endl;
+
+    if( m_matchedUCol.size()>1 ){
+      for(int i=1; i<m_matchedUCol.size(); i++){
+        m_matchedUCol[0]->mergeHalfCluster( m_matchedUCol[i] );
+        tmp_deleteClus.push_back(m_matchedUCol[i]);
+      }
+    }
+    if( m_matchedVCol.size()>1 ){
+      for(int i=1; i<m_matchedVCol.size(); i++){
+        m_matchedVCol[0]->mergeHalfCluster( m_matchedVCol[i] );
+        tmp_deleteClus.push_back(m_matchedVCol[i]);
+      }
+    }
+  }//End loop track
+
+
+  //Check vector: clean the merged clusters
+  for(int ihc=0; ihc<p_HalfClusterU->size(); ihc++){
+    if( find(tmp_deleteClus.begin(), tmp_deleteClus.end(), p_HalfClusterU->at(ihc).get())!=tmp_deleteClus.end() ){
+      p_HalfClusterU->erase(p_HalfClusterU->begin()+ihc);
+      ihc--;
+    }
+  }
+
+  for(int ihc=0; ihc<p_HalfClusterV->size(); ihc++){
+    if( find(tmp_deleteClus.begin(), tmp_deleteClus.end(), p_HalfClusterV->at(ihc).get())!=tmp_deleteClus.end() ){
+      p_HalfClusterV->erase(p_HalfClusterV->begin()+ihc);
+      ihc--;
+    }
+  }
+
+  //Clean the duplicated axes
+  for(int ihc=0; ihc<p_HalfClusterU->size(); ihc++){
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_axis = p_HalfClusterU->at(ihc)->getHalfClusterCol(settings.map_stringPars["OutputLongiClusName"]);
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_mergedAxis; m_mergedAxis.clear();
+
+//cout<<"  In HFCluster #"<<ihc<<": track axis size "<<m_axis.size();
+    if(m_axis.size()<=1){ 
+//cout<<endl;
+      continue;
+    }
+
+    std::map<const PandoraPlus::Track*, std::vector<const PandoraPlus::CaloHalfCluster*>> map_trk; map_trk.clear();
+    for(int iax=0; iax<m_axis.size(); iax++){
+      if(m_axis[iax]->getAssociatedTracks().size()==0){ 
+        m_mergedAxis.push_back(m_axis[iax]);
+        continue;
+      }
+      if(m_axis[iax]->getAssociatedTracks().size()>1)
+        std::cout<<"Warning: track axis has "<<m_axis[iax]->getAssociatedTracks().size()<<" matched tracks! Need to check! "<<endl;
+
+      map_trk[m_axis[iax]->getAssociatedTracks()[0]].push_back(m_axis[iax]);
+    }
+//cout<<"  map size "<<map_trk.size()<<", neutral axis size "<<m_mergedAxis.size()<<endl;;
+
+    for(auto &itrk: map_trk){
+//cout<<"  In track: axis size "<<itrk.second.size()<<endl;
+      if(itrk.second.size()==0) continue;
+      if(itrk.second.size()==1){ 
+        m_mergedAxis.push_back(itrk.second[0]);
+        continue;
+      }
+      PandoraPlus::CaloHalfCluster* p_axis = const_cast<PandoraPlus::CaloHalfCluster*>(itrk.second[0]);
+      for(int iax=1; iax<itrk.second.size(); iax++) p_axis->mergeHalfCluster(itrk.second[iax]);
+      m_mergedAxis.push_back(p_axis);
+    }
+    
+//cout<<". After merge: axis size "<<m_mergedAxis.size()<<endl;
+     p_HalfClusterU->at(ihc)->setHalfClusters(settings.map_stringPars["OutputLongiClusName"], m_mergedAxis);
+  }
+
+  for(int ihc=0; ihc<p_HalfClusterV->size(); ihc++){
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_axis = p_HalfClusterV->at(ihc)->getHalfClusterCol(settings.map_stringPars["OutputLongiClusName"]);
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_mergedAxis; m_mergedAxis.clear();
+
+//cout<<"  In HFCluster #"<<ihc<<": track axis size "<<m_axis.size();
+    if(m_axis.size()<=1){
+//cout<<endl;
+      continue;
+    }
+
+    std::map<const PandoraPlus::Track*, std::vector<const PandoraPlus::CaloHalfCluster*>> map_trk; map_trk.clear();
+    for(int iax=0; iax<m_axis.size(); iax++){
+      if(m_axis[iax]->getAssociatedTracks().size()==0){
+        m_mergedAxis.push_back(m_axis[iax]);
+        continue;
+      }
+      if(m_axis[iax]->getAssociatedTracks().size()>1)
+        std::cout<<"Warning: track axis has "<<m_axis[iax]->getAssociatedTracks().size()<<" matched tracks! Need to check! "<<endl;
+
+      map_trk[m_axis[iax]->getAssociatedTracks()[0]].push_back(m_axis[iax]);
+    }
+//cout<<"  map size "<<map_trk.size()<<", neutral axis size "<<m_mergedAxis.size()<<endl;;
+
+    for(auto &itrk: map_trk){
+//cout<<"  In track: axis size "<<itrk.second.size()<<endl;
+      if(itrk.second.size()==0) continue;
+      if(itrk.second.size()==1){
+        m_mergedAxis.push_back(itrk.second[0]);
+        continue;
+      }
+      PandoraPlus::CaloHalfCluster* p_axis = const_cast<PandoraPlus::CaloHalfCluster*>(itrk.second[0]);
+      for(int iax=1; iax<itrk.second.size(); iax++) p_axis->mergeHalfCluster(itrk.second[iax]);
+      m_mergedAxis.push_back(p_axis);
+    }
+
+//cout<<". After merge: axis size "<<m_mergedAxis.size()<<endl;
+     p_HalfClusterV->at(ihc)->setHalfClusters(settings.map_stringPars["OutputLongiClusName"], m_mergedAxis);
+  }
+
+
+
+/*
+cout<<"Track size: "<<m_TrackCol.size()<<endl;
+cout<<"Print HalfClusters and axis"<<endl;
+cout<<"  HalfClusterU size: "<<p_HalfClusterU->size()<<endl;
+for(int i=0; i<p_HalfClusterU->size(); i++){
+  std::vector<const PandoraPlus::Calo1DCluster*> m_1dcluster = p_HalfClusterU->at(i)->getCluster();
+  std::vector<const PandoraPlus::Calo1DCluster*> m_localMax = p_HalfClusterU->at(i).get()->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+  std::vector<const PandoraPlus::CaloHalfCluster*> m_axis = p_HalfClusterU->at(i)->getHalfClusterCol(settings.map_stringPars["OutputLongiClusName"]);
+  printf("    In HalfCluster #%d: 1D cluster size %d, localMax size %d, axis size %d \n", i, m_1dcluster.size(), m_localMax.size(), m_axis.size());
+
+  for(int iax=0; iax<m_axis.size(); iax++){
+    printf("      In axis #%d: localMax size %d \n", iax, m_axis[iax]->getCluster().size() );
+    for(int ilm=0; ilm<m_axis[iax]->getCluster().size(); ilm++){
+      printf("        LocalMax #%d: layer %d, towersize %d, towerID [%d, %d, %d], En %.4f, leadingMC pid %d, address %p \n", 
+        ilm, m_axis[iax]->getCluster()[ilm]->getDlayer(), m_axis[iax]->getCluster()[ilm]->getTowerID().size(), 
+        m_axis[iax]->getCluster()[ilm]->getTowerID()[0][0], m_axis[iax]->getCluster()[ilm]->getTowerID()[0][1],m_axis[iax]->getCluster()[ilm]->getTowerID()[0][2],
+        m_axis[iax]->getCluster()[ilm]->getEnergy(), m_axis[iax]->getCluster()[ilm]->getLeadingMCP().getPDG(), m_axis[iax]->getCluster()[ilm] );
+    }
+  }
+}
+
+cout<<"  HalfClusterV size: "<<p_HalfClusterV->size()<<endl;
+for(int i=0; i<p_HalfClusterV->size(); i++){
+  std::vector<const PandoraPlus::Calo1DCluster*> m_1dcluster = p_HalfClusterV->at(i)->getCluster();
+  std::vector<const PandoraPlus::Calo1DCluster*> m_localMax = p_HalfClusterV->at(i).get()->getLocalMaxCol(settings.map_stringPars["ReadinLocalMaxName"]);
+  std::vector<const PandoraPlus::CaloHalfCluster*> m_axis = p_HalfClusterV->at(i)->getHalfClusterCol(settings.map_stringPars["OutputLongiClusName"]);
+  printf("    In HalfCluster #%d: 1D cluster size %d, localMax size %d, axis size %d \n", i, m_1dcluster.size(), m_localMax.size(), m_axis.size());
+
+  for(int iax=0; iax<m_axis.size(); iax++){
+    printf("      In axis #%d: localMax size %d \n", iax, m_axis[iax]->getCluster().size() );
+    for(int ilm=0; ilm<m_axis[iax]->getCluster().size(); ilm++){
+      printf("        LocalMax #%d: layer %d, towersize %d, towerID [%d, %d, %d], En %.4f, leadingMC pid %d, address %p \n",
+        ilm, m_axis[iax]->getCluster()[ilm]->getDlayer(), m_axis[iax]->getCluster()[ilm]->getTowerID().size(),
+        m_axis[iax]->getCluster()[ilm]->getTowerID()[0][0], m_axis[iax]->getCluster()[ilm]->getTowerID()[0][1],m_axis[iax]->getCluster()[ilm]->getTowerID()[0][2],
+        m_axis[iax]->getCluster()[ilm]->getEnergy(), m_axis[iax]->getCluster()[ilm]->getLeadingMCP().getPDG(), m_axis[iax]->getCluster()[ilm] );
+    }
+  }
+}
+*/
+
+  return StatusCode::SUCCESS;
+};
+
+StatusCode TruthTrackMatchingAlg::ClearAlgorithm(){
+  m_TrackCol.clear();
+  p_HalfClusterV = nullptr;
+  p_HalfClusterU = nullptr;
+
+  return StatusCode::SUCCESS;
+};
+
+
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Objects/Calo1DCluster.cc b/Reconstruction/CrystalCaloRec/src/Objects/Calo1DCluster.cc
new file mode 100644
index 00000000..5ca151ca
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Objects/Calo1DCluster.cc
@@ -0,0 +1,249 @@
+#ifndef CALO_1DCLUSTER_C
+#define CALO_1DCLUSTER_C
+
+#include "Objects/CaloUnit.h"
+#include "Objects/Calo1DCluster.h"
+using namespace std; 
+namespace PandoraPlus{
+
+  void Calo1DCluster::Clear(){
+    Bars.clear();
+    Seeds.clear();
+    Energy=0.;
+    pos.SetXYZ(0.,0.,0.);
+    towerID.clear();
+    //m_modules.clear();
+    //m_parts.clear();
+    //m_staves.clear();
+  }
+
+  void Calo1DCluster::Clean() {
+    //for(int i=0; i<Bars.size(); i++) { delete Bars[i]; Bars[i]=NULL; }
+    //for(int i=0; i<Seeds.size(); i++) { delete Seeds[i]; Seeds[i]=NULL; }
+    //std::vector<int>().swap(m_modules);
+    //std::vector<int>().swap(m_parts);
+    //std::vector<int>().swap(m_staves);
+    Clear();
+  }
+
+  void Calo1DCluster::Check() {
+    for(int i=0; i<Bars.size(); i++)
+      if(!Bars[i]) { Bars.erase(Bars.begin()+i); i--; }
+    for(int i=0; i<Seeds.size(); i++)
+      if(!Seeds[i]) { Seeds.erase(Seeds.begin()+i); i--; }
+  }
+
+  std::shared_ptr<PandoraPlus::Calo1DCluster> Calo1DCluster::Clone() const{
+    std::shared_ptr<PandoraPlus::Calo1DCluster> p_cluster = std::make_shared<PandoraPlus::Calo1DCluster>();
+    p_cluster->setBars( Bars );
+    p_cluster->setSeeds( Seeds );
+    p_cluster->setIDInfo();
+    p_cluster->setLinkedMCP( MCParticleWeight );
+    for(int i=0; i<CousinClusters.size(); i++) p_cluster->addCousinCluster( CousinClusters[i] );
+    for(int i=0; i<ChildClusters.size(); i++) p_cluster->addChildCluster( ChildClusters[i] );
+   
+    return p_cluster;
+  }
+
+
+  bool Calo1DCluster::isNeighbor(const PandoraPlus::CaloUnit* m_bar) const
+  {
+    for(int i1d = 0; i1d<Bars.size(); i1d++){
+      if(Bars[i1d]->isNeighbor(m_bar)){ /*cout<<" isNeighbor! "<<endl;*/ return true;}
+    }
+    return false;
+  }
+
+  bool Calo1DCluster::inCluster(const PandoraPlus::CaloUnit* iBar) const{
+    return (find(Bars.begin(), Bars.end(), iBar)!=Bars.end() );
+  }
+
+  double Calo1DCluster::getEnergy() const{
+    double E=0;
+    for(int i=0;i<Bars.size();i++) E+=Bars[i]->getEnergy();
+    return E;
+  }
+
+  TVector3 Calo1DCluster::getPos() const{
+    TVector3 pos(0,0,0);
+    double Etot=getEnergy();
+    for(int i=0;i<Bars.size();i++) pos += Bars[i]->getPosition() * (Bars[i]->getEnergy()/Etot);
+    return pos;
+  }
+
+  double Calo1DCluster::getT1() const{
+    double T1=0;
+    double Etot = getEnergy();
+    for(int i=0;i<Bars.size();i++) T1 += (Bars[i]->getT1() * Bars[i]->getEnergy())/Etot;
+    return T1;
+  }
+
+  double Calo1DCluster::getT2() const{
+    double T2=0;
+    double Etot = getEnergy();
+    for(int i=0;i<Bars.size();i++) T2 += (Bars[i]->getT2() * Bars[i]->getEnergy())/Etot;
+    return T2;
+  }
+
+  double Calo1DCluster::getWidth() const{
+    TVector3 centPos = getPos();
+    double Etot = getEnergy();
+    double sigmax=0;
+    double sigmay=0;
+    double sigmaz=0;
+    for(int i=0; i<Bars.size(); i++){
+      double wi = Bars[i]->getEnergy()/Etot;
+      sigmax += wi*(Bars[i]->getPosition().x()-centPos.x())*(Bars[i]->getPosition().x()-centPos.x());
+      sigmay += wi*(Bars[i]->getPosition().y()-centPos.y())*(Bars[i]->getPosition().y()-centPos.y());
+      sigmaz += wi*(Bars[i]->getPosition().z()-centPos.z())*(Bars[i]->getPosition().z()-centPos.z());
+    }
+
+    if(sigmaz!=0) return sigmaz;  //sLayer=1, bars along z-axis.
+    else if(sigmax==0 && sigmaz==0) return sigmay; //Module 2, 6
+    else if(sigmay==0 && sigmaz==0) return sigmax; //Module 0, 4
+    else if(sigmax!=0 && sigmay!=0 && sigmaz==0) return sqrt(sigmax*sigmax+sigmay*sigmay); //Module 1, 3, 5, 7;
+    else return 0.;
+  }
+
+  double Calo1DCluster::getScndMoment() const{
+    if(Bars.size()<=1) return 0.;
+    TVector3 pos = getPos();
+    double Etot = getEnergy();
+    double scndM = 0;
+    for(int i=0;i<Bars.size();i++) scndM += (Bars[i]->getEnergy() * (pos-Bars[i]->getPosition()).Mag2()) / Etot;
+    return scndM;
+  }
+
+  bool Calo1DCluster::getGlobalRange( double& xmin,  double& ymin, double& zmin, double& xmax, double& ymax, double& zmax ) const{
+    if(Bars.size()==0) return false; 
+
+    std::vector<double> posx; posx.clear();
+    std::vector<double> posy; posx.clear();
+    std::vector<double> posz; posx.clear();
+    for(int i=0; i<Bars.size(); i++){  
+      posx.push_back(Bars[i]->getPosition().x());  
+      posy.push_back(Bars[i]->getPosition().y());
+      posz.push_back(Bars[i]->getPosition().z());
+    }
+    auto xminptr = std::min_element( std::begin(posx), std::end(posx) );
+    auto xmaxptr = std::max_element( std::begin(posx), std::end(posx) );
+    auto yminptr = std::min_element( std::begin(posy), std::end(posy) );
+    auto ymaxptr = std::max_element( std::begin(posy), std::end(posy) );
+    auto zminptr = std::min_element( std::begin(posz), std::end(posz) );
+    auto zmaxptr = std::max_element( std::begin(posz), std::end(posz) );
+
+    int slayer = Bars[0]->getSlayer(); 
+    if(slayer==0){
+      xmin = -9999.;    xmax = 9999.; 
+      ymin = -9999.;    ymax = 9999.; 
+      zmin = *zminptr-6.;  zmax = *zmaxptr+6.;
+    }
+    else if(slayer==1){
+      xmin = *xminptr-6;  xmax = *xmaxptr+6;
+      ymin = *yminptr-6;  ymax = *ymaxptr+6;
+      zmin = -9999;     zmax = 9999; 
+    }
+    else return false; 
+
+    return true;
+  }
+
+  int Calo1DCluster::getLeftEdge(){
+    std::sort(Bars.begin(), Bars.end());
+    if(Bars.size()==0) return -99;
+    int edge = -99;
+    if( Bars[0]->getSlayer()==0 ) edge = Bars[0]->getBar() + Bars[0]->getStave()*PandoraPlus::CaloUnit::NbarZ; 
+    if( Bars[0]->getSlayer()==1 ){ 
+      if(Bars[0]->getModule()%2==0) edge = Bars[0]->getBar() + Bars[0]->getModule()*(CaloUnit::NbarPhi_even[Bars[0]->getDlayer()]);
+      else edge = Bars[0]->getBar() + Bars[0]->getModule()*(CaloUnit::NbarPhi_odd[Bars[0]->getDlayer()]);
+    }
+    return edge;
+  }
+
+  int Calo1DCluster::getRightEdge(){
+    std::sort(Bars.begin(), Bars.end());
+    if(Bars.size()==0) return -99;
+    int edge = -99;
+    if( Bars[Bars.size()-1]->getSlayer()==0 ) edge = Bars[Bars.size()-1]->getBar() + Bars[Bars.size()-1]->getStave()*CaloUnit::NbarZ;
+    if( Bars[Bars.size()-1]->getSlayer()==1 ){ 
+      if(Bars[Bars.size()-1]->getModule()%2==0) edge = Bars[Bars.size()-1]->getBar() + Bars[Bars.size()-1]->getModule()*(CaloUnit::NbarPhi_even[Bars[Bars.size()-1]->getDlayer()]);
+      else edge = Bars[Bars.size()-1]->getBar() + Bars[Bars.size()-1]->getModule()*(CaloUnit::NbarPhi_odd[Bars[Bars.size()-1]->getDlayer()]);
+    }
+    return edge;
+  }
+
+  void Calo1DCluster::addUnit(const PandoraPlus::CaloUnit* _bar ) 
+  {
+    Bars.push_back(_bar);
+    std::vector<int> id(2);
+    id[0] = _bar->getModule();
+    id[1] = _bar->getStave();
+    if(find(towerID.begin(), towerID.end(), id)==towerID.end()) towerID.push_back(id);
+  }
+
+  void Calo1DCluster::deleteCousinCluster( const PandoraPlus::Calo1DCluster* _cl ){
+    auto iter = find( CousinClusters.begin(), CousinClusters.end(), _cl );
+    if(iter!=CousinClusters.end()) CousinClusters.erase( iter );
+  }
+
+
+  void Calo1DCluster::setIDInfo() {
+    for(int i=0; i<Bars.size(); i++){
+      std::vector<int> id(2);
+      id[0] = Bars[i]->getModule();
+      id[1] = Bars[i]->getStave();
+      if(find(towerID.begin(), towerID.end(), id)==towerID.end()) towerID.push_back(id);  
+    }
+  }
+
+  void Calo1DCluster::setSeed(){
+    double maxE = -1; 
+    int index = -1; 
+    for(int i=0; i<Bars.size(); ++i){
+      if(Bars[i]->getEnergy()>maxE) { maxE = Bars[i]->getEnergy(); index = i; }
+    }
+    //if(Seeds.size()==0 && index>=0 && maxE>0.005 ) Seeds.push_back( Bars[index] );
+    if(Seeds.size()==0 && index>=0 ) Seeds.push_back( Bars[index] );
+  }
+
+
+  std::vector< std::pair<edm4hep::MCParticle, float> > Calo1DCluster::getLinkedMCPfromUnit(){
+    MCParticleWeight.clear();
+
+    std::map<edm4hep::MCParticle, float> map_truthP_totE; map_truthP_totE.clear();
+    for(auto ibar : Bars ){
+      for(auto ipair : ibar->getLinkedMCP()) map_truthP_totE[ipair.first] += ibar->getEnergy()*ipair.second;
+    }
+
+    for(auto imcp: map_truthP_totE){
+      MCParticleWeight.push_back( std::make_pair(imcp.first, imcp.second/getEnergy()) );
+    }
+
+    return MCParticleWeight;
+  }
+
+  edm4hep::MCParticle Calo1DCluster::getLeadingMCP() const{
+    float maxWeight = -1.;
+    edm4hep::MCParticle mcp;
+    for(auto& iter: MCParticleWeight){
+      if(iter.second>maxWeight){
+        mcp = iter.first;
+        maxWeight = iter.second;
+      }
+    }
+
+    return mcp;
+  }
+
+  float Calo1DCluster::getLeadingMCPweight() const{
+    float maxWeight = -1.;
+    for(auto& iter: MCParticleWeight){
+      if(iter.second>maxWeight){
+        maxWeight = iter.second;
+      }
+    }
+    return maxWeight;
+  }
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Objects/Calo2DCluster.cc b/Reconstruction/CrystalCaloRec/src/Objects/Calo2DCluster.cc
new file mode 100644
index 00000000..adf23240
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Objects/Calo2DCluster.cc
@@ -0,0 +1,122 @@
+#ifndef CALO_2DCLUSTER_C
+#define CALO_2DCLUSTER_C
+
+#include "Objects/Calo2DCluster.h"
+#include <cmath>
+using namespace std;
+namespace PandoraPlus{
+
+  void Calo2DCluster::Clear() {
+    towerID.clear();
+    barUCol.clear();
+    barVCol.clear();
+    barShowerUCol.clear(); 
+    barShowerVCol.clear();  
+  }
+
+  void Calo2DCluster::ClearShower() {
+    barShowerUCol.clear(); 
+    barShowerVCol.clear();
+  }
+
+  void Calo2DCluster::Check(){
+    for(int i=0; i<barUCol.size(); i++)
+      if(!barUCol[i]) { barUCol.erase(barUCol.begin()+i); i--; }	  
+    for(int i=0; i<barVCol.size(); i++)
+      if(!barVCol[i]) { barVCol.erase(barVCol.begin()+i); i--; }
+    for(int i=0; i<barShowerUCol.size(); i++)
+      if(!barShowerUCol[i]) { barShowerUCol.erase(barShowerUCol.begin()+i); i--; }
+    for(int i=0; i<barShowerVCol.size(); i++)
+      if(!barShowerVCol[i]) { barShowerVCol.erase(barShowerVCol.begin()+i); i--; }
+  	}
+
+  void Calo2DCluster::Clean(){
+    //for(int i=0; i<barUCol.size(); i++) { delete barUCol[i]; barUCol[i]=NULL; }
+    //for(int i=0; i<barVCol.size(); i++) { delete barVCol[i]; barVCol[i]=NULL; }
+    //for(int i=0; i<barShowerUCol.size(); i++) { delete barShowerUCol[i]; barShowerUCol[i]=NULL; }
+    //for(int i=0; i<barShowerVCol.size(); i++) { delete barShowerVCol[i]; barShowerVCol[i]=NULL; }
+    //std::vector<int>().swap(m_modules);
+    //std::vector<int>().swap(m_parts);
+    //std::vector<int>().swap(m_staves);
+    Clear();
+  	}
+
+  bool Calo2DCluster::isNeighbor(const PandoraPlus::Calo1DCluster* m_1dcluster) const{
+    assert(m_1dcluster->getBars().size() > 0 && getCluster().at(0)->getBars().size()>0 );
+    if(m_1dcluster->getDlayer() != getDlayer()  ) return false; 
+
+    for(int i=0; i<m_1dcluster->getTowerID().size(); i++){
+		for(int j=0; j<towerID.size(); j++){
+      if( m_1dcluster->getTowerID()[i]==towerID[j] ) return true;
+    }}
+    
+    return false;
+  }
+
+ 
+  void Calo2DCluster::addUnit(const Calo1DCluster* _1dcluster)
+  {
+    if(_1dcluster->getSlayer()==0) barShowerUCol.push_back(_1dcluster);
+    if(_1dcluster->getSlayer()==1) barShowerVCol.push_back(_1dcluster); 
+    for(int ib=0; ib<_1dcluster->getBars().size(); ib++) addBar(_1dcluster->getBars()[ib]);
+
+    std::vector< std::vector<int> > id = _1dcluster->getTowerID();
+    for(int ii=0; ii<id.size(); ii++) 
+      if( find(towerID.begin(), towerID.end(), id[ii])==towerID.end() ) towerID.push_back(id[ii]);
+
+  }
+
+  std::vector<const Calo1DCluster*> Calo2DCluster::getCluster() const{ 
+    std::vector<const Calo1DCluster*> m_1dclusters; 
+    m_1dclusters.clear();
+    m_1dclusters.insert(m_1dclusters.end(),barShowerUCol.begin(),barShowerUCol.end());
+    return  m_1dclusters;
+  }
+	
+  std::vector<const CaloUnit*> Calo2DCluster::getBars() const
+  {
+	std::vector<const CaloUnit*> results;
+	results.clear();
+	std::vector<const Calo1DCluster*> m_1dclusters; 
+	m_1dclusters.clear();
+	m_1dclusters.insert(m_1dclusters.end(),barShowerUCol.begin(),barShowerUCol.end());
+	for(int i=0; i<m_1dclusters.size(); i++)
+	{
+		for(int j=0; j<m_1dclusters.at(i)->getBars().size(); j++)
+		{
+			results.push_back(m_1dclusters.at(i)->getBars().at(j));
+		}
+	}
+	return results;
+  }
+	
+  double Calo2DCluster::getEnergy() const {
+    double sumE = 0;
+
+    for(int m=0; m<barShowerUCol.size(); m++) sumE += barShowerUCol[m]->getEnergy();
+    for(int m=0; m<barShowerVCol.size(); m++) sumE += barShowerVCol[m]->getEnergy();
+    return sumE;
+  }
+
+
+  TVector3 Calo2DCluster::getPos() const{
+    TVector3 m_pos(0, 0, 0); 
+    if(towerID.size()==0) return m_pos; 
+
+    float rotAngle = -towerID[0][0]*TMath::TwoPi()/PandoraPlus::CaloUnit::Nmodule;
+    TVector3 m_vecX(0., 0., 0.);  
+    TVector3 m_vecY(0., 0., 0.);
+    for(int m=0; m<barShowerUCol.size(); m++) m_vecX += barShowerUCol[m]->getPos();
+    m_vecX *= (1./barShowerUCol.size());
+    for(int m=0; m<barShowerVCol.size(); m++) m_vecY += barShowerVCol[m]->getPos();
+    m_vecY *= (1./barShowerVCol.size());
+    m_vecX.RotateZ(rotAngle);
+    m_vecY.RotateZ(rotAngle);
+    m_pos.SetXYZ( m_vecY.x(), (m_vecX.y()+m_vecY.y())/2 , m_vecX.z() );
+    m_pos.RotateZ(-rotAngle);
+    return m_pos;
+  }
+
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Objects/Calo3DCluster.cc b/Reconstruction/CrystalCaloRec/src/Objects/Calo3DCluster.cc
new file mode 100644
index 00000000..adc8dbc3
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Objects/Calo3DCluster.cc
@@ -0,0 +1,436 @@
+#ifndef CALO_3DCLUSTER_C
+#define CALO_3DCLUSTER_C
+
+#include "Objects/Calo3DCluster.h"
+#include <cmath>
+using namespace std;
+namespace PandoraPlus{
+
+  void Calo3DCluster::Clear() 
+  {
+	  m_2dclusters.clear();
+	  m_towers.clear();
+    towerID.clear(); 
+	  //m_modules.clear();
+	  //m_parts.clear();
+	  //m_staves.clear();
+  }
+
+  void Calo3DCluster::Clean(){
+    //for(int i=0; i<m_2dclusters.size(); i++) { delete m_2dclusters[i]; m_2dclusters[i]=NULL; }
+    //for(int i=0; i<m_towers.size(); i++) { delete m_towers[i]; m_towers[i]=NULL; }
+    //std::vector<int>().swap(m_modules);
+    //std::vector<int>().swap(m_parts);
+    //std::vector<int>().swap(m_staves);
+    Clear();
+  }
+
+  void Calo3DCluster::Check()
+  {
+    for(int i=0; i<m_2dclusters.size(); i++)
+    if(!m_2dclusters[i]) { m_2dclusters.erase(m_2dclusters.begin()+i); i--; }
+    //for(int i=0; i<m_towers.size(); i++)
+    //if(!m_towers[i]) { m_towers.erase(m_towers.begin()+i); i--; }
+  }
+
+
+  std::shared_ptr<PandoraPlus::Calo3DCluster> Calo3DCluster::Clone() const{
+    std::shared_ptr<PandoraPlus::Calo3DCluster> m_clus = std::make_shared<PandoraPlus::Calo3DCluster>();
+    m_clus->setCaloHits(hits);
+    m_clus->setClusters(m_2dclusters);
+    m_clus->setTowers(m_towers);
+    for(auto iter:towerID)
+      m_clus->addTowerID(iter);
+    for(auto iter:map_localMaxU) 
+      for(int ilm=0; ilm<iter.second.size(); ilm++) m_clus->addLocalMaxU(iter.first, iter.second[ilm]);
+    for(auto iter:map_localMaxV)
+      for(int ilm=0; ilm<iter.second.size(); ilm++) m_clus->addLocalMaxV(iter.first, iter.second[ilm]);
+    for(auto iter:map_halfClusUCol)
+      for(int ihf=0; ihf<iter.second.size(); ihf++) m_clus->addHalfClusterU(iter.first, iter.second[ihf]);
+    for(auto iter:map_halfClusVCol)
+      for(int ihf=0; ihf<iter.second.size(); ihf++) m_clus->addHalfClusterV(iter.first, iter.second[ihf]);
+    for(auto iter:m_TrackCol)
+      m_clus->addAssociatedTrack(iter);
+    m_clus->setLinkedMCP(MCParticleWeight);
+    m_clus->FitAxis();
+
+    return m_clus;
+  }
+
+
+  //TODO: This function is sooooooo time consumeing now!!
+/*  bool Calo3DCluster::isNeighbor(const PandoraPlus::Calo2DCluster* m_2dcluster) const
+  {
+
+    //Inner module
+    for(int i=0; i<m_2dcluster->getTowerID().size(); i++){
+    for(int j=0; j<towerID.size(); j++){
+      if( m_2dcluster->getTowerID()[i]==towerID[j] ) return true;
+    }}
+
+    //for(int i=0; i<m_2dcluster->getModules().size(); i++){
+    //  for(int j=0; j<m_modules.size(); j++){
+    //    if(m_2dcluster->getModules().at(i)==m_modules.at(j) && m_2dcluster->getParts().at(i)==m_parts.at(j) && m_2dcluster->getStaves().at(i)==m_staves.at(j)){
+    //      return true;
+    //  }}
+    //}
+
+    //Inner module but in adjacent Dlayer
+    for(int i=0; i<m_2dclusters.size(); i++){
+      //If not in the same module:
+      bool inSameModule = false; 
+      std::vector<int> ID_module; ID_module.clear();
+      for(int it=0; it<towerID.size(); it++) ID_module.push_back(towerID[it][0]); 
+      for(int it=0; it<m_2dcluster->getTowerID().size(); it++){
+        if( find(ID_module.begin(), ID_module.end(), m_2dcluster->getTowerID()[it][0])!=ID_module.end() ) { inSameModule=true; break; }
+      }
+      if(!inSameModule) continue;
+
+      //If not the adjacent Dlayer: 
+      if(fabs(m_2dcluster->getDlayer()-m_2dclusters[i]->getDlayer())>1) continue; 
+
+      std::vector<const PandoraPlus::CaloUnit*> m_EdgeBarU_clus1; m_EdgeBarU_clus1.clear(); //Income 2DCluster
+      std::vector<const PandoraPlus::CaloUnit*> m_EdgeBarV_clus1; m_EdgeBarV_clus1.clear();
+      std::vector<const PandoraPlus::CaloUnit*> m_EdgeBarU_clus2; m_EdgeBarU_clus2.clear(); //2DCluster in present 3DCluster. 
+      std::vector<const PandoraPlus::CaloUnit*> m_EdgeBarV_clus2; m_EdgeBarV_clus2.clear();
+      
+      for(int ib=0; ib<m_2dcluster->getBarUCol().size(); ib++){
+        if(m_2dcluster->getBarUCol()[ib]->isAtLowerEdgeZ() || m_2dcluster->getBarUCol()[ib]->isAtUpperEdgeZ()) m_EdgeBarU_clus1.push_back(m_2dcluster->getBarUCol()[ib]);
+      }
+      for(int ib=0; ib<m_2dcluster->getBarVCol().size(); ib++){
+        if(m_2dcluster->getBarVCol()[ib]->isAtLowerEdgePhi() || m_2dcluster->getBarVCol()[ib]->isAtUpperEdgePhi()) m_EdgeBarV_clus1.push_back(m_2dcluster->getBarVCol()[ib]);
+      }
+      for(int ib=0; ib<m_2dclusters[i]->getBarUCol().size(); ib++){
+        if(m_2dclusters[i]->getBarUCol()[ib]->isAtLowerEdgeZ() || m_2dclusters[i]->getBarUCol()[ib]->isAtUpperEdgeZ()) m_EdgeBarU_clus2.push_back(m_2dclusters[i]->getBarUCol()[ib]);
+      }
+      for(int ib=0; ib<m_2dclusters[i]->getBarVCol().size(); ib++){
+        if(m_2dclusters[i]->getBarVCol()[ib]->isAtLowerEdgePhi() || m_2dclusters[i]->getBarVCol()[ib]->isAtUpperEdgePhi()) m_EdgeBarV_clus2.push_back(m_2dclusters[i]->getBarVCol()[ib]);
+      }
+
+
+      for(int ib1=0; ib1<m_EdgeBarU_clus1.size(); ib1++){
+        for(int ib2=0; ib2<m_2dclusters[i]->getBarVCol().size(); ib2++){
+          if( m_EdgeBarU_clus1[ib1]->getPart()==m_2dclusters[i]->getBarVCol()[ib2]->getPart() && abs(m_EdgeBarU_clus1[ib1]->getStave()-m_2dclusters[i]->getBarVCol()[ib2]->getStave())==1 ) 
+            return true; 
+      }}
+      for(int ib1=0; ib1<m_EdgeBarV_clus1.size(); ib1++){
+        for(int ib2=0; ib2<m_2dclusters[i]->getBarUCol().size(); ib2++){
+          if( m_EdgeBarV_clus1[ib1]->getStave()==m_2dclusters[i]->getBarUCol()[ib2]->getStave() && abs(m_EdgeBarV_clus1[ib1]->getPart()-m_2dclusters[i]->getBarUCol()[ib2]->getPart())==1 )
+            return true;
+      }}
+      for(int ib1=0; ib1<m_EdgeBarU_clus2.size(); ib1++){
+        for(int ib2=0; ib2<m_2dcluster->getBarVCol().size(); ib2++){
+          if( m_EdgeBarU_clus2[ib1]->getPart()==m_2dcluster->getBarVCol()[ib2]->getPart() && abs(m_EdgeBarU_clus2[ib1]->getStave()-m_2dcluster->getBarVCol()[ib2]->getStave())==1 )
+            return true;
+      }}
+      for(int ib1=0; ib1<m_EdgeBarV_clus2.size(); ib1++){
+        for(int ib2=0; ib2<m_2dcluster->getBarUCol().size(); ib2++){
+          if( m_EdgeBarV_clus2[ib1]->getStave()==m_2dcluster->getBarUCol()[ib2]->getStave() && abs(m_EdgeBarV_clus2[ib1]->getPart()-m_2dcluster->getBarUCol()[ib2]->getPart())==1 )
+            return true;
+      }}
+
+	  }
+
+    //Over modules
+    std::vector<const PandoraPlus::CaloUnit*> bars_2d = m_2dcluster->getBars();
+    for(int ib2d=0; ib2d<bars_2d.size(); ib2d++){
+      for(int ic=0; ic<m_2dclusters.size(); ic++){
+        for(int ib3d=0; ib3d<m_2dclusters[ic]->getBars().size(); ib3d++){
+          if(bars_2d[ib2d]->isModuleAdjacent(m_2dclusters[ic]->getBars()[ib3d])) return true;
+        }
+      }
+    }
+	  return false;
+  }
+*/
+
+  void Calo3DCluster::addUnit(const Calo2DCluster* _2dcluster){
+
+    m_2dclusters.push_back(_2dcluster);
+    std::vector< std::vector<int> > id = _2dcluster->getTowerID();
+    for(int ii=0; ii<id.size(); ii++)
+      if( find(towerID.begin(), towerID.end(), id[ii])==towerID.end() ) towerID.push_back(id[ii]);
+  }
+
+
+  void Calo3DCluster::mergeCluster( const PandoraPlus::Calo3DCluster* _clus ){
+    for(int i=0; i<_clus->getCluster().size(); i++)
+      addUnit( _clus->getCluster()[i] );
+
+    for(int i=0; i<_clus->getTowers().size(); i++)
+      addTower( _clus->getTowers()[i] );
+
+    for(int itrk=0; itrk<_clus->getAssociatedTracks().size(); itrk++){
+      if( find(m_TrackCol.begin(), m_TrackCol.end(), _clus->getAssociatedTracks()[itrk])==m_TrackCol.end() ) 
+        m_TrackCol.push_back( _clus->getAssociatedTracks()[itrk] );
+    }
+    
+    for(auto iter:_clus->getLocalMaxUMap() ){
+      if(map_localMaxU.find(iter.first)==map_localMaxU.end()) map_localMaxU[iter.first] = iter.second;
+      else{
+        for(int il=0; il<iter.second.size(); il++)
+          if( find(map_localMaxU[iter.first].begin(), map_localMaxU[iter.first].end(), iter.second[il])==map_localMaxU[iter.first].end() )
+            map_localMaxU[iter.first].push_back( iter.second[il] );
+      }
+    }
+    for(auto iter:_clus->getLocalMaxVMap() ){
+      if(map_localMaxV.find(iter.first)==map_localMaxV.end()) map_localMaxV[iter.first] = iter.second;
+      else{
+        for(int il=0; il<iter.second.size(); il++)
+          if( find(map_localMaxV[iter.first].begin(), map_localMaxV[iter.first].end(), iter.second[il])==map_localMaxV[iter.first].end() )
+            map_localMaxV[iter.first].push_back( iter.second[il] );
+      }
+    }
+
+    for(auto iter:_clus->getHalfClusterUMap() ){
+      if(map_halfClusUCol.find(iter.first)==map_halfClusUCol.end()) map_halfClusUCol[iter.first] = iter.second;
+      else{
+        for(int il=0; il<iter.second.size(); il++)
+          if( find(map_halfClusUCol[iter.first].begin(), map_halfClusUCol[iter.first].end(), iter.second[il])==map_halfClusUCol[iter.first].end() )
+            map_halfClusUCol[iter.first].push_back( iter.second[il] );
+      }
+    }
+    for(auto iter:_clus->getHalfClusterVMap() ){
+      if(map_halfClusVCol.find(iter.first)==map_halfClusVCol.end()) map_halfClusVCol[iter.first] = iter.second;
+      else{
+        for(int il=0; il<iter.second.size(); il++)
+          if( find(map_halfClusVCol[iter.first].begin(), map_halfClusVCol[iter.first].end(), iter.second[il])==map_halfClusVCol[iter.first].end() )
+            map_halfClusVCol[iter.first].push_back( iter.second[il] );
+      }
+    }
+
+  }
+
+
+  std::vector<const PandoraPlus::CaloUnit*> Calo3DCluster::getBars() const{
+    std::vector<const PandoraPlus::CaloUnit*> results; results.clear();
+    for(int i=0; i<m_2dclusters.size(); i++){
+      for(int j=0; j<m_2dclusters.at(i)->getBars().size(); j++){
+        results.push_back(m_2dclusters.at(i)->getBars().at(j));
+      }
+    }
+    return results;
+  }
+
+  double Calo3DCluster::getHitsE() const{
+    double en=0;
+    for(int i=0;i<hits.size(); i++) en+=hits[i]->getEnergy();
+    return en;
+  }
+
+  double Calo3DCluster::getEnergy() const{
+    double result = 0;
+    for(int m=0; m<m_2dclusters.size(); m++)
+      result += m_2dclusters[m]->getEnergy();
+    return result;
+  }
+
+  double Calo3DCluster::getLongiE() const{
+    double en=0;
+    for(auto iter: map_halfClusUCol){
+      if(iter.first!="LinkedLongiCluster") continue;
+      for(auto iclus: iter.second)
+        en += iclus->getEnergy();
+    }
+    for(auto iter: map_halfClusVCol){
+      if(iter.first!="LinkedLongiCluster") continue;
+      for(auto iclus: iter.second)
+        en += iclus->getEnergy();
+    }
+    return en;
+  }
+
+  TVector3 Calo3DCluster::getHitCenter() const{
+    TVector3 vec(0,0,0);
+    double totE = getHitsE();
+    for(int i=0;i<hits.size(); i++){
+       TVector3 v_cent = hits[i]->getPosition();
+       vec += v_cent * (hits[i]->getEnergy()/totE);
+    }
+    return vec;
+  }
+
+  TVector3 Calo3DCluster::getShowerCenter() const{
+    TVector3 spos(0,0,0);
+    double totE = 0.;
+    for(int i=0;i<m_2dclusters.size(); i++){ spos += m_2dclusters[i]->getPos()*m_2dclusters[i]->getEnergy(); totE += m_2dclusters[i]->getEnergy(); }
+    spos = spos*(1./totE);
+    return spos;
+  }
+
+  int Calo3DCluster::getBeginningDlayer() const{
+    int re_dlayer = -99;
+    std::vector<int> dlayers; dlayers.clear();
+    if(hits.size()!=0)  for(int ih=0; ih<hits.size(); ih++) dlayers.push_back(hits[ih]->getLayer());
+    else                for(int ish=0; ish<m_2dclusters.size(); ish++) dlayers.push_back(m_2dclusters[ish]->getDlayer());
+    re_dlayer = *std::min_element(dlayers.begin(), dlayers.end());
+
+    return re_dlayer;
+  }
+
+  int Calo3DCluster::getEndDlayer() const{
+    int re_dlayer = -99;
+    std::vector<int> dlayers; dlayers.clear();
+    if(hits.size()!=0)  for(int ih=0; ih<hits.size(); ih++) dlayers.push_back(hits[ih]->getLayer());
+    else                for(int ish=0; ish<m_2dclusters.size(); ish++) dlayers.push_back(m_2dclusters[ish]->getDlayer());
+    re_dlayer = *std::max_element(dlayers.begin(), dlayers.end());
+
+    return re_dlayer;
+  }
+
+  double Calo3DCluster::getDepthToECALSurface() const{
+    TVector3 pos = getShowerCenter();
+    return pos.Perp() - PandoraPlus::CaloUnit::ecal_innerR;
+  }
+
+
+  std::vector<const PandoraPlus::CaloHalfCluster*> Calo3DCluster::getHalfClusterUCol(std::string name) const {
+    std::vector<const CaloHalfCluster*> emptyCol; emptyCol.clear(); 
+    if(map_halfClusUCol.find(name)!=map_halfClusUCol.end()) emptyCol = map_halfClusUCol.at(name);
+    return emptyCol;
+  }
+
+  std::vector<const PandoraPlus::CaloHalfCluster*> Calo3DCluster::getHalfClusterVCol(std::string name) const {
+    std::vector<const CaloHalfCluster*> emptyCol; emptyCol.clear(); 
+    if(map_halfClusVCol.find(name)!=map_halfClusVCol.end()) emptyCol = map_halfClusVCol.at(name);
+    return emptyCol;
+  }
+
+  std::vector<const PandoraPlus::Calo1DCluster*> Calo3DCluster::getLocalMaxUCol(std::string name) const{
+    std::vector<const Calo1DCluster*> emptyCol; emptyCol.clear(); 
+    if(map_localMaxU.find(name)!=map_localMaxU.end()) emptyCol = map_localMaxU.at(name);
+    return emptyCol;
+  }
+
+  std::vector<const PandoraPlus::Calo1DCluster*> Calo3DCluster::getLocalMaxVCol(std::string name) const{
+    std::vector<const Calo1DCluster*> emptyCol; emptyCol.clear(); 
+    if(map_localMaxV.find(name)!=map_localMaxV.end()) emptyCol = map_localMaxV.at(name);
+    return emptyCol; 
+  }
+
+  std::vector< std::pair<edm4hep::MCParticle, float> > Calo3DCluster::getLinkedMCPfromHFCluster(std::string name){
+    MCParticleWeight.clear();
+
+    std::map<edm4hep::MCParticle, float> map_truthP_totE; map_truthP_totE.clear();
+    for(auto icl: map_halfClusUCol[name]){
+      for(auto ipair: icl->getLinkedMCP()) map_truthP_totE[ipair.first] += icl->getEnergy()*ipair.second;
+    }
+    for(auto icl: map_halfClusVCol[name]){
+      for(auto ipair: icl->getLinkedMCP()) map_truthP_totE[ipair.first] += icl->getEnergy()*ipair.second;
+    }
+
+    for(auto imcp: map_truthP_totE){
+      MCParticleWeight.push_back( std::make_pair(imcp.first, imcp.second/getLongiE()) );
+    }
+
+    return MCParticleWeight;
+  }
+
+  std::vector< std::pair<edm4hep::MCParticle, float> > Calo3DCluster::getLinkedMCPfromHit(){
+    MCParticleWeight.clear();
+
+    std::map<edm4hep::MCParticle, float> map_truthP_totE; map_truthP_totE.clear();
+    for(auto ihit: hits){
+      for(auto ipair: ihit->getLinkedMCP()) map_truthP_totE[ipair.first] += ihit->getEnergy()*ipair.second;
+    }
+
+    for(auto imcp: map_truthP_totE){
+      MCParticleWeight.push_back( std::make_pair(imcp.first, imcp.second/getHitsE()) );
+    }
+
+    return MCParticleWeight;
+  }  
+
+  edm4hep::MCParticle Calo3DCluster::getLeadingMCP() const{
+    float maxWeight = -1.;
+    edm4hep::MCParticle mcp;
+    for(auto& iter: MCParticleWeight){
+      if(iter.second>maxWeight){
+        mcp = iter.first;
+        maxWeight = iter.second;
+      }
+    }
+
+    return mcp;
+  }
+
+  float Calo3DCluster::getLeadingMCPweight() const{
+    float maxWeight = -1.;
+    for(auto& iter: MCParticleWeight){
+      if(iter.second>maxWeight){
+        maxWeight = iter.second;
+      }
+    }
+    return maxWeight;
+  }
+
+
+  void Calo3DCluster::FitAxis(){
+    if(m_2dclusters.size()==0) axis.SetXYZ(0,0,0);
+
+    else if(m_2dclusters.size()==1){
+      axis = m_2dclusters[0]->getPos();
+      axis *= 1./axis.Mag();
+    }
+
+    else if( m_2dclusters.size()==2 ){
+      TVector3 pos1 = m_2dclusters[0]->getPos();
+      TVector3 pos2 = m_2dclusters[1]->getPos();
+
+      axis = ( pos1.Mag()>pos2.Mag() ? pos1-pos2 : pos2-pos1 );
+      axis *= 1./axis.Mag();
+    }
+
+    else{
+      trackFitter.clear();
+      //track->setImpactParameter(0., 0.); //fix dr=0, dz=0.
+
+      double barAngle = (towerID[0][0]+2)*TMath::Pi()/4.;
+      double posErr = 10./sqrt(12);
+      if(barAngle>=TMath::TwoPi()) barAngle = barAngle-TMath::TwoPi();
+      trackFitter.setBarAngle(barAngle);
+      for(int is=0;is<m_2dclusters.size();is++){
+        TVector3 pos_barsX = m_2dclusters[is]->getShowerUCol()[0]->getPos(); //U
+        TVector3 pos_barsY = m_2dclusters[is]->getShowerVCol()[0]->getPos(); //Z
+//printf("\t DEBUG: input pointX (%.3f, %.3f, %.3f) \n", barsX.getPos().x(), barsX.getPos().y(), barsX.getPos().z());
+//printf("\t DEBUG: input pointY (%.3f, %.3f, %.3f) \n", barsY.getPos().x(), barsY.getPos().y(), barsY.getPos().z());
+        trackFitter.setGlobalPoint(1, pos_barsX.x(), posErr, pos_barsX.y(), posErr, pos_barsX.z(), posErr);
+        trackFitter.setGlobalPoint(0, pos_barsY.x(), posErr, pos_barsY.y(), posErr, pos_barsY.z(), posErr);
+      }
+      trackFitter.fitTrack();
+      double fitPhi =   trackFitter.getTrkPar(2);
+      double fitTheta = trackFitter.getTrkPar(3);
+//printf("\t DEBUG: fitted phi and theta: %.3f \t %.3f \n", fitPhi, fitTheta);
+
+      axis.SetPhi(fitPhi);
+      axis.SetTheta(fitTheta);
+      axis.SetMag(1.);
+    }
+  }
+
+  //void Calo3DCluster::FitAxisHit(){
+  //}
+
+  //void Calo3DCluster::FitProfile(){
+  //}
+
+  bool Calo3DCluster::isHCALNeighbor(const PandoraPlus::CaloHit* m_hit) const
+  {
+    for(int i=0; i<hits.size(); i++)
+    {
+      if( sqrt( pow(m_hit->getPosition().x()-hits[i]->getPosition().x(),2) + pow(m_hit->getPosition().y()-hits[i]->getPosition().y(),2) + pow(m_hit->getPosition().z()-hits[i]->getPosition().z(),2) ) <= sqrt(pow(40,2)*3) )
+        return true;
+    }
+    return false;
+  }
+
+  void Calo3DCluster::setCaloHitsFrom2DCluster(){
+    hits.clear();
+    for(int i=0; i<m_2dclusters.size(); i++){
+      std::vector<const CaloHit*> tmp_hits = m_2dclusters[i]->getCaloHits();
+      hits.insert(hits.end(), tmp_hits.begin(), tmp_hits.end());
+    }
+  }
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Objects/CaloHalfCluster.cc b/Reconstruction/CrystalCaloRec/src/Objects/CaloHalfCluster.cc
new file mode 100644
index 00000000..e5a46395
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Objects/CaloHalfCluster.cc
@@ -0,0 +1,453 @@
+#ifndef CALO_HALFCLUSTER_C
+#define CALO_HALFCLUSTER_C
+
+#include "Objects/CaloHalfCluster.h"
+
+namespace PandoraPlus{
+
+  void CaloHalfCluster::Clear() {
+    type = -1; 
+    slayer=-99;
+    towerID.clear(); 
+    m_1dclusters.clear(); 
+    map_localMax.clear();
+    map_halfClusCol.clear();
+  }
+
+  void CaloHalfCluster::Check(){
+    for(int i=0; i<m_1dclusters.size(); i++)
+      if(!m_1dclusters[i]) { m_1dclusters.erase(m_1dclusters.begin()+i); i--; }
+  }
+
+  void CaloHalfCluster::Clean(){
+    //for(int i=0; i<m_1dclusters.size(); i++){ delete m_1dclusters[i]; m_1dclusters[i]=NULL; }
+    for(auto it: map_localMax) { 
+      //for(auto iter: it.second){ delete it.second[iter]; it.second[iter]=NULL; }
+      it.second.clear();
+    }
+    for(auto it: map_halfClusCol) {
+      //for(auto iter: it.second){ delete it.second[iter]; it.second[iter]=NULL; }
+      it.second.clear();
+    }
+    Clear();
+  }
+
+  std::shared_ptr<PandoraPlus::CaloHalfCluster> CaloHalfCluster::Clone() const{
+    std::shared_ptr<PandoraPlus::CaloHalfCluster> m_clus = std::make_shared<PandoraPlus::CaloHalfCluster>();
+    for(int i1d=0; i1d<m_1dclusters.size(); i1d++)  m_clus->addUnit(m_1dclusters[i1d]);
+    for(int itrk=0; itrk<m_TrackCol.size(); itrk++) m_clus->addAssociatedTrack(m_TrackCol[itrk]);
+    for(auto iter:map_localMax)     m_clus->setLocalMax( iter.first, iter.second );
+    for(auto iter:map_halfClusCol)  m_clus->setHalfClusters( iter.first, iter.second );
+    m_clus->setLinkedMCP( MCParticleWeight );
+    m_clus->setHoughPars( Hough_alpha, Hough_rho );
+    m_clus->setIntercept( Hough_intercept );
+    m_clus->setType( type );
+
+    return m_clus;
+  }
+
+  bool CaloHalfCluster::isNeighbor(const PandoraPlus::Calo1DCluster* m_1dcluster) const{
+    assert(m_1dcluster->getBars().size() > 0 && getCluster().at(0)->getBars().size()>0 );
+    if(m_1dcluster->getSlayer() != getSlayer()  ) return false; 
+
+    for(int i1d=0; i1d<m_1dclusters.size(); i1d++)
+    {
+      for(int ibar=0; ibar<m_1dcluster->getBars().size(); ibar++)
+      {
+        for(int jbar=0; jbar<m_1dclusters.at(i1d)->getBars().size(); jbar++)
+        {
+          if( m_1dcluster->getBars().at(ibar)->isLongiNeighbor(m_1dclusters.at(i1d)->getBars().at(jbar)) ) return true;
+          //if( m_1dcluster->getBars().at(ibar)->isLongiModuleAdjacent(m_1dclusters.at(i1d)->getBars().at(jbar)) ) return true;
+        }
+      }
+    }
+    
+    return false;
+  }
+
+ 
+  void CaloHalfCluster::addUnit(const Calo1DCluster* _1dcluster)
+  {
+    if( find( m_1dclusters.begin(), m_1dclusters.end(), _1dcluster)!=m_1dclusters.end() )
+      //std::cout<<"ERROR: attempt to add an existing 1DCluster into HalfCluster! Skip it "<<std::endl;
+      return; 
+    else{
+      if(_1dcluster->getSlayer()==0) slayer=0;
+      if(_1dcluster->getSlayer()==1) slayer=1;
+      m_1dclusters.push_back(_1dcluster);
+   
+      std::vector< std::vector<int> > id = _1dcluster->getTowerID();
+      for(int ii=0; ii<id.size(); ii++)
+        if( find(towerID.begin(), towerID.end(), id[ii])==towerID.end() ) towerID.push_back(id[ii]);    
+   
+      fitAxis("");
+    }
+  }
+  
+
+  void CaloHalfCluster::deleteUnit(const Calo1DCluster* _1dcluster)
+  {
+    auto iter = find( m_1dclusters.begin(), m_1dclusters.end(), _1dcluster); 
+    if( iter != m_1dclusters.end() ){
+      m_1dclusters.erase(iter);
+      fitAxis("");
+    }
+  }
+
+
+  std::vector<const CaloUnit*> CaloHalfCluster::getBars() const
+  {
+    std::vector<const CaloUnit*> results;
+    results.clear();
+    for(int i=0; i<m_1dclusters.size(); i++)
+    {
+      for(int j=0; j<m_1dclusters.at(i)->getBars().size(); j++)
+      {
+        results.push_back(m_1dclusters.at(i)->getBars().at(j));
+      }
+    }
+    return results;
+  }
+
+
+  double CaloHalfCluster::getEnergy() const {
+    double sumE = 0;
+    for(int i=0; i<m_1dclusters.size(); i++)
+    {
+      sumE = sumE + m_1dclusters.at(i)->getEnergy();
+    }
+    return sumE;
+  }
+
+  TVector3 CaloHalfCluster::getPos() const{
+    TVector3 pos(0, 0, 0);
+    double Etot = getEnergy();
+    for(int i=0; i<m_1dclusters.size(); i++){
+      TVector3 m_pos(m_1dclusters[i]->getPos().x(), m_1dclusters[i]->getPos().y(), m_1dclusters[i]->getPos().z());
+      pos += m_pos * (m_1dclusters[i]->getEnergy()/Etot);
+    }
+    return pos;
+  }
+
+  TVector3 CaloHalfCluster::getEnergyCenter() const{
+    TVector3 pos = getPos();
+    double maxEn = -99;
+    for(int i=0; i<m_1dclusters.size(); i++){
+      if(m_1dclusters[i]->getEnergy()>maxEn){
+        maxEn = m_1dclusters[i]->getEnergy();
+        pos = m_1dclusters[i]->getPos();
+      }
+    }
+    return pos;
+  }
+
+  std::vector<int> CaloHalfCluster::getEnergyCenterTower() const{
+    std::vector<int> tower = getTowerID()[0];
+    double maxEn = -99;
+    for(int i=0; i<m_1dclusters.size(); i++){
+      if(m_1dclusters[i]->getEnergy()>maxEn){
+        maxEn = m_1dclusters[i]->getEnergy();
+        tower = m_1dclusters[i]->getTowerID()[0];
+      }
+    }
+    return tower;
+  }
+
+  std::vector<const PandoraPlus::Calo1DCluster*> CaloHalfCluster::getLocalMaxCol(std::string name) const{
+    std::vector<const PandoraPlus::Calo1DCluster*> emptyCol; emptyCol.clear(); 
+    if(map_localMax.find(name)!=map_localMax.end()) emptyCol = map_localMax.at(name);
+    return emptyCol;
+  }
+
+  std::vector<const Calo1DCluster*> CaloHalfCluster::getAllLocalMaxCol() const{
+    std::vector<const PandoraPlus::Calo1DCluster*> emptyCol; emptyCol.clear();
+    for(auto &iter: map_localMax) emptyCol.insert(emptyCol.end(), iter.second.begin(), iter.second.end());
+    return emptyCol;
+  }
+
+  std::vector<const PandoraPlus::CaloHalfCluster*> CaloHalfCluster::getHalfClusterCol(std::string name) const{
+    std::vector<const PandoraPlus::CaloHalfCluster*> emptyCol; emptyCol.clear(); 
+    if(map_halfClusCol.find(name)!=map_halfClusCol.end()) emptyCol = map_halfClusCol.at(name);
+    return emptyCol;
+  }
+
+  std::vector<const PandoraPlus::CaloHalfCluster*> CaloHalfCluster::getAllHalfClusterCol() const{
+    std::vector<const PandoraPlus::CaloHalfCluster*> emptyCol; emptyCol.clear();
+    for(auto &iter: map_halfClusCol) emptyCol.insert(emptyCol.end(), iter.second.begin(), iter.second.end());
+    return emptyCol;
+  }
+
+  std::vector<const PandoraPlus::Calo1DCluster*> CaloHalfCluster::getClusterInLayer(int _layer) const{
+    std::vector<const PandoraPlus::Calo1DCluster*> outShowers; outShowers.clear();
+    for(int i=0; i<m_1dclusters.size(); i++)
+      if(m_1dclusters[i]->getDlayer()==_layer) outShowers.push_back(m_1dclusters[i]);
+    return outShowers;
+  }
+
+
+  int CaloHalfCluster::getBeginningDlayer() const{
+    int Lstart = 99;
+    for(int i=0; i<m_1dclusters.size(); i++)
+      if(m_1dclusters[i]->getDlayer()<Lstart) Lstart = m_1dclusters[i]->getDlayer();
+    if(Lstart==99) return -99;
+    else return Lstart;
+  }
+
+
+  int CaloHalfCluster::getEndDlayer() const{
+    int Lend = -99;
+    for(int i=0; i<m_1dclusters.size(); i++)
+      if(m_1dclusters[i]->getDlayer()>Lend) Lend = m_1dclusters[i]->getDlayer();
+    return Lend;
+  }  
+
+  bool CaloHalfCluster::isContinue() const{
+    int ly_max = -1;
+    int ly_min = 99;
+    std::vector<int> vec_layers; vec_layers.clear();
+    for(int il=0; il<m_1dclusters.size(); il++){
+      vec_layers.push_back(m_1dclusters[il]->getDlayer());
+      if(m_1dclusters[il]->getDlayer()>ly_max) ly_max = m_1dclusters[il]->getDlayer();
+      if(m_1dclusters[il]->getDlayer()<ly_min) ly_min = m_1dclusters[il]->getDlayer();
+    }
+
+    bool flag = true;
+    std::sort(vec_layers.begin(), vec_layers.end());
+    for(int il=0; il<vec_layers.size() && vec_layers[il]!=ly_max; il++)
+      if( find(vec_layers.begin(), vec_layers.end(), vec_layers[il]+1) == vec_layers.end() ){ flag=false; break; }
+
+    return flag;
+  }
+
+  bool CaloHalfCluster::isContinueN(int n) const{
+    if(n<=0) return true;
+
+    int ly_max = -1;
+    int ly_min = 99;
+    std::vector<int> vec_layers; vec_layers.clear();
+    for(int il=0; il<m_1dclusters.size(); il++){
+      vec_layers.push_back(m_1dclusters[il]->getDlayer());
+      if(m_1dclusters[il]->getDlayer()>ly_max) ly_max = m_1dclusters[il]->getDlayer();
+      if(m_1dclusters[il]->getDlayer()<ly_min) ly_min = m_1dclusters[il]->getDlayer();
+    }
+    if(n>(ly_max-ly_min+1)) return false;
+
+    bool flag = false;
+    std::sort(vec_layers.begin(), vec_layers.end());
+    for(int il=0; il<vec_layers.size(); il++){
+      bool fl_continueN = true;
+      for(int in=1; in<n; in++)
+        if( find(vec_layers.begin(), vec_layers.end(), vec_layers[il]+in)==vec_layers.end() ) { fl_continueN=false; break; }
+
+      if(fl_continueN) {flag = true; break;}
+    }
+    return flag;
+  }
+
+
+  bool CaloHalfCluster::isSubset(const CaloHalfCluster* clus) const{
+
+    for(int is=0; is<clus->getCluster().size(); is++)
+      if( find(m_1dclusters.begin(), m_1dclusters.end(), clus->getCluster()[is])==m_1dclusters.end() ) {return false; }
+
+    if(m_1dclusters.size() > clus->getCluster().size())
+      return true;
+    else{
+      if( TMath::Abs(Hough_rho) <= TMath::Abs(clus->getHoughRho()) ) { return true; }
+      else { return false; }
+    }
+  }
+
+  double CaloHalfCluster::OverlapRatioE( const CaloHalfCluster* clus) const{
+    double Eshare = 0.;
+    for(int is=0; is<clus->getCluster().size(); is++)
+      if( find(m_1dclusters.begin(), m_1dclusters.end(), clus->getCluster()[is])!=m_1dclusters.end() ) Eshare += clus->getCluster()[is]->getEnergy();
+
+    return Eshare / getEnergy() ;
+  }
+
+
+  void CaloHalfCluster::fitAxis( std::string name ){
+    std::vector<const PandoraPlus::Calo1DCluster*> barShowerCol; barShowerCol.clear();
+    if(!name.empty() && map_localMax.find(name)!=map_localMax.end() ) barShowerCol = map_localMax.at(name);
+    else barShowerCol = m_1dclusters; 
+
+    if(barShowerCol.size()==0){ axis.SetXYZ(0,0,0); return; }
+    else if(barShowerCol.size()==1){
+      axis.SetXYZ(barShowerCol[0]->getPos().x(), barShowerCol[0]->getPos().y(), barShowerCol[0]->getPos().z());
+      axis = axis.Unit();
+      return;
+    }
+    else if(barShowerCol.size()==2){
+      TVector3 rpos = barShowerCol.back()->getPos() - barShowerCol.front()->getPos();
+      axis.SetXYZ( rpos.x(), rpos.y(), rpos.z() );
+      axis = axis.Unit();
+      return;
+    }
+    else{
+      track->clear();
+      double barAngle = (barShowerCol[0]->getTowerID()[0][0]+PandoraPlus::CaloUnit::Nmodule/4.)*2*TMath::Pi()/PandoraPlus::CaloUnit::Nmodule;
+      double posErr = PandoraPlus::CaloUnit::barsize/sqrt(12);
+      if(barAngle>=TMath::TwoPi()) barAngle = barAngle-TMath::TwoPi();
+      track->setBarAngle(barAngle);
+      for(int is=0; is<barShowerCol.size(); is++){
+        TVector3 b_pos = barShowerCol[is]->getPos();
+        track->setGlobalPoint(0, b_pos.x(), posErr, b_pos.y(), posErr, b_pos.z(), posErr);
+        track->setGlobalPoint(1, b_pos.x(), posErr, b_pos.y(), posErr, b_pos.z(), posErr);
+      }
+
+      track->fitTrack();
+      double fitPhi = track->getPhi();
+      double fitTheta = track->getTheta();
+
+      trk_dr = track->getDr();
+      trk_dz = track->getDz();
+      axis.SetPhi(fitPhi);
+      axis.SetTheta(fitTheta);
+      axis.SetMag(1.);
+    }
+  }
+
+  void CaloHalfCluster::mergeHalfCluster(const CaloHalfCluster* clus ){
+    for(int is=0; is<clus->getCluster().size(); is++){
+      if( find(m_1dclusters.begin(), m_1dclusters.end(), clus->getCluster()[is])==m_1dclusters.end() )
+        addUnit( clus->getCluster()[is] );
+    }
+
+    for(int itrk=0; itrk<clus->getAssociatedTracks().size(); itrk++){
+      if( find(m_TrackCol.begin(), m_TrackCol.end(), clus->getAssociatedTracks()[itrk])==m_TrackCol.end() )
+        m_TrackCol.push_back( clus->getAssociatedTracks()[itrk] );
+    }
+
+    for(auto iter:clus->getLocalMaxMap() ){
+      if(map_localMax.find(iter.first)==map_localMax.end()) map_localMax[iter.first] = iter.second;
+      else{
+        for(int il=0; il<iter.second.size(); il++)
+          if( find(map_localMax[iter.first].begin(), map_localMax[iter.first].end(), iter.second[il])==map_localMax[iter.first].end() ) 
+            map_localMax[iter.first].push_back( iter.second[il] );
+      }
+    }
+
+    for(auto iter:clus->getHalfClusterMap() ){
+      if(map_halfClusCol.find(iter.first)==map_halfClusCol.end()) map_halfClusCol[iter.first] = iter.second;
+      else{
+        for(int il=0; il<iter.second.size(); il++)
+          if( find(map_halfClusCol[iter.first].begin(), map_halfClusCol[iter.first].end(), iter.second[il])==map_halfClusCol[iter.first].end() )
+            map_halfClusCol[iter.first].push_back( iter.second[il] );
+      }
+    }
+
+    fitAxis("");
+  }
+
+
+  void CaloHalfCluster::mergeClusterInLayer(){
+    //std::map<std::vector<int>, std::vector<const Calo1DCluster*>> map_showerinTowers; map_showerinTowers.clear();
+    //for(int is=0; is<m_1dclusters.size(); is++){
+    //  if(m_1dclusters[is]->getNseeds()==0) continue;
+    //  std::vector<int> m_seedID(3);
+    //  m_seedID[0] = m_1dclusters[is]->getSeeds()[0]->getModule();
+    //  m_seedID[1] = m_1dclusters[is]->getSeeds()[0]->getPart();
+    //  m_seedID[2] = m_1dclusters[is]->getSeeds()[0]->getStave();
+    //  map_showerinTowers[m_seedID].push_back( m_1dclusters[is] );
+    //}
+
+    //m_1dclusters.clear();    
+    //for(auto itower : map_showerinTowers){
+
+      std::map<int, std::vector<const Calo1DCluster*>> showersinlayer; showersinlayer.clear();
+      //for(int is=0; is<itower.second.size(); is++)
+        //showersinlayer[itower.second[is]->getDlayer()].push_back( itower.second[is] );
+      for(int is=0; is<m_1dclusters.size(); is++)
+        showersinlayer[m_1dclusters[is]->getDlayer()].push_back( m_1dclusters[is] );
+
+      m_1dclusters.clear();    
+      for(auto &iter : showersinlayer){
+        std::vector<const Calo1DCluster*> tmp_shower = iter.second; 
+        if(tmp_shower.size()>1){
+          //Merge following showers into the first: 
+   
+          PandoraPlus::CaloUnit* p_seed = nullptr;
+          float maxEseed = -99;
+          for(int is=0; is<tmp_shower[0]->getNseeds(); is++){
+            if(tmp_shower[0]->getSeeds()[is]->getEnergy()>maxEseed){
+              p_seed = const_cast<PandoraPlus::CaloUnit*>(tmp_shower[0]->getSeeds()[is]);
+              maxEseed=tmp_shower[0]->getSeeds()[is]->getEnergy();
+            }
+          }
+          
+          for(int is=1; is<tmp_shower.size(); is++){
+            //Find maxE as seed
+            for(int iseed=0; iseed<tmp_shower[is]->getNseeds(); iseed++){
+              if(tmp_shower[is]->getSeeds()[iseed]->getEnergy()>maxEseed){ 
+                p_seed = const_cast<PandoraPlus::CaloUnit*>(tmp_shower[is]->getSeeds()[iseed]); 
+                maxEseed=tmp_shower[is]->getSeeds()[iseed]->getEnergy(); 
+              }
+            }
+            //Bars
+            for(int icl=0; icl<tmp_shower[is]->getBars().size(); icl++){
+              const_cast<PandoraPlus::Calo1DCluster*>(tmp_shower[0])->addUnit(tmp_shower[is]->getBars()[icl]);
+            }
+            tmp_shower.erase(tmp_shower.begin()+is);
+            is--;
+          }
+          if(p_seed){
+            std::vector<const PandoraPlus::CaloUnit*> tmp_seed; tmp_seed.clear();
+            tmp_seed.push_back(p_seed);
+            const_cast<PandoraPlus::Calo1DCluster*>(tmp_shower[0])->setSeeds( tmp_seed );
+          }
+        }
+   
+        addUnit(tmp_shower[0]);
+      }
+    //}
+  }
+
+
+  void CaloHalfCluster::deleteCousinCluster( const PandoraPlus::CaloHalfCluster* _cl ){
+    auto iter = find( map_halfClusCol["CousinCluster"].begin(), map_halfClusCol["CousinCluster"].end(), _cl );
+    if(iter!=map_halfClusCol["CousinCluster"].end()) map_halfClusCol["CousinCluster"].erase( iter );
+  }
+
+
+  std::vector< std::pair<edm4hep::MCParticle, float> > CaloHalfCluster::getLinkedMCPfromUnit(){
+    MCParticleWeight.clear();
+
+    std::map<edm4hep::MCParticle, float> map_truthP_totE; map_truthP_totE.clear();
+    for(auto ish : m_1dclusters ){
+      for(auto ibar : ish->getBars()){
+        for(auto ipair : ibar->getLinkedMCP()) map_truthP_totE[ipair.first] += ibar->getEnergy()*ipair.second;
+      }
+    }
+
+    for(auto imcp: map_truthP_totE){
+      MCParticleWeight.push_back( std::make_pair(imcp.first, imcp.second/getEnergy()) );
+    }
+
+    return MCParticleWeight;
+  }
+
+  edm4hep::MCParticle CaloHalfCluster::getLeadingMCP() const{
+    float maxWeight = -1.;
+    edm4hep::MCParticle mcp; 
+    for(auto& iter: MCParticleWeight){
+      if(iter.second>maxWeight){
+        mcp = iter.first;
+        maxWeight = iter.second;
+      }
+    }
+
+    return mcp;
+  }
+
+  float CaloHalfCluster::getLeadingMCPweight() const{
+    float maxWeight = -1.;
+    for(auto& iter: MCParticleWeight){
+      if(iter.second>maxWeight){
+        maxWeight = iter.second;
+      }
+    }
+    return maxWeight;
+  }
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Objects/CaloHit.cc b/Reconstruction/CrystalCaloRec/src/Objects/CaloHit.cc
new file mode 100644
index 00000000..baac05bb
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Objects/CaloHit.cc
@@ -0,0 +1,46 @@
+#ifndef CALO_HIT_C
+#define CALO_HIT_C
+
+#include "Objects/CaloHit.h"
+
+namespace PandoraPlus{
+
+
+  std::shared_ptr<CaloHit> CaloHit::Clone() const{
+    std::shared_ptr<CaloHit> _newhit = std::make_shared<CaloHit>();
+    _newhit->setcellID(cellID);
+    _newhit->setLayer(layer);
+    _newhit->setPosition(position);
+    _newhit->setEnergy(energy);
+    _newhit->setParentShower(ParentShower);
+    _newhit->setLinkedMCP(MCParticleWeight);
+    edm4hep::CalorimeterHit originhit = getOriginHit();
+    _newhit->setOriginHit( originhit );
+    return _newhit;
+  }
+
+  edm4hep::MCParticle CaloHit::getLeadingMCP() const{
+    float maxWeight = -1.;
+    edm4hep::MCParticle mcp;
+    for(auto& iter: MCParticleWeight){
+      if(iter.second>maxWeight){
+        mcp = iter.first;
+        maxWeight = iter.second;
+      }
+    }
+
+    return mcp;
+  }
+
+  float CaloHit::getLeadingMCPweight() const{
+    float maxWeight = -1.;
+    for(auto& iter: MCParticleWeight){
+      if(iter.second>maxWeight){
+        maxWeight = iter.second;
+      }
+    }
+    return maxWeight;
+  }
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Objects/CaloUnit.cc b/Reconstruction/CrystalCaloRec/src/Objects/CaloUnit.cc
new file mode 100644
index 00000000..694ecc71
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Objects/CaloUnit.cc
@@ -0,0 +1,188 @@
+#ifndef CALOBAR_C
+#define CALOBAR_C
+
+#include "Objects/CaloUnit.h"
+#include <cmath>
+
+namespace PandoraPlus{
+
+  bool CaloUnit::isNeighbor(const CaloUnit* x) const {
+    if( cellID==x->getcellID() ) return false;
+    if( system!=x->getSystem() || dlayer!=x->getDlayer() || slayer!=x->getSlayer() ) return false; 
+
+    if( module==x->getModule() && stave==x->getStave() && fabs(bar - x->getBar())==1 ) return true;
+    if( slayer==0 && stave==x->getStave() && (fabs(module-x->getModule())<=1 || fabs(module-x->getModule())==Nmodule-1 ) && fabs(bar - x->getBar())<=1 ) return true;
+    if( slayer==1 && module==x->getModule() && fabs(stave-x->getStave())<=1 && fabs(bar - x->getBar())<=1 ) return true;
+
+    if( isAtLowerEdgeZ()   && x->isAtUpperEdgeZ()   && x->getStave()==stave-1 && (fabs(x->getModule()-module)<=1 || fabs(x->getModule()-module)==Nmodule-1 ) ) return true;
+    if( isAtUpperEdgeZ()   && x->isAtLowerEdgeZ()   && x->getStave()==stave+1 && (fabs(x->getModule()-module)<=1 || fabs(x->getModule()-module)==Nmodule-1 ) ) return true;
+    if( isAtLowerEdgePhi() && x->isAtUpperEdgePhi() && (x->getModule()==module-1 || (x->getModule() - module)==Nmodule-1 ) && fabs(x->getStave()-stave)<=1 ) return true;
+    if( isAtUpperEdgePhi() && x->isAtLowerEdgePhi() && (x->getModule()==module+1 || (module - x->getModule())==Nmodule-1 ) && fabs(x->getStave()-stave)<=1 ) return true;
+
+    return false;
+  }
+
+  bool CaloUnit::isLongiNeighbor(const CaloUnit* x) const {
+    if( cellID==x->getcellID() ) return false;
+    if( system!=x->getSystem() || slayer!=x->getSlayer() ) return false; 
+    if( abs(dlayer-x->getDlayer())!=1) return false;
+    if( slayer==0 )
+    {
+      if( stave==x->getStave() && (fabs(module-x->getModule())<=1 || fabs(module-x->getModule())==Nmodule-1 ) && fabs(bar - x->getBar())<=1 ) return true;
+      if( isAtLowerEdgeZ()   && x->isAtUpperEdgeZ()   && x->getStave()==stave-1 && (fabs(x->getModule()-module)<=1 || fabs(x->getModule()-module)==Nmodule-1 ) ) return true;
+      if( isAtUpperEdgeZ()   && x->isAtLowerEdgeZ()   && x->getStave()==stave+1 && (fabs(x->getModule()-module)<=1 || fabs(x->getModule()-module)==Nmodule-1 ) ) return true;
+    }
+    if( slayer==1 )
+    {
+      if(module==x->getModule() && fabs(stave-x->getStave())<=1 && fabs(bar - x->getBar())<=1 ) return true;
+      if( isAtLowerEdgePhi() && x->isAtUpperEdgePhi() && (x->getModule()==module-1 || (x->getModule() - module)==Nmodule-1 ) && fabs(x->getStave()-stave)<=1 ) return true;
+      if( isAtUpperEdgePhi() && x->isAtLowerEdgePhi() && (x->getModule()==module+1 || (module - x->getModule())==Nmodule-1 ) && fabs(x->getStave()-stave)<=1 ) return true;
+    }
+    
+    return false;
+  }
+
+  bool CaloUnit::isAtLowerEdgePhi() const{
+    return ( slayer==1 && bar==0 ); 
+  }
+
+
+  bool CaloUnit::isAtUpperEdgePhi() const{
+    bool isEdge = false; 
+    if(module%2==0){
+      if(slayer==1 && bar==NbarPhi_even[dlayer]-1) isEdge = true;
+    }
+    else{
+      if(slayer==1 && bar==NbarPhi_odd[dlayer]-1) isEdge = true;
+    }
+    return isEdge; 
+  }
+
+
+  bool CaloUnit::isAtLowerEdgeZ() const{
+    return ( slayer==0 && bar==0 );
+  }
+
+
+  bool CaloUnit::isAtUpperEdgeZ() const{
+    return ( slayer==0 && bar==NbarZ-1 );
+  }
+
+/*
+  bool CaloUnit::isModuleAdjacent( const CaloUnit* x ) const{
+
+    if(module==x->getModule()) return false;
+    if( fabs(module-x->getModule())>1 && abs(module-x->getModule())!=Nmodule-1 ) return false; 
+
+    int dlayer_lo, slayer_lo, part_lo, stave_lo, bar_lo;
+    int dlayer_hi, slayer_hi, part_hi, stave_hi, bar_hi;
+    if( module - x->getModule()==1 || x->getModule()-module==Nmodule-1 ) {
+      dlayer_lo=x->getDlayer(); slayer_lo=x->getSlayer(); part_lo=x->getPart(); stave_lo=x->getStave(); bar_lo=x->getBar(); 
+      dlayer_hi=dlayer; slayer_hi=slayer; part_hi=part; stave_hi=stave; bar_hi=bar; 
+    }
+    else{
+      dlayer_lo=dlayer; slayer_lo=slayer; part_lo=part; stave_lo=stave; bar_lo=bar;
+      dlayer_hi=x->getDlayer(); slayer_hi=x->getSlayer(); part_hi=x->getPart(); stave_hi=x->getStave(); bar_hi=x->getBar();
+    }
+
+
+    if( dlayer_lo!=1 || slayer_lo!=0 || part_lo!=Npart || part_hi!=1 ) return false;
+    if( slayer_hi==0 ){
+      if( (stave_lo==stave_hi && abs(bar_lo-bar_hi)<=1) || 
+          (abs(stave_lo-stave_hi)==1 && isAtLowerEdgeZ() && x->isAtUpperEdgeZ() ) ||
+          (abs(stave_lo-stave_hi)==1 && isAtUpperEdgeZ() && x->isAtLowerEdgeZ() )  ) return true; 
+    }
+    else if( slayer_hi==1 && stave_lo==stave_hi && bar_hi==1 ) return true;
+
+    return false; 
+  }
+*/
+
+  std::shared_ptr<CaloUnit> CaloUnit::Clone() const{
+    std::shared_ptr<CaloUnit> m_bar = std::make_shared<CaloUnit>();
+    m_bar->setcellID(cellID);
+    m_bar->setcellID( system, module, stave, dlayer, slayer, bar );
+    m_bar->setPosition(position);
+    m_bar->setQ(Q1, Q2);
+    m_bar->setT(T1, T2);
+    for(auto ilink : MCParticleWeight) m_bar->addLinkedMCP(ilink);
+    return m_bar;
+  }
+
+  edm4hep::MCParticle CaloUnit::getLeadingMCP() const{
+    float maxWeight = -1.;
+    edm4hep::MCParticle mcp;
+    for(auto& iter: MCParticleWeight){
+      if(iter.second>maxWeight){
+        mcp = iter.first;
+        maxWeight = iter.second;
+      }
+    }
+
+    return mcp;
+  }
+
+  float CaloUnit::getLeadingMCPweight() const{
+    float maxWeight = -1.;
+    for(auto& iter: MCParticleWeight){
+      if(iter.second>maxWeight){
+        maxWeight = iter.second;
+      }
+    }
+    return maxWeight;
+  }
+
+/*  bool CaloUnit::isLongiModuleAdjacent( const CaloUnit* x ) const{
+
+    if(module==x->getModule()) return false;
+    if( fabs(module-x->getModule())>1 && abs(module-x->getModule())!=Nmodule-1 ) return false; 
+
+    int dlayer_lo, slayer_lo, part_lo, stave_lo, bar_lo;
+    int dlayer_hi, slayer_hi, part_hi, stave_hi, bar_hi;
+
+    if( module - x->getModule()==1 || x->getModule()-module==Nmodule-1 ) {
+      dlayer_lo=x->getDlayer(); slayer_lo=x->getSlayer(); part_lo=x->getPart(); stave_lo=x->getStave(); bar_lo=x->getBar(); 
+      dlayer_hi=dlayer; slayer_hi=slayer; part_hi=part; stave_hi=stave; bar_hi=bar; 
+    }
+    else{
+      dlayer_lo=dlayer; slayer_lo=slayer; part_lo=part; stave_lo=stave; bar_lo=bar;
+      dlayer_hi=x->getDlayer(); slayer_hi=x->getSlayer(); part_hi=x->getPart(); stave_hi=x->getStave(); bar_hi=x->getBar();
+    }
+
+    // if( dlayer_lo!=1 || slayer_lo!=0 || part_lo!=Npart || part_hi!=1 ) return false;
+    // if( slayer_hi==0 ){
+    //   if( (stave_lo==stave_hi && abs(bar_lo-bar_hi)<=1) || 
+    //       (abs(stave_lo-stave_hi)==1 && isAtLowerEdgeZ() && x->isAtUpperEdgeZ() ) ||
+    //       (abs(stave_lo-stave_hi)==1 && isAtUpperEdgeZ() && x->isAtLowerEdgeZ() )  ) return true; 
+    // }
+    // else if( slayer_hi==1 && stave_lo==stave_hi && bar_hi==1 ) return true;
+    
+    if(slayer_lo==0)
+    {
+      if(part_lo==Npart && part_hi==1 && dlayer_lo==1)
+      {
+        if( (stave_lo==stave_hi && abs(bar_lo-bar_hi)<=1) || 
+          (abs(stave_lo-stave_hi)==1 && isAtLowerEdgeZ() && x->isAtUpperEdgeZ() ) ||
+          (abs(stave_lo-stave_hi)==1 && isAtUpperEdgeZ() && x->isAtLowerEdgeZ() )  ) return true; 
+      } 
+    }
+    else
+    {
+      if(part_lo==Npart && part_hi==1 && dlayer_lo==1)
+      {
+        if(bar_hi==1 && abs(stave_lo-stave_hi)<=1)
+        {
+          if(abs(bar_lo-over_module[(dlayer_hi-1)*2])<=over_module_set || abs(bar_lo-over_module[(dlayer_hi-1)*2+1])<=over_module_set)
+          {
+            return true;
+          }
+        } 
+        //method1: abs(dlayer_hi*2*sqrt(2)-(bar_lo-14+1))<=3
+        //method2: abs(bar_lo - (14+(dlayer_hi-1)*3))<=3
+      }
+    }
+    return false; 
+  }
+*/  
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Objects/HoughObject.cc b/Reconstruction/CrystalCaloRec/src/Objects/HoughObject.cc
new file mode 100644
index 00000000..4225bede
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Objects/HoughObject.cc
@@ -0,0 +1,72 @@
+#ifndef CALOHOUGHOBJECT_C
+#define CALOHOUGHOBJECT_C
+
+#include "Objects/HoughObject.h"
+
+namespace PandoraPlus{
+
+  HoughObject::HoughObject( const PandoraPlus::Calo1DCluster* _localmax, double _cellSize, double _ecal_inner_radius, double _phi){
+    m_local_max = _localmax;
+
+    setCellSize(_cellSize);
+    setCenterPoint(_ecal_inner_radius, _phi);
+  }
+
+
+  void HoughObject::setCenterPoint(double& _ecal_inner_radius, double _phi){
+    if(m_local_max->getSlayer()==0){
+      TVector3 tmp_vec = m_local_max->getPos();
+      m_center_point.Set(tmp_vec.Perp(), tmp_vec.z());
+
+/*      if(_phi==0)
+        m_center_point.Set( (m_local_max->getDlayer()-1)*20. + _ecal_inner_radius + m_cell_size*0.5, m_local_max->getPos().z() );
+      else{
+        double intPart, fracPart;
+        fracPart = modf((_phi+TMath::Pi())/(TMath::Pi()/4.), &intPart);  // yyy: _phi + TMath::Pi() ranges from 0 to 2pi
+        if(fracPart<0.489 || fracPart>0.711)  //Not in crack region.
+          m_center_point.Set( (m_local_max->getDlayer()-1)*20. + _ecal_inner_radius + m_cell_size*0.5, m_local_max->getPos().z() );
+        else{
+          int iCrack = intPart+2;
+          if(iCrack>=8) iCrack = iCrack-8;
+
+          double tmp_phi = _phi;
+          while(tmp_phi<0.) tmp_phi += TMath::Pi() / 4.;  
+          while(tmp_phi>=TMath::Pi() / 4.) tmp_phi -= TMath::Pi() / 4.;
+
+          int imodule = m_local_max->getTowerID()[0][0];
+          if(imodule==iCrack){
+            double Rref = _ecal_inner_radius/cos( tmp_phi );
+            m_center_point.Set( (m_local_max->getDlayer()-1)*20. + Rref + m_cell_size*0.5 ,
+                          m_local_max->getPos().z());
+          }
+          else{
+            double Rref = _ecal_inner_radius/cos( TMath::Pi() / 4. - tmp_phi );
+            m_center_point.Set( (m_local_max->getDlayer()-1)*20. + Rref + m_cell_size*0.5 ,
+                          m_local_max->getPos().z());
+          }
+
+        }
+      }
+*/
+    }
+    else if(m_local_max->getSlayer()==1){
+      m_center_point.Set(m_local_max->getPos().x(), m_local_max->getPos().y());
+    }
+    else{
+      std::cout<<"Error: Slayer="<<m_local_max->getSlayer()<<", do not use setCenterPoint()!"<<std::endl;
+    }
+    
+  }
+
+
+  void HoughObject::setHoughLine(TF1& _line1, TF1& _line2){
+    m_Hough_line_1 = _line1;  
+    m_Hough_line_2 = _line2;
+    //m_Hough_line_3 = _line3;  
+    //m_Hough_line_4 = _line4;
+  }
+
+
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Objects/HoughSpace.cc b/Reconstruction/CrystalCaloRec/src/Objects/HoughSpace.cc
new file mode 100644
index 00000000..996fa56a
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Objects/HoughSpace.cc
@@ -0,0 +1,128 @@
+#ifndef HOUGHSPACE_C
+#define HOUGHSPACE_C
+
+#include "Objects/HoughSpace.h"
+#include <iostream>
+namespace PandoraPlus{
+
+  int HoughSpace::getAlphaBin(double alpha) const{ 
+    if(alpha<alpha_low){
+      // cout << "int HoughSpace::getAlphaBin(double alpha): wrong alpha input: " << alpha << ", return 1" << endl;
+      return 1;
+    }
+    if(alpha>alpha_high){
+      // cout << "int HoughSpace::getAlphaBin(double alpha): wrong alpha input: " << alpha << ", return Nbins_alpha" << endl;
+      return Nbins_alpha;
+    }
+
+    int alpha_bin = ceil((alpha-alpha_low)/bin_width_alpha);
+    return alpha_bin;
+  }
+
+  double HoughSpace::getAlphaBinCenter(int alpha_bin) const{ 
+    if(alpha_bin<1){
+      // cout << "double HoughSpace::getAlphaBinCenter(int alpha_bin): wrong alpha_bin input: " << alpha_bin << ", return bincenter when alpha_bin=1" << endl;
+      return alpha_low + (bin_width_alpha * 0.5);
+    }
+    if(alpha_bin>Nbins_alpha){
+      // cout << "double HoughSpace::getAlphaBinCenter(int alpha_bin): wrong alpha_bin input: " << alpha_bin << ", return bincenter when alpha_bin=Nbins_alpha" << endl;
+      return alpha_high - (bin_width_alpha * 0.5);
+    }
+
+    double alpha_bin_center = alpha_low + (alpha_bin*1.*bin_width_alpha) - (bin_width_alpha*0.5) ;
+    return alpha_bin_center;
+  }
+
+  double HoughSpace::getAlphaBinLowEdge(int alpha_bin) const{ 
+    if(alpha_bin<1){
+      // cout << "double HoughSpace::getAlphaBinLowEdge(int alpha_bin): wrong alpha_bin input: " << alpha_bin << ", return bin low edge when alpha_bin=1" << endl;
+      return alpha_low;
+    }
+    if(alpha_bin>Nbins_alpha){
+      // cout << "double HoughSpace::getAlphaBinLowEdge(int alpha_bin): wrong alpha_bin input: " << alpha_bin << ", return bin low edge when alpha_bin=Nbins_alpha" << endl;
+      return alpha_high - bin_width_alpha;
+    }
+
+    double alpha_bin_low_edge = alpha_low + (alpha_bin*1.*bin_width_alpha) - bin_width_alpha;
+    return alpha_bin_low_edge;
+  }
+
+  double HoughSpace::getAlphaBinUpEdge(int alpha_bin) const{
+    if(alpha_bin<1){
+      // cout << "double HoughSpace::getAlphaBinUpEdge(int alpha_bin): wrong alpha_bin input: " << alpha_bin << ", return bin up edge when alpha_bin=1" << endl;
+      return alpha_low + bin_width_alpha;
+    }
+    if(alpha_bin>Nbins_alpha){
+      // cout << "double HoughSpace::getAlphaBinUpEdge(int alpha_bin): wrong alpha_bin input: " << alpha_bin << ", return bin up edge when alpha_bin=Nbins_alpha" << endl;
+      return alpha_high;
+    }
+    
+    double alpha_bin_up_edge = alpha_low + (alpha_bin*1.*bin_width_alpha);
+    return alpha_bin_up_edge;
+  }
+
+  int HoughSpace::getRhoBin(double rho) const{
+    if(rho<rho_low){
+      // cout << "int HoughSpace::getRhoBin(double rho): wrong rho input: " << rho << ", return 1" << endl;
+      return 1;
+    }
+    if(rho>rho_high){
+      // cout << "int HoughSpace::getRhoBin(double rho): wrong rho input: " << rho << ", return Nbins_rho" << endl;
+      return Nbins_rho;
+    }
+
+    int rho_bin = ceil((rho-rho_low)/bin_width_rho);
+    return rho_bin;
+  }
+
+  double HoughSpace::getRhoBinCenter(int rho_bin) const{
+    if(rho_bin<1){
+      // cout << "double HoughSpace::getRhoBinCenter(int rho_bin): wrong rho_bin input: " << rho_bin << ", return bincenter when rho_bin=1" << endl;
+      return rho_low + (bin_width_rho * 0.5);
+    }
+    if(rho_bin>Nbins_rho){
+      // cout << "double HoughSpace::getRhoBinCenter(int rho_bin): wrong rho_bin input: " << rho_bin << ", return bincenter when rho_bin=Nbins_rho" << endl;
+      return rho_high - (bin_width_rho * 0.5);
+    }
+
+    double rho_bin_center = rho_low + (rho_bin*1.*bin_width_rho) - (bin_width_rho*0.5) ;
+    return rho_bin_center;
+  }
+
+  double HoughSpace::getRhoBinLowEdge(int rho_bin) const{
+    if(rho_bin<1){
+      // cout << "double HoughSpace::getRhoBinLowEdge(int rho_bin): wrong rho_bin input: " << rho_bin << ", return bin low edge when rho_bin=1" << endl;
+      return rho_low;
+    }
+    if(rho_bin>Nbins_rho){
+      // cout << "double HoughSpace::getRhoBinLowEdge(int rho_bin): wrong rho_bin input: " << rho_bin << ", return bin low edge when rho_bin=Nbins_rho" << endl;
+      return rho_high - bin_width_rho;
+    }
+
+    double rho_bin_low_edge = rho_low + (rho_bin*1.*bin_width_rho) - bin_width_rho;
+    return rho_bin_low_edge;
+  }
+
+  double HoughSpace::getRhoBinUpEdge(int rho_bin) const{
+    if(rho_bin<1){
+      // cout << "double HoughSpace::getRhoBinUpEdge(int rho_bin): wrong rho_bin input: " << rho_bin << ", return bin up edge when rho_bin=1" << endl;
+      return rho_low + bin_width_rho;
+    }
+    if(rho_bin>Nbins_rho){
+      // cout << "double HoughSpace::getRhoBinUpEdge(int rho_bin): wrong rho_bin input: " << rho_bin << ", return bin up edge when rho_bin=Nbins_rho" << endl;
+      return rho_high;
+    }
+    
+    double rho_bin_up_edge = rho_low + (rho_bin*1.*bin_width_rho);
+    return rho_bin_up_edge;
+  }
+
+  void HoughSpace::AddBinHobj(int bin_alpha, int bin_rho, int index_Hobj){
+    pair<int, int> bin(bin_alpha, bin_rho);
+    Hough_bins[bin].insert(index_Hobj);
+  }
+ 
+
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Objects/PFObject.cc b/Reconstruction/CrystalCaloRec/src/Objects/PFObject.cc
new file mode 100644
index 00000000..3205ff56
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Objects/PFObject.cc
@@ -0,0 +1,81 @@
+#ifndef PFOBJECT_C
+#define PFOBJECT_C
+
+#include "Objects/PFObject.h"
+namespace PandoraPlus{
+
+  void PFObject::Clear()
+  {
+    m_pid = 0;
+    m_tracks.clear();
+    m_ecal_clusters.clear();
+    m_hcal_clusters.clear();
+  }
+
+  std::shared_ptr<PandoraPlus::PFObject> PFObject::Clone() const{
+    std::shared_ptr<PandoraPlus::PFObject> m_newpfo = std::make_shared<PandoraPlus::PFObject>();
+    m_newpfo->setTrack(m_tracks);
+    m_newpfo->setECALCluster(m_ecal_clusters);
+    m_newpfo->setHCALCluster(m_hcal_clusters);
+    m_newpfo->setPID(m_pid);
+
+    return m_newpfo;
+  }
+
+  void PFObject::addTrack(const Track* _track){
+    if( find( m_tracks.begin(), m_tracks.end(), _track)!=m_tracks.end() ){
+      std::cout<<"ERROR: attempt to add an existing track into PFO! Skip it "<<std::endl;
+    }
+    else{
+      m_tracks.push_back(_track);
+    }
+  }
+
+  void PFObject::addECALCluster(const Calo3DCluster* _ecal_cluster){
+    if( find( m_ecal_clusters.begin(), m_ecal_clusters.end(), _ecal_cluster)!=m_ecal_clusters.end() ){
+      std::cout<<"ERROR: attempt to add an existing ECAL cluster into PFO! Skip it "<<std::endl;
+    }
+    else{
+      m_ecal_clusters.push_back(_ecal_cluster);
+    }
+  }
+
+  void PFObject::addHCALCluster(const Calo3DCluster* _hcal_cluster){
+    if( find( m_hcal_clusters.begin(), m_hcal_clusters.end(), _hcal_cluster)!=m_hcal_clusters.end() ){
+      std::cout<<"ERROR: attempt to add an existing HCAL cluster into PFO! Skip it "<<std::endl;
+    }
+    else{
+      m_hcal_clusters.push_back(_hcal_cluster);
+    }
+  }
+
+  double PFObject::getECALClusterEnergy() const{
+    if(m_ecal_clusters.size()==0) return 0.;
+
+    double sumEn = 0;
+    for(int i=0; i<m_ecal_clusters.size(); i++) sumEn += m_ecal_clusters[i]->getLongiE();
+    return sumEn;
+
+  }
+
+  double PFObject::getHCALClusterEnergy() const{
+    if(m_hcal_clusters.size()==0) return 0.;
+
+    double sumEn = 0;
+    for(int i=0; i<m_hcal_clusters.size(); i++) sumEn += m_hcal_clusters[i]->getHitsE();
+    return sumEn;
+  }
+
+  double PFObject::getTrackMomentum() const{
+    if(m_tracks.size()==0) return -99;
+
+    double maxP = -1;
+    for(int i=0; i<m_tracks.size(); i++){
+      if(m_tracks[i]->getMomentum()>maxP) maxP = m_tracks[i]->getMomentum();
+    }
+    return maxP;
+  }
+
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Objects/Track.cc b/Reconstruction/CrystalCaloRec/src/Objects/Track.cc
new file mode 100644
index 00000000..b74fbf40
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Objects/Track.cc
@@ -0,0 +1,113 @@
+#ifndef TRACK_C
+#define TRACK_C
+
+#include "Objects/Track.h"
+#include <cmath>
+#include "TGraph.h"
+
+namespace PandoraPlus{
+
+  const double Track::B = 3.;
+
+  int Track::trackStates_size(std::string name) const{
+    std::vector<TrackState> emptyCol; emptyCol.clear(); 
+    if(m_trackStates.find(name)!=m_trackStates.end()) emptyCol = m_trackStates.at(name);
+    return emptyCol.size();
+  }
+
+  int Track::trackStates_size() const{
+    std::vector<TrackState> emptyCol; emptyCol.clear();
+    for(auto iter: m_trackStates) emptyCol.insert(emptyCol.end(), iter.second.begin(), iter.second.end());
+    return emptyCol.size();
+  }
+
+  std::vector<TrackState> Track::getTrackStates(std::string name) const{
+    std::vector<TrackState> emptyCol; emptyCol.clear(); 
+    if(m_trackStates.find(name)!=m_trackStates.end()) emptyCol = m_trackStates.at(name);
+    return emptyCol;
+  }
+
+  std::vector<TrackState> Track::getAllTrackStates() const{
+    std::vector<TrackState> emptyCol; emptyCol.clear();
+    for(auto iter: m_trackStates) emptyCol.insert(emptyCol.end(), iter.second.begin(), iter.second.end());
+    return emptyCol;
+  }
+
+  float Track::getPt() const{
+    std::vector<TrackState> trkStates = getTrackStates("Input");
+    float pt = -99.;
+    for(auto it: trkStates){
+      if(it.location==4 || it.location==1){  //Calorimeter(for real track) or IP (for truth track)
+        pt = 1./it.Kappa;
+      }
+    }
+
+    return pt;
+  }
+
+  float Track::getPz() const{
+    std::vector<TrackState> trkStates = getTrackStates("Input");
+    float pz = -99.;
+    for(auto it: trkStates){
+      if(it.location==4 || it.location==1){ //Calorimeter(for real track) or IP (for truth track)
+        pz = it.tanLambda/it.Kappa;
+      }
+    }
+
+    return pz;
+  }
+
+  TVector3 Track::getP3() const{
+    std::vector<TrackState> trkStates = getTrackStates("Input");
+    float phi = -99;
+    float pt = -99.;
+    float pz = -99.;
+    for(auto it: trkStates){
+      if(it.location==4 || it.location==1){  //Calorimeter(for real track) or IP (for truth track)
+        pt = 1./it.Kappa;
+        phi = it.phi0;
+        pz = it.tanLambda/it.Kappa;
+      }
+    }
+  
+    TVector3 p3(pt*cos(phi), pt*sin(phi), pz);
+    return p3; 
+  }
+
+  float Track::getCharge() const{
+    std::vector<TrackState> trkStates = getTrackStates("Input");
+    float omega = -99.;
+    for(auto it: trkStates){
+      if(it.location==4 || it.location==1){ //Calorimeter(for real track) or IP (for truth track)
+        omega = it.Omega;
+      }
+    }
+
+    return omega/fabs(omega);
+  }
+
+  edm4hep::MCParticle Track::getLeadingMCP() const{
+    float maxWeight = -1.;
+    edm4hep::MCParticle mcp;
+    for(auto& iter: MCParticleWeight){
+      if(iter.second>maxWeight){
+        mcp = iter.first;
+        maxWeight = iter.second;
+      }
+    }
+
+    return mcp;
+  }
+
+  float Track::getLeadingMCPweight() const{
+    float maxWeight = -1.;
+    for(auto& iter: MCParticleWeight){
+      if(iter.second>maxWeight){
+        maxWeight = iter.second;
+      }
+    }  
+    return maxWeight;
+  }
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/PandoraPlusDataCol.cpp b/Reconstruction/CrystalCaloRec/src/PandoraPlusDataCol.cpp
new file mode 100644
index 00000000..5ee658b9
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/PandoraPlusDataCol.cpp
@@ -0,0 +1,27 @@
+#ifndef _PANDORAPLUS_DATA_C
+#define _PANDORAPLUS_DATA_C
+
+#include "PandoraPlusDataCol.h"
+
+StatusCode PandoraPlusDataCol::Clear(){
+  collectionMap_MC.clear();
+  collectionMap_CaloHit.clear();
+  collectionMap_Vertex.clear();
+  collectionMap_Track.clear();
+  collectionMap_CaloRel.clear();
+  collectionMap_TrkRel.clear();
+
+  TrackCol.clear();
+  map_CaloHit.clear();
+
+  map_BarCol.clear();
+  map_1DCluster.clear();
+  map_HalfCluster.clear();
+  map_2DCluster.clear();
+  map_CaloCluster.clear();
+  map_PFObjects.clear();
+  
+  return StatusCode::SUCCESS; 
+};
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/PandoraPlusPFAlg.cpp b/Reconstruction/CrystalCaloRec/src/PandoraPlusPFAlg.cpp
new file mode 100644
index 00000000..de6314ae
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/PandoraPlusPFAlg.cpp
@@ -0,0 +1,2158 @@
+#ifndef PANDORAPLUS_ALG_C
+#define PANDORAPLUS_ALG_C
+
+#include "PandoraPlusPFAlg.h"
+
+// #include <fstream>
+// #include <ctime>
+
+using namespace std;
+using namespace dd4hep;
+
+int PandoraPlus::CaloUnit::Nmodule = 32;
+int PandoraPlus::CaloUnit::Nstave = 15;
+int PandoraPlus::CaloUnit::Nlayer = 14;
+int PandoraPlus::CaloUnit::NbarPhi_odd[14] = {39, 39, 39, 39, 37, 37, 37, 37, 37, 35, 35, 35, 35, 33};
+int PandoraPlus::CaloUnit::NbarPhi_even[14] = {29, 29, 31, 31, 33, 35, 35, 37, 37, 39, 41, 41, 43, 43};
+int PandoraPlus::CaloUnit::NbarZ = 36;
+//int PandoraPlus::CaloUnit::over_module[28] = {13,15,16,18,19,21,22,24,25,26,28,29,30,32,33,35,36,38,39,41,42,43,45,46};
+//int PandoraPlus::CaloUnit::over_module_set = 2;
+float PandoraPlus::CaloUnit::barsize = 10.; //mm
+float PandoraPlus::CaloUnit::ecal_innerR = 1830;  //mm
+
+DECLARE_COMPONENT( PandoraPlusPFAlg )
+
+PandoraPlusPFAlg::PandoraPlusPFAlg(const std::string& name, ISvcLocator* svcLoc)
+  : GaudiAlgorithm(name, svcLoc),
+    _nEvt(0)
+{
+ 
+  // Output collections
+  declareProperty("RecCaloHitCollection", w_RecEcalCol, "Handle of Reconstructed CaloHit collection");
+  declareProperty("RecCaloCoreCollection", w_RecCoreCol, "Handle of Reconstructed ECAL Core collection");
+  declareProperty("RecHCALHitCollection",  w_RecHcalCol, "Handle of Reconstructed HCAL CaloHit collection");
+  declareProperty("RecTrackCollection", w_RecTrkCol, "Handle of Reconstructed Tracks linked to PFO"); 
+  declareProperty("RecoPFOCollection", w_ReconstructedParticleCollection, "Handle of Reconstructed PFO collection");   
+  // declareProperty("RecoVtxCollection", w_VertexCollection, "Handle of Reconstructed vertex collection");   
+  // declareProperty("MCRecoPFOAssociationCollection", w_MCRecoParticleAssociationCollection, "Handle of MC-RecoPFO association collection");   
+
+}
+
+StatusCode PandoraPlusPFAlg::initialize()
+{
+  //Initialize global settings
+  m_GlobalSettings.map_floatPars["BField"] = m_BField;
+  m_GlobalSettings.map_floatPars["Seed"] = m_seed;
+  m_GlobalSettings.map_intPars["Debug"] = m_Debug;
+  m_GlobalSettings.map_intPars["SkipEvt"] = m_Nskip;
+
+
+  //Initialize Creator settings
+  m_pMCParticleCreatorSettings.map_stringPars["MCParticleCollections"] = name_MCParticleCol.value();
+
+  m_pTrackCreatorSettings.map_stringVecPars["trackCollections"] = name_TrackCol.value();
+  m_pTrackCreatorSettings.map_floatPars["BField"] = m_BField; 
+
+  std::vector<std::string> name_CaloHits = name_EcalHits; 
+  std::vector<std::string> name_CaloReadout = name_EcalReadout;
+  name_CaloHits.insert( name_CaloHits.end(), name_HcalHits.begin(), name_HcalHits.end() );
+  name_CaloReadout.insert(name_CaloReadout.end(), name_HcalReadout.begin(), name_HcalReadout.end());
+
+  m_CaloHitsCreatorSettings.map_stringVecPars["CaloHitCollections"] = name_CaloHits;
+  m_CaloHitsCreatorSettings.map_stringPars["EcalType"] = m_EcalType.value();
+
+  m_OutputCreatorSettings.map_stringPars["OutputPFO"] = name_PFObject.value();
+  m_OutputCreatorSettings.map_boolPars["UseTruthTrk"] = m_useTruthTrk.value();
+  m_OutputCreatorSettings.map_floatPars["BField"] = m_BField.value();
+  m_OutputCreatorSettings.map_floatPars["ECALCalib"] = m_EcalCalib.value();
+  m_OutputCreatorSettings.map_floatPars["HCALCalib"] = m_HcalCalib.value();
+
+  //Initialize Creators
+  m_pMCParticleCreator = new MCParticleCreator( m_pMCParticleCreatorSettings );
+  m_pTrackCreator      = new TrackCreator( m_pTrackCreatorSettings );
+  m_pCaloHitsCreator   = new CaloHitsCreator( m_CaloHitsCreatorSettings );
+  m_pOutputCreator     = new OutputCreator( m_OutputCreatorSettings );
+
+  //Readin collections
+
+  //---MC particle---
+  if(!name_MCParticleCol.empty()) r_MCParticleCol = new DataHandle<edm4hep::MCParticleCollection> (name_MCParticleCol, Gaudi::DataHandle::Reader, this);
+
+  //---Tracks---
+  for(auto& _trk : name_TrackCol) if(!_trk.empty()) r_TrackCols.push_back( new TrackType(_trk, Gaudi::DataHandle::Reader, this) );
+
+  //---Calo Hits---
+  for(auto& _ecal : name_EcalHits){
+    if(!_ecal.empty()){ 
+      //r_ECalHitCols.push_back( new CaloType(_ecal, Gaudi::DataHandle::Reader, this) );
+      r_CaloHitCols.push_back( new CaloType(_ecal, Gaudi::DataHandle::Reader, this) );
+  }}
+  for(auto& _hcal : name_HcalHits){ 
+    if(!_hcal.empty()){
+      //r_HCalHitCols.push_back( new CaloType(_hcal, Gaudi::DataHandle::Reader, this) );
+      r_CaloHitCols.push_back( new CaloType(_hcal, Gaudi::DataHandle::Reader, this) );
+  }}
+
+  //---MCParticle CaloHit Association
+  if(!name_MCPTrkAssoCol.empty())      r_MCPTrkAssoCol = new DataHandle<edm4hep::MCRecoTrackParticleAssociationCollection> (name_MCPTrkAssoCol, Gaudi::DataHandle::Reader, this);
+
+  std::vector<std::string> name_CaloAssoCol = name_EcalMCPAssociation; 
+  name_CaloAssoCol.insert(name_CaloAssoCol.end(), name_HcalMCPAssociation.begin(), name_HcalMCPAssociation.end());
+  if(name_CaloAssoCol.size()==name_CaloHits.size()){
+    for(int iCol=0; iCol<name_CaloAssoCol.size(); iCol++){
+      map_CaloMCPAssoCols[name_CaloHits[iCol]] = new CaloParticleAssoType(name_CaloAssoCol[iCol], Gaudi::DataHandle::Reader, this);
+    }
+  }
+
+
+  //Register Algorithms
+  //--- Initialize algorithm maps ---
+  m_algorithmManager.RegisterAlgorithmFactory("ExampleAlg",             new ExampleAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("GlobalClusteringAlg",    new GlobalClusteringAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("HcalClusteringAlg",      new HcalClusteringAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("LocalMaxFindingAlg",     new LocalMaxFindingAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("HoughClusteringAlg",     new HoughClusteringAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("TrackMatchingAlg",       new TrackMatchingAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("ConeClustering2DAlg",    new ConeClustering2DAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("AxisMergingAlg",         new AxisMergingAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("EnergySplittingAlg",     new EnergySplittingAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("EnergyTimeMatchingAlg",  new EnergyTimeMatchingAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("PFOCreatingAlg",         new PFOCreatingAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("ConeClusteringAlg",      new ConeClusteringAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("TrackClusterConnectingAlg",   new TrackClusterConnectingAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("PFOReclusteringAlg",          new PFOReclusteringAlg::Factory);
+  //m_algorithmManager.RegisterAlgorithmFactory("ConeClusteringAlgHCAL",  new ConeClusteringAlg::Factory);
+
+  m_algorithmManager.RegisterAlgorithmFactory("TruthTrackMatchingAlg",       new TruthTrackMatchingAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("TruthPatternRecAlg",          new TruthPatternRecAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("TruthEnergySplittingAlg",     new TruthEnergySplittingAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("TruthMatchingAlg",            new TruthMatchingAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("TruthClusteringAlg",          new TruthClusteringAlg::Factory);
+  m_algorithmManager.RegisterAlgorithmFactory("TruthClusterMergingAlg",      new TruthClusterMergingAlg::Factory);
+
+  //--- Create algorithm from readin settings ---
+  for(int ialg=0; ialg<name_Algs.value().size(); ialg++){
+    Settings m_settings; 
+    for(int ipar=0; ipar<name_AlgPars.value()[ialg].size(); ipar++){
+      if(type_AlgPars.value()[ialg].at(ipar)=="int")    m_settings.map_intPars[name_AlgPars.value()[ialg].at(ipar)] = std::stoi( (string)value_AlgPars.value()[ialg].at(ipar) );
+      if(type_AlgPars.value()[ialg].at(ipar)=="double") m_settings.map_floatPars[name_AlgPars.value()[ialg].at(ipar)] = std::stod( (string)value_AlgPars.value()[ialg].at(ipar) );
+      if(type_AlgPars.value()[ialg].at(ipar)=="string") m_settings.map_stringPars[name_AlgPars.value()[ialg].at(ipar)] = value_AlgPars.value()[ialg].at(ipar) ;
+      if(type_AlgPars.value()[ialg].at(ipar)=="bool")   m_settings.map_boolPars[name_AlgPars.value()[ialg].at(ipar)] = (bool)std::stoi( (string)value_AlgPars.value()[ialg].at(ipar) );
+    }
+
+    m_algorithmManager.RegisterAlgorithm( name_Algs.value()[ialg], m_settings );
+  }
+
+
+  //Initialize services
+  m_geosvc = service<IGeomSvc>("GeomSvc");
+  if ( !m_geosvc )  throw "PandoraPlusPFAlg :Failed to find GeomSvc ...";
+
+  m_energycorsvc = service<ICrystalEcalSvc>("CrystalEcalEnergyCorrectionSvc");
+  if ( !m_energycorsvc )  throw "PandoraPlusPFAlg :Failed to find CrystalEcalEnergyCorrectionSvc ...";
+  //m_energycorsvc->initialize();
+
+  for(unsigned int i=0; i<name_CaloReadout.size(); i++){
+    if(name_CaloReadout[i].empty()) continue;
+    dd4hep::DDSegmentation::BitFieldCoder* tmp_decoder = m_geosvc->getDecoder(name_CaloReadout[i]);
+    if (!tmp_decoder) {
+      error() << "Failed to get the decoder for: " << name_CaloReadout[i] << endmsg;
+      return StatusCode::FAILURE;
+    }
+    map_readout_decoder[name_CaloHits[i]] = tmp_decoder;
+  }
+
+  rndm.SetSeed(m_seed);
+  std::cout<<"PandoraPlusPFAlg::initialize"<<std::endl;
+
+
+  //Output collections
+  w_ClusterCollection["EcalCluster"] = new DataHandle<edm4hep::ClusterCollection>("EcalCluster", Gaudi::DataHandle::Writer, this);
+  w_ClusterCollection["EcalCore"]    = new DataHandle<edm4hep::ClusterCollection>("EcalCore", Gaudi::DataHandle::Writer, this);
+  w_ClusterCollection["HcalCluster"] = new DataHandle<edm4hep::ClusterCollection>("HcalCluster", Gaudi::DataHandle::Writer, this);
+
+  //Output ntuple for analysis.
+  if(m_WriteAna){
+    std::string s_outfile = m_filename;
+    m_wfile = new TFile(s_outfile.c_str(), "recreate");
+    t_MCParticle = new TTree("MCParticle", "MCParticle");
+    t_SimBar = new TTree("SimBarHit", "SimBarHit");
+    t_LocalMax = new TTree("LocalMax", "LocalMax");
+    t_Layers = new TTree("RecLayers","RecLayers");
+    t_Hough = new TTree("Hough", "Hough");
+    t_Cone = new TTree("Cone", "Cone");
+    t_TrackAxis = new TTree("TrackAxis", "TrackAxis");
+    t_Axis = new TTree("Axis", "Axis");
+    t_HalfCluster = new TTree("HalfCluster","HalfCluster");
+    t_Tower = new TTree("Tower", "Tower");
+    t_Cluster = new TTree("RecClusters", "RecClusters");
+    t_Track = new TTree("Track", "Track");
+    t_PFO = new TTree("PFO", "PFO");
+
+    //MC particle 
+    t_MCParticle->Branch("mcPdgid",     &m_mcPdgid);
+    t_MCParticle->Branch("mcStatus",    &m_mcStatus);
+    t_MCParticle->Branch("mcPx", &m_mcPx);
+    t_MCParticle->Branch("mcPy", &m_mcPy);
+    t_MCParticle->Branch("mcPz", &m_mcPz);
+    t_MCParticle->Branch("mcEn", &m_mcEn);
+    t_MCParticle->Branch("mcMass", &m_mcMass);
+    t_MCParticle->Branch("mcCharge", &m_mcCharge);
+    t_MCParticle->Branch("mcEPx", &m_mcEPx);
+    t_MCParticle->Branch("mcEPy", &m_mcEPy);
+    t_MCParticle->Branch("mcEPz", &m_mcEPz);
+    t_MCParticle->Branch("mcdepEn_ecal", &m_depEn_ecal);
+    t_MCParticle->Branch("mcdepEn_hcal", &m_depEn_hcal);
+
+    //Bar
+    t_SimBar->Branch("totE_EcalSim", &m_totE_EcalSim);
+    t_SimBar->Branch("simBar_x", &m_simBar_x);
+    t_SimBar->Branch("simBar_y", &m_simBar_y);
+    t_SimBar->Branch("simBar_z", &m_simBar_z);
+    t_SimBar->Branch("simBar_T1", &m_simBar_T1);
+    t_SimBar->Branch("simBar_T2", &m_simBar_T2);
+    t_SimBar->Branch("simBar_Q1", &m_simBar_Q1);
+    t_SimBar->Branch("simBar_Q2", &m_simBar_Q2);
+    t_SimBar->Branch("simBar_module", &m_simBar_module);
+    t_SimBar->Branch("simBar_dlayer", &m_simBar_dlayer);
+    t_SimBar->Branch("simBar_stave", &m_simBar_stave);
+    t_SimBar->Branch("simBar_slayer", &m_simBar_slayer);
+    t_SimBar->Branch("simBar_bar", &m_simBar_bar);
+    t_SimBar->Branch("simBar_truthMC_tag", &m_simBar_truthMC_tag);
+    t_SimBar->Branch("simBar_truthMC_pid", &m_simBar_truthMC_pid);
+    t_SimBar->Branch("simBar_truthMC_px", &m_simBar_truthMC_px);
+    t_SimBar->Branch("simBar_truthMC_py", &m_simBar_truthMC_py);
+    t_SimBar->Branch("simBar_truthMC_pz", &m_simBar_truthMC_pz);
+    t_SimBar->Branch("simBar_truthMC_E", &m_simBar_truthMC_E);
+    t_SimBar->Branch("simBar_truthMC_EPx", &m_simBar_truthMC_EPx);
+    t_SimBar->Branch("simBar_truthMC_EPy", &m_simBar_truthMC_EPy);
+    t_SimBar->Branch("simBar_truthMC_EPz", &m_simBar_truthMC_EPz);
+    t_SimBar->Branch("simBar_truthMC_weight", &m_simBar_truthMC_weight);
+    t_SimBar->Branch("totE_HcalSim", &m_totE_HcalSim);
+    t_SimBar->Branch("HcalHit_x", &m_HcalHit_x); 
+    t_SimBar->Branch("HcalHit_y", &m_HcalHit_y); 
+    t_SimBar->Branch("HcalHit_z", &m_HcalHit_z); 
+    t_SimBar->Branch("HcalHit_E", &m_HcalHit_E); 
+    t_SimBar->Branch("HcalHit_layer", &m_HcalHit_layer); 
+    t_SimBar->Branch("HcalHit_truthMC_tag", &m_HcalHit_truthMC_tag);
+    t_SimBar->Branch("HcalHit_truthMC_pid", &m_HcalHit_truthMC_pid);
+    t_SimBar->Branch("HcalHit_truthMC_px", &m_HcalHit_truthMC_px);
+    t_SimBar->Branch("HcalHit_truthMC_py", &m_HcalHit_truthMC_py);
+    t_SimBar->Branch("HcalHit_truthMC_pz", &m_HcalHit_truthMC_pz);
+    t_SimBar->Branch("HcalHit_truthMC_E", &m_HcalHit_truthMC_E);
+    t_SimBar->Branch("HcalHit_truthMC_EPx", &m_HcalHit_truthMC_EPx);
+    t_SimBar->Branch("HcalHit_truthMC_EPy", &m_HcalHit_truthMC_EPy);
+    t_SimBar->Branch("HcalHit_truthMC_EPz", &m_HcalHit_truthMC_EPz);
+    t_SimBar->Branch("HcalHit_truthMC_weight", &m_HcalHit_truthMC_weight);    
+
+    //ECAL local max
+    t_LocalMax->Branch("NlocalMaxU", &m_NlmU);
+    t_LocalMax->Branch("NlocalMaxV", &m_NlmV);
+    t_LocalMax->Branch("localMaxU_tag", &m_localMaxU_tag);
+    t_LocalMax->Branch("localMaxU_x", &m_localMaxU_x);
+    t_LocalMax->Branch("localMaxU_y", &m_localMaxU_y);
+    t_LocalMax->Branch("localMaxU_z", &m_localMaxU_z);
+    t_LocalMax->Branch("localMaxU_E", &m_localMaxU_E);
+    t_LocalMax->Branch("localMaxU_mc_tag", &m_localMaxU_mc_tag);
+    t_LocalMax->Branch("localMaxU_mc_pdg", &m_localMaxU_mc_pdg);
+    t_LocalMax->Branch("localMaxU_mc_px", &m_localMaxU_mc_px);
+    t_LocalMax->Branch("localMaxU_mc_py", &m_localMaxU_mc_py);
+    t_LocalMax->Branch("localMaxU_mc_pz", &m_localMaxU_mc_pz);
+    t_LocalMax->Branch("localMaxU_mc_weight", &m_localMaxU_mc_weight);
+    t_LocalMax->Branch("localMaxV_tag", &m_localMaxV_tag);
+    t_LocalMax->Branch("localMaxV_x", &m_localMaxV_x);
+    t_LocalMax->Branch("localMaxV_y", &m_localMaxV_y);
+    t_LocalMax->Branch("localMaxV_z", &m_localMaxV_z);
+    t_LocalMax->Branch("localMaxV_E", &m_localMaxV_E);
+    t_LocalMax->Branch("localMaxV_mc_tag", &m_localMaxV_mc_tag);
+    t_LocalMax->Branch("localMaxV_mc_pdg", &m_localMaxV_mc_pdg);
+    t_LocalMax->Branch("localMaxV_mc_px", &m_localMaxV_mc_px);
+    t_LocalMax->Branch("localMaxV_mc_py", &m_localMaxV_mc_py);
+    t_LocalMax->Branch("localMaxV_mc_pz", &m_localMaxV_mc_pz);
+    t_LocalMax->Branch("localMaxV_mc_weight", &m_localMaxV_mc_weight);
+
+    //1D Showers
+    t_Layers->Branch("NshowerU", &m_NshowerU);
+    t_Layers->Branch("NshowerV", &m_NshowerV);
+    t_Layers->Branch("barShowerU_tag", &m_barShowerU_tag);
+    t_Layers->Branch("barShowerU_x", &m_barShowerU_x);
+    t_Layers->Branch("barShowerU_y", &m_barShowerU_y);
+    t_Layers->Branch("barShowerU_z", &m_barShowerU_z);
+    t_Layers->Branch("barShowerU_E", &m_barShowerU_E);
+    t_Layers->Branch("barShowerU_mc_tag", &m_barShowerU_mc_tag);
+    t_Layers->Branch("barShowerU_mc_pdg", &m_barShowerU_mc_pdg);
+    t_Layers->Branch("barShowerU_mc_px", &m_barShowerU_mc_px);
+    t_Layers->Branch("barShowerU_mc_py", &m_barShowerU_mc_py);
+    t_Layers->Branch("barShowerU_mc_pz", &m_barShowerU_mc_pz);
+    t_Layers->Branch("barShowerU_mc_weight", &m_barShowerU_mc_weight);
+    t_Layers->Branch("barShowerV_tag", &m_barShowerV_tag);
+    t_Layers->Branch("barShowerV_x", &m_barShowerV_x);
+    t_Layers->Branch("barShowerV_y", &m_barShowerV_y);
+    t_Layers->Branch("barShowerV_z", &m_barShowerV_z);
+    t_Layers->Branch("barShowerV_E", &m_barShowerV_E);
+    t_Layers->Branch("barShowerV_mc_tag", &m_barShowerV_mc_tag);
+    t_Layers->Branch("barShowerV_mc_pdg", &m_barShowerV_mc_pdg);
+    t_Layers->Branch("barShowerV_mc_px", &m_barShowerV_mc_px);
+    t_Layers->Branch("barShowerV_mc_py", &m_barShowerV_mc_py);
+    t_Layers->Branch("barShowerV_mc_pz", &m_barShowerV_mc_pz);
+    t_Layers->Branch("barShowerV_mc_weight", &m_barShowerV_mc_weight);
+
+    // Hough
+    t_Hough->Branch("houghU_tag", &m_houghU_tag);
+    t_Hough->Branch("houghU_type", &m_houghU_type);
+    t_Hough->Branch("houghU_x", &m_houghU_x);
+    t_Hough->Branch("houghU_y", &m_houghU_y);
+    t_Hough->Branch("houghU_z", &m_houghU_z);
+    t_Hough->Branch("houghU_E", &m_houghU_E);
+    t_Hough->Branch("houghU_truth_tag", &m_houghU_truth_tag);
+    t_Hough->Branch("houghU_truth_MC_px", &m_houghU_truth_MC_px);
+    t_Hough->Branch("houghU_truth_MC_py", &m_houghU_truth_MC_py);
+    t_Hough->Branch("houghU_truth_MC_pz", &m_houghU_truth_MC_pz);
+    t_Hough->Branch("houghU_truth_MC_E", &m_houghU_truth_MC_E);
+    t_Hough->Branch("houghU_truth_MC_weight", &m_houghU_truth_MC_weight);    
+    t_Hough->Branch("houghU_hit_tag", &m_houghU_hit_tag);
+    t_Hough->Branch("houghU_hit_x", &m_houghU_hit_x);
+    t_Hough->Branch("houghU_hit_y", &m_houghU_hit_y);
+    t_Hough->Branch("houghU_hit_z", &m_houghU_hit_z);
+    t_Hough->Branch("houghU_hit_E", &m_houghU_hit_E);
+    t_Hough->Branch("houghV_tag", &m_houghV_tag);
+    t_Hough->Branch("houghV_type", &m_houghV_type);
+    t_Hough->Branch("houghV_x", &m_houghV_x);
+    t_Hough->Branch("houghV_y", &m_houghV_y);
+    t_Hough->Branch("houghV_z", &m_houghV_z);
+    t_Hough->Branch("houghV_E", &m_houghV_E);
+    t_Hough->Branch("houghV_alpha", &m_houghV_alpha);
+    t_Hough->Branch("houghV_rho", &m_houghV_rho);
+    t_Hough->Branch("houghV_truth_tag", &m_houghV_truth_tag);
+    t_Hough->Branch("houghV_truth_MC_px", &m_houghV_truth_MC_px);
+    t_Hough->Branch("houghV_truth_MC_py", &m_houghV_truth_MC_py);
+    t_Hough->Branch("houghV_truth_MC_pz", &m_houghV_truth_MC_pz);
+    t_Hough->Branch("houghV_truth_MC_E", &m_houghV_truth_MC_E);
+    t_Hough->Branch("houghV_truth_MC_weight", &m_houghV_truth_MC_weight);   
+    t_Hough->Branch("houghV_hit_tag", &m_houghV_hit_tag);
+    t_Hough->Branch("houghV_hit_x", &m_houghV_hit_x);
+    t_Hough->Branch("houghV_hit_y", &m_houghV_hit_y);
+    t_Hough->Branch("houghV_hit_z", &m_houghV_hit_z);
+    t_Hough->Branch("houghV_hit_E", &m_houghV_hit_E);
+    // Cone
+    t_Cone->Branch("coneU_tag", &m_coneU_tag);
+    t_Cone->Branch("coneU_type", &m_coneU_type);
+    t_Cone->Branch("coneU_x", &m_coneU_x);
+    t_Cone->Branch("coneU_y", &m_coneU_y);
+    t_Cone->Branch("coneU_z", &m_coneU_z);
+    t_Cone->Branch("coneU_E", &m_coneU_E);
+    t_Cone->Branch("coneU_truth_tag", &m_coneU_truth_tag);
+    t_Cone->Branch("coneU_truth_MC_px", &m_coneU_truth_MC_px);
+    t_Cone->Branch("coneU_truth_MC_py", &m_coneU_truth_MC_py);
+    t_Cone->Branch("coneU_truth_MC_pz", &m_coneU_truth_MC_pz);
+    t_Cone->Branch("coneU_truth_MC_E", &m_coneU_truth_MC_E);
+    t_Cone->Branch("coneU_truth_MC_weight", &m_coneU_truth_MC_weight);    
+    t_Cone->Branch("coneU_hit_tag", &m_coneU_hit_tag);
+    t_Cone->Branch("coneU_hit_x", &m_coneU_hit_x);
+    t_Cone->Branch("coneU_hit_y", &m_coneU_hit_y);
+    t_Cone->Branch("coneU_hit_z", &m_coneU_hit_z);
+    t_Cone->Branch("coneU_hit_E", &m_coneU_hit_E);
+    t_Cone->Branch("coneV_tag", &m_coneV_tag);
+    t_Cone->Branch("coneV_type", &m_coneV_type);
+    t_Cone->Branch("coneV_x", &m_coneV_x);
+    t_Cone->Branch("coneV_y", &m_coneV_y);
+    t_Cone->Branch("coneV_z", &m_coneV_z);
+    t_Cone->Branch("coneV_E", &m_coneV_E);
+    t_Cone->Branch("coneV_truth_tag", &m_coneV_truth_tag);
+    t_Cone->Branch("coneV_truth_MC_px", &m_coneV_truth_MC_px);
+    t_Cone->Branch("coneV_truth_MC_py", &m_coneV_truth_MC_py);
+    t_Cone->Branch("coneV_truth_MC_pz", &m_coneV_truth_MC_pz);
+    t_Cone->Branch("coneV_truth_MC_E", &m_coneV_truth_MC_E);
+    t_Cone->Branch("coneV_truth_MC_weight", &m_coneV_truth_MC_weight);   
+    t_Cone->Branch("coneV_hit_tag", &m_coneV_hit_tag);
+    t_Cone->Branch("coneV_hit_x", &m_coneV_hit_x);
+    t_Cone->Branch("coneV_hit_y", &m_coneV_hit_y);
+    t_Cone->Branch("coneV_hit_z", &m_coneV_hit_z);
+    t_Cone->Branch("coneV_hit_E", &m_coneV_hit_E);
+    // Track Axis
+    t_TrackAxis->Branch("trackU_tag", &m_trackU_tag);
+    t_TrackAxis->Branch("trackU_type", &m_trackU_type);
+    t_TrackAxis->Branch("trackU_x", &m_trackU_x);
+    t_TrackAxis->Branch("trackU_y", &m_trackU_y);
+    t_TrackAxis->Branch("trackU_z", &m_trackU_z);
+    t_TrackAxis->Branch("trackU_E", &m_trackU_E);
+    t_TrackAxis->Branch("trackU_truth_tag", &m_trackU_truth_tag);
+    t_TrackAxis->Branch("trackU_truth_MC_px", &m_trackU_truth_MC_px);
+    t_TrackAxis->Branch("trackU_truth_MC_py", &m_trackU_truth_MC_py);
+    t_TrackAxis->Branch("trackU_truth_MC_pz", &m_trackU_truth_MC_pz);
+    t_TrackAxis->Branch("trackU_truth_MC_E", &m_trackU_truth_MC_E);
+    t_TrackAxis->Branch("trackU_truth_MC_weight", &m_trackU_truth_MC_weight);    
+    t_TrackAxis->Branch("trackU_hit_tag", &m_trackU_hit_tag);
+    t_TrackAxis->Branch("trackU_hit_x", &m_trackU_hit_x);
+    t_TrackAxis->Branch("trackU_hit_y", &m_trackU_hit_y);
+    t_TrackAxis->Branch("trackU_hit_z", &m_trackU_hit_z);
+    t_TrackAxis->Branch("trackU_hit_E", &m_trackU_hit_E);
+    t_TrackAxis->Branch("trackV_tag", &m_trackV_tag);
+    t_TrackAxis->Branch("trackV_type", &m_trackV_type);
+    t_TrackAxis->Branch("trackV_x", &m_trackV_x);
+    t_TrackAxis->Branch("trackV_y", &m_trackV_y);
+    t_TrackAxis->Branch("trackV_z", &m_trackV_z);
+    t_TrackAxis->Branch("trackV_E", &m_trackV_E);
+    t_TrackAxis->Branch("trackV_truth_tag", &m_trackV_truth_tag);
+    t_TrackAxis->Branch("trackV_truth_MC_px", &m_trackV_truth_MC_px);
+    t_TrackAxis->Branch("trackV_truth_MC_py", &m_trackV_truth_MC_py);
+    t_TrackAxis->Branch("trackV_truth_MC_pz", &m_trackV_truth_MC_pz);
+    t_TrackAxis->Branch("trackV_truth_MC_E", &m_trackV_truth_MC_E);
+    t_TrackAxis->Branch("trackV_truth_MC_weight", &m_trackV_truth_MC_weight);   
+    t_TrackAxis->Branch("trackV_hit_tag", &m_trackV_hit_tag);
+    t_TrackAxis->Branch("trackV_hit_x", &m_trackV_hit_x);
+    t_TrackAxis->Branch("trackV_hit_y", &m_trackV_hit_y);
+    t_TrackAxis->Branch("trackV_hit_z", &m_trackV_hit_z);
+    t_TrackAxis->Branch("trackV_hit_E", &m_trackV_hit_E);
+    //Axis
+    t_Axis->Branch("axisU_tag", &m_axisU_tag);
+    t_Axis->Branch("axisU_type", &m_axisU_type);
+    t_Axis->Branch("axisU_x", &m_axisU_x);
+    t_Axis->Branch("axisU_y", &m_axisU_y);
+    t_Axis->Branch("axisU_z", &m_axisU_z);
+    t_Axis->Branch("axisU_E", &m_axisU_E);
+    t_Axis->Branch("axisU_truth_tag", &m_axisU_truth_tag);
+    t_Axis->Branch("axisU_truth_MC_px", &m_axisU_truth_MC_px);
+    t_Axis->Branch("axisU_truth_MC_py", &m_axisU_truth_MC_py);
+    t_Axis->Branch("axisU_truth_MC_pz", &m_axisU_truth_MC_pz);
+    t_Axis->Branch("axisU_truth_MC_E", &m_axisU_truth_MC_E);
+    t_Axis->Branch("axisU_truth_MC_weight", &m_axisU_truth_MC_weight);
+    t_Axis->Branch("axisU_hit_tag", &m_axisU_hit_tag);
+    t_Axis->Branch("axisU_hit_x", &m_axisU_hit_x);
+    t_Axis->Branch("axisU_hit_y", &m_axisU_hit_y);
+    t_Axis->Branch("axisU_hit_z", &m_axisU_hit_z);
+    t_Axis->Branch("axisU_hit_E", &m_axisU_hit_E);
+    t_Axis->Branch("axisV_tag", &m_axisV_tag);
+    t_Axis->Branch("axisV_type", &m_axisV_type);
+    t_Axis->Branch("axisV_x", &m_axisV_x);
+    t_Axis->Branch("axisV_y", &m_axisV_y);
+    t_Axis->Branch("axisV_z", &m_axisV_z);
+    t_Axis->Branch("axisV_E", &m_axisV_E);
+    t_Axis->Branch("axisV_truth_tag", &m_axisV_truth_tag);
+    t_Axis->Branch("axisV_truth_MC_px", &m_axisV_truth_MC_px);
+    t_Axis->Branch("axisV_truth_MC_py", &m_axisV_truth_MC_py);
+    t_Axis->Branch("axisV_truth_MC_pz", &m_axisV_truth_MC_pz);
+    t_Axis->Branch("axisV_truth_MC_E", &m_axisV_truth_MC_E);
+    t_Axis->Branch("axisV_truth_MC_weight", &m_axisV_truth_MC_weight);
+    t_Axis->Branch("axisV_hit_tag", &m_axisV_hit_tag);
+    t_Axis->Branch("axisV_hit_x", &m_axisV_hit_x);
+    t_Axis->Branch("axisV_hit_y", &m_axisV_hit_y);
+    t_Axis->Branch("axisV_hit_z", &m_axisV_hit_z);
+    t_Axis->Branch("axisV_hit_E", &m_axisV_hit_E);    
+
+    t_Axis->Branch("emptyAxisU_tag", &m_emptyAxisU_tag);
+    t_Axis->Branch("emptyAxisU_x", &m_emptyAxisU_x);
+    t_Axis->Branch("emptyAxisU_y", &m_emptyAxisU_y);
+    t_Axis->Branch("emptyAxisU_z", &m_emptyAxisU_z);
+    t_Axis->Branch("emptyAxisU_E", &m_emptyAxisU_E);
+    t_Axis->Branch("emptyAxisV_tag", &m_emptyAxisV_tag);
+    t_Axis->Branch("emptyAxisV_x", &m_emptyAxisV_x);
+    t_Axis->Branch("emptyAxisV_y", &m_emptyAxisV_y);
+    t_Axis->Branch("emptyAxisV_z", &m_emptyAxisV_z);
+    t_Axis->Branch("emptyAxisV_E", &m_emptyAxisV_E);
+
+    //Half clusters
+    t_HalfCluster->Branch("totE_HFClusV", &m_totE_HFClusV);
+    t_HalfCluster->Branch("HalfClusterV_x", &m_HalfClusterV_x);
+    t_HalfCluster->Branch("HalfClusterV_y", &m_HalfClusterV_y);
+    t_HalfCluster->Branch("HalfClusterV_z", &m_HalfClusterV_z);
+    t_HalfCluster->Branch("HalfClusterV_E", &m_HalfClusterV_E);
+    t_HalfCluster->Branch("HalfClusterV_tag", &m_HalfClusterV_tag);
+    t_HalfCluster->Branch("HalfClusterV_type", &m_HalfClusterV_type);
+    t_HalfCluster->Branch("HalfClusterV_hit_x", &m_HalfClusterV_hit_x);
+    t_HalfCluster->Branch("HalfClusterV_hit_y", &m_HalfClusterV_hit_y);
+    t_HalfCluster->Branch("HalfClusterV_hit_z", &m_HalfClusterV_hit_z);
+    t_HalfCluster->Branch("HalfClusterV_hit_E", &m_HalfClusterV_hit_E);
+    t_HalfCluster->Branch("HalfClusterV_hit_tag", &m_HalfClusterV_hit_tag);
+    t_HalfCluster->Branch("HalfClusterV_truth_tag", &m_HalfClusterV_truth_tag);
+    t_HalfCluster->Branch("HalfClusterV_truthMC_px", &m_HalfClusterV_truthMC_px);
+    t_HalfCluster->Branch("HalfClusterV_truthMC_py", &m_HalfClusterV_truthMC_py);
+    t_HalfCluster->Branch("HalfClusterV_truthMC_pz", &m_HalfClusterV_truthMC_pz);
+    t_HalfCluster->Branch("HalfClusterV_truthMC_E", &m_HalfClusterV_truthMC_E);
+    t_HalfCluster->Branch("HalfClusterV_truthMC_weight", &m_HalfClusterV_truthMC_weight);
+
+    t_HalfCluster->Branch("totE_HFClusU", &m_totE_HFClusU);
+    t_HalfCluster->Branch("HalfClusterU_x", &m_HalfClusterU_x);
+    t_HalfCluster->Branch("HalfClusterU_y", &m_HalfClusterU_y);
+    t_HalfCluster->Branch("HalfClusterU_z", &m_HalfClusterU_z);
+    t_HalfCluster->Branch("HalfClusterU_E", &m_HalfClusterU_E);  
+    t_HalfCluster->Branch("HalfClusterU_tag", &m_HalfClusterU_tag);  
+    t_HalfCluster->Branch("HalfClusterU_type", &m_HalfClusterU_type);  
+    t_HalfCluster->Branch("HalfClusterU_hit_x", &m_HalfClusterU_hit_x);
+    t_HalfCluster->Branch("HalfClusterU_hit_y", &m_HalfClusterU_hit_y);
+    t_HalfCluster->Branch("HalfClusterU_hit_z", &m_HalfClusterU_hit_z);
+    t_HalfCluster->Branch("HalfClusterU_hit_E", &m_HalfClusterU_hit_E);
+    t_HalfCluster->Branch("HalfClusterU_hit_tag", &m_HalfClusterU_hit_tag);
+    t_HalfCluster->Branch("HalfClusterU_truth_tag", &m_HalfClusterU_truth_tag);
+    t_HalfCluster->Branch("HalfClusterU_truthMC_px", &m_HalfClusterU_truthMC_px);
+    t_HalfCluster->Branch("HalfClusterU_truthMC_py", &m_HalfClusterU_truthMC_py);
+    t_HalfCluster->Branch("HalfClusterU_truthMC_pz", &m_HalfClusterU_truthMC_pz);
+    t_HalfCluster->Branch("HalfClusterU_truthMC_E", &m_HalfClusterU_truthMC_E);
+    t_HalfCluster->Branch("HalfClusterU_truthMC_weight", &m_HalfClusterU_truthMC_weight);
+
+
+    //Tower
+    t_Tower->Branch("towerID", towerID, "towerID[3]/I");
+    t_Tower->Branch("NclusU", &m_NclusU);
+    t_Tower->Branch("NclusV", &m_NclusV);
+    t_Tower->Branch("totEn", &m_totEn);
+    t_Tower->Branch("totEn_U", &m_totEn_U);
+    t_Tower->Branch("totEn_V", &m_totEn_V);
+    t_Tower->Branch("HalfClusterU_x", &m_HalfClusterU_x);
+    t_Tower->Branch("HalfClusterU_y", &m_HalfClusterU_y);
+    t_Tower->Branch("HalfClusterU_z", &m_HalfClusterU_z);
+    t_Tower->Branch("HalfClusterU_E", &m_HalfClusterU_E);
+    t_Tower->Branch("HalfClusterU_tag", &m_HalfClusterU_tag);
+    t_Tower->Branch("HalfClusterU_type", &m_HalfClusterU_type);
+    t_Tower->Branch("HalfClusterU_nTrk", &m_HalfClusterU_nTrk);
+    t_Tower->Branch("HalfClusterV_x", &m_HalfClusterV_x);
+    t_Tower->Branch("HalfClusterV_y", &m_HalfClusterV_y);
+    t_Tower->Branch("HalfClusterV_z", &m_HalfClusterV_z);
+    t_Tower->Branch("HalfClusterV_E", &m_HalfClusterV_E);
+    t_Tower->Branch("HalfClusterV_tag", &m_HalfClusterV_tag);
+    t_Tower->Branch("HalfClusterV_type", &m_HalfClusterV_type);
+    t_Tower->Branch("HalfClusterV_nTrk", &m_HalfClusterV_nTrk);
+
+    //Clusters
+    t_Cluster->Branch("totE_Ecal", &m_totE_Ecal);
+    t_Cluster->Branch("totE_Hcal", &m_totE_Hcal);
+    t_Cluster->Branch("Nclus_Ecal", &m_Nclus_Ecal);
+    t_Cluster->Branch("Nclus_Hcal", &m_Nclus_Hcal);
+    t_Cluster->Branch("EcalClus_x", &m_EcalClus_x);
+    t_Cluster->Branch("EcalClus_y", &m_EcalClus_y);
+    t_Cluster->Branch("EcalClus_z", &m_EcalClus_z);
+    t_Cluster->Branch("EcalClus_E", &m_EcalClus_E);
+    t_Cluster->Branch("EcalClus_Escale", &m_EcalClus_Escale);
+    t_Cluster->Branch("EcalClus_nTrk", &m_EcalClus_nTrk);
+    t_Cluster->Branch("EcalClus_ptrk", &m_EcalClus_pTrk);
+    t_Cluster->Branch("EcalClus_typeU", &m_EcalClus_typeU);
+    t_Cluster->Branch("EcalClus_typeV", &m_EcalClus_typeV);
+    t_Cluster->Branch("EcalClus_hitU_x", &m_EcalClus_hitU_x);
+    t_Cluster->Branch("EcalClus_hitU_y", &m_EcalClus_hitU_y);
+    t_Cluster->Branch("EcalClus_hitU_z", &m_EcalClus_hitU_z);
+    t_Cluster->Branch("EcalClus_hitU_E", &m_EcalClus_hitU_E);
+    t_Cluster->Branch("EcalClus_hitU_tag", &m_EcalClus_hitU_tag);
+    t_Cluster->Branch("EcalClus_hitV_x", &m_EcalClus_hitV_x);
+    t_Cluster->Branch("EcalClus_hitV_y", &m_EcalClus_hitV_y);
+    t_Cluster->Branch("EcalClus_hitV_z", &m_EcalClus_hitV_z);
+    t_Cluster->Branch("EcalClus_hitV_E", &m_EcalClus_hitV_E);
+    t_Cluster->Branch("EcalClus_hitV_tag", &m_EcalClus_hitV_tag);
+    t_Cluster->Branch("EcalClus_trk_location", &m_EcalClus_trk_location);
+    t_Cluster->Branch("EcalClus_trk_tag", &m_EcalClus_trk_tag);
+    t_Cluster->Branch("EcalClus_trk_d0", &m_EcalClus_trk_d0);
+    t_Cluster->Branch("EcalClus_trk_z0", &m_EcalClus_trk_z0);
+    t_Cluster->Branch("EcalClus_trk_phi", &m_EcalClus_trk_phi);
+    t_Cluster->Branch("EcalClus_trk_tanL", &m_EcalClus_trk_tanL);
+    t_Cluster->Branch("EcalClus_trk_omega", &m_EcalClus_trk_omega);
+    t_Cluster->Branch("EcalClus_trk_kappa", &m_EcalClus_trk_kappa);
+    t_Cluster->Branch("EcalClus_truthMC_tag", &m_EcalClus_truthMC_tag);
+    t_Cluster->Branch("EcalClus_truthMC_pid", &m_EcalClus_truthMC_pid);
+    t_Cluster->Branch("EcalClus_truthMC_px", &m_EcalClus_truthMC_px);
+    t_Cluster->Branch("EcalClus_truthMC_py", &m_EcalClus_truthMC_py);
+    t_Cluster->Branch("EcalClus_truthMC_pz", &m_EcalClus_truthMC_pz);
+    t_Cluster->Branch("EcalClus_truthMC_E", &m_EcalClus_truthMC_E);
+    t_Cluster->Branch("EcalClus_truthMC_EPx", &m_EcalClus_truthMC_EPx);
+    t_Cluster->Branch("EcalClus_truthMC_EPy", &m_EcalClus_truthMC_EPy);
+    t_Cluster->Branch("EcalClus_truthMC_EPz", &m_EcalClus_truthMC_EPz);
+    t_Cluster->Branch("EcalClus_truthMC_weight", &m_EcalClus_truthMC_weight);
+    t_Cluster->Branch("HcalClus_x", &m_HcalClus_x);
+    t_Cluster->Branch("HcalClus_y", &m_HcalClus_y);
+    t_Cluster->Branch("HcalClus_z", &m_HcalClus_z);
+    t_Cluster->Branch("HcalClus_E", &m_HcalClus_E);
+    t_Cluster->Branch("HcalClus_nTrk", &m_HcalClus_nTrk);
+    t_Cluster->Branch("HcalClus_ptrk", &m_HcalClus_pTrk);
+    t_Cluster->Branch("HcalClus_hit_x", &m_HcalClus_hit_x);
+    t_Cluster->Branch("HcalClus_hit_y", &m_HcalClus_hit_y);
+    t_Cluster->Branch("HcalClus_hit_z", &m_HcalClus_hit_z);
+    t_Cluster->Branch("HcalClus_hit_E", &m_HcalClus_hit_E);
+    t_Cluster->Branch("HcalClus_hit_tag", &m_HcalClus_hit_tag);
+    t_Cluster->Branch("HcalClus_truthMC_tag", &m_HcalClus_truthMC_tag);
+    t_Cluster->Branch("HcalClus_truthMC_pid", &m_HcalClus_truthMC_pid);
+    t_Cluster->Branch("HcalClus_truthMC_px", &m_HcalClus_truthMC_px);
+    t_Cluster->Branch("HcalClus_truthMC_py", &m_HcalClus_truthMC_py);
+    t_Cluster->Branch("HcalClus_truthMC_pz", &m_HcalClus_truthMC_pz);
+    t_Cluster->Branch("HcalClus_truthMC_E", &m_HcalClus_truthMC_E);
+    t_Cluster->Branch("HcalClus_truthMC_EPx", &m_HcalClus_truthMC_EPx);
+    t_Cluster->Branch("HcalClus_truthMC_EPy", &m_HcalClus_truthMC_EPy);
+    t_Cluster->Branch("HcalClus_truthMC_EPz", &m_HcalClus_truthMC_EPz);
+    t_Cluster->Branch("HcalClus_truthMC_weight", &m_HcalClus_truthMC_weight);
+    t_Cluster->Branch("SimpleHcalClus_x", &m_SimpleHcalClus_x);
+    t_Cluster->Branch("SimpleHcalClus_y", &m_SimpleHcalClus_y);
+    t_Cluster->Branch("SimpleHcalClus_z", &m_SimpleHcalClus_z);
+    t_Cluster->Branch("SimpleHcalClus_E", &m_SimpleHcalClus_E);
+    t_Cluster->Branch("SimpleHcalClus_nTrk", &m_SimpleHcalClus_nTrk);
+    t_Cluster->Branch("SimpleHcalClus_ptrk", &m_SimpleHcalClus_pTrk);
+    t_Cluster->Branch("SimpleHcalClus_hit_x", &m_SimpleHcalClus_hit_x);
+    t_Cluster->Branch("SimpleHcalClus_hit_y", &m_SimpleHcalClus_hit_y);
+    t_Cluster->Branch("SimpleHcalClus_hit_z", &m_SimpleHcalClus_hit_z);
+    t_Cluster->Branch("SimpleHcalClus_hit_E", &m_SimpleHcalClus_hit_E);
+    t_Cluster->Branch("SimpleHcalClus_hit_tag", &m_SimpleHcalClus_hit_tag);
+    t_Cluster->Branch("SimpleHcalClus_truthMC_tag", &m_SimpleHcalClus_truthMC_tag);
+    t_Cluster->Branch("SimpleHcalClus_truthMC_pid", &m_SimpleHcalClus_truthMC_pid);
+    t_Cluster->Branch("SimpleHcalClus_truthMC_px", &m_SimpleHcalClus_truthMC_px);
+    t_Cluster->Branch("SimpleHcalClus_truthMC_py", &m_SimpleHcalClus_truthMC_py);
+    t_Cluster->Branch("SimpleHcalClus_truthMC_pz", &m_SimpleHcalClus_truthMC_pz);
+    t_Cluster->Branch("SimpleHcalClus_truthMC_E", &m_SimpleHcalClus_truthMC_E);
+    t_Cluster->Branch("SimpleHcalClus_truthMC_EPx", &m_SimpleHcalClus_truthMC_EPx);
+    t_Cluster->Branch("SimpleHcalClus_truthMC_EPy", &m_SimpleHcalClus_truthMC_EPy);
+    t_Cluster->Branch("SimpleHcalClus_truthMC_EPz", &m_SimpleHcalClus_truthMC_EPz);
+    t_Cluster->Branch("SimpleHcalClus_truthMC_weight", &m_SimpleHcalClus_truthMC_weight);
+
+    // Tracks
+    t_Track->Branch("m_Ntrk", &m_Ntrk);
+    t_Track->Branch("m_type", &m_type);
+    t_Track->Branch("m_trkstate_d0", &m_trkstate_d0);
+    t_Track->Branch("m_trkstate_z0", &m_trkstate_z0);
+    t_Track->Branch("m_trkstate_phi", &m_trkstate_phi);
+    t_Track->Branch("m_trkstate_tanL", &m_trkstate_tanL);
+    t_Track->Branch("m_trkstate_kappa", &m_trkstate_kappa);
+    t_Track->Branch("m_trkstate_omega", &m_trkstate_omega);
+    t_Track->Branch("m_trkstate_refx", &m_trkstate_refx);
+    t_Track->Branch("m_trkstate_refy", &m_trkstate_refy);
+    t_Track->Branch("m_trkstate_refz", &m_trkstate_refz);
+    t_Track->Branch("m_trkstate_location", &m_trkstate_location);
+    t_Track->Branch("m_trkstate_tag", &m_trkstate_tag);
+
+    //PFOs
+    t_PFO->Branch("pfo_tag", &pfo_tag);
+    t_PFO->Branch("pfo_n_track", &pfo_n_track);
+    t_PFO->Branch("pfo_n_ecal_clus", &pfo_n_ecal_clus);
+    t_PFO->Branch("pfo_n_hcal_clus", &pfo_n_hcal_clus);
+    t_PFO->Branch("pfo_ecal_tag", &pfo_ecal_tag);
+    t_PFO->Branch("pfo_hcal_tag", &pfo_hcal_tag);
+    t_PFO->Branch("pfo_ecal_clus_x", &pfo_ecal_clus_x);
+    t_PFO->Branch("pfo_ecal_clus_y", &pfo_ecal_clus_y);
+    t_PFO->Branch("pfo_ecal_clus_z", &pfo_ecal_clus_z);
+    t_PFO->Branch("pfo_ecal_clus_E", &pfo_ecal_clus_E);
+    t_PFO->Branch("pfo_ecal_clus_Escale", &pfo_ecal_clus_Escale);
+    t_PFO->Branch("pfo_hcal_clus_x", &pfo_hcal_clus_x);
+    t_PFO->Branch("pfo_hcal_clus_y", &pfo_hcal_clus_y);
+    t_PFO->Branch("pfo_hcal_clus_z", &pfo_hcal_clus_z);
+    t_PFO->Branch("pfo_hcal_clus_E", &pfo_hcal_clus_E);
+    t_PFO->Branch("pfo_trk_tag", &pfo_trk_tag);
+    t_PFO->Branch("pfo_trk_location", &pfo_trk_location);
+    t_PFO->Branch("pfo_trk_d0", &pfo_trk_d0);
+    t_PFO->Branch("pfo_trk_z0", &pfo_trk_z0);
+    t_PFO->Branch("pfo_trk_phi", &pfo_trk_phi);
+    t_PFO->Branch("pfo_trk_tanL", &pfo_trk_tanL);
+    t_PFO->Branch("pfo_trk_omega", &pfo_trk_omega);
+    t_PFO->Branch("pfo_trk_kappa", &pfo_trk_kappa);    
+
+  }
+
+  return GaudiAlgorithm::initialize();
+}
+
+StatusCode PandoraPlusPFAlg::execute()
+{
+// clock_t yyy_start, yyy_endrec, yyy_endfill;
+// yyy_start = clock(); // 记录开始时间
+
+  if(_nEvt==0) std::cout<<"PandoraPlusPFAlg::execute Start"<<std::endl;
+  std::cout<<"Processing event: "<<_nEvt<<std::endl;
+
+  if(_nEvt<m_Nskip){ _nEvt++;  return GaudiAlgorithm::initialize(); }
+
+  //InitializeForNewEvent(); 
+  PandoraPlusDataCol     m_DataCol;
+  m_DataCol.Clear();
+  m_DataCol.EnergyCorrSvc = m_energycorsvc; 
+
+
+  //Readin collections 
+  m_pMCParticleCreator->CreateMCParticle( m_DataCol, *r_MCParticleCol );
+
+  if(m_useTruthTrk) m_pTrackCreator->CreateTracksFromMCParticle(m_DataCol, *r_MCParticleCol);
+  else m_pTrackCreator->CreateTracks( m_DataCol, r_TrackCols, r_MCPTrkAssoCol );
+
+  m_pCaloHitsCreator->CreateCaloHits( m_DataCol, r_CaloHitCols, map_readout_decoder, map_CaloMCPAssoCols );
+
+  //Perform PFA algorithm
+  m_algorithmManager.RunAlgorithm( m_DataCol );
+
+  m_pOutputCreator->CreateOutputCollections( m_DataCol, 
+                                             w_RecEcalCol, 
+                                             w_RecCoreCol, 
+                                             w_RecHcalCol,
+                                             w_RecTrkCol, 
+                                             w_ClusterCollection, 
+                                             w_ReconstructedParticleCollection);
+
+// yyy_endrec = clock();  // 重建结束的时间
+
+cout<<"Write tuples"<<endl;
+  //---------------------Write Ana tuples-------------------------
+  // MC particles  
+  ClearMCParticle();
+  std::vector<edm4hep::MCParticle> m_MCPCol = m_DataCol.collectionMap_MC[name_MCParticleCol.value()];
+  for(int imc=0; imc<m_MCPCol.size(); imc++){
+    m_mcPdgid.push_back( m_MCPCol[imc].getPDG() );
+    m_mcStatus.push_back( m_MCPCol[imc].getGeneratorStatus() );
+    m_mcPx.push_back( m_MCPCol[imc].getMomentum()[0] );
+    m_mcPy.push_back( m_MCPCol[imc].getMomentum()[1] );
+    m_mcPz.push_back( m_MCPCol[imc].getMomentum()[2] );
+    m_mcEn.push_back( m_MCPCol[imc].getEnergy() );
+    m_mcMass.push_back( m_MCPCol[imc].getMass() );
+    m_mcCharge.push_back( m_MCPCol[imc].getCharge() );
+    m_mcEPx.push_back( m_MCPCol[imc].getEndpoint()[0] );
+    m_mcEPy.push_back( m_MCPCol[imc].getEndpoint()[1] );
+    m_mcEPz.push_back( m_MCPCol[imc].getEndpoint()[2] );
+    //double tmp_phi = std::atan2(m_MCPCol[imc].getMomentum()[1], m_MCPCol[imc].getMomentum()[0])* 180.0 / M_PI;
+    //if (tmp_phi < 0) tmp_phi += 360.0;
+    //double tmp_theta = std::atan2(m_MCPCol[imc].getMomentum()[2], sqrt(m_MCPCol[imc].getMomentum()[1]*m_MCPCol[imc].getMomentum()[1]+m_MCPCol[imc].getMomentum()[0]*m_MCPCol[imc].getMomentum()[0]))* 180.0 / M_PI + 90; 
+    //cout<<"MCParticle: "<<imc<<" PDG: "<<m_MCPCol[imc].getPDG()<<" Theta: "<<tmp_theta<<" Phi: "<<tmp_phi<<endl;
+
+    double EnDep_ecal = GetParticleDepEnergy(m_MCPCol[imc], m_DataCol.map_BarCol["BarCol"]);
+    double EnDep_hcal = GetParticleDepEnergy(m_MCPCol[imc], m_DataCol.map_CaloHit["HCALBarrel"]);
+    m_depEn_ecal.push_back(EnDep_ecal);
+    m_depEn_hcal.push_back(EnDep_hcal);
+  }
+  t_MCParticle->Fill();
+
+
+  //Save Raw bars information
+  ClearBar();
+  m_totE_EcalSim = 0.;
+  for(int ibar=0;ibar<m_DataCol.map_BarCol["BarCol"].size();ibar++){
+    auto p_hitbar = m_DataCol.map_BarCol["BarCol"][ibar].get();
+    m_simBar_x.push_back(p_hitbar->getPosition().x());
+    m_simBar_y.push_back(p_hitbar->getPosition().y());
+    m_simBar_z.push_back(p_hitbar->getPosition().z());
+    m_simBar_Q1.push_back(p_hitbar->getQ1());
+    m_simBar_Q2.push_back(p_hitbar->getQ2());
+    m_simBar_T1.push_back(p_hitbar->getT1());
+    m_simBar_T2.push_back(p_hitbar->getT2());
+    m_simBar_module.push_back(p_hitbar->getModule());
+    m_simBar_dlayer.push_back(p_hitbar->getDlayer());
+    m_simBar_stave.push_back(p_hitbar->getStave());
+    m_simBar_slayer.push_back(p_hitbar->getSlayer());
+    m_simBar_bar.push_back(p_hitbar->getBar());
+    m_totE_EcalSim += (p_hitbar->getQ1()+p_hitbar->getQ2())/2.; 
+
+    auto truthMap = p_hitbar->getLinkedMCP();
+    for(auto iter: truthMap){
+      m_simBar_truthMC_tag.push_back(ibar);
+      m_simBar_truthMC_pid.push_back(iter.first.getPDG());
+      m_simBar_truthMC_px.push_back(iter.first.getMomentum().x);
+      m_simBar_truthMC_py.push_back(iter.first.getMomentum().y);
+      m_simBar_truthMC_pz.push_back(iter.first.getMomentum().z);
+      m_simBar_truthMC_E.push_back(iter.first.getEnergy());
+      m_simBar_truthMC_EPx.push_back(iter.first.getEndpoint().x);
+      m_simBar_truthMC_EPy.push_back(iter.first.getEndpoint().y);
+      m_simBar_truthMC_EPz.push_back(iter.first.getEndpoint().z);
+      m_simBar_truthMC_weight.push_back(iter.second);
+    }
+  }
+
+  std::vector<PandoraPlus::CaloHit*> m_hcalHitsCol; m_hcalHitsCol.clear();
+  for(int ih=0; ih<m_DataCol.map_CaloHit["HCALBarrel"].size(); ih++)
+    m_hcalHitsCol.push_back( m_DataCol.map_CaloHit["HCALBarrel"][ih].get() );
+
+  m_totE_HcalSim = 0.;
+  for(int ihit=0; ihit<m_hcalHitsCol.size(); ihit++){
+    m_HcalHit_x.push_back( m_hcalHitsCol[ihit]->getPosition().x() );
+    m_HcalHit_y.push_back( m_hcalHitsCol[ihit]->getPosition().y() );
+    m_HcalHit_z.push_back( m_hcalHitsCol[ihit]->getPosition().z() );
+    m_HcalHit_E.push_back( m_hcalHitsCol[ihit]->getEnergy() );
+    m_HcalHit_layer.push_back( m_hcalHitsCol[ihit]->getLayer() );
+    m_totE_HcalSim += m_hcalHitsCol[ihit]->getEnergy(); 
+
+    auto truthMap = m_hcalHitsCol[ihit]->getLinkedMCP();
+    for(auto iter: truthMap){
+      m_HcalHit_truthMC_tag.push_back(ihit);
+      m_HcalHit_truthMC_pid.push_back(iter.first.getPDG());
+      m_HcalHit_truthMC_px.push_back(iter.first.getMomentum().x);
+      m_HcalHit_truthMC_py.push_back(iter.first.getMomentum().y);
+      m_HcalHit_truthMC_pz.push_back(iter.first.getMomentum().z);
+      m_HcalHit_truthMC_E.push_back(iter.first.getEnergy());
+      m_HcalHit_truthMC_EPx.push_back(iter.first.getEndpoint().x);
+      m_HcalHit_truthMC_EPy.push_back(iter.first.getEndpoint().y);
+      m_HcalHit_truthMC_EPz.push_back(iter.first.getEndpoint().z);
+      m_HcalHit_truthMC_weight.push_back(iter.second);
+    }    
+
+  }
+  t_SimBar->Fill();
+
+  //Save localMax
+  ClearLocalMax();
+  std::vector<PandoraPlus::CaloHalfCluster*> m_halfclusters; m_halfclusters.clear();
+  for(int i=0; i<m_DataCol.map_HalfCluster["HalfClusterColU"].size(); i++)
+    m_halfclusters.push_back( m_DataCol.map_HalfCluster["HalfClusterColU"][i].get() );
+
+  std::vector<const Calo1DCluster*> m_local_max; m_local_max.clear();
+  for(int ic=0;ic<m_halfclusters.size(); ic++){
+    std::vector<const Calo1DCluster*> tmp_shower = m_halfclusters[ic]->getLocalMaxCol("AllLocalMax");
+    m_local_max.insert(m_local_max.end(), tmp_shower.begin(), tmp_shower.end());
+  }
+  for(int il=0; il<m_local_max.size(); il++){
+    m_localMaxU_tag.push_back( il );
+    m_localMaxU_x.push_back( m_local_max[il]->getPos().x() );
+    m_localMaxU_y.push_back( m_local_max[il]->getPos().y() );
+    m_localMaxU_z.push_back( m_local_max[il]->getPos().z() );
+    m_localMaxU_E.push_back( m_local_max[il]->getEnergy() );
+
+    auto truthMap = m_local_max[il]->getLinkedMCP();
+    for(auto iter: truthMap){
+      m_localMaxU_mc_tag.push_back(il);
+      m_localMaxU_mc_pdg.push_back(iter.first.getPDG());
+      m_localMaxU_mc_px.push_back(iter.first.getMomentum().x);
+      m_localMaxU_mc_py.push_back(iter.first.getMomentum().y);
+      m_localMaxU_mc_pz.push_back(iter.first.getMomentum().z);
+      m_localMaxU_mc_weight.push_back(iter.second);
+    }
+  }
+  m_halfclusters.clear();
+  m_local_max.clear(); 
+  for(int i=0; i<m_DataCol.map_HalfCluster["HalfClusterColV"].size(); i++)
+    m_halfclusters.push_back( m_DataCol.map_HalfCluster["HalfClusterColV"][i].get() );
+
+  for(int ic=0;ic<m_halfclusters.size(); ic++){
+    std::vector<const Calo1DCluster*> tmp_shower = m_halfclusters[ic]->getLocalMaxCol("AllLocalMax");
+    m_local_max.insert(m_local_max.end(), tmp_shower.begin(), tmp_shower.end());
+  }
+  for(int il=0; il<m_local_max.size(); il++){
+    m_localMaxV_tag.push_back( il );
+    m_localMaxV_x.push_back( m_local_max[il]->getPos().x() );
+    m_localMaxV_y.push_back( m_local_max[il]->getPos().y() );
+    m_localMaxV_z.push_back( m_local_max[il]->getPos().z() );
+    m_localMaxV_E.push_back( m_local_max[il]->getEnergy() );
+
+    auto truthMap = m_local_max[il]->getLinkedMCP();
+    for(auto iter: truthMap){
+      m_localMaxV_mc_tag.push_back(il);
+      m_localMaxV_mc_pdg.push_back(iter.first.getPDG());
+      m_localMaxV_mc_px.push_back(iter.first.getMomentum().x);
+      m_localMaxV_mc_py.push_back(iter.first.getMomentum().y);
+      m_localMaxV_mc_pz.push_back(iter.first.getMomentum().z);
+      m_localMaxV_mc_weight.push_back(iter.second);
+    }
+  }
+  t_LocalMax->Fill();
+
+  //Save 1DCluster
+  ClearLayer();
+  m_halfclusters.clear();
+  for(int i=0; i<m_DataCol.map_HalfCluster["ESHalfClusterU"].size(); i++)
+    m_halfclusters.push_back( m_DataCol.map_HalfCluster["ESHalfClusterU"][i].get() );
+
+  m_local_max.clear();
+  for(int ic=0;ic<m_halfclusters.size(); ic++){
+    //std::vector<const Calo1DCluster*> tmp_shower = m_halfclusters[ic]->getLocalMaxCol("AllLocalMax");
+    std::vector<const CaloHalfCluster*> m_axis = m_halfclusters[ic]->getHalfClusterCol("MergedAxis");
+    if(m_axis.size()>0){
+      std::vector<const Calo1DCluster*> tmp_shower = m_axis[0]->getCluster();
+      m_local_max.insert(m_local_max.end(), tmp_shower.begin(), tmp_shower.end());
+    }
+  }
+  for(int il=0; il<m_local_max.size(); il++){
+    m_barShowerU_tag.push_back( il );
+    m_barShowerU_x.push_back( m_local_max[il]->getPos().x() );
+    m_barShowerU_y.push_back( m_local_max[il]->getPos().y() );
+    m_barShowerU_z.push_back( m_local_max[il]->getPos().z() );
+    m_barShowerU_E.push_back( m_local_max[il]->getEnergy() );  
+
+    auto truthMap = m_local_max[il]->getLinkedMCP();
+    for(auto iter: truthMap){
+      m_barShowerU_mc_tag.push_back(il);
+      m_barShowerU_mc_pdg.push_back(iter.first.getPDG());
+      m_barShowerU_mc_px.push_back(iter.first.getMomentum().x);
+      m_barShowerU_mc_py.push_back(iter.first.getMomentum().y);
+      m_barShowerU_mc_pz.push_back(iter.first.getMomentum().z);
+      m_barShowerU_mc_weight.push_back(iter.second);
+    }
+  }
+  m_halfclusters.clear();
+  m_local_max.clear();
+  for(int i=0; i<m_DataCol.map_HalfCluster["ESHalfClusterV"].size(); i++)
+    m_halfclusters.push_back( m_DataCol.map_HalfCluster["ESHalfClusterV"][i].get() );
+
+  for(int ic=0;ic<m_halfclusters.size(); ic++){
+    //std::vector<const Calo1DCluster*> tmp_shower = m_halfclusters[ic]->getLocalMaxCol("AllLocalMax");
+    std::vector<const CaloHalfCluster*> m_axis = m_halfclusters[ic]->getHalfClusterCol("MergedAxis");
+    if(m_axis.size()>0){
+      std::vector<const Calo1DCluster*> tmp_shower = m_axis[0]->getCluster();
+      m_local_max.insert(m_local_max.end(), tmp_shower.begin(), tmp_shower.end());
+    }
+  }
+  for(int il=0; il<m_local_max.size(); il++){
+    m_barShowerV_tag.push_back( il );
+    m_barShowerV_x.push_back( m_local_max[il]->getPos().x() );
+    m_barShowerV_y.push_back( m_local_max[il]->getPos().y() );
+    m_barShowerV_z.push_back( m_local_max[il]->getPos().z() );
+    m_barShowerV_E.push_back( m_local_max[il]->getEnergy() );
+
+    auto truthMap = m_local_max[il]->getLinkedMCP();
+    for(auto iter: truthMap){
+      m_barShowerV_mc_tag.push_back(il);
+      m_barShowerV_mc_pdg.push_back(iter.first.getPDG());
+      m_barShowerV_mc_px.push_back(iter.first.getMomentum().x);
+      m_barShowerV_mc_py.push_back(iter.first.getMomentum().y);
+      m_barShowerV_mc_pz.push_back(iter.first.getMomentum().z);
+      m_barShowerV_mc_weight.push_back(iter.second);
+    }
+  }
+  t_Layers->Fill();
+
+  std::vector<const PandoraPlus::CaloHalfCluster*> m_halfclusterV; m_halfclusterV.clear();
+  std::vector<const PandoraPlus::CaloHalfCluster*> m_halfclusterU; m_halfclusterU.clear();
+  for(int i=0; i<m_DataCol.map_HalfCluster["HalfClusterColU"].size(); i++){
+    m_halfclusterU.push_back( m_DataCol.map_HalfCluster["HalfClusterColU"][i].get() );
+  }
+  for(int i=0; i<m_DataCol.map_HalfCluster["HalfClusterColV"].size(); i++){
+    m_halfclusterV.push_back( m_DataCol.map_HalfCluster["HalfClusterColV"][i].get() );
+  }
+  // Hough
+  ClearHough();
+  int houghU_index=0;
+  int houghV_index=0;
+  for(int i=0; i<m_halfclusterU.size(); i++){  // loop half cluster U
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_mergedaxisU = m_halfclusterU[i]->getHalfClusterCol("HoughAxis");
+    for(int ita=0; ita<m_mergedaxisU.size(); ita++){ // loop  axis U
+      // General information of the axis
+      m_houghU_tag.push_back(houghU_index);
+      m_houghU_type.push_back(m_mergedaxisU[ita]->getType());
+      m_houghU_x.push_back(m_mergedaxisU[ita]->getPos().x());
+      m_houghU_y.push_back(m_mergedaxisU[ita]->getPos().y());
+      m_houghU_z.push_back(m_mergedaxisU[ita]->getPos().z());
+      m_houghU_E.push_back(m_mergedaxisU[ita]->getEnergy());
+
+      // MC truth information of the Axis
+      auto truthMap = m_mergedaxisU[ita]->getLinkedMCP();
+      for(auto iter: truthMap){
+        m_houghU_truth_tag.push_back(houghU_index);
+        m_houghU_truth_MC_px.push_back(iter.first.getMomentum().x);
+        m_houghU_truth_MC_py.push_back(iter.first.getMomentum().y);
+        m_houghU_truth_MC_pz.push_back(iter.first.getMomentum().z);
+        m_houghU_truth_MC_E.push_back(iter.first.getEnergy());
+        m_houghU_truth_MC_weight.push_back(iter.second);
+      }
+
+      // Hits on axis
+      for(int ilm=0; ilm<m_mergedaxisU[ita]->getCluster().size(); ilm++){ // loop local max
+        m_houghU_hit_tag.push_back(houghU_index);
+        m_houghU_hit_x.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getPos().x() );
+        m_houghU_hit_y.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getPos().y() );
+        m_houghU_hit_z.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getPos().z() );
+        m_houghU_hit_E.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getEnergy()  );
+      }
+
+      houghU_index++;
+    }
+  }
+  for(int i=0; i<m_halfclusterV.size(); i++){  // loop half cluster V
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_mergedaxisV = m_halfclusterV[i]->getHalfClusterCol("HoughAxis");
+    for(int ita=0; ita<m_mergedaxisV.size(); ita++){ // loop  axis V
+      // General information of the axis
+      m_houghV_tag.push_back(houghV_index);
+      m_houghV_type.push_back(m_mergedaxisV[ita]->getType());
+      m_houghV_x.push_back(m_mergedaxisV[ita]->getPos().x());
+      m_houghV_y.push_back(m_mergedaxisV[ita]->getPos().y());
+      m_houghV_z.push_back(m_mergedaxisV[ita]->getPos().z());
+      m_houghV_E.push_back(m_mergedaxisV[ita]->getEnergy());
+      m_houghV_alpha.push_back(m_mergedaxisV[ita]->getHoughAlpha());
+      m_houghV_rho.push_back(m_mergedaxisV[ita]->getHoughRho());
+
+      // MC truth information of the Axis
+      auto truthMap = m_mergedaxisV[ita]->getLinkedMCP();
+      for(auto iter: truthMap){
+        m_houghV_truth_tag.push_back(houghV_index);
+        m_houghV_truth_MC_px.push_back(iter.first.getMomentum().x);
+        m_houghV_truth_MC_py.push_back(iter.first.getMomentum().y);
+        m_houghV_truth_MC_pz.push_back(iter.first.getMomentum().z);
+        m_houghV_truth_MC_E.push_back(iter.first.getEnergy());
+        m_houghV_truth_MC_weight.push_back(iter.second);
+      }
+
+      // Hits on axis
+      for(int ilm=0; ilm<m_mergedaxisV[ita]->getCluster().size(); ilm++){
+        m_houghV_hit_tag.push_back(houghV_index);
+        m_houghV_hit_x.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getPos().x());
+        m_houghV_hit_y.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getPos().y());
+        m_houghV_hit_z.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getPos().z());
+        m_houghV_hit_E.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getEnergy());
+      }
+
+      houghV_index++;
+    }
+  }
+  t_Hough->Fill();
+  // Cone
+  ClearCone();
+  int coneU_index=0;
+  int coneV_index=0;
+  for(int i=0; i<m_halfclusterU.size(); i++){  // loop half cluster U
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_mergedaxisU = m_halfclusterU[i]->getHalfClusterCol("ConeAxis");
+    for(int ita=0; ita<m_mergedaxisU.size(); ita++){ // loop  axis U
+      // General information of the axis
+      m_coneU_tag.push_back(coneU_index);
+      m_coneU_type.push_back(m_mergedaxisU[ita]->getType());
+      m_coneU_x.push_back(m_mergedaxisU[ita]->getPos().x());
+      m_coneU_y.push_back(m_mergedaxisU[ita]->getPos().y());
+      m_coneU_z.push_back(m_mergedaxisU[ita]->getPos().z());
+      m_coneU_E.push_back(m_mergedaxisU[ita]->getEnergy());
+
+      // MC truth information of the Axis
+      auto truthMap = m_mergedaxisU[ita]->getLinkedMCP();
+      for(auto iter: truthMap){
+        m_coneU_truth_tag.push_back(coneU_index);
+        m_coneU_truth_MC_px.push_back(iter.first.getMomentum().x);
+        m_coneU_truth_MC_py.push_back(iter.first.getMomentum().y);
+        m_coneU_truth_MC_pz.push_back(iter.first.getMomentum().z);
+        m_coneU_truth_MC_E.push_back(iter.first.getEnergy());
+        m_coneU_truth_MC_weight.push_back(iter.second);
+      }
+
+      // Hits on axis
+      for(int ilm=0; ilm<m_mergedaxisU[ita]->getCluster().size(); ilm++){ // loop local max
+        m_coneU_hit_tag.push_back(coneU_index);
+        m_coneU_hit_x.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getPos().x() );
+        m_coneU_hit_y.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getPos().y() );
+        m_coneU_hit_z.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getPos().z() );
+        m_coneU_hit_E.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getEnergy()  );
+      }
+
+      coneU_index++;
+    }
+  }
+  for(int i=0; i<m_halfclusterV.size(); i++){  // loop half cluster V
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_mergedaxisV = m_halfclusterV[i]->getHalfClusterCol("ConeAxis");
+    for(int ita=0; ita<m_mergedaxisV.size(); ita++){ // loop  axis V
+      // General information of the axis
+      m_coneV_tag.push_back(coneV_index);
+      m_coneV_type.push_back(m_mergedaxisV[ita]->getType());
+      m_coneV_x.push_back(m_mergedaxisV[ita]->getPos().x());
+      m_coneV_y.push_back(m_mergedaxisV[ita]->getPos().y());
+      m_coneV_z.push_back(m_mergedaxisV[ita]->getPos().z());
+      m_coneV_E.push_back(m_mergedaxisV[ita]->getEnergy());
+
+      // MC truth information of the Axis
+      auto truthMap = m_mergedaxisV[ita]->getLinkedMCP();
+      for(auto iter: truthMap){
+        m_coneV_truth_tag.push_back(coneV_index);
+        m_coneV_truth_MC_px.push_back(iter.first.getMomentum().x);
+        m_coneV_truth_MC_py.push_back(iter.first.getMomentum().y);
+        m_coneV_truth_MC_pz.push_back(iter.first.getMomentum().z);
+        m_coneV_truth_MC_E.push_back(iter.first.getEnergy());
+        m_coneV_truth_MC_weight.push_back(iter.second);
+      }
+
+      // Hits on axis
+      for(int ilm=0; ilm<m_mergedaxisV[ita]->getCluster().size(); ilm++){
+        m_coneV_hit_tag.push_back(coneV_index);
+        m_coneV_hit_x.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getPos().x());
+        m_coneV_hit_y.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getPos().y());
+        m_coneV_hit_z.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getPos().z());
+        m_coneV_hit_E.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getEnergy());
+      }
+
+      coneV_index++;
+    }
+  }
+  t_Cone->Fill();
+  // Track axis
+  ClearTrackAxis();
+  int trackU_index=0;
+  int trackV_index=0;
+  for(int i=0; i<m_halfclusterU.size(); i++){  // loop half cluster U
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_mergedaxisU = m_halfclusterU[i]->getHalfClusterCol("TrackAxis");
+    for(int ita=0; ita<m_mergedaxisU.size(); ita++){ // loop  axis U
+      // General information of the axis
+      m_trackU_tag.push_back(trackU_index);
+      m_trackU_type.push_back(m_mergedaxisU[ita]->getType());
+      m_trackU_x.push_back(m_mergedaxisU[ita]->getPos().x());
+      m_trackU_y.push_back(m_mergedaxisU[ita]->getPos().y());
+      m_trackU_z.push_back(m_mergedaxisU[ita]->getPos().z());
+      m_trackU_E.push_back(m_mergedaxisU[ita]->getEnergy());
+
+      // MC truth information of the Axis
+      auto truthMap = m_mergedaxisU[ita]->getLinkedMCP();
+      for(auto iter: truthMap){
+        m_trackU_truth_tag.push_back(trackU_index);
+        m_trackU_truth_MC_px.push_back(iter.first.getMomentum().x);
+        m_trackU_truth_MC_py.push_back(iter.first.getMomentum().y);
+        m_trackU_truth_MC_pz.push_back(iter.first.getMomentum().z);
+        m_trackU_truth_MC_E.push_back(iter.first.getEnergy());
+        m_trackU_truth_MC_weight.push_back(iter.second);
+      }
+
+      // Hits on axis
+      for(int ilm=0; ilm<m_mergedaxisU[ita]->getCluster().size(); ilm++){ // loop local max
+        m_trackU_hit_tag.push_back(trackU_index);
+        m_trackU_hit_x.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getPos().x() );
+        m_trackU_hit_y.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getPos().y() );
+        m_trackU_hit_z.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getPos().z() );
+        m_trackU_hit_E.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getEnergy()  );
+      }
+
+      trackU_index++;
+    }
+  }
+  for(int i=0; i<m_halfclusterV.size(); i++){  // loop half cluster V
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_mergedaxisV = m_halfclusterV[i]->getHalfClusterCol("TrackAxis");
+    for(int ita=0; ita<m_mergedaxisV.size(); ita++){ // loop  axis V
+      // General information of the axis
+      m_trackV_tag.push_back(trackV_index);
+      m_trackV_type.push_back(m_mergedaxisV[ita]->getType());
+      m_trackV_x.push_back(m_mergedaxisV[ita]->getPos().x());
+      m_trackV_y.push_back(m_mergedaxisV[ita]->getPos().y());
+      m_trackV_z.push_back(m_mergedaxisV[ita]->getPos().z());
+      m_trackV_E.push_back(m_mergedaxisV[ita]->getEnergy());
+
+      // MC truth information of the Axis
+      auto truthMap = m_mergedaxisV[ita]->getLinkedMCP();
+      for(auto iter: truthMap){
+        m_trackV_truth_tag.push_back(trackV_index);
+        m_trackV_truth_MC_px.push_back(iter.first.getMomentum().x);
+        m_trackV_truth_MC_py.push_back(iter.first.getMomentum().y);
+        m_trackV_truth_MC_pz.push_back(iter.first.getMomentum().z);
+        m_trackV_truth_MC_E.push_back(iter.first.getEnergy());
+        m_trackV_truth_MC_weight.push_back(iter.second);
+      }
+
+      // Hits on axis
+      for(int ilm=0; ilm<m_mergedaxisV[ita]->getCluster().size(); ilm++){
+        m_trackV_hit_tag.push_back(trackV_index);
+        m_trackV_hit_x.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getPos().x());
+        m_trackV_hit_y.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getPos().y());
+        m_trackV_hit_z.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getPos().z());
+        m_trackV_hit_E.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getEnergy());
+      }
+
+      trackV_index++;
+    }
+  }
+  t_TrackAxis->Fill();
+  //Axis
+  ClearAxis();
+  int axisU_index=0;
+  int axisV_index=0;
+  for(int i=0; i<m_halfclusterU.size(); i++){  // loop half cluster U
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_mergedaxisU = m_halfclusterU[i]->getHalfClusterCol("MergedAxis");
+    for(int ita=0; ita<m_mergedaxisU.size(); ita++){ // loop  axis U
+      // General information of the axis
+      m_axisU_tag.push_back(axisU_index);
+      m_axisU_type.push_back(m_mergedaxisU[ita]->getType());
+      m_axisU_x.push_back(m_mergedaxisU[ita]->getPos().x());
+      m_axisU_y.push_back(m_mergedaxisU[ita]->getPos().y());
+      m_axisU_z.push_back(m_mergedaxisU[ita]->getPos().z());
+      m_axisU_E.push_back(m_mergedaxisU[ita]->getEnergy());
+
+      // MC truth information of the Axis
+      auto truthMap = m_mergedaxisU[ita]->getLinkedMCP();
+      for(auto iter: truthMap){
+        m_axisU_truth_tag.push_back(axisU_index);
+        m_axisU_truth_MC_px.push_back(iter.first.getMomentum().x);
+        m_axisU_truth_MC_py.push_back(iter.first.getMomentum().y);
+        m_axisU_truth_MC_pz.push_back(iter.first.getMomentum().z);
+        m_axisU_truth_MC_E.push_back(iter.first.getEnergy());
+        m_axisU_truth_MC_weight.push_back(iter.second);
+      }
+      // Hits on axis
+      for(int ilm=0; ilm<m_mergedaxisU[ita]->getCluster().size(); ilm++){ // loop local max
+        m_axisU_hit_tag.push_back(axisU_index);
+        m_axisU_hit_x.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getPos().x() );
+        m_axisU_hit_y.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getPos().y() );
+        m_axisU_hit_z.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getPos().z() );
+        m_axisU_hit_E.push_back( m_mergedaxisU[ita]->getCluster()[ilm]->getEnergy()  );
+      }
+
+      axisU_index++;
+    }
+  }
+  for(int i=0; i<m_halfclusterV.size(); i++){  // loop half cluster V
+    std::vector<const PandoraPlus::CaloHalfCluster*> m_mergedaxisV = m_halfclusterV[i]->getHalfClusterCol("MergedAxis");
+    for(int ita=0; ita<m_mergedaxisV.size(); ita++){ // loop  axis V
+      // General information of the axis
+      m_axisV_tag.push_back(axisV_index);
+      m_axisV_type.push_back(m_mergedaxisV[ita]->getType());
+      m_axisV_x.push_back(m_mergedaxisV[ita]->getPos().x());
+      m_axisV_y.push_back(m_mergedaxisV[ita]->getPos().y());
+      m_axisV_z.push_back(m_mergedaxisV[ita]->getPos().z());
+      m_axisV_E.push_back(m_mergedaxisV[ita]->getEnergy());
+
+      // MC truth information of the Axis
+      auto truthMap = m_mergedaxisV[ita]->getLinkedMCP();
+      for(auto iter: truthMap){
+        m_axisV_truth_tag.push_back(axisV_index);
+        m_axisV_truth_MC_px.push_back(iter.first.getMomentum().x);
+        m_axisV_truth_MC_py.push_back(iter.first.getMomentum().y);
+        m_axisV_truth_MC_pz.push_back(iter.first.getMomentum().z);
+        m_axisV_truth_MC_E.push_back(iter.first.getEnergy());
+        m_axisV_truth_MC_weight.push_back(iter.second);
+      }
+      // Hits on axis
+      for(int ilm=0; ilm<m_mergedaxisV[ita]->getCluster().size(); ilm++){
+        m_axisV_hit_tag.push_back(axisV_index);
+        m_axisV_hit_x.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getPos().x());
+        m_axisV_hit_y.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getPos().y());
+        m_axisV_hit_z.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getPos().z());
+        m_axisV_hit_E.push_back(m_mergedaxisV[ita]->getCluster()[ilm]->getEnergy());
+      }
+
+      axisV_index++;
+    }
+  }
+  m_halfclusterU.clear();
+  m_halfclusterV.clear();
+  for(int i=0; i<m_DataCol.map_HalfCluster["emptyHalfClusterU"].size(); i++){
+    m_halfclusterU.push_back( m_DataCol.map_HalfCluster["emptyHalfClusterU"][i].get() );
+  }
+  for(int i=0; i<m_DataCol.map_HalfCluster["emptyHalfClusterV"].size(); i++){
+    m_halfclusterV.push_back( m_DataCol.map_HalfCluster["emptyHalfClusterV"][i].get() );
+  }
+  for(int i=0; i<m_halfclusterU.size(); i++){
+    m_emptyAxisU_tag.push_back(m_halfclusterU[i]->getType());
+    m_emptyAxisU_x.push_back(m_halfclusterU[i]->getPos().x());
+    m_emptyAxisU_y.push_back(m_halfclusterU[i]->getPos().y());
+    m_emptyAxisU_z.push_back(m_halfclusterU[i]->getPos().z());
+    m_emptyAxisU_E.push_back(m_halfclusterU[i]->getEnergy());
+  }
+  for(int i=0; i<m_halfclusterV.size(); i++){
+    m_emptyAxisV_tag.push_back(m_halfclusterV[i]->getType());
+    m_emptyAxisV_x.push_back(m_halfclusterV[i]->getPos().x());
+    m_emptyAxisV_y.push_back(m_halfclusterV[i]->getPos().y());
+    m_emptyAxisV_z.push_back(m_halfclusterV[i]->getPos().z());
+    m_emptyAxisV_E.push_back(m_halfclusterV[i]->getEnergy());
+  }
+  
+  t_Axis->Fill();
+
+
+  //Half cluster
+  ClearHalfCluster();
+  m_halfclusterV.clear();
+  m_halfclusterU.clear();
+  m_totE_HFClusV = 0;
+  m_totE_HFClusU = 0;
+  //for(int i=0; i<m_DataCol.map_HalfCluster["ESHalfClusterU"].size(); i++){
+  //  m_halfclusterU.push_back( m_DataCol.map_HalfCluster["ESHalfClusterU"][i]->getHalfClusterCol("MergedAxis")[0] );
+  //}
+  //for(int i=0; i<m_DataCol.map_HalfCluster["ESHalfClusterV"].size(); i++){
+  //  m_halfclusterV.push_back( m_DataCol.map_HalfCluster["ESHalfClusterV"][i]->getHalfClusterCol("MergedAxis")[0] );
+  //}
+  for(int i=0; i<m_DataCol.map_HalfCluster["HalfClusterColU"].size(); i++){
+    m_halfclusterU.push_back( m_DataCol.map_HalfCluster["HalfClusterColU"][i].get() );
+  }
+  for(int i=0; i<m_DataCol.map_HalfCluster["HalfClusterColV"].size(); i++){
+    m_halfclusterV.push_back( m_DataCol.map_HalfCluster["HalfClusterColV"][i].get() );
+  }
+  for(int i=0; i<m_halfclusterV.size(); i++){
+    m_HalfClusterV_x.push_back(m_halfclusterV[i]->getPos().x());
+    m_HalfClusterV_y.push_back(m_halfclusterV[i]->getPos().y());
+    m_HalfClusterV_z.push_back(m_halfclusterV[i]->getPos().z());
+    m_HalfClusterV_E.push_back(m_halfclusterV[i]->getEnergy());
+    m_HalfClusterV_tag.push_back(i);
+    m_HalfClusterV_type.push_back(m_halfclusterV[i]->getType());
+    m_totE_HFClusV += m_halfclusterV[i]->getEnergy();    
+
+      // MC truth information of the HFCluster
+      auto truthMap = m_halfclusterV[i]->getLinkedMCP();
+      for(auto iter: truthMap){
+        m_HalfClusterV_truth_tag.push_back(i);
+        m_HalfClusterV_truthMC_px.push_back(iter.first.getMomentum().x);
+        m_HalfClusterV_truthMC_py.push_back(iter.first.getMomentum().y);
+        m_HalfClusterV_truthMC_pz.push_back(iter.first.getMomentum().z);
+        m_HalfClusterV_truthMC_E.push_back(iter.first.getEnergy());
+        m_HalfClusterV_truthMC_weight.push_back(iter.second);
+      }
+
+      // 1DClusters (hits)
+      for(int ilm=0; ilm<m_halfclusterV[i]->getCluster().size(); ilm++){ 
+        m_HalfClusterV_hit_tag.push_back(i);
+        m_HalfClusterV_hit_x.push_back( m_halfclusterV[i]->getCluster()[ilm]->getPos().x() );
+        m_HalfClusterV_hit_y.push_back( m_halfclusterV[i]->getCluster()[ilm]->getPos().y() );
+        m_HalfClusterV_hit_z.push_back( m_halfclusterV[i]->getCluster()[ilm]->getPos().z() );
+        m_HalfClusterV_hit_E.push_back( m_halfclusterV[i]->getCluster()[ilm]->getEnergy()  );
+      }
+  }
+  for(int i=0; i<m_halfclusterU.size(); i++){
+    m_HalfClusterU_x.push_back(m_halfclusterU[i]->getPos().x());
+    m_HalfClusterU_y.push_back(m_halfclusterU[i]->getPos().y());
+    m_HalfClusterU_z.push_back(m_halfclusterU[i]->getPos().z());
+    m_HalfClusterU_E.push_back(m_halfclusterU[i]->getEnergy());
+    m_HalfClusterU_tag.push_back(i);
+    m_HalfClusterU_type.push_back(m_halfclusterU[i]->getType());
+    m_totE_HFClusU += m_halfclusterU[i]->getEnergy();
+
+      // MC truth information of the HFCluster
+      auto truthMap = m_halfclusterU[i]->getLinkedMCP();
+      for(auto iter: truthMap){
+        m_HalfClusterU_truth_tag.push_back(i);
+        m_HalfClusterU_truthMC_px.push_back(iter.first.getMomentum().x);
+        m_HalfClusterU_truthMC_py.push_back(iter.first.getMomentum().y);
+        m_HalfClusterU_truthMC_pz.push_back(iter.first.getMomentum().z);
+        m_HalfClusterU_truthMC_E.push_back(iter.first.getEnergy());
+        m_HalfClusterU_truthMC_weight.push_back(iter.second);
+      }
+
+      // 1DClusters (hits)
+      for(int ilm=0; ilm<m_halfclusterU[i]->getCluster().size(); ilm++){
+        m_HalfClusterU_hit_tag.push_back(i);
+        m_HalfClusterU_hit_x.push_back( m_halfclusterU[i]->getCluster()[ilm]->getPos().x() );
+        m_HalfClusterU_hit_y.push_back( m_halfclusterU[i]->getCluster()[ilm]->getPos().y() );
+        m_HalfClusterU_hit_z.push_back( m_halfclusterU[i]->getCluster()[ilm]->getPos().z() );
+        m_HalfClusterU_hit_E.push_back( m_halfclusterU[i]->getCluster()[ilm]->getEnergy()  );
+      }
+  }
+  t_HalfCluster->Fill();
+
+
+  //Tower
+  ClearTower();
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_tower = m_DataCol.map_CaloCluster["ESTower"];
+  for(int it=0; it<m_tower.size(); it++){
+    ClearTower();
+    towerID[0] = m_tower[it]->getTowerID()[0][0];
+    towerID[1] = m_tower[it]->getTowerID()[0][1];
+    towerID[2] = m_tower[it]->getTowerID()[0][2];
+
+    std::vector<const CaloHalfCluster*> m_HFClusU = m_tower[it]->getHalfClusterUCol("ESHalfClusterU");
+    std::vector<const CaloHalfCluster*> m_HFClusV = m_tower[it]->getHalfClusterVCol("ESHalfClusterV");
+
+    m_NclusU = m_HFClusU.size();
+    m_NclusV = m_HFClusV.size();
+    m_totEn = m_tower[it]->getEnergy();
+    m_totEn_U = 0.;
+    m_totEn_V = 0.;
+    for(int ic=0; ic<m_NclusU; ic++){
+      m_HalfClusterU_tag.push_back(it);
+      m_HalfClusterU_x.push_back(m_HFClusU[ic]->getPos().x());
+      m_HalfClusterU_y.push_back(m_HFClusU[ic]->getPos().y());
+      m_HalfClusterU_z.push_back(m_HFClusU[ic]->getPos().z());
+      m_HalfClusterU_E.push_back(m_HFClusU[ic]->getEnergy());
+      m_HalfClusterU_type.push_back(m_HFClusU[ic]->getType());
+      m_HalfClusterU_nTrk.push_back(m_HFClusU[ic]->getAssociatedTracks().size());
+      m_totEn_U += m_HFClusU[ic]->getEnergy();
+    }
+
+    for(int ic=0; ic<m_NclusV; ic++){
+      m_HalfClusterV_tag.push_back(it);
+      m_HalfClusterV_x.push_back(m_HFClusV[ic]->getPos().x());
+      m_HalfClusterV_y.push_back(m_HFClusV[ic]->getPos().y());
+      m_HalfClusterV_z.push_back(m_HFClusV[ic]->getPos().z());
+      m_HalfClusterV_E.push_back(m_HFClusV[ic]->getEnergy());
+      m_HalfClusterV_type.push_back(m_HFClusV[ic]->getType());
+      m_HalfClusterV_nTrk.push_back(m_HFClusV[ic]->getAssociatedTracks().size());
+      m_totEn_V += m_HFClusV[ic]->getEnergy();
+    }
+
+    t_Tower->Fill();
+  }
+
+cout<<"  Write 3D cluster"<<endl;
+  //3D cluster
+  ClearCluster();
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_EcalClusterCol = m_DataCol.map_CaloCluster["TrkMergedECAL"];
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_HcalClusterCol = m_DataCol.map_CaloCluster["HCALCluster"];
+  std::vector<std::shared_ptr<PandoraPlus::Calo3DCluster>> m_SimpleHcalClusterCol = m_DataCol.map_CaloCluster["SimpleHCALCluster"];
+  m_totE_Ecal = 0.;
+  m_totE_Hcal = 0.;
+  m_Nclus_Ecal = m_EcalClusterCol.size();
+  m_Nclus_Hcal = m_HcalClusterCol.size();
+  for(int icl=0; icl<m_EcalClusterCol.size(); icl++){
+    m_EcalClus_x.push_back(m_EcalClusterCol[icl]->getShowerCenter().x());
+    m_EcalClus_y.push_back(m_EcalClusterCol[icl]->getShowerCenter().y());
+    m_EcalClus_z.push_back(m_EcalClusterCol[icl]->getShowerCenter().z());
+    m_EcalClus_E.push_back(m_EcalClusterCol[icl]->getLongiE());
+    m_EcalClus_nTrk.push_back(m_EcalClusterCol[icl]->getAssociatedTracks().size());
+
+    double tmp_phi = std::atan2(m_EcalClusterCol[icl]->getShowerCenter().y(), m_EcalClusterCol[icl]->getShowerCenter().x())* 180.0 / M_PI;
+    if (tmp_phi < 0) tmp_phi += 360.0;
+    double tmp_theta = std::atan2(m_EcalClusterCol[icl]->getShowerCenter().z(), m_EcalClusterCol[icl]->getShowerCenter().Perp())* 180.0 / M_PI + 90; 
+    //cout<<" Theta: "<<tmp_theta<<" Phi: "<<tmp_phi<<endl;
+    m_EcalClus_Escale.push_back(m_energycorsvc->energyCorrection(m_EcalClusterCol[icl]->getLongiE(), tmp_phi, tmp_theta));
+
+
+    if(m_EcalClusterCol[icl]->getAssociatedTracks().size()==1){
+      const Track* trk = m_EcalClusterCol[icl]->getAssociatedTracks()[0];
+      m_EcalClus_pTrk.push_back(trk->getMomentum());
+
+      std::vector<TrackState> AllTrackStates = trk->getAllTrackStates();
+      for(int istate=0; istate<AllTrackStates.size(); istate++){
+        m_EcalClus_trk_tag.push_back(icl);
+        m_EcalClus_trk_d0.push_back(AllTrackStates[istate].D0);
+        m_EcalClus_trk_z0.push_back(AllTrackStates[istate].Z0);
+        m_EcalClus_trk_phi.push_back(AllTrackStates[istate].phi0);
+        m_EcalClus_trk_tanL.push_back( AllTrackStates[istate].tanLambda );
+        m_EcalClus_trk_kappa.push_back( AllTrackStates[istate].Kappa);
+        m_EcalClus_trk_omega.push_back( AllTrackStates[istate].Omega );
+        m_EcalClus_trk_location.push_back( AllTrackStates[istate].location );
+      }
+
+    }
+    else
+      m_EcalClus_pTrk.push_back(-99);
+
+    m_EcalClus_typeU.push_back(m_EcalClusterCol[icl]->getHalfClusterUCol("LinkedLongiCluster")[0]->getType());
+    m_EcalClus_typeV.push_back(m_EcalClusterCol[icl]->getHalfClusterVCol("LinkedLongiCluster")[0]->getType());
+    for(int ii=0; ii<m_EcalClusterCol[icl]->getHalfClusterUCol("LinkedLongiCluster").size(); ii++){
+      for(int ihit=0; ihit<m_EcalClusterCol[icl]->getHalfClusterUCol("LinkedLongiCluster")[ii]->getBars().size(); ihit++){
+        auto shower = m_EcalClusterCol[icl]->getHalfClusterUCol("LinkedLongiCluster")[ii]->getBars()[ihit];
+        m_EcalClus_hitU_tag.push_back(icl);
+        m_EcalClus_hitU_x.push_back(shower->getPosition().x());
+        m_EcalClus_hitU_y.push_back(shower->getPosition().y());
+        m_EcalClus_hitU_z.push_back(shower->getPosition().z());
+        m_EcalClus_hitU_E.push_back(shower->getEnergy());
+      }
+    }
+    for(int ii=0; ii<m_EcalClusterCol[icl]->getHalfClusterVCol("LinkedLongiCluster").size(); ii++){
+      for(int ihit=0; ihit<m_EcalClusterCol[icl]->getHalfClusterVCol("LinkedLongiCluster")[ii]->getBars().size(); ihit++){
+        auto shower = m_EcalClusterCol[icl]->getHalfClusterVCol("LinkedLongiCluster")[ii]->getBars()[ihit];
+        m_EcalClus_hitV_tag.push_back(icl);
+        m_EcalClus_hitV_x.push_back(shower->getPosition().x());
+        m_EcalClus_hitV_y.push_back(shower->getPosition().y());
+        m_EcalClus_hitV_z.push_back(shower->getPosition().z());
+        m_EcalClus_hitV_E.push_back(shower->getEnergy());
+      }
+    }
+
+    m_totE_Ecal += m_EcalClusterCol[icl]->getLongiE();
+    auto truthMap = m_EcalClusterCol[icl]->getLinkedMCP();
+    for(auto iter: truthMap){
+      m_EcalClus_truthMC_tag.push_back(icl);
+      m_EcalClus_truthMC_pid.push_back(iter.first.getPDG() );
+      m_EcalClus_truthMC_px.push_back(iter.first.getMomentum().x);
+      m_EcalClus_truthMC_py.push_back(iter.first.getMomentum().y);
+      m_EcalClus_truthMC_pz.push_back(iter.first.getMomentum().z);
+      m_EcalClus_truthMC_E.push_back(iter.first.getEnergy());
+      m_EcalClus_truthMC_EPx.push_back(iter.first.getEndpoint().x);
+      m_EcalClus_truthMC_EPy.push_back(iter.first.getEndpoint().y);
+      m_EcalClus_truthMC_EPz.push_back(iter.first.getEndpoint().z);
+      m_EcalClus_truthMC_weight.push_back(iter.second);
+    }
+  }
+
+  for(int icl=0; icl<m_HcalClusterCol.size(); icl++){
+    m_HcalClus_x.push_back(m_HcalClusterCol[icl]->getHitCenter().x());
+    m_HcalClus_y.push_back(m_HcalClusterCol[icl]->getHitCenter().y());
+    m_HcalClus_z.push_back(m_HcalClusterCol[icl]->getHitCenter().z());
+    m_HcalClus_E.push_back(m_HcalClusterCol[icl]->getHitsE());
+    m_HcalClus_nTrk.push_back(m_HcalClusterCol[icl]->getAssociatedTracks().size());
+    if(m_HcalClusterCol[icl]->getAssociatedTracks().size()==1)
+      m_HcalClus_pTrk.push_back(m_HcalClusterCol[icl]->getAssociatedTracks()[0]->getMomentum());
+    else
+      m_HcalClus_pTrk.push_back(-99);
+
+    for(int ih=0; ih<m_HcalClusterCol[icl]->getCaloHits().size(); ih++){
+      m_HcalClus_hit_tag.push_back(icl);
+      m_HcalClus_hit_x.push_back(m_HcalClusterCol[icl]->getCaloHits()[ih]->getPosition().x());
+      m_HcalClus_hit_y.push_back(m_HcalClusterCol[icl]->getCaloHits()[ih]->getPosition().y());
+      m_HcalClus_hit_z.push_back(m_HcalClusterCol[icl]->getCaloHits()[ih]->getPosition().z());
+      m_HcalClus_hit_E.push_back(m_HcalClusterCol[icl]->getCaloHits()[ih]->getEnergy());
+    }
+
+    m_totE_Hcal += m_HcalClusterCol[icl]->getHitsE();
+    auto truthMap = m_HcalClusterCol[icl]->getLinkedMCP();
+    for(auto iter: truthMap){
+      m_HcalClus_truthMC_tag.push_back(icl);
+      m_HcalClus_truthMC_pid.push_back(iter.first.getPDG() );
+      m_HcalClus_truthMC_px.push_back(iter.first.getMomentum().x);
+      m_HcalClus_truthMC_py.push_back(iter.first.getMomentum().y);
+      m_HcalClus_truthMC_pz.push_back(iter.first.getMomentum().z);
+      m_HcalClus_truthMC_E.push_back(iter.first.getEnergy());
+      m_HcalClus_truthMC_EPx.push_back(iter.first.getEndpoint().x);
+      m_HcalClus_truthMC_EPy.push_back(iter.first.getEndpoint().y);
+      m_HcalClus_truthMC_EPz.push_back(iter.first.getEndpoint().z);
+      m_HcalClus_truthMC_weight.push_back(iter.second);
+    }
+  }
+
+  for(int icl=0; icl<m_SimpleHcalClusterCol.size(); icl++){
+    m_SimpleHcalClus_x.push_back(m_SimpleHcalClusterCol[icl]->getHitCenter().x());
+    m_SimpleHcalClus_y.push_back(m_SimpleHcalClusterCol[icl]->getHitCenter().y());
+    m_SimpleHcalClus_z.push_back(m_SimpleHcalClusterCol[icl]->getHitCenter().z());
+    m_SimpleHcalClus_E.push_back(m_SimpleHcalClusterCol[icl]->getHitsE());
+    m_SimpleHcalClus_nTrk.push_back(m_SimpleHcalClusterCol[icl]->getAssociatedTracks().size());
+    if(m_SimpleHcalClusterCol[icl]->getAssociatedTracks().size()==1)
+      m_SimpleHcalClus_pTrk.push_back(m_SimpleHcalClusterCol[icl]->getAssociatedTracks()[0]->getMomentum());
+    else
+      m_SimpleHcalClus_pTrk.push_back(-99);
+
+    for(int ih=0; ih<m_SimpleHcalClusterCol[icl]->getCaloHits().size(); ih++){
+      m_SimpleHcalClus_hit_tag.push_back(icl);
+      m_SimpleHcalClus_hit_x.push_back(m_SimpleHcalClusterCol[icl]->getCaloHits()[ih]->getPosition().x());
+      m_SimpleHcalClus_hit_y.push_back(m_SimpleHcalClusterCol[icl]->getCaloHits()[ih]->getPosition().y());
+      m_SimpleHcalClus_hit_z.push_back(m_SimpleHcalClusterCol[icl]->getCaloHits()[ih]->getPosition().z());
+      m_SimpleHcalClus_hit_E.push_back(m_SimpleHcalClusterCol[icl]->getCaloHits()[ih]->getEnergy());
+    }
+
+    auto truthMap = m_SimpleHcalClusterCol[icl]->getLinkedMCP();
+    for(auto iter: truthMap){
+      m_SimpleHcalClus_truthMC_tag.push_back(icl);
+      m_SimpleHcalClus_truthMC_pid.push_back(iter.first.getPDG() );
+      m_SimpleHcalClus_truthMC_px.push_back(iter.first.getMomentum().x);
+      m_SimpleHcalClus_truthMC_py.push_back(iter.first.getMomentum().y);
+      m_SimpleHcalClus_truthMC_pz.push_back(iter.first.getMomentum().z);
+      m_SimpleHcalClus_truthMC_E.push_back(iter.first.getEnergy());
+      m_SimpleHcalClus_truthMC_EPx.push_back(iter.first.getEndpoint().x);
+      m_SimpleHcalClus_truthMC_EPy.push_back(iter.first.getEndpoint().y);
+      m_SimpleHcalClus_truthMC_EPz.push_back(iter.first.getEndpoint().z);
+      m_SimpleHcalClus_truthMC_weight.push_back(iter.second);
+    }
+  }
+  t_Cluster->Fill();
+
+  // Save Track info
+  ClearTrack();
+  std::vector<PandoraPlus::Track*> m_trkCol; 
+  for(int it=0; it<m_DataCol.TrackCol.size(); it++)
+    m_trkCol.push_back( m_DataCol.TrackCol[it].get() );
+
+  m_Ntrk = m_trkCol.size();
+  for(int itrk=0; itrk<m_Ntrk; itrk++){
+    m_type.push_back(m_trkCol[itrk]->getType());
+    std::vector<TrackState> AllTrackStates = m_trkCol[itrk]->getAllTrackStates();
+    for(int istate=0; istate<AllTrackStates.size(); istate++){
+      m_trkstate_d0.push_back( AllTrackStates[istate].D0 );
+      m_trkstate_z0.push_back( AllTrackStates[istate].Z0 );
+      m_trkstate_phi.push_back( AllTrackStates[istate].phi0 );
+      m_trkstate_tanL.push_back( AllTrackStates[istate].tanLambda );
+      m_trkstate_kappa.push_back( AllTrackStates[istate].Kappa);
+      m_trkstate_omega.push_back( AllTrackStates[istate].Omega );
+      m_trkstate_refx.push_back( AllTrackStates[istate].referencePoint.X() );
+      m_trkstate_refy.push_back( AllTrackStates[istate].referencePoint.Y() );
+      m_trkstate_refz.push_back( AllTrackStates[istate].referencePoint.Z() );
+      m_trkstate_location.push_back( AllTrackStates[istate].location );
+      m_trkstate_tag.push_back(itrk);
+  }}
+  t_Track->Fill();
+
+  // yyy: pfo
+  ClearPFO();
+  std::vector<PandoraPlus::PFObject*> m_pfobjects; m_pfobjects.clear();
+  std::vector<PandoraPlus::PFObject*> m_chargedPFOs, m_neutralPFOs;
+  for(int ip=0; ip<m_DataCol.map_PFObjects["outputPFO"].size(); ip++){
+    m_pfobjects.push_back(m_DataCol.map_PFObjects["outputPFO"][ip].get());
+    if(m_DataCol.map_PFObjects["outputPFO"][ip]->getTracks().size()!=0) m_chargedPFOs.push_back( m_DataCol.map_PFObjects["outputPFO"][ip].get() );
+    else m_neutralPFOs.push_back( m_DataCol.map_PFObjects["outputPFO"][ip].get() );
+  }
+
+ double totE_Ecal = 0;
+ double totE_Hcal = 0;
+ cout<<"After merge all virtual to Ch: charged "<<m_chargedPFOs.size()<<", neutral "<<m_neutralPFOs.size()<<", total "<<m_pfobjects.size()<<endl;
+ for(int i=0; i<m_neutralPFOs.size(); i++){
+   cout<<"    PFO #"<<i<<": track size "<<m_neutralPFOs[i]->getTracks().size()<<", leading P "<<m_neutralPFOs[i]->getTrackMomentum();
+   cout<<", ECAL cluster size "<<m_neutralPFOs[i]->getECALClusters().size()<<", totE "<<m_neutralPFOs[i]->getECALClusterEnergy();
+   cout<<", HCAL cluster size "<<m_neutralPFOs[i]->getHCALClusters().size()<<", totE "<<m_neutralPFOs[i]->getHCALClusterEnergy()<<endl;
+   totE_Ecal += m_neutralPFOs[i]->getECALClusterEnergy();
+   totE_Hcal += m_neutralPFOs[i]->getHCALClusterEnergy();
+ }
+ cout<<"-----Neutral cluster Ecal total energy: "<<totE_Ecal<<", Hcal total energy: "<<totE_Hcal<<endl;
+ totE_Ecal = 0;
+ totE_Hcal = 0;
+ for(int i=0; i<m_chargedPFOs.size(); i++){
+   cout<<"    PFO #"<<i<<": track size "<<m_chargedPFOs[i]->getTracks().size()<<", leading P "<<m_chargedPFOs[i]->getTrackMomentum();
+   cout<<", ECAL cluster size "<<m_chargedPFOs[i]->getECALClusters().size()<<", totE "<<m_chargedPFOs[i]->getECALClusterEnergy();
+   cout<<", HCAL cluster size "<<m_chargedPFOs[i]->getHCALClusters().size()<<", totE "<<m_chargedPFOs[i]->getHCALClusterEnergy()<<endl;
+   totE_Ecal += m_chargedPFOs[i]->getECALClusterEnergy();
+   totE_Hcal += m_chargedPFOs[i]->getHCALClusterEnergy();
+ }
+ cout<<"-----Charged cluster Ecal total energy: "<<totE_Ecal<<", Hcal total energy: "<<totE_Hcal<<endl;
+
+
+
+  for(int ip=0; ip<m_pfobjects.size(); ip++){
+    std::vector<const Track*> t_tracks = m_pfobjects[ip]->getTracks();
+    std::vector<const Calo3DCluster*> t_ecal_clusters = m_pfobjects[ip]->getECALClusters();
+    std::vector<const Calo3DCluster*> t_hcal_clusters =  m_pfobjects[ip]->getHCALClusters();
+
+    pfo_tag.push_back(ip);
+    pfo_n_track.push_back(t_tracks.size());
+    pfo_n_ecal_clus.push_back(t_ecal_clusters.size());
+    pfo_n_hcal_clus.push_back(t_hcal_clusters.size());
+
+    for(int it=0; it<t_tracks.size(); it++){
+      std::vector<TrackState> AllTrackStates = t_tracks[it]->getAllTrackStates();
+      for(int istate=0; istate<AllTrackStates.size(); istate++){
+        pfo_trk_tag.push_back(ip);
+        pfo_trk_d0.push_back( AllTrackStates[istate].D0 );
+        pfo_trk_z0.push_back( AllTrackStates[istate].Z0 );
+        pfo_trk_phi.push_back( AllTrackStates[istate].phi0 );
+        pfo_trk_tanL.push_back( AllTrackStates[istate].tanLambda );
+        pfo_trk_kappa.push_back( AllTrackStates[istate].Kappa);
+        pfo_trk_omega.push_back( AllTrackStates[istate].Omega );
+        pfo_trk_location.push_back( AllTrackStates[istate].location );
+      }
+    }
+
+    for(int ie=0; ie<t_ecal_clusters.size(); ie++){
+      pfo_ecal_tag.push_back(ip);
+      pfo_ecal_clus_x.push_back(t_ecal_clusters[ie]->getShowerCenter().x());
+      pfo_ecal_clus_y.push_back(t_ecal_clusters[ie]->getShowerCenter().y());
+      pfo_ecal_clus_z.push_back(t_ecal_clusters[ie]->getShowerCenter().z());
+      pfo_ecal_clus_E.push_back(t_ecal_clusters[ie]->getLongiE());
+
+      double tmp_phi = std::atan2(t_ecal_clusters[ie]->getShowerCenter().y(), t_ecal_clusters[ie]->getShowerCenter().x())* 180.0 / M_PI;
+      if (tmp_phi < 0) tmp_phi += 360.0;
+      double tmp_theta = std::atan2(t_ecal_clusters[ie]->getShowerCenter().z(), t_ecal_clusters[ie]->getShowerCenter().Perp())* 180.0 / M_PI + 90; 
+      pfo_ecal_clus_Escale.push_back(m_energycorsvc->energyCorrection(t_ecal_clusters[ie]->getLongiE(), tmp_phi, tmp_theta));
+
+    }
+    for(int ih=0; ih<t_hcal_clusters.size(); ih++){
+      pfo_hcal_tag.push_back(ip);
+      pfo_hcal_clus_x.push_back(t_hcal_clusters[ih]->getHitCenter().x());
+      pfo_hcal_clus_y.push_back(t_hcal_clusters[ih]->getHitCenter().y());
+      pfo_hcal_clus_z.push_back(t_hcal_clusters[ih]->getHitCenter().z());
+      pfo_hcal_clus_E.push_back(t_hcal_clusters[ih]->getHitsE());
+    }
+  }
+  t_PFO->Fill();
+
+  //Clean Events
+  //system("/cefs/higgs/songwz/winter22/CEPCSW/workarea/memory/memory_test.sh before_clean");
+  m_DataCol.Clear();
+  //system("/cefs/higgs/songwz/winter22/CEPCSW/workarea/memory/memory_test.sh event_end");
+
+
+  std::cout<<"Event: "<<_nEvt<<" is done"<<std::endl;
+  _nEvt ++ ;
+  return StatusCode::SUCCESS;
+}
+
+StatusCode PandoraPlusPFAlg::finalize()
+{
+  m_wfile->cd();
+  t_MCParticle->Write();
+  t_SimBar->Write();
+  t_LocalMax->Write();
+  t_Layers->Write();
+  t_Hough->Write();
+  t_Cone->Write();
+  t_TrackAxis->Write();
+  t_Axis->Write();
+  t_HalfCluster->Write();
+  t_Tower->Write();
+  t_Cluster->Write();
+  t_Track->Write();
+  t_PFO->Write();
+  m_wfile->Close();
+  delete m_wfile, t_MCParticle, t_SimBar, t_LocalMax, t_Layers, t_Hough, t_Cone, t_TrackAxis, t_Axis, t_HalfCluster, t_Tower, t_Cluster, t_Track, t_PFO; 
+
+  delete m_pMCParticleCreator;
+  delete m_pTrackCreator; 
+  delete m_pCaloHitsCreator;
+  delete m_pOutputCreator;
+
+  delete r_MCParticleCol;
+  for(auto iter : r_TrackCols) delete iter; 
+  //for(auto iter : r_ECalHitCols) delete iter; 
+  //for(auto iter : r_HCalHitCols) delete iter; 
+  for(auto iter : r_CaloHitCols) delete iter; 
+  for(auto iter : map_CaloMCPAssoCols) delete iter.second;
+  r_TrackCols.clear();
+  for(auto iter : w_ClusterCollection) delete iter.second;
+  w_ClusterCollection.clear(); 
+  //r_ECalHitCols.clear();
+  //r_HCalHitCols.clear();
+  r_CaloHitCols.clear();
+  //m_energycorsvc->finalize();
+
+  info() << "Processed " << _nEvt << " events " << endmsg;
+  return GaudiAlgorithm::finalize();
+}
+
+void PandoraPlusPFAlg::ClearMCParticle(){
+  m_mcPdgid.clear();
+  m_mcStatus.clear();
+  m_mcPx.clear();
+  m_mcPy.clear();
+  m_mcPz.clear();
+  m_mcEn.clear();
+  m_mcMass.clear();
+  m_mcCharge.clear();
+  m_mcEPx.clear();
+  m_mcEPy.clear();
+  m_mcEPz.clear();
+  m_depEn_ecal.clear();
+  m_depEn_hcal.clear();
+}
+
+void PandoraPlusPFAlg::ClearBar(){
+  m_totE_EcalSim = -99;
+  m_totE_HcalSim = -99;
+  m_simBar_x.clear();
+  m_simBar_y.clear();
+  m_simBar_z.clear();
+  m_simBar_T1.clear();
+  m_simBar_T2.clear();
+  m_simBar_Q1.clear();
+  m_simBar_Q2.clear();
+  m_simBar_module.clear();
+  m_simBar_dlayer.clear();
+  m_simBar_stave.clear();
+  m_simBar_slayer.clear();
+  m_simBar_bar.clear();
+  m_simBar_truthMC_tag.clear(); 
+  m_simBar_truthMC_pid.clear();
+  m_simBar_truthMC_px.clear();
+  m_simBar_truthMC_py.clear();
+  m_simBar_truthMC_pz.clear();
+  m_simBar_truthMC_E.clear();
+  m_simBar_truthMC_EPx.clear();
+  m_simBar_truthMC_EPy.clear();
+  m_simBar_truthMC_EPz.clear();
+  m_simBar_truthMC_weight.clear();
+
+  m_HcalHit_x.clear();
+  m_HcalHit_y.clear();
+  m_HcalHit_z.clear();
+  m_HcalHit_E.clear();
+  m_HcalHit_layer.clear();
+  m_HcalHit_truthMC_tag.clear();
+  m_HcalHit_truthMC_pid.clear();
+  m_HcalHit_truthMC_px.clear();
+  m_HcalHit_truthMC_py.clear();
+  m_HcalHit_truthMC_pz.clear();
+  m_HcalHit_truthMC_E.clear();
+  m_HcalHit_truthMC_EPx.clear();
+  m_HcalHit_truthMC_EPy.clear();
+  m_HcalHit_truthMC_EPz.clear();
+  m_HcalHit_truthMC_weight.clear();
+}
+
+void PandoraPlusPFAlg::ClearLocalMax(){
+  m_NlmU=-99;
+  m_NlmV=-99;
+  m_localMaxU_tag.clear();
+  m_localMaxU_x.clear();
+  m_localMaxU_y.clear();
+  m_localMaxU_z.clear();
+  m_localMaxU_E.clear();
+  m_localMaxU_mc_tag.clear();
+  m_localMaxU_mc_pdg.clear();
+  m_localMaxU_mc_px.clear();
+  m_localMaxU_mc_py.clear();
+  m_localMaxU_mc_pz.clear();
+  m_localMaxU_mc_weight.clear();
+  m_localMaxV_tag.clear();
+  m_localMaxV_x.clear();
+  m_localMaxV_y.clear();
+  m_localMaxV_z.clear();
+  m_localMaxV_E.clear();
+  m_localMaxV_mc_tag.clear();
+  m_localMaxV_mc_pdg.clear();
+  m_localMaxV_mc_px.clear();
+  m_localMaxV_mc_py.clear();
+  m_localMaxV_mc_pz.clear();
+  m_localMaxV_mc_weight.clear();
+}
+
+void PandoraPlusPFAlg::ClearLayer(){
+  m_NshowerU=-99;
+  m_NshowerV=-99;
+  m_barShowerU_tag.clear();
+  m_barShowerU_x.clear();
+  m_barShowerU_y.clear();
+  m_barShowerU_z.clear();
+  m_barShowerU_E.clear();
+  m_barShowerU_mc_tag.clear();
+  m_barShowerU_mc_pdg.clear();
+  m_barShowerU_mc_px.clear();
+  m_barShowerU_mc_py.clear();
+  m_barShowerU_mc_pz.clear();
+  m_barShowerU_mc_weight.clear();
+  m_barShowerV_tag.clear();
+  m_barShowerV_x.clear();
+  m_barShowerV_y.clear();
+  m_barShowerV_z.clear();
+  m_barShowerV_E.clear();
+  m_barShowerV_mc_tag.clear();
+  m_barShowerV_mc_pdg.clear();
+  m_barShowerV_mc_px.clear();
+  m_barShowerV_mc_py.clear();
+  m_barShowerV_mc_pz.clear();
+  m_barShowerV_mc_weight.clear();
+}
+
+
+void PandoraPlusPFAlg::ClearHough(){
+  m_houghU_tag.clear();
+  m_houghU_type.clear();
+  m_houghU_x.clear();
+  m_houghU_y.clear();
+  m_houghU_z.clear();
+  m_houghU_E.clear();
+  m_houghU_truth_tag.clear();
+  m_houghU_truth_MC_px.clear();
+  m_houghU_truth_MC_py.clear();
+  m_houghU_truth_MC_pz.clear();
+  m_houghU_truth_MC_E.clear();
+  m_houghU_truth_MC_weight.clear();
+  m_houghU_hit_tag.clear();
+  m_houghU_hit_x.clear();
+  m_houghU_hit_y.clear();
+  m_houghU_hit_z.clear();
+  m_houghU_hit_E.clear();
+  m_houghV_tag.clear();
+  m_houghV_type.clear();
+  m_houghV_x.clear();
+  m_houghV_y.clear();
+  m_houghV_z.clear();
+  m_houghV_E.clear();
+  m_houghV_alpha.clear();
+  m_houghV_rho.clear();
+  m_houghV_truth_tag.clear();
+  m_houghV_truth_MC_px.clear();
+  m_houghV_truth_MC_py.clear();
+  m_houghV_truth_MC_pz.clear();
+  m_houghV_truth_MC_E.clear();
+  m_houghV_truth_MC_weight.clear();
+  m_houghV_hit_tag.clear();
+  m_houghV_hit_x.clear();
+  m_houghV_hit_y.clear();
+  m_houghV_hit_z.clear();
+  m_houghV_hit_E.clear();
+}
+
+
+void PandoraPlusPFAlg::ClearCone(){
+  m_coneU_tag.clear();
+  m_coneU_type.clear();
+  m_coneU_x.clear();
+  m_coneU_y.clear();
+  m_coneU_z.clear();
+  m_coneU_E.clear();
+  m_coneU_truth_tag.clear();
+  m_coneU_truth_MC_px.clear();
+  m_coneU_truth_MC_py.clear();
+  m_coneU_truth_MC_pz.clear();
+  m_coneU_truth_MC_E.clear();
+  m_coneU_truth_MC_weight.clear();
+  m_coneU_hit_tag.clear();
+  m_coneU_hit_x.clear();
+  m_coneU_hit_y.clear();
+  m_coneU_hit_z.clear();
+  m_coneU_hit_E.clear();
+  m_coneV_tag.clear();
+  m_coneV_type.clear();
+  m_coneV_x.clear();
+  m_coneV_y.clear();
+  m_coneV_z.clear();
+  m_coneV_E.clear();
+  m_coneV_truth_tag.clear();
+  m_coneV_truth_MC_px.clear();
+  m_coneV_truth_MC_py.clear();
+  m_coneV_truth_MC_pz.clear();
+  m_coneV_truth_MC_E.clear();
+  m_coneV_truth_MC_weight.clear();
+  m_coneV_hit_tag.clear();
+  m_coneV_hit_x.clear();
+  m_coneV_hit_y.clear();
+  m_coneV_hit_z.clear();
+  m_coneV_hit_E.clear();
+}
+
+
+void PandoraPlusPFAlg::ClearTrackAxis(){
+  m_trackU_tag.clear();
+  m_trackU_type.clear();
+  m_trackU_x.clear();
+  m_trackU_y.clear();
+  m_trackU_z.clear();
+  m_trackU_E.clear();
+  m_trackU_truth_tag.clear();
+  m_trackU_truth_MC_px.clear();
+  m_trackU_truth_MC_py.clear();
+  m_trackU_truth_MC_pz.clear();
+  m_trackU_truth_MC_E.clear();
+  m_trackU_truth_MC_weight.clear();
+  m_trackU_hit_tag.clear();
+  m_trackU_hit_x.clear();
+  m_trackU_hit_y.clear();
+  m_trackU_hit_z.clear();
+  m_trackU_hit_E.clear();
+  m_trackV_tag.clear();
+  m_trackV_type.clear();
+  m_trackV_x.clear();
+  m_trackV_y.clear();
+  m_trackV_z.clear();
+  m_trackV_E.clear();
+  m_trackV_truth_tag.clear();
+  m_trackV_truth_MC_px.clear();
+  m_trackV_truth_MC_py.clear();
+  m_trackV_truth_MC_pz.clear();
+  m_trackV_truth_MC_E.clear();
+  m_trackV_truth_MC_weight.clear();
+  m_trackV_hit_tag.clear();
+  m_trackV_hit_x.clear();
+  m_trackV_hit_y.clear();
+  m_trackV_hit_z.clear();
+  m_trackV_hit_E.clear();
+}
+
+
+void PandoraPlusPFAlg::ClearAxis(){
+  m_axisU_tag.clear();
+  m_axisU_type.clear();
+  m_axisU_x.clear();
+  m_axisU_y.clear();
+  m_axisU_z.clear();
+  m_axisU_E.clear();
+  m_axisU_truth_tag.clear();
+  m_axisU_truth_MC_px.clear();
+  m_axisU_truth_MC_py.clear();
+  m_axisU_truth_MC_pz.clear();
+  m_axisU_truth_MC_E.clear();
+  m_axisU_truth_MC_weight.clear();
+  m_axisU_hit_tag.clear();
+  m_axisU_hit_x.clear();
+  m_axisU_hit_y.clear();
+  m_axisU_hit_z.clear();
+  m_axisU_hit_E.clear();
+  m_axisV_tag.clear();
+  m_axisV_type.clear();
+  m_axisV_x.clear();
+  m_axisV_y.clear();
+  m_axisV_z.clear();
+  m_axisV_E.clear();
+  m_axisV_truth_tag.clear();
+  m_axisV_truth_MC_px.clear();
+  m_axisV_truth_MC_py.clear();
+  m_axisV_truth_MC_pz.clear();
+  m_axisV_truth_MC_E.clear();
+  m_axisV_truth_MC_weight.clear();
+  m_axisV_hit_tag.clear();
+  m_axisV_hit_x.clear();
+  m_axisV_hit_y.clear();
+  m_axisV_hit_z.clear();
+  m_axisV_hit_E.clear();
+
+  m_emptyAxisU_tag.clear();
+  m_emptyAxisU_x.clear();
+  m_emptyAxisU_y.clear();
+  m_emptyAxisU_z.clear();
+  m_emptyAxisU_E.clear();
+  m_emptyAxisV_tag.clear();
+  m_emptyAxisV_x.clear();
+  m_emptyAxisV_y.clear();
+  m_emptyAxisV_z.clear();
+  m_emptyAxisV_E.clear();
+}
+
+void PandoraPlusPFAlg::ClearHalfCluster(){
+  m_totE_HFClusU = -99.;
+  m_totE_HFClusV = -99.;
+  m_HalfClusterV_x.clear();
+  m_HalfClusterV_y.clear();
+  m_HalfClusterV_z.clear();
+  m_HalfClusterV_E.clear();
+  m_HalfClusterV_tag.clear();
+  m_HalfClusterV_type.clear();
+  m_HalfClusterV_nTrk.clear();
+  m_HalfClusterV_hit_x.clear();
+  m_HalfClusterV_hit_y.clear();
+  m_HalfClusterV_hit_z.clear();
+  m_HalfClusterV_hit_E.clear();
+  m_HalfClusterV_hit_tag.clear();
+  m_HalfClusterV_truth_tag.clear();
+  m_HalfClusterV_truthMC_px.clear();
+  m_HalfClusterV_truthMC_py.clear();
+  m_HalfClusterV_truthMC_pz.clear();
+  m_HalfClusterV_truthMC_E.clear();
+  m_HalfClusterV_truthMC_weight.clear();
+  m_HalfClusterU_x.clear();
+  m_HalfClusterU_y.clear();
+  m_HalfClusterU_z.clear();
+  m_HalfClusterU_E.clear();
+  m_HalfClusterU_tag.clear();
+  m_HalfClusterU_type.clear();
+  m_HalfClusterU_nTrk.clear();
+  m_HalfClusterU_hit_x.clear();
+  m_HalfClusterU_hit_y.clear();
+  m_HalfClusterU_hit_z.clear();
+  m_HalfClusterU_hit_E.clear();
+  m_HalfClusterU_hit_tag.clear();
+  m_HalfClusterU_truth_tag.clear();
+  m_HalfClusterU_truthMC_px.clear();
+  m_HalfClusterU_truthMC_py.clear();
+  m_HalfClusterU_truthMC_pz.clear();
+  m_HalfClusterU_truthMC_E.clear();
+  m_HalfClusterU_truthMC_weight.clear();
+}
+
+void PandoraPlusPFAlg::ClearTower(){
+  towerID[0] = 0;
+  towerID[1] = 0;
+  towerID[2] = 0;
+  m_NclusU = -99;
+  m_NclusV = -99;
+  m_totEn = -99;
+  m_totEn_U = -99.;
+  m_totEn_V = -99.;
+  m_HalfClusterV_x.clear();
+  m_HalfClusterV_y.clear();
+  m_HalfClusterV_z.clear();
+  m_HalfClusterV_E.clear();
+  m_HalfClusterV_tag.clear();
+  m_HalfClusterV_type.clear();
+  m_HalfClusterV_nTrk.clear();
+  m_HalfClusterU_x.clear();
+  m_HalfClusterU_y.clear();
+  m_HalfClusterU_z.clear();
+  m_HalfClusterU_E.clear();
+  m_HalfClusterU_tag.clear();
+  m_HalfClusterU_type.clear();
+  m_HalfClusterU_nTrk.clear();
+
+}
+
+void PandoraPlusPFAlg::ClearCluster(){
+  m_totE_Ecal = -99.;
+  m_totE_Hcal = -99.;
+  m_Nclus_Ecal = -99;
+  m_Nclus_Hcal = -99;
+  m_EcalClus_x.clear();
+  m_EcalClus_y.clear();
+  m_EcalClus_z.clear();
+  m_EcalClus_E.clear();
+  m_EcalClus_Escale.clear();
+  m_EcalClus_nTrk.clear();
+  m_EcalClus_pTrk.clear();
+  m_EcalClus_typeU.clear();
+  m_EcalClus_typeV.clear();
+  m_EcalClus_hitU_x.clear();
+  m_EcalClus_hitU_y.clear();
+  m_EcalClus_hitU_z.clear();
+  m_EcalClus_hitU_E.clear();
+  m_EcalClus_hitU_tag.clear();
+  m_EcalClus_hitV_x.clear();
+  m_EcalClus_hitV_y.clear();
+  m_EcalClus_hitV_z.clear();
+  m_EcalClus_hitV_E.clear();
+  m_EcalClus_hitV_tag.clear();
+  m_EcalClus_trk_location.clear();
+  m_EcalClus_trk_tag.clear();
+  m_EcalClus_trk_d0.clear();
+  m_EcalClus_trk_z0.clear();
+  m_EcalClus_trk_phi.clear();
+  m_EcalClus_trk_tanL.clear();
+  m_EcalClus_trk_kappa.clear();
+  m_EcalClus_trk_omega.clear();
+  m_EcalClus_truthMC_tag.clear();
+  m_EcalClus_truthMC_pid.clear();
+  m_EcalClus_truthMC_px.clear();
+  m_EcalClus_truthMC_py.clear();
+  m_EcalClus_truthMC_pz.clear();
+  m_EcalClus_truthMC_E.clear();
+  m_EcalClus_truthMC_EPx.clear();
+  m_EcalClus_truthMC_EPy.clear();
+  m_EcalClus_truthMC_EPz.clear();
+  m_EcalClus_truthMC_weight.clear();
+  m_HcalClus_x.clear();
+  m_HcalClus_y.clear();
+  m_HcalClus_z.clear();
+  m_HcalClus_E.clear();
+  m_HcalClus_nTrk.clear();
+  m_HcalClus_pTrk.clear();
+  m_HcalClus_hit_x.clear();
+  m_HcalClus_hit_y.clear();
+  m_HcalClus_hit_z.clear();
+  m_HcalClus_hit_E.clear();
+  m_HcalClus_hit_tag.clear();
+  m_HcalClus_truthMC_tag.clear();
+  m_HcalClus_truthMC_pid.clear();
+  m_HcalClus_truthMC_px.clear();
+  m_HcalClus_truthMC_py.clear();
+  m_HcalClus_truthMC_pz.clear();
+  m_HcalClus_truthMC_E.clear();
+  m_HcalClus_truthMC_EPx.clear();
+  m_HcalClus_truthMC_EPy.clear();
+  m_HcalClus_truthMC_EPz.clear();
+  m_HcalClus_truthMC_weight.clear();
+  m_SimpleHcalClus_x.clear();
+  m_SimpleHcalClus_y.clear();
+  m_SimpleHcalClus_z.clear();
+  m_SimpleHcalClus_E.clear();
+  m_SimpleHcalClus_nTrk.clear();
+  m_SimpleHcalClus_pTrk.clear();
+  m_SimpleHcalClus_hit_x.clear();
+  m_SimpleHcalClus_hit_y.clear();
+  m_SimpleHcalClus_hit_z.clear();
+  m_SimpleHcalClus_hit_E.clear();
+  m_SimpleHcalClus_hit_tag.clear();
+  m_SimpleHcalClus_truthMC_tag.clear();
+  m_SimpleHcalClus_truthMC_pid.clear();
+  m_SimpleHcalClus_truthMC_px.clear();
+  m_SimpleHcalClus_truthMC_py.clear();
+  m_SimpleHcalClus_truthMC_pz.clear();
+  m_SimpleHcalClus_truthMC_E.clear();
+  m_SimpleHcalClus_truthMC_EPx.clear();
+  m_SimpleHcalClus_truthMC_EPy.clear();
+  m_SimpleHcalClus_truthMC_EPz.clear();
+  m_SimpleHcalClus_truthMC_weight.clear();
+}
+
+void PandoraPlusPFAlg::ClearTrack(){
+  m_type.clear();
+  m_trkstate_d0.clear();
+  m_trkstate_z0.clear();
+  m_trkstate_phi.clear();
+  m_trkstate_tanL.clear();
+  m_trkstate_kappa.clear();
+  m_trkstate_omega.clear();
+  m_trkstate_refx.clear();
+  m_trkstate_refy.clear();
+  m_trkstate_refz.clear();
+  m_trkstate_location.clear();
+  m_trkstate_tag.clear();
+}
+
+void PandoraPlusPFAlg::ClearPFO(){
+  pfo_tag.clear();
+  pfo_n_track.clear();
+  pfo_n_ecal_clus.clear();
+  pfo_n_hcal_clus.clear();
+  pfo_trk_tag.clear();
+  pfo_trk_d0.clear();
+  pfo_trk_z0.clear();
+  pfo_trk_phi.clear();
+  pfo_trk_tanL.clear();
+  pfo_trk_kappa.clear();
+  pfo_trk_omega.clear();
+  pfo_trk_location.clear();
+  pfo_ecal_tag.clear();
+  pfo_ecal_clus_x.clear();
+  pfo_ecal_clus_y.clear();
+  pfo_ecal_clus_z.clear();
+  pfo_ecal_clus_E.clear();
+  pfo_ecal_clus_Escale.clear();
+  pfo_hcal_tag.clear();
+  pfo_hcal_clus_x.clear();
+  pfo_hcal_clus_y.clear();
+  pfo_hcal_clus_z.clear();
+  pfo_hcal_clus_E.clear();
+}
+
+
+double PandoraPlusPFAlg::GetParticleDepEnergy(edm4hep::MCParticle& mcp, std::vector<std::shared_ptr<PandoraPlus::CaloUnit>>& barcol){
+
+  double EnDep = 0.;
+  for(int i=0; i<barcol.size(); i++){
+    std::vector< std::pair<edm4hep::MCParticle, float> > mcp_map = barcol[i]->getLinkedMCP();
+    for(int ip=0; ip<mcp_map.size(); ip++){
+      if(mcp_map[ip].first==mcp){
+        EnDep += barcol[i]->getEnergy() * mcp_map[ip].second;
+      }
+    }
+  }
+  return EnDep;
+
+}
+
+double PandoraPlusPFAlg::GetParticleDepEnergy(edm4hep::MCParticle& mcp, std::vector<std::shared_ptr<PandoraPlus::CaloHit>>& hitcol){
+
+  double EnDep = 0.;
+  for(int i=0; i<hitcol.size(); i++){
+    std::vector< std::pair<edm4hep::MCParticle, float> > mcp_map = hitcol[i]->getLinkedMCP();
+    for(int ip=0; ip<mcp_map.size(); ip++){
+      if(mcp_map[ip].first==mcp){
+        EnDep += hitcol[i]->getEnergy() * mcp_map[ip].second;
+      }
+    }
+  }
+  return EnDep;
+
+}
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Tools/CaloHitsCreator.cpp b/Reconstruction/CrystalCaloRec/src/Tools/CaloHitsCreator.cpp
new file mode 100644
index 00000000..873d2f6a
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Tools/CaloHitsCreator.cpp
@@ -0,0 +1,128 @@
+#ifndef TRACK_CREATOR_C
+#define TRACK_CREATOR_C
+
+#include "Tools/CaloHitsCreator.h"
+
+namespace PandoraPlus{
+  CaloHitsCreator::CaloHitsCreator(const Settings& m_settings) : settings( m_settings ){
+
+  }; 
+
+  StatusCode CaloHitsCreator::CreateCaloHits( PandoraPlusDataCol& m_DataCol, 
+                                              std::vector<DataHandle<edm4hep::CalorimeterHitCollection>*>& r_CaloHitCols, 
+                                              std::map<std::string, dd4hep::DDSegmentation::BitFieldCoder*>& map_decoder,
+                                              std::map<std::string, DataHandle<edm4hep::MCRecoCaloParticleAssociationCollection>*>& map_CaloParticleAssoCol )
+  {
+    if(r_CaloHitCols.size()==0 || settings.map_stringVecPars.at("CaloHitCollections").size()==0) StatusCode::SUCCESS;
+
+    //Save readin collections
+    m_DataCol.collectionMap_CaloHit.clear(); 
+    for(unsigned int icol=0; icol<r_CaloHitCols.size(); icol++){
+
+      const edm4hep::CalorimeterHitCollection* const_CaloHitCol = r_CaloHitCols[icol]->get(); 
+
+      std::vector<edm4hep::CalorimeterHit> m_HitCol; m_HitCol.clear(); 
+      for(unsigned int ihit=0; ihit<const_CaloHitCol->size(); ihit++){
+        edm4hep::CalorimeterHit m_hit = const_CaloHitCol->at(ihit);
+        m_HitCol.push_back(m_hit);
+      }
+
+      m_DataCol.collectionMap_CaloHit[settings.map_stringVecPars.at("CaloHitCollections")[icol]] = m_HitCol; 
+    }
+
+    //Convert to local objects: 
+    for(auto iter : m_DataCol.collectionMap_CaloHit){
+      if( settings.map_stringPars.at("EcalType")=="BarEcal" && iter.first == "ECALBarrel") continue; 
+      
+      std::vector<std::shared_ptr<PandoraPlus::CaloHit>> m_hitCol; m_hitCol.clear();
+      const edm4hep::MCRecoCaloParticleAssociationCollection* const_MCPCaloAssoCol;
+      if( map_CaloParticleAssoCol.find(iter.first)!=map_CaloParticleAssoCol.end()) 
+        const_MCPCaloAssoCol = map_CaloParticleAssoCol[iter.first]->get();
+
+      for(int ihit=0; ihit<iter.second.size(); ihit++){
+        //PandoraPlus::CaloHit* m_hit = new PandoraPlus::CaloHit();
+        std::shared_ptr<PandoraPlus::CaloHit> m_hit = std::make_shared<PandoraPlus::CaloHit>();
+
+        m_hit->setOriginHit( iter.second[ihit] );
+        m_hit->setcellID( iter.second[ihit].getCellID() );
+        m_hit->setLayer( map_decoder[iter.first]->get(iter.second[ihit].getCellID(), "layer") );
+
+        TVector3 pos( iter.second[ihit].getPosition().x, iter.second[ihit].getPosition().y, iter.second[ihit].getPosition().z );
+        m_hit->setPosition( pos );
+        m_hit->setEnergy( iter.second[ihit].getEnergy() );
+
+        for(int ilink=0; ilink<const_MCPCaloAssoCol->size(); ilink++){
+          if( iter.second[ihit] == const_MCPCaloAssoCol->at(ilink).getRec() ) m_hit->addLinkedMCP( std::make_pair(const_MCPCaloAssoCol->at(ilink).getSim(), const_MCPCaloAssoCol->at(ilink).getWeight()) );
+        }
+        m_hitCol.push_back( m_hit );
+      }
+      m_DataCol.map_CaloHit[iter.first] = m_hitCol;
+      const_MCPCaloAssoCol = nullptr;
+    }
+
+
+    //Convert to local objects: CalorimeterHit to CaloUnit (For ECALBarrel only)
+    if(settings.map_stringPars.at("EcalType")=="BarEcal"){
+      std::vector<std::shared_ptr<PandoraPlus::CaloUnit>> m_barCol; m_barCol.clear(); 
+
+      const edm4hep::MCRecoCaloParticleAssociationCollection* const_MCPCaloAssoCol = map_CaloParticleAssoCol["ECALBarrel"]->get();   
+      auto CaloHits = m_DataCol.collectionMap_CaloHit["ECALBarrel"]; 
+      std::map<std::uint64_t, std::vector<PandoraPlus::CaloUnit> > map_cellID_hits; map_cellID_hits.clear();
+      for(auto& hit : CaloHits){ 
+        PandoraPlus::CaloUnit m_bar; 
+        m_bar.setcellID(hit.getCellID());
+        m_bar.setPosition( TVector3(hit.getPosition().x, hit.getPosition().y, hit.getPosition().z) );
+        m_bar.setQ(hit.getEnergy()/2., hit.getEnergy()/2.);
+        m_bar.setT(hit.getTime(), hit.getTime());
+        for(int ilink=0; ilink<const_MCPCaloAssoCol->size(); ilink++){
+          if( hit == const_MCPCaloAssoCol->at(ilink).getRec() ) m_bar.addLinkedMCP( std::make_pair(const_MCPCaloAssoCol->at(ilink).getSim(), const_MCPCaloAssoCol->at(ilink).getWeight()) );
+        } 
+
+
+        map_cellID_hits[hit.getCellID()].push_back(m_bar);
+      }
+      for(auto& hit : map_cellID_hits){
+        if(hit.second.size()!=2){ std::cout<<"WARNING: didn't find correct hit pairs! "<<std::endl; continue; }
+   
+        //PandoraPlus::CaloUnit* m_bar = new PandoraPlus::CaloUnit(); 
+        std::shared_ptr<PandoraPlus::CaloUnit> m_bar = std::make_shared<PandoraPlus::CaloUnit>();
+   
+        unsigned long long id = hit.first; 
+        m_bar->setcellID( id );
+        m_bar->setcellID( map_decoder["ECALBarrel"]->get(id, "system"),
+                          map_decoder["ECALBarrel"]->get(id, "module"),
+                          map_decoder["ECALBarrel"]->get(id, "stave"),
+                          map_decoder["ECALBarrel"]->get(id, "dlayer"),
+                          map_decoder["ECALBarrel"]->get(id, "slayer"),
+                          map_decoder["ECALBarrel"]->get(id, "bar"));
+        m_bar->setPosition(hit.second[0].getPosition());
+        m_bar->setQ( hit.second[0].getEnergy(), hit.second[1].getEnergy() );
+        m_bar->setT( hit.second[0].getT1(), hit.second[1].getT1() );
+
+        //---oooOOO000OOOooo---set bar length---oooOOO000OOOooo---
+        //TODO: reading bar length from geosvc. 
+        double t_bar_length;
+        if(m_bar->getSlayer()==1) t_bar_length = 375.133;
+        else{
+          if( m_bar->getModule()%2==0 ) t_bar_length = 295.905 + (m_bar->getDlayer()-1)* 6.13231;
+          else t_bar_length = 416.843 - (m_bar->getDlayer()+1)* 2.25221;
+        }
+        m_bar->setBarLength(t_bar_length);
+        //---oooOOO000OOOooo---set bar length---oooOOO000OOOooo---
+
+        //add MCParticle link
+        for(int ilink=0; ilink<hit.second[0].getLinkedMCP().size(); ilink++) m_bar->addLinkedMCP( hit.second[0].getLinkedMCP()[ilink] );
+
+        m_barCol.push_back(m_bar);  //Save for later use in algorithms
+      }
+
+   
+      m_DataCol.map_BarCol["BarCol"] = m_barCol; 
+      const_MCPCaloAssoCol = nullptr;
+    }
+    return StatusCode::SUCCESS;
+  };
+
+};
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Tools/MCParticleCreator.cpp b/Reconstruction/CrystalCaloRec/src/Tools/MCParticleCreator.cpp
new file mode 100644
index 00000000..561dafc4
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Tools/MCParticleCreator.cpp
@@ -0,0 +1,30 @@
+#ifndef MCPARTICLE_CREATOR_C
+#define MCPARTICLE_CREATOR_C
+
+#include "Tools/MCParticleCreator.h"
+
+namespace PandoraPlus{
+  MCParticleCreator::MCParticleCreator( const Settings& m_settings ): settings(m_settings){
+
+  }
+
+
+  StatusCode MCParticleCreator::CreateMCParticle( PandoraPlusDataCol& m_DataCol, DataHandle<edm4hep::MCParticleCollection>& r_MCParticleCol ){
+    if(settings.map_stringPars.at("MCParticleCollections").empty()) return StatusCode::SUCCESS;
+    m_DataCol.collectionMap_MC.clear();
+
+    const edm4hep::MCParticleCollection* const_MCPCol = r_MCParticleCol.get();
+
+    std::vector<edm4hep::MCParticle> m_MCPvec; m_MCPvec.clear(); 
+    for(int imc=0; imc<const_MCPCol->size(); imc++){
+      edm4hep::MCParticle m_MCp = const_MCPCol->at(imc);
+      m_MCPvec.push_back(m_MCp);
+    }
+
+    m_DataCol.collectionMap_MC[ settings.map_stringPars.at("MCParticleCollections") ] = m_MCPvec; 
+
+    return StatusCode::SUCCESS;
+  };
+
+}
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Tools/OutputCreator.cpp b/Reconstruction/CrystalCaloRec/src/Tools/OutputCreator.cpp
new file mode 100644
index 00000000..8e95bb3f
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Tools/OutputCreator.cpp
@@ -0,0 +1,233 @@
+#ifndef OUTPUT_CREATOR_C
+#define OUTPUT_CREATOR_C
+
+#include "Tools/OutputCreator.h"
+
+namespace PandoraPlus{
+
+  OutputCreator::OutputCreator( const Settings& m_settings ): settings(m_settings){
+
+  }
+
+
+  StatusCode OutputCreator::CreateOutputCollections( PandoraPlusDataCol& m_DataCol,
+                                                     DataHandle<edm4hep::CalorimeterHitCollection>& m_outRecHitsHandler,
+                                                     DataHandle<edm4hep::CalorimeterHitCollection>& m_outRecCoreHandler,
+                                                     DataHandle<edm4hep::CalorimeterHitCollection>& m_outRecHcalHitsHandler,
+                                                     DataHandle<edm4hep::TrackCollection>& m_outTrkHandler,
+                                                     std::map<std::string, DataHandle<edm4hep::ClusterCollection>*>& m_outClusterColHandler,
+                                                     DataHandle<edm4hep::ReconstructedParticleCollection>& m_recPFOHandler ){
+
+    //Register the collections
+    edm4hep::CalorimeterHitCollection* m_calohitCol = m_outRecHitsHandler.createAndPut();
+    edm4hep::CalorimeterHitCollection* m_corehitCol = m_outRecCoreHandler.createAndPut();
+    edm4hep::CalorimeterHitCollection* m_hcalhitCol = m_outRecHcalHitsHandler.createAndPut();
+    edm4hep::ReconstructedParticleCollection* m_pfocol = m_recPFOHandler.createAndPut();
+    edm4hep::ClusterCollection* m_Ecalcluster = m_outClusterColHandler["EcalCluster"]->createAndPut();
+    edm4hep::ClusterCollection* m_Ecalcore    = m_outClusterColHandler["EcalCore"]->createAndPut();
+    edm4hep::ClusterCollection* m_Hcalcluster = m_outClusterColHandler["HcalCluster"]->createAndPut();
+
+    edm4hep::TrackCollection* m_trkCol = nullptr;
+    if(settings.map_boolPars.at("UseTruthTrk")) m_trkCol = m_outTrkHandler.createAndPut();
+
+
+    //PFO
+    std::vector<std::shared_ptr<PandoraPlus::PFObject>> p_pfos = m_DataCol.map_PFObjects[settings.map_stringPars.at("OutputPFO")];
+std::cout<<"  Input PFO size: "<<p_pfos.size()<<std::endl;    
+    for(int ip=0; ip<p_pfos.size(); ip++){
+      auto m_pfo = m_pfocol->create();
+      std::vector<const Track*> vec_trks = p_pfos[ip]->getTracks();
+      std::vector<const Calo3DCluster*> vec_Ecalclus = p_pfos[ip]->getECALClusters();
+      std::vector<const Calo3DCluster*> vec_Hcalclus = p_pfos[ip]->getHCALClusters();
+
+      TVector3 vec_Pos(0.,0.,0.);
+
+      //Write ECAL cluster
+      double EcalClusE = 0;
+      for(int ic=0; ic<vec_Ecalclus.size(); ic++){
+        auto p_clus = vec_Ecalclus[ic];
+        auto m_clus = m_Ecalcluster->create();
+
+        //create high granularity ECAL hits
+        for(int ih=0; ih<p_clus->getCaloHits().size(); ih++){
+          const PandoraPlus::CaloHit* p_hit = p_clus->getCaloHits()[ih];
+          edm4hep::Vector3f pos(p_hit->getPosition().x(), p_hit->getPosition().y(), p_hit->getPosition().z());
+          int _module = p_hit->getModule();
+          int _layer = p_hit->getLayer();
+          int _cellID = (_module<<5) + _layer;  //A self-defined cell-ID. 
+   
+          auto _hit = m_calohitCol->create();
+          _hit.setCellID(_cellID);
+          _hit.setEnergy( p_hit->getEnergy()*settings.map_floatPars.at("ECALCalib") );
+          _hit.setPosition( pos );
+          _hit.setType(1); //Ecal barrel
+          m_clus.addToHits(_hit);
+        }
+   
+        //create cluster core from 2D cluster
+        auto m_core = m_Ecalcore->create();
+        double totE = 0.;
+        for(int ih=0; ih<p_clus->getCluster().size(); ih++){
+          const PandoraPlus::Calo2DCluster* p_core = p_clus->getCluster()[ih];
+          edm4hep::Vector3f pos(p_core->getPos().x(), p_core->getPos().y(), p_core->getPos().z());
+          int _module = p_core->getTowerID()[0][0];
+          int _layer = p_core->getDlayer();
+          int _cellID = (_module<<5) + _layer;
+   
+          auto _hit = m_corehitCol->create();
+          _hit.setCellID(_cellID);
+          _hit.setEnergy( p_core->getEnergy()*settings.map_floatPars.at("ECALCalib") );
+          _hit.setPosition( pos );
+          _hit.setType(1); //Ecal barrel
+          m_core.addToHits(_hit);
+          totE += p_core->getEnergy()*settings.map_floatPars.at("ECALCalib");
+        }
+   
+        double tmp_clusE = p_clus->getEnergy()*settings.map_floatPars.at("ECALCalib");
+        TVector3 clus_pos = p_clus->getShowerCenter();
+        edm4hep::Vector3f pos( clus_pos.x(), clus_pos.y(), clus_pos.z() );
+        double tmp_phi = std::atan2(clus_pos.y(), clus_pos.x())* 180.0 / M_PI; //TODO: use TVector3 to calculate
+        if (tmp_phi < 0) tmp_phi += 360.0;
+        double tmp_theta = std::atan2(clus_pos.z(), clus_pos.Perp())* 180.0 / M_PI + 90;
+        tmp_clusE = m_DataCol.EnergyCorrSvc->energyCorrection(tmp_clusE, tmp_phi, tmp_theta);
+        totE = m_DataCol.EnergyCorrSvc->energyCorrection(totE, tmp_phi, tmp_theta);
+
+        m_core.setEnergy(totE);
+        m_core.setPosition( pos );
+        m_clus.setEnergy( tmp_clusE );
+        m_clus.setPosition( pos );
+        m_clus.addToClusters(m_core);
+        m_pfo.addToClusters( m_clus );
+
+        EcalClusE += tmp_clusE;
+        vec_Pos += tmp_clusE*p_clus->getShowerCenter();
+      }
+
+
+      //Write HCAL cluster
+      double HcalClusE = 0;
+      for(int ic=0; ic<vec_Hcalclus.size(); ic++){
+        auto p_clus = vec_Hcalclus[ic];
+        auto m_clus = m_Hcalcluster->create();
+   
+        for(int ih=0; ih<p_clus->getCaloHits().size(); ih++){
+          const PandoraPlus::CaloHit* p_hit = p_clus->getCaloHits()[ih];
+          //auto _hit = m_hcalhitCol->create();
+          auto _hit = p_hit->getOriginHit().clone();
+          m_hcalhitCol->push_back(_hit);
+          m_clus.addToHits(_hit);
+        }
+   
+        double tmp_clusE = p_clus->getEnergy()*settings.map_floatPars.at("HCALCalib");
+        m_clus.setEnergy( tmp_clusE );
+        edm4hep::Vector3f pos( p_clus->getHitCenter().x(), p_clus->getHitCenter().y(), p_clus->getHitCenter().z() );
+        m_clus.setPosition( pos );
+        m_pfo.addToClusters( m_clus );
+
+        HcalClusE += tmp_clusE;
+        vec_Pos += tmp_clusE*p_clus->getHitCenter();
+      }
+
+
+      //Write Track
+      if(vec_trks.size()==0){
+        TVector3 p3vec = vec_Pos*(  (EcalClusE+HcalClusE)/vec_Pos.Mag() );
+        edm4hep::Vector3f p3(p3vec.x(), p3vec.y(), p3vec.z());
+
+        m_pfo.setEnergy( EcalClusE+HcalClusE );
+        m_pfo.setCharge( 0 );
+        m_pfo.setMomentum( p3 );
+      }
+      else{
+        double trk_maxP = -99;
+        int trkIndex = -1;
+        for(int itrk=0; itrk<vec_trks.size(); itrk++){
+          if( trk_maxP<vec_trks[itrk]->getMomentum() ){
+            trk_maxP = vec_trks[itrk]->getMomentum();
+            trkIndex = itrk;
+          }
+        }
+        if(trkIndex>=0){
+          auto m_trk = vec_trks[trkIndex]->getOriginTrack();
+          if( !m_trk.isAvailable() || settings.map_boolPars.at("UseTruthTrk") )
+            m_trk = TruthTrack( vec_trks[trkIndex]->getLeadingMCP(), m_trkCol );
+
+          m_pfo.addToTracks( m_trk );
+          m_pfo.setCharge( vec_trks[trkIndex]->getCharge() );
+
+          TVector3 p3vec = vec_trks[trkIndex]->getP3();
+          edm4hep::Vector3f p3(p3vec.x(), p3vec.y(), p3vec.z());
+          m_pfo.setMomentum(p3);
+          m_pfo.setEnergy( vec_trks[trkIndex]->getMomentum() );
+        }
+        else{
+          TVector3 p3vec = vec_Pos*(  (EcalClusE+HcalClusE)/vec_Pos.Mag() );
+          edm4hep::Vector3f p3(p3vec.x(), p3vec.y(), p3vec.z());
+
+          m_pfo.setEnergy( EcalClusE+HcalClusE );
+          m_pfo.setCharge( 0 );
+          m_pfo.setMomentum( p3 );
+        }
+
+      }
+
+    }
+
+std::cout<<"  Created PFO size: "<<m_pfocol->size()<<std::endl;
+
+    return StatusCode::SUCCESS;
+  }
+
+
+
+  edm4hep::Track OutputCreator::TruthTrack(edm4hep::MCParticle _mcp, edm4hep::TrackCollection* _trkCol ){
+    
+    auto m_track = _trkCol->create();
+    if( _mcp.getGeneratorStatus()!=1 || _mcp.getCharge()==0 ) return m_track;
+
+    edm4hep::Vector3f mcp_vertex(_mcp.getVertex().x, _mcp.getVertex().y, _mcp.getVertex().z);
+    edm4hep::Vector3f mcp_p(_mcp.getMomentum().x, _mcp.getMomentum().y, _mcp.getMomentum().z);
+    double mcp_pT = TMath::Sqrt(mcp_p.x*mcp_p.x + mcp_p.y*mcp_p.y);
+    double charge = _mcp.getCharge();
+
+    //Assign the track state at IP
+    edm4hep::TrackState m_trkst;
+    m_trkst.location = 1;  // At IP
+    m_trkst.D0 = 0;
+    m_trkst.Z0 = 0;
+    m_trkst.phi = TMath::ATan2(mcp_p.x, mcp_p.x);
+    m_trkst.tanLambda = mcp_p.z / mcp_pT;
+    m_trkst.omega = 0.3 * settings.map_floatPars.at("BField") / 1000. / mcp_pT;
+    m_trkst.referencePoint = mcp_vertex;
+    m_track.addToTrackStates(m_trkst);
+
+    ////Use helix to calculate the track endpoint
+    //HelixClassD * TrkInit_Helix = new HelixClassD();
+    //TrkInit_Helix->Initialize_Canonical(m_trkst.phi, m_trkst.D0, m_trkst.Z0, m_trkst.omega, m_trkst.tanLambda, settings.map_floatPars["BField"]);
+    //float intPoint[3];
+    //float refPoint[3] = {mcp_vertex.x, mcp_vertex.y, mcp_vertex.z};
+    //TrkInit_Helix->getPointOnCircle(PandoraPlus::CaloUnit::ecal_innerR, refPoint, intPoint);
+    ////If track reach to the edge of barrel
+    //if(fabs(intPoint[2])>ECALHalfZ ){
+    //  TrkInit_Helix->getPointInZ(ECALHalfZ, refPoint, intPoint);
+    //}
+
+    ////Assign the begin and end tracker hits
+    //auto m_hit_IP = m_trkHitCol->create();
+    //m_hit_IP.setPosition(edm4hep::Vector3d(0.,0.,0.));
+    //m_track.addToTrackerHits(m_hit_IP);
+
+    //auto m_hit_End = m_trkHitCol->create();
+    //m_hit_End.setPosition(edm4hep::Vector3d(intPoint[0], intPoint[1], intPoint[2]));
+    //m_track.addToTrackerHits(m_hit_End);
+
+    //delete TrkInit_Helix;
+    return m_track; 
+  }
+
+}
+
+
+
+
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Tools/TrackCreator.cpp b/Reconstruction/CrystalCaloRec/src/Tools/TrackCreator.cpp
new file mode 100644
index 00000000..1beddc5b
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Tools/TrackCreator.cpp
@@ -0,0 +1,154 @@
+#ifndef TRACK_CREATOR_C
+#define TRACK_CREATOR_C
+
+#include "Tools/TrackCreator.h"
+
+namespace PandoraPlus{
+
+  TrackCreator::TrackCreator(const Settings& m_settings) : settings( m_settings ){
+
+  };
+
+
+  StatusCode TrackCreator::CreateTracks( PandoraPlusDataCol& m_DataCol, 
+                                         std::vector<DataHandle<edm4hep::TrackCollection>*>& r_TrackCols, 
+                                         DataHandle<edm4hep::MCRecoTrackParticleAssociationCollection>* r_MCParticleTrkCol ){
+
+    if(r_TrackCols.size()==0 || settings.map_stringVecPars.at("trackCollections").size()==0) StatusCode::SUCCESS;
+
+    //Save readin collections
+    m_DataCol.collectionMap_Track.clear(); 
+    for(int icol=0; icol<r_TrackCols.size(); icol++){
+      const edm4hep::TrackCollection* const_TrkCol = r_TrackCols[icol]->get(); 
+
+      std::vector<edm4hep::Track> m_TrkCol; m_TrkCol.clear();
+      for(unsigned int icol=0; icol<const_TrkCol->size(); icol++){
+        edm4hep::Track m_trk = const_TrkCol->at(icol);
+        m_TrkCol.push_back(m_trk);
+      }
+
+      m_DataCol.collectionMap_Track[ settings.map_stringVecPars.at("trackCollections")[icol] ] = m_TrkCol; 
+    }
+
+
+    //Convert to local objects
+    std::vector<std::shared_ptr<PandoraPlus::Track>> m_trkCol; m_trkCol.clear();
+    const edm4hep::MCRecoTrackParticleAssociationCollection* const_MCPTrkAssoCol = r_MCParticleTrkCol->get();
+
+    for(auto iter : m_DataCol.collectionMap_Track){
+      auto const_TrkCol = iter.second; 
+      for(int itrk=0; itrk<const_TrkCol.size(); itrk++){
+        //PandoraPlus::Track* m_trk = new PandoraPlus::Track();
+        std::shared_ptr<PandoraPlus::Track> m_trk = std::make_shared<PandoraPlus::Track>();
+        std::vector<PandoraPlus::TrackState> m_trkstates;
+
+        for(int its=0; its<const_TrkCol[itrk].trackStates_size(); its++){
+          PandoraPlus::TrackState m_trkst;
+          m_trkst.D0 = const_TrkCol[itrk].getTrackStates(its).D0;
+          m_trkst.Z0 = const_TrkCol[itrk].getTrackStates(its).Z0;
+          m_trkst.phi0 = const_TrkCol[itrk].getTrackStates(its).phi;
+          m_trkst.tanLambda = const_TrkCol[itrk].getTrackStates(its).tanLambda;
+          m_trkst.Omega = const_TrkCol[itrk].getTrackStates(its).omega;
+          m_trkst.Kappa = m_trkst.Omega*1000./(0.3*settings.map_floatPars.at("BField"));   
+          m_trkst.location = const_TrkCol[itrk].getTrackStates(its).location;
+          m_trkst.referencePoint.SetXYZ( const_TrkCol[itrk].getTrackStates(its).referencePoint[0],
+                                         const_TrkCol[itrk].getTrackStates(its).referencePoint[1],
+                                         const_TrkCol[itrk].getTrackStates(its).referencePoint[2] );
+
+          m_trkstates.push_back(m_trkst);        
+        }
+        m_trk->setTrackStates("Input", m_trkstates);
+        m_trk->setType(const_TrkCol[itrk].getType());
+        m_trk->setOriginTrack( const_TrkCol[itrk] );
+
+        for(int ilink=0; ilink<const_MCPTrkAssoCol->size(); ilink++){
+          if( const_TrkCol[itrk] == const_MCPTrkAssoCol->at(ilink).getRec() ) {
+            m_trk->addLinkedMCP( std::make_pair(const_MCPTrkAssoCol->at(ilink).getSim(), const_MCPTrkAssoCol->at(ilink).getWeight()) );
+            break;
+          }
+        }
+
+        m_trkCol.push_back(m_trk);
+      }
+    }
+    m_DataCol.TrackCol = m_trkCol;
+
+
+    //Track extrapolation
+    //  Write settings: geometry description
+    //  m_TrkExtraSettings.map_floatPar["Nlayers"] = 28;
+
+    m_TrkExtraAlg = new TrackExtrapolatingAlg();
+    m_TrkExtraAlg->ReadSettings(m_TrkExtraSettings);
+    m_TrkExtraAlg->Initialize( m_DataCol );
+    m_TrkExtraAlg->RunAlgorithm( m_DataCol );
+    m_TrkExtraAlg->ClearAlgorithm();
+    delete m_TrkExtraAlg;
+    m_TrkExtraAlg = nullptr;
+
+    return StatusCode::SUCCESS;
+  }
+
+
+  StatusCode TrackCreator::CreateTracksFromMCParticle(PandoraPlusDataCol& m_DataCol, 
+                                          DataHandle<edm4hep::MCParticleCollection>& r_MCParticleCol){
+
+
+    // Convert MC charged particles to local Track objects.
+    // Assuming the tracks are ideal helixes.
+
+    // Get charged MC particles with generatorStatus==1
+    const edm4hep::MCParticleCollection* const_MCPCol = r_MCParticleCol.get();
+    std::vector<edm4hep::MCParticle> m_MCPvec; m_MCPvec.clear(); 
+    for(int i=0; i<const_MCPCol->size(); i++){
+      edm4hep::MCParticle m_MCp = const_MCPCol->at(i);
+      if(m_MCp.getGeneratorStatus()==1 && m_MCp.getCharge()!=0)
+        m_MCPvec.push_back(m_MCp);
+    }
+
+    // Convert to local Track objects
+    std::vector<std::shared_ptr<PandoraPlus::Track>> m_trkCol; m_trkCol.clear();
+    for(auto mcp : m_MCPvec){
+      std::shared_ptr<PandoraPlus::Track> m_trk = std::make_shared<PandoraPlus::Track>();
+      std::vector<PandoraPlus::TrackState> m_trkstates;
+
+      TVector3 mcp_vertex(mcp.getVertex().x, mcp.getVertex().y, mcp.getVertex().z);
+      TVector3 mcp_p(mcp.getMomentum().x, mcp.getMomentum().y, mcp.getMomentum().z);
+      double mcp_pT = TMath::Sqrt(mcp_p.X()*mcp_p.X() + mcp_p.Y()*mcp_p.Y());
+      double charge = mcp.getCharge();
+
+      // Evaluate track state at vertex
+      PandoraPlus::TrackState m_trkst;  
+      m_trkst.location = 1;  // At IP
+      m_trkst.D0 = 0;
+      m_trkst.Z0 = 0;
+      m_trkst.phi0 = TMath::ATan2(mcp_p.Y(), mcp_p.X());
+      m_trkst.Kappa = 1 / mcp_pT;
+      if(charge<0) m_trkst.Kappa = -m_trkst.Kappa;
+      m_trkst.tanLambda = mcp_p.Z() / mcp_pT;
+      m_trkst.Omega = 0.3 * settings.map_floatPars.at("BField") / 1000. / mcp_pT;
+      m_trkst.referencePoint = mcp_vertex;
+      m_trkstates.push_back(m_trkst);
+
+      m_trk->setTrackStates("Input", m_trkstates);
+      m_trk->setType(0);  // It is a "MC track" and not detected by any tracker system
+      m_trk->addLinkedMCP( std::make_pair(mcp, 1.) );
+      m_trkCol.push_back(m_trk);
+    }
+    m_DataCol.TrackCol = m_trkCol;
+
+    m_TrkExtraSettings.map_intPars["Input_track"] = 1;
+
+    m_TrkExtraAlg = new TrackExtrapolatingAlg();
+    m_TrkExtraAlg->ReadSettings(m_TrkExtraSettings);
+    m_TrkExtraAlg->Initialize( m_DataCol );
+    m_TrkExtraAlg->RunAlgorithm( m_DataCol );
+    m_TrkExtraAlg->ClearAlgorithm();
+    delete m_TrkExtraAlg;
+    m_TrkExtraAlg = nullptr;
+
+    return StatusCode::SUCCESS;
+  }
+
+};
+#endif
diff --git a/Reconstruction/CrystalCaloRec/src/Tools/TrackFitInEcal.cpp b/Reconstruction/CrystalCaloRec/src/Tools/TrackFitInEcal.cpp
new file mode 100644
index 00000000..6bf5dff0
--- /dev/null
+++ b/Reconstruction/CrystalCaloRec/src/Tools/TrackFitInEcal.cpp
@@ -0,0 +1,221 @@
+#include "Tools/TrackFitInEcal.h"
+
+#include <fstream>
+#include <iomanip>
+#include <cmath>
+
+#include "TMinuit.h"
+#include "TMath.h"
+#include "TGraphErrors.h"
+#include "TF1.h"
+#include "TVector3.h"
+
+using namespace std;
+
+vector<int> TrackFitInEcal::m_flagUZ;
+vector<double> TrackFitInEcal::m_uzPos;
+vector<double> TrackFitInEcal::m_uzPosErr;
+vector<double> TrackFitInEcal::m_depth;
+vector<double> TrackFitInEcal::m_depthErr;
+
+TrackFitInEcal::TrackFitInEcal(double barAngle){
+	 m_barAngle = barAngle;
+    m_IPvar[0]=0; m_IPvar[1]=0; 
+    m_FixIP = false; 
+}
+
+TrackFitInEcal::~TrackFitInEcal(){
+
+}
+
+void TrackFitInEcal::setPoint(int flagUZ, double uzPos, double uzPosErr, double depth, double depthErr){
+	 m_flagUZ.push_back(flagUZ);
+	 m_uzPos.push_back(uzPos);
+	 m_uzPosErr.push_back(uzPosErr);
+	 m_depth.push_back(depth);
+	 m_depthErr.push_back(depthErr);
+}
+
+void TrackFitInEcal::setGlobalPoint(int flagUZ, double x, double xerr, double y, double yerr, double z, double zerr){
+  TVector3 vecp(x, y, z);
+  TVector3 vecerr(xerr, yerr, zerr);
+  vecp.RotateZ(-1.*m_barAngle);
+  vecerr.RotateZ(-1.*m_barAngle);
+
+  if(flagUZ==0) setPoint(flagUZ, vecp.y(), vecerr.y(), vecp.x(), vecerr.x());  //U direction
+  else          setPoint(flagUZ, vecp.z(), vecerr.z(), vecp.x(), vecerr.x());  //Z direction
+}
+
+bool TrackFitInEcal::fitTrack(){
+	 int iXY = 0;
+	 int iWZ = 0;
+	 for(unsigned int i=0; i<m_flagUZ.size(); i++){
+		  int flag = m_flagUZ[i];
+		  if(0==flag) iXY++;
+		  else if(1==flag) iWZ++;
+	 }
+	 if((iXY<2) || (iWZ<2)){
+		  cout << "WARNING: too few hits for track fit" << endl;
+		  return false;
+	 }
+
+	 if( ! fit2D() ){
+		  cout << "ERROR in 2-D fit" << endl;
+		  clear();
+		  return false;
+	 }
+
+	 if( ! mnFit3D() ){
+	 	  clear();
+	 	  return false;
+	 }
+
+	 clear();
+	 return true;
+}
+
+bool TrackFitInEcal::fit2D(){
+	 int iPointXY = 0;
+    // cout<<"Fit IP: "<<m_FixIP<<'\t'<<m_IPvar[0]<<'\t'<<m_IPvar[1]<<endl;
+
+	 TGraphErrors* grXY = new TGraphErrors();
+	 for(unsigned int i=0; i<m_flagUZ.size(); i++){
+		  int flag = m_flagUZ[i];
+		  if(0 != flag) continue;
+		  grXY->SetPoint(iPointXY, m_depth[i], m_uzPos[i]);
+		  grXY->SetPointError(iPointXY, m_depthErr[i], m_uzPosErr[i]);
+		  iPointXY++;
+	 }
+   TF1 *func1 = new TF1("func1", "pol1", 1800, 2100);
+	 grXY->Fit("func1", "Q");
+	 double a0 = func1->GetParameter(0);
+	 double a1 = func1->GetParameter(1);
+    if(m_FixIP){
+      func1->FixParameter(0, m_IPvar[0]/cos(atan(a1))); //fix dr. 
+      grXY->Fit("func1", "Q");
+      a0 = func1->GetParameter(0);
+      a1 = func1->GetParameter(1);
+    }
+
+	 m_trkPar[2] = atan(a1); // phi
+	 m_trkPar[0] = a0 * cos(m_trkPar[2]); // dr
+	 double x0 = m_trkPar[0] * cos(m_trkPar[2] + TMath::PiOver2());
+	 // cout << "fit in x-y: " << setw(15) << a0 << setw(15) << a1 << setw(15) << m_trkPar[0] << setw(15) << m_trkPar[2] << endl;
+
+
+	 int iPointWZ = 0;
+	 TGraphErrors* grWZ = new TGraphErrors();
+	 for(unsigned int i=0; i<m_flagUZ.size(); i++){
+		  int flag = m_flagUZ[i];
+		  if(1 != flag) continue;
+
+		  double w = (m_depth[i] - x0)/cos(m_trkPar[2]);
+		  grWZ->SetPoint(iPointWZ, w, m_uzPos[i]);
+		  grWZ->SetPointError(iPointWZ, m_depthErr[i]/cos(m_trkPar[2]), m_uzPosErr[i]);
+		  // cout << "iPointWZ " << setw(5) << iPointWZ << setw(15) << w << setw(15) << m_uzPos[i] << endl;
+		  iPointWZ++;
+	 }
+   TF1 *func2 = new TF1("func2", "pol1", 1800, 2100);
+   if(m_FixIP) func2->FixParameter(0, m_IPvar[1]); //fix dz.
+	 grWZ->Fit("func2", "Q");
+	 a0 = func2->GetParameter(0);
+	 a1 = func2->GetParameter(1);
+
+	 double lamda = atan(a1);
+	 m_trkPar[3] = TMath::PiOver2() - lamda; // theta
+	 m_trkPar[1] = a0;						 // dz
+	 //cout << "fit in w-z: " << setw(15) << a0 << setw(15) << a1 << setw(15) << m_trkPar[1]
+	 // 	  << setw(15) << lamda << setw(15) << m_trkPar[3] << endl;
+
+	 delete grXY;
+	 delete grWZ;
+   delete func1;
+   delete func2;
+	 return true;
+}
+
+bool TrackFitInEcal::mnFit3D(){
+    Int_t ierflg;
+    Int_t istat;
+    Int_t nvpar;
+    Int_t nparx;
+    Double_t fmin;
+    Double_t edm;
+    Double_t errdef;
+    Double_t arglist[10];
+
+    TMinuit* mnTrk = new TMinuit(NTRKPAR);
+    mnTrk->SetPrintLevel(-1);
+    mnTrk->SetFCN(fcnTrk);
+    mnTrk->SetErrorDef(1.0);
+    mnTrk->mnparm(0, "dr", m_IPvar[0], 0.1, 0, 0, ierflg);
+    mnTrk->mnparm(1, "dz", m_IPvar[1], 0.1, 0, 0, ierflg);
+    mnTrk->mnparm(2, "phi", 0, 0.1, 0, 0, ierflg);
+    mnTrk->mnparm(3, "theta", 0, 0.1, 0, 0, ierflg);
+	  arglist[0] = 0;
+    if(m_FixIP){ mnTrk->FixParameter(0); mnTrk->FixParameter(1); }
+
+    mnTrk->mnexcm("SET NOW", arglist, 0, ierflg);
+    
+    for(int i=0; i<NTRKPAR; i++){
+       arglist[0] = i + 1;
+       arglist[1] = m_trkPar[i];
+       mnTrk->mnexcm("SET PARameter", arglist, 2, ierflg);
+    }
+    
+    arglist[0] = 1000;
+    arglist[1] = 0.1;
+    mnTrk->mnexcm("MIGRAD", arglist, 2, ierflg);
+    mnTrk->mnstat(fmin, edm, errdef, nvpar, nparx, istat);
+    
+    if( (0==ierflg) && (3==istat) ){
+      for(int i=0; i<5; i++)
+        mnTrk->GetParameter(i, m_trkPar[i], m_trkParErr[i]);
+           
+    
+      mnTrk->mnemat(*m_covariance, 4);
+      m_chisq = fmin;
+      delete mnTrk;
+      return true;
+    } 
+    else{
+//	 	  cout << "ERROR in fit with minuit! ";
+//      cout <<"ierflg = "<<ierflg<<" , stat = "<<istat<<endl;
+      delete mnTrk;
+      return false;
+    }
+}
+
+void TrackFitInEcal::clear(){
+	 m_flagUZ.clear();
+	 m_uzPos.clear();
+	 m_uzPosErr.clear();
+	 m_depth.clear();
+	 m_depthErr.clear();
+    m_IPvar[0]=0; m_IPvar[1]=0;
+    m_FixIP = false; 
+}
+
+void TrackFitInEcal::fcnTrk(Int_t &npar, Double_t *gin, Double_t &f, Double_t *par, Int_t iflag){
+     Double_t chisq = 0.0;
+
+	 double fi0 = par[2] + TMath::PiOver2();
+	 double x0 = par[0] * cos(fi0);
+	 double y0 = par[0] * sin(fi0);
+	 for(unsigned int i=0; i<m_flagUZ.size(); i++){
+		  if(0==m_flagUZ[i]){
+			   double uzPosFit = (m_depth[i]-x0)*tan(par[2]) + y0;
+			   Double_t deta = (uzPosFit - m_uzPos[i])/m_uzPosErr[i];
+			   chisq += deta*deta;
+			   // cout << "fcn x-y: " << setw(15) << uzPosFit << setw(15) << m_uzPos[i] << endl;
+		  } else if(1==m_flagUZ[i]){
+			   double lamda = TMath::PiOver2() - par[3];
+			   double uzPosFit = (m_depth[i]-x0)*tan(lamda)/cos(par[2]) + par[1];
+			   Double_t deta = (uzPosFit - m_uzPos[i])/m_uzPosErr[i];
+			   chisq += deta*deta;
+			   // cout << "fcn w-z: " << setw(15) << uzPosFit << setw(15) << m_uzPos[i] << endl;
+		  }
+	 }
+	 f = chisq;
+}
+
diff --git a/Reconstruction/SiliconTracking/src/SiliconTrackingAlg.cpp b/Reconstruction/SiliconTracking/src/SiliconTrackingAlg.cpp
index df203553..9515f3f3 100644
--- a/Reconstruction/SiliconTracking/src/SiliconTrackingAlg.cpp
+++ b/Reconstruction/SiliconTracking/src/SiliconTrackingAlg.cpp
@@ -1239,7 +1239,7 @@ TrackExtended * SiliconTrackingAlg::TestTriplet(TrackerHitExtended * outerHit,
   
   debug() << " TestTriplet: Use fastHelixFit " << endmsg ;  
   
-  _fastfitter->fastHelixFit(NPT, xh, yh, rh, ph, wrh, zh, wzh,iopt, par, epar, chi2RPhi, chi2Z);
+  _fastfitter->fastHelixFit(NPT, xh, yh, rh, ph, wrh, zh, wzh,iopt, par, epar, chi2RPhi, chi2Z, _helix_max_chi2);
   par[3] = par[3]*par[0]/fabs(par[0]);
 
   // get helix parameters
@@ -1606,7 +1606,7 @@ int SiliconTrackingAlg::BuildTrack(TrackerHitExtended * outerHit,
       
       //debug() << "######## number of hits to fit with _fastfitter = " << NPT << endmsg;
       
-      _fastfitter->fastHelixFit(NPT, xh, yh, rh, ph, wrh, zh, wzh,iopt, par, epar, chi2RPhi, chi2Z);
+      _fastfitter->fastHelixFit(NPT, xh, yh, rh, ph, wrh, zh, wzh,iopt, par, epar, chi2RPhi, chi2Z, _helix_max_chi2);
       par[3] = par[3]*par[0]/fabs(par[0]);
       
       
@@ -1770,7 +1770,7 @@ void SiliconTrackingAlg::CreateTrack(TrackExtended * trackAR ) {
       float chi2Z;
       int ndf = 2*NPT - 5;
       
-      _fastfitter->fastHelixFit(NPT, xh, yh, rh, ph, wrh, zh, wzh,iopt, par, epar, chi2RPhi, chi2Z);
+      _fastfitter->fastHelixFit(NPT, xh, yh, rh, ph, wrh, zh, wzh,iopt, par, epar, chi2RPhi, chi2Z, _helix_max_chi2);
       par[3] = par[3]*par[0]/fabs(par[0]);
       
       float omega = par[0];
@@ -1816,7 +1816,7 @@ void SiliconTrackingAlg::CreateTrack(TrackExtended * trackAR ) {
             }
           }
           
-          _fastfitter->fastHelixFit(NPT, xh, yh, rh, ph, wrh, zh, wzh,iopt, par, epar, chi2RPhi, chi2Z);
+          _fastfitter->fastHelixFit(NPT, xh, yh, rh, ph, wrh, zh, wzh,iopt, par, epar, chi2RPhi, chi2Z, _helix_max_chi2);
           par[3] = par[3]*par[0]/fabs(par[0]);
           
           float chi2Cur = chi2RPhi*_chi2WRPhiSeptet+chi2Z*_chi2WZSeptet;
@@ -2570,7 +2570,7 @@ int SiliconTrackingAlg::AttachHitToTrack(TrackExtended * trackAR, TrackerHitExte
   float chi2Z = 0 ;
   
   
-  int error = _fastfitter->fastHelixFit(NPT, xh, yh, rh, ph, wrh, zh, wzh,iopt, par, epar, chi2RPhi, chi2Z);
+  int error = _fastfitter->fastHelixFit(NPT, xh, yh, rh, ph, wrh, zh, wzh,iopt, par, epar, chi2RPhi, chi2Z, _helix_max_chi2);
   par[3] = par[3]*par[0]/fabs(par[0]);
   
   
diff --git a/Reconstruction/SiliconTracking/src/SiliconTrackingAlg.h b/Reconstruction/SiliconTracking/src/SiliconTrackingAlg.h
index 9579ed8e..2d0bbd2d 100644
--- a/Reconstruction/SiliconTracking/src/SiliconTrackingAlg.h
+++ b/Reconstruction/SiliconTracking/src/SiliconTrackingAlg.h
@@ -255,6 +255,7 @@ class SiliconTrackingAlg : public GaudiAlgorithm {
   Gaudi::Property<bool> _ElossOn{this, "EnergyLossOn", true};
   Gaudi::Property<bool> _SmoothOn{this, "SmoothOn", true};
   Gaudi::Property<float> _helix_max_r{this, "HelixMaxR", 2000.};
+  Gaudi::Property<float> _helix_max_chi2{this, "HelixMaxChi2", 5000.};
   Gaudi::Property<bool> m_dumpTime{this, "DumpTime", false};
   Gaudi::Property<bool> m_debug{this, "Debug", false};
 
diff --git a/Reconstruction/Tracking/src/Clupatra/ClupatraAlg.cpp b/Reconstruction/Tracking/src/Clupatra/ClupatraAlg.cpp
index 8889bc19..678beddd 100644
--- a/Reconstruction/Tracking/src/Clupatra/ClupatraAlg.cpp
+++ b/Reconstruction/Tracking/src/Clupatra/ClupatraAlg.cpp
@@ -46,7 +46,7 @@
 #include "edm4hep/TrackerHitCollection.h"
 #include "edm4hep/TrackCollection.h"
 // #include "edm4hep/TrackerHitPlane.h"
-
+#include <TStopwatch.h>
 
 using namespace edm4hep ;
 using namespace MarlinTrk ;
@@ -227,6 +227,23 @@ StatusCode ClupatraAlg::initialize() {
         //tree->Branch("omega", &omega, "omega/D");
         //tree->Branch("totalCandidates", &totalCandidates, "totalCandidates/I");
         //tree->Branch("eventNumber", &_nEvt, "eventNumber/I");
+	if(_DumpTime){
+	  NTuplePtr nt1(ntupleSvc(), "MyTuples/Time"+name());
+	  if ( !nt1 ) {
+	    m_tuple = ntupleSvc()->book("MyTuples/Time"+name(),CLID_ColumnWiseTuple,"Tracking time");
+	    if ( 0 != m_tuple ) {
+	      m_tuple->addItem ("timeTotal", m_timeTotal ).ignore();
+	      m_tuple->addItem ("timeKalman", m_timeKalman ).ignore();
+	    }
+	    else {
+	      fatal() << "Cannot book MyTuples/Time"+name() <<endmsg;
+	      return StatusCode::FAILURE;
+	    }
+	  }
+	  else{
+	    m_tuple = nt1;
+	  }
+	}
 
 	return GaudiAlgorithm::initialize();
 
@@ -237,7 +254,7 @@ StatusCode ClupatraAlg::execute() {
 
 
   info() << "Clupatra Algorithm started" << endmsg;
-
+  auto stopwatch = TStopwatch();
 	//  clock_t start =  clock() ;
 	Timer timer ;
 	unsigned t_init       = timer.registerTimer(" initialization      " ) ;
@@ -1264,6 +1281,11 @@ StatusCode ClupatraAlg::execute() {
             delete t;
         }
 
+	if(m_tuple){
+	  m_timeTotal = stopwatch.RealTime()*1000;
+	  m_tuple->write();
+	}
+
 	_nEvt++ ;
 
 	TrackInfo_of_edm4hepTrack.clear();
diff --git a/Reconstruction/Tracking/src/Clupatra/ClupatraAlg.h b/Reconstruction/Tracking/src/Clupatra/ClupatraAlg.h
index fb451483..2e81b9e6 100644
--- a/Reconstruction/Tracking/src/Clupatra/ClupatraAlg.h
+++ b/Reconstruction/Tracking/src/Clupatra/ClupatraAlg.h
@@ -24,6 +24,8 @@
 #include "TrackSystemSvc/IMarlinTrack.h"
 #include "Tracking/ITrackFitterTool.h"
 
+#include "GaudiKernel/NTuple.h"
+
 namespace MarlinTrk{
   class IMarlinTrkSystem ;
 }
@@ -141,7 +143,7 @@ class ClupatraAlg : public GaudiAlgorithm {
   Gaudi::Property<bool> _ElossOn{this, "EnergyLossOn", true};
   Gaudi::Property<bool> _SmoothOn{this, "SmoothOn", false};
   Gaudi::Property<std::string> m_fitToolName{this, "FitterTool", "KalTestTool/KalTest010"};
-
+  Gaudi::Property<bool> _DumpTime{this, "DumpTime", false};
 
   DataHandle<edm4hep::TrackerHitCollection> _TPCHitCollectionHandle{"TPCTrackerHits", Gaudi::DataHandle::Reader, this};
   DataHandle<edm4hep::TrackerHitCollection> _SITHitCollectionHandle{"SIDTrackerHits", Gaudi::DataHandle::Reader, this};
@@ -165,6 +167,9 @@ class ClupatraAlg : public GaudiAlgorithm {
   double pt;
   int totalCandidates;
 
+  NTuple::Tuple*       m_tuple = nullptr;
+  NTuple::Item<float>  m_timeTotal;
+  NTuple::Item<float>  m_timeKalman;
 //   NNClusterer* _clusterer ;
 
 } ;
diff --git a/Reconstruction/Tracking/src/FullLDCTracking/FullLDCTrackingAlg.cpp b/Reconstruction/Tracking/src/FullLDCTracking/FullLDCTrackingAlg.cpp
index 95265a4c..4c6b2551 100755
--- a/Reconstruction/Tracking/src/FullLDCTracking/FullLDCTrackingAlg.cpp
+++ b/Reconstruction/Tracking/src/FullLDCTracking/FullLDCTrackingAlg.cpp
@@ -62,6 +62,7 @@
 #include <bitset>
 
 #include <TStopwatch.h>
+#include "TMath.h"
 
 typedef std::vector<edm4hep::TrackerHit> TrackerHitVec;
 
@@ -285,7 +286,8 @@ void FullLDCTrackingAlg::AddTrackColToEvt(TrackExtendedVec & trkVec, edm4hep::Tr
   float pxTot = 0.0;
   float pyTot = 0.0;
   float pzTot = 0.0;
-  
+
+  bool fit_backwards = _backward ? (IMarlinTrack::backward) : (!IMarlinTrack::backward);
   //SJA:FIXME: So here we are going to do one final refit. This can certainly be optimised, but rather than worry about the mememory management right now lets make it work, and optimise it later ...
   
   debug() << "Total " << nTrkCand << " trkCand to deal" << endmsg;
@@ -327,8 +329,6 @@ void FullLDCTrackingAlg::AddTrackColToEvt(TrackExtendedVec & trkVec, edm4hep::Tr
       continue ;
     }
     
-    edm4hep::MutableTrack track;// = new edm4hep::Track;
-    
     // setup initial dummy covariance matrix
     decltype(edm4hep::TrackState::covMatrix) covMatrix;
     
@@ -341,6 +341,8 @@ void FullLDCTrackingAlg::AddTrackColToEvt(TrackExtendedVec & trkVec, edm4hep::Tr
     covMatrix[5]  = ( _initialTrackError_omega ); //sigma_omega^2
     covMatrix[9]  = ( _initialTrackError_z0    ); //sigma_z0^2
     covMatrix[14] = ( _initialTrackError_tanL  ); //sigma_tanl^2
+
+    bool fit_backwards = _backward ? (IMarlinTrack::backward) : (!IMarlinTrack::backward);
     
     // get the track state at the last hit at the outer most hit
     
@@ -362,14 +364,32 @@ void FullLDCTrackingAlg::AddTrackColToEvt(TrackExtendedVec & trkVec, edm4hep::Tr
       
       if(group->getTrackExtendedVec().size()==1) {
         te = group->getTrackExtendedVec()[0];
+	fit_backwards = IMarlinTrack::backward;
       } else {
-        te = group->getTrackExtendedVec()[1];
+        //te = group->getTrackExtendedVec()[1];
+	auto trk0 = group->getTrackExtendedVec()[0]->getTrack();
+	auto trk1 = group->getTrackExtendedVec()[1]->getTrack();
+	int nhit0 = trk0.trackerHits_size();
+	int nhit1 = trk0.trackerHits_size();
+	if (nhit0>=_lowestTrackerHitNumberSi) {
+	  te = group->getTrackExtendedVec()[0];
+	}
+	else if (nhit1>=_lowestTrackerHitNumberTPC) {
+	  te = group->getTrackExtendedVec()[1];
+	}
+	else {
+	  double prob0 = TMath::Prob(trk0.getChi2(), trk0.getNdf());
+	  double prob1 = TMath::Prob(trk1.getChi2(), trk1.getNdf());
+	  te = (prob0>prob1) ? group->getTrackExtendedVec()[0] : group->getTrackExtendedVec()[1];
+	}
       }
-      
-      if(hasTrackStateAt(te->getTrack(), 3/*lcio::TrackState::AtLastHit*/)){
-        debug() << "Initialise Fit with trackstate from last hit " << group << " " << te << " " << te->getTrack().id() << endmsg;
+
+      int location = (fit_backwards==IMarlinTrack::backward) ? edm4hep::TrackState::AtLastHit : edm4hep::TrackState::AtIP;
+
+      if(hasTrackStateAt(te->getTrack(), location)){
+        debug() << "Initialise Fit with trackstate from location " << location << " " << group << " " << te << " " << te->getTrack().id() << endmsg;
 	if(te->getTrack().trackStates_size()>4) warning() << "TrackState size > 4" << endmsg;
-        ts_initial = getTrackStateAt(te->getTrack(), 3/*lcio::TrackState::AtLastHit*/);
+        ts_initial = getTrackStateAt(te->getTrack(), location);
         prefit_set = true;
 	debug() << "ts_initial " << ts_initial << endmsg;
       }
@@ -388,7 +408,7 @@ void FullLDCTrackingAlg::AddTrackColToEvt(TrackExtendedVec & trkVec, edm4hep::Tr
       
       ts_initial.referencePoint = ref;
       
-      ts_initial.location = 1/*lcio::TrackState::AtIP*/;
+      ts_initial.location = edm4hep::TrackState::AtIP;
       debug() << "ts_initial " << ts_initial << endmsg;
     }
     
@@ -419,23 +439,30 @@ void FullLDCTrackingAlg::AddTrackColToEvt(TrackExtendedVec & trkVec, edm4hep::Tr
     //}
 
     debug() << "trkHits ready" << endmsg;
-    bool fit_backwards = IMarlinTrack::backward;
-    
-    //MarlinTrk::IMarlinTrack* marlinTrk = _trksystem->createTrack();
-    //debug() << "marlinTrk created " << endmsg;
+    //bool fit_backwards = _backward ? (IMarlinTrack::backward) : (!IMarlinTrack::backward);
+    int  fit_count = 0;
     
+fitstart:
+
     int error_code = 0;
-    
+    edm4hep::MutableTrack track;
+    fit_count++;
+
     try {
-      //error_code = MarlinTrk::createFinalisedLCIOTrack(marlinTrk, trkHits, &track, fit_backwards, &ts_initial, _bField, _maxChi2PerHit);
-      error_code = m_fitTool->Fit(track, trkHits, ts_initial, _maxChi2PerHit, fit_backwards);
+      debug() << "initial TrackState: " << ts_initial << endmsg;
+      debug() << "fir direction: " << ((fit_backwards==IMarlinTrack::backward) ? "backward" : "forward") << endmsg;
+      debug() << "MaxChi2PerHit: " << _maxChi2PerHit << endmsg;
+      if (fit_count==1) {
+	error_code = m_fitTool->Fit(track, trkHits, ts_initial, _maxChi2PerHit, fit_backwards);
+      }
+      else {
+	error_code = m_fitTool->Fit(track, trkHits, covMatrix, _maxChi2PerHit, fit_backwards);
+      }
     } catch (...) {
-      
       //      delete track;
       //      delete marlinTrk;
       error() << "exception happened while createFinalisedLCIOTrack!" << endmsg;
       throw std::runtime_error("Need more check");
-      
     }
     debug() << "createFinalisedLCIOTrack finished" << endmsg;
     
@@ -446,7 +473,16 @@ void FullLDCTrackingAlg::AddTrackColToEvt(TrackExtendedVec & trkVec, edm4hep::Tr
       dc->skip_current_track();
     }
 #endif
-    
+
+    if (error_code != IMarlinTrack::success) {
+      debug() << "FullLDCTrackingAlg::AddTrackColToEvt: Track fit failed with error code " << error_code << " track dropped. Number of hits = "<< trkHits.size() << endmsg;
+      m_fitTool->Clear();
+      if (fit_count==1) {
+	goto fitstart;
+      }
+      // TODO: to pick up old tracks
+      continue ;
+    }
     
     std::vector<std::pair<edm4hep::TrackerHit , double> > hits_in_fit ;
     std::vector<std::pair<edm4hep::TrackerHit , double> > outliers ;
@@ -475,12 +511,6 @@ void FullLDCTrackingAlg::AddTrackColToEvt(TrackExtendedVec & trkVec, edm4hep::Tr
     
     //delete marlinTrk;
     m_fitTool->Clear();
-
-    if( error_code != IMarlinTrack::success ) {
-      debug() << "FullLDCTrackingAlg::AddTrackColToEvt: Track fit failed with error code " << error_code << " track dropped. Number of hits = "<< trkHits.size() << endmsg;
-      //delete Track;
-      continue ;
-    }
     
     if( track.getNdf() < 0) {
       debug() << "FullLDCTrackingAlg::AddTrackColToEvt: Track fit returns " << track.getNdf() << " degress of freedom track dropped. Number of hits = "<< trkHits.size() << endmsg;
@@ -491,10 +521,10 @@ void FullLDCTrackingAlg::AddTrackColToEvt(TrackExtendedVec & trkVec, edm4hep::Tr
     edm4hep::TrackState trkStateIP;
     for(int i=0;i<track.trackStates_size();i++){
       trkStateIP = track.getTrackStates(i);
-      if(trkStateIP.location ==1/*lcio::TrackState::AtIP*/) break;
+      if(trkStateIP.location == edm4hep::TrackState::AtIP) break;
     }
     
-    if (trkStateIP.location != 1/*lcio::TrackState::AtIP*/) {
+    if (trkStateIP.location != edm4hep::TrackState::AtIP) {
       debug() << "FullLDCTrackingAlg::AddTrackColToEvt: Track fit returns " << track.getNdf() << " degress of freedom track dropped. Number of hits = "<< trkHits.size() << endmsg;
       throw EVENT::Exception( std::string("FullLDCTracking_MarlinTrk::AddTrackColToEvt: trkStateIP pointer == NULL ")  ) ;
     }
@@ -568,48 +598,90 @@ void FullLDCTrackingAlg::AddTrackColToEvt(TrackExtendedVec & trkVec, edm4hep::Tr
     if (nhits_in_set > 0) track.setType( track.getType()| (1<<lcio::ILDDetID::SET) ) ;
     
     bool rejectTrack_onTPCHits = (nhits_in_tpc < _cutOnTPCHits) && (nHitsSi<=0);
-    
+    bool rejectTrack_onITKHits = ( (nhits_in_tpc<=0) && (nhits_in_sit<1 && nhits_in_ftd<1) );
     bool rejectTrackonSiliconHits = ( (nhits_in_tpc<=0) && (nHitsSi<_cutOnSiHits) );
     bool rejectTrackonImpactParameters =  ( fabs(d0TrkCand) > _d0TrkCut ) || ( fabs(z0TrkCand) > _z0TrkCut );
     
-    if ( rejectTrack_onTPCHits || rejectTrackonSiliconHits  || rejectTrackonImpactParameters) {
+    edm4hep::TrackState trkState;
+
+    if ( rejectTrack_onTPCHits || rejectTrack_onITKHits || rejectTrackonSiliconHits  || rejectTrackonImpactParameters) {
 
       debug() << " Track " << trkCand
 	      << " rejected : rejectTrack_onTPCHits = " << rejectTrack_onTPCHits
+	      << " rejectTrackonITKHits " << rejectTrack_onITKHits
 	      << " rejectTrackonSiliconHits " << rejectTrackonSiliconHits
 	      << " rejectTrackonImpactParameters " << rejectTrackonImpactParameters
 	      << endmsg;
-      //delete Track;
+
+      if (fit_count==1) {
+	//fit_backwards = !fit_backwards;
+	goto fitstart;
+      }
+      warning() << "twice failed, select one of old tracks as output, others as subtrack" << endmsg;
+      auto oldtrk = group->getTrackExtendedVec()[0]->getTrack().clone();
+      if (group != NULL) {
+	TrackExtendedVec trkVecGrp = group->getTrackExtendedVec();
+	int nGrTRK = int(trkVecGrp.size());
+	for (int iGr=0;iGr<nGrTRK;++iGr) {
+	  TrackExtended * subTrack = trkVecGrp[iGr];
+	  oldtrk.addToTracks(subTrack->getTrack());
+
+	  // check if it is a tpc looper ...
+	  if( UTIL::BitSet32( subTrack->getTrack().getType() )[  lcio::ILDDetID::TPC   ] )  {
+	    int nseg = subTrack->getTrack().tracks_size();
+	    for(unsigned iSeg=0;iSeg<nseg;iSeg++){
+	      oldtrk.addToTracks(subTrack->getTrack().getTracks(iSeg));
+	    }
+	  }
+	}
+      }
+
+      edm4hep::TrackState trkStateOld;
+      for(int i=0;i<oldtrk.trackStates_size();i++){
+	trkStateOld = oldtrk.getTrackStates(i);
+	if(trkStateOld.location == edm4hep::TrackState::AtIP) break;
+      }
+
+      if (trkStateOld.location != edm4hep::TrackState::AtIP) {
+	debug() << "AddTrackColToEvt: old track has not TrackState::AtIP" << endmsg;
+	throw EVENT::Exception( std::string("FullLDCTracking_MarlinTrk::AddTrackColToEvt: trkStateIP pointer == NULL "));
+      }
+      trkState = trkStateOld;
+
+      colTRK->push_back(oldtrk);
+      debug() << " Add Track to final Collection: ID = " << oldtrk.id() << " for trkCand "<< trkCand << " old" << endmsg;
     }
     else {
-      float omega = trkStateIP.omega;
-      float tanLambda = trkStateIP.tanLambda;
-      float phi0 = trkStateIP.phi;
-      float d0 = trkStateIP.D0;
-      float z0 = trkStateIP.Z0;
-      
-      HelixClass helix;
-      helix.Initialize_Canonical(phi0,d0,z0,omega,tanLambda,_bField);
-      
-      float trkPx = helix.getMomentum()[0];
-      float trkPy = helix.getMomentum()[1];
-      float trkPz = helix.getMomentum()[2];
-      float trkP = sqrt(trkPx*trkPx+trkPy*trkPy+trkPz*trkPz);
-      
-      eTot += trkP;
-      pxTot += trkPx;
-      pyTot += trkPy;
-      pzTot += trkPz;
-      nTotTracks++;
-   
+      trkState = trkStateIP;
+
       colTRK->push_back(track);
       debug() << " Add Track to final Collection: ID = " << track.id() << " for trkCand "<< trkCand << endmsg;
     }
+
+    float omega = trkState.omega;
+    float tanLambda = trkState.tanLambda;
+    float phi0 = trkState.phi;
+    float d0 = trkState.D0;
+    float z0 = trkState.Z0;
+
+    HelixClass helix;
+    helix.Initialize_Canonical(phi0,d0,z0,omega,tanLambda,_bField);
+
+    float trkPx = helix.getMomentum()[0];
+    float trkPy = helix.getMomentum()[1];
+    float trkPz = helix.getMomentum()[2];
+    float trkP = sqrt(trkPx*trkPx+trkPy*trkPy+trkPz*trkPz);
+
+    eTot += trkP;
+    pxTot += trkPx;
+    pyTot += trkPy;
+    pzTot += trkPz;
+    nTotTracks++;
   }
   if(m_tuple) m_timeKalman = stopwatch.RealTime()*1000;
   // streamlog_out(DEBUG5) << endmsg;
-  debug() << "Number of accepted " << _OutputTrackColHdl.fullKey() << " = " << nTotTracks << endmsg;
-  debug() << "Total 4-momentum of " << _OutputTrackColHdl.fullKey() << " : E = " << eTot
+  info() << "Number of accepted " << _OutputTrackColHdl.fullKey() << " = " << nTotTracks << endmsg;
+  info() << "Total 4-momentum of " << _OutputTrackColHdl.fullKey() << " : E = " << eTot
 	  << " Px = " << pxTot
 	  << " Py = " << pyTot
 	  << " Pz = " << pzTot << endmsg;
@@ -1628,12 +1700,12 @@ TrackExtended * FullLDCTrackingAlg::CombineTracks(TrackExtended * tpcTrack, Trac
   int errorCode = IMarlinTrack::success;
 
   try{
-    pre_fit = getTrackStateAt(tpcTrack->getTrack(), 3/*lcio::TrackState::AtLastHit*/);
+    pre_fit = getTrackStateAt(tpcTrack->getTrack(), edm4hep::TrackState::AtLastHit);
   }
   catch(std::runtime_error& e){
     error() << e.what() << " should be checked (TPC track) " << tpcTrack << endmsg;
     try{
-      pre_fit = getTrackStateAt(siTrack->getTrack(), 3/*lcio::TrackState::AtLastHit*/);
+      pre_fit = getTrackStateAt(siTrack->getTrack(), edm4hep::TrackState::AtLastHit);
     }
     catch(std::runtime_error& e){
       error() << e.what() << " should be checked (Si track)" << endmsg;
@@ -3696,7 +3768,7 @@ void FullLDCTrackingAlg::AssignOuterHitsToTracks(TrackerHitExtendedVec hitVec, f
           
           pre_fit.referencePoint = ref;
           
-          pre_fit.location = 1/*lcio::TrackState::AtIP*/;
+          pre_fit.location = edm4hep::TrackState::AtIP;
           // setup initial dummy covariance matrix
           decltype(edm4hep::TrackState::covMatrix) covMatrix;
           
@@ -3823,15 +3895,15 @@ HelixClass * FullLDCTrackingAlg::GetExtrapolationHelix( TrackExtended * track) {
     if (trk_lcio.isAvailable()) {
   
       // use the tracks state at the calorimeter because that will have accounted for energy loss already
-      if (hasTrackStateAt(trk_lcio, 4/*lcio::TrackState::AtCalorimeter*/)) {
+      if (hasTrackStateAt(trk_lcio, edm4hep::TrackState::AtCalorimeter)) {
         
-        TrackState ts_at_last_hit = getTrackStateAt(trk_lcio, 3/*lcio::TrackState::AtLastHit*/);
+        TrackState ts_at_last_hit = getTrackStateAt(trk_lcio, edm4hep::TrackState::AtLastHit);
         float z_ref = ts_at_last_hit.referencePoint[2];
 
         // make sure we use the one closest to the calo face
         if ( fabs(z_ref) >  z_ref_max) {
           z_ref_max = fabs(z_ref);
-          ts_at_calo = getTrackStateAt(trk_lcio, 4/*lcio::TrackState::AtCalorimeter*/);
+          ts_at_calo = getTrackStateAt(trk_lcio, edm4hep::TrackState::AtCalorimeter);
 
           debug() << "FullLDCTrackingAlg::GetExtrapolationHelix set ts_at_calo with ref_z = " << z_ref << endmsg;
 	}
@@ -3845,7 +3917,7 @@ HelixClass * FullLDCTrackingAlg::GetExtrapolationHelix( TrackExtended * track) {
         
     LCIOTrackPropagators::PropagateLCIOToNewRef(ts_at_calo_forIP,0.0,0.0,0.0);
     
-    ts_at_calo_forIP.location = 1/*lcio::TrackState::AtIP*/;
+    ts_at_calo_forIP.location = edm4hep::TrackState::AtIP;
     
     helix = new HelixClass();
     
@@ -4205,7 +4277,7 @@ void FullLDCTrackingAlg::AssignSiHitsToTracks(TrackerHitExtendedVec hitVec,
         
         pre_fit.referencePoint = ref;
         
-        pre_fit.location = 1/*lcio::TrackState::AtIP*/;
+        pre_fit.location = edm4hep::TrackState::AtIP;
         
         // setup initial dummy covariance matrix
         decltype(edm4hep::TrackState::covMatrix) covMatrix;
@@ -5017,13 +5089,13 @@ void FullLDCTrackingAlg::setupGearGeom(){
   }
 }
 
-void FullLDCTrackingAlg::checkTrackState(int type){
+void FullLDCTrackingAlg::checkTrackState(int location){
   debug() << "check TrackState " << endmsg;
   for(int i=0;i<_allTPCTracks.size();i++){
     TrackExtended* tpcTrack=_allTPCTracks[i];
-    if(hasTrackStateAt(tpcTrack->getTrack(), type/*lcio::TrackState::AtLastHit*/)){
+    if(hasTrackStateAt(tpcTrack->getTrack(), location)){
       //info() << tpcTrack->getTrack().id() << endmsg;
-      edm4hep::TrackState ts = getTrackStateAt(tpcTrack->getTrack(), type/*lcio::TrackState::AtLastHit*/);
+      edm4hep::TrackState ts = getTrackStateAt(tpcTrack->getTrack(), location);
       debug() << i << " " << tpcTrack->getTrack().id() << ": ts " << ts << endmsg;
     }
   }
diff --git a/Reconstruction/Tracking/src/FullLDCTracking/FullLDCTrackingAlg.h b/Reconstruction/Tracking/src/FullLDCTracking/FullLDCTrackingAlg.h
index 9a4561f2..7dcc57d4 100755
--- a/Reconstruction/Tracking/src/FullLDCTracking/FullLDCTrackingAlg.h
+++ b/Reconstruction/Tracking/src/FullLDCTracking/FullLDCTrackingAlg.h
@@ -310,7 +310,7 @@ protected:
   int SegmentRadialOverlap(TrackExtended* pTracki, TrackExtended* pTrackj);
   bool VetoMerge(TrackExtended* firstTrackExt, TrackExtended* secondTrackExt);
 
-  void checkTrackState(int type=0);
+  void checkTrackState(int location=0);
   
   int _nRun ;
   int _nEvt ;
@@ -397,6 +397,9 @@ protected:
   Gaudi::Property<float> _vetoMergeMomentumCut{this, "VetoMergeMomentumCut", 2.5};
   Gaudi::Property<float> _maxAllowedPercentageOfOutliersForTrackCombination{this, "MaxAllowedPercentageOfOutliersForTrackCombination", 0.3};
   Gaudi::Property<int>   _maxAllowedSiHitRejectionsForTrackCombination{this, "MaxAllowedSiHitRejectionsForTrackCombination", 2};
+  Gaudi::Property<int>   _lowestTrackerHitNumberSi{this, "LowestSiHitsNumberForInitial", 7};
+  Gaudi::Property<int>   _lowestTrackerHitNumberTPC{this, "LowestTPCHitsNumberForInitial", 200};
+  Gaudi::Property<bool>  _backward{this, "FitBackward", false};
   Gaudi::Property<bool>  m_dumpTime{this, "DumpTime", false};
   //float _dPCutForForcedMerging;
   Gaudi::Property<std::string> m_fitToolName{this, "FitterTool", "KalTestTool/KalTest111"};
diff --git a/Service/CMakeLists.txt b/Service/CMakeLists.txt
index 14c79538..8d47862c 100644
--- a/Service/CMakeLists.txt
+++ b/Service/CMakeLists.txt
@@ -2,3 +2,4 @@ add_subdirectory(EventSeeder)
 add_subdirectory(GearSvc)
 add_subdirectory(TrackSystemSvc)
 add_subdirectory(SimplePIDSvc)
+add_subdirectory(CrystalEcalSvc)
diff --git a/Service/CrystalEcalSvc/CMakeLists.txt b/Service/CrystalEcalSvc/CMakeLists.txt
new file mode 100644
index 00000000..70966a29
--- /dev/null
+++ b/Service/CrystalEcalSvc/CMakeLists.txt
@@ -0,0 +1,29 @@
+# gaudi_subdir(CrystalEcalSvc v0r0)
+
+gaudi_add_header_only_library(CrystalEcalSvcLib)
+
+gaudi_add_module(CrystalEcalSvc 
+                 SOURCES src/CrystalEcalEnergyCorrectionSvc.cpp
+                 LINK Gaudi::GaudiKernel
+                      CrystalEcalSvcLib
+                      #DataHelperLib
+                      #CRDEcalRecLib
+                      #GearSvc
+                      #Gaudi::GaudiKernel
+                      #${ROOT_LIBRARIES}
+                      #${CLHEP_LIBRARIES}
+                      #${GEAR_LIBRARIES}
+                      #${LCIO_LIBRARIES}
+                      #EDM4HEP::edm4hep EDM4HEP::edm4hepDict
+)
+
+#target_include_directories(CrystalEcalSvc PUBLIC
+#  $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>/include
+#  $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+
+install(TARGETS  CrystalEcalSvcLib CrystalEcalSvc
+  EXPORT CEPCSWTargets
+  RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin
+  LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shlib
+  COMPONENT dev)
+
diff --git a/Service/CrystalEcalSvc/include/CrystalEcalSvc/ICrystalEcalSvc.h b/Service/CrystalEcalSvc/include/CrystalEcalSvc/ICrystalEcalSvc.h
new file mode 100644
index 00000000..9cf5f75f
--- /dev/null
+++ b/Service/CrystalEcalSvc/include/CrystalEcalSvc/ICrystalEcalSvc.h
@@ -0,0 +1,19 @@
+#ifndef IXTALECALSvc_h
+#define IXTALECALSvc_h
+
+#include "GaudiKernel/IService.h"
+
+class ICrystalEcalSvc: virtual public IInterface {
+public:
+    DeclareInterfaceID(ICrystalEcalSvc, 0, 1); // major/minor version
+    virtual ~ICrystalEcalSvc() = default;
+
+    virtual double energyCorrection(double energy, double phi, double theta) = 0;
+
+    // virtual void ClearSystem() = 0;
+
+private:
+
+};
+
+#endif
diff --git a/Service/CrystalEcalSvc/src/CrystalEcalEnergyCorrectionSvc.cpp b/Service/CrystalEcalSvc/src/CrystalEcalEnergyCorrectionSvc.cpp
new file mode 100755
index 00000000..c7fa1e86
--- /dev/null
+++ b/Service/CrystalEcalSvc/src/CrystalEcalEnergyCorrectionSvc.cpp
@@ -0,0 +1,110 @@
+#ifndef XTALECALENERGYCORRECTIONSvc_C
+#define XTALECALENERGYCORRECTIONSvc_C
+
+#include "CrystalEcalEnergyCorrectionSvc.h"
+
+DECLARE_COMPONENT(CrystalEcalEnergyCorrectionSvc)
+
+double CrystalEcalEnergyCorrectionSvc::thetaCorrectionAngleStart[7] = {94.5, 105, 115, 123.5, 130.5, 136.5, 141};
+double CrystalEcalEnergyCorrectionSvc::thetaCorrectionAngleEnd[7] = {97.0, 107.5, 117.5, 126, 133, 139.0, 143.5};
+double CrystalEcalEnergyCorrectionSvc::phiCorrectionAngleStart[2] = {4, 15.5};
+double CrystalEcalEnergyCorrectionSvc::phiCorrectionAngleEnd[2] = {7, 18.5};
+double CrystalEcalEnergyCorrectionSvc::mpvCorrectionFactor[5] = {-1524.86, -2098.74, 0.01, 191.79, 0.98};
+int CrystalEcalEnergyCorrectionSvc::angleBinNumber = 100;
+double CrystalEcalEnergyCorrectionSvc::angleRangePhi = 3.0;
+double CrystalEcalEnergyCorrectionSvc::angleRangeTheta = 2.5;
+int CrystalEcalEnergyCorrectionSvc::moduleNumberPhi = 32;
+
+StatusCode CrystalEcalEnergyCorrectionSvc::initialize() {
+    
+    // read correction file
+    std::string m_correctionFile = _correctionFile;
+    file = TFile::Open(m_correctionFile.c_str());
+
+    // phi
+    treePhi = (TTree*)(file->Get("scalePhi")); 
+    treePhi->SetBranchAddress("phi", &_phiAngle);
+    treePhi->SetBranchAddress("scale", &_phiScale); 
+    for(int i = 0; i < treePhi->GetEntries(); i++) {
+        treePhi->GetEntry(i);
+        phiAngle.push_back(_phiAngle);
+        phiScale.push_back(_phiScale);
+    }
+
+    //theta
+    treeTheta = (TTree*)(file->Get("scaleTheta"));
+    treeTheta->SetBranchAddress("theta", &_thetaAngle);
+    treeTheta->SetBranchAddress("scale", &_thetaScale);
+    for(int i = 0; i < treeTheta->GetEntries(); i++) {
+        treeTheta->GetEntry(i);
+        thetaAngle.push_back(_thetaAngle);
+        thetaScale.push_back(_thetaScale);
+    }
+
+    StatusCode sc = Service::initialize();
+    return sc;
+}
+
+StatusCode CrystalEcalEnergyCorrectionSvc::finalize() {
+    
+    file->Close();
+
+    phiAngle.clear();
+    phiScale.clear();
+    thetaAngle.clear();
+    thetaScale.clear();
+    
+    StatusCode sc = Service::finalize();
+    return sc;
+}
+
+double CrystalEcalEnergyCorrectionSvc::energyCorrection(double energy, double phi, double theta) { // energy: GeV, phi: 0-360, theta: 0-180
+
+    //std::cout<<"Before correction: "<<energy<<" phi "<<phi<<" theta "<<theta<<std::endl;
+
+    // #####  mpv deviation #####
+    double energyCor = 0;
+    energyCor = energy / (mpvCorrectionFactor[3]/(mpvCorrectionFactor[0] + mpvCorrectionFactor[1]*energy + mpvCorrectionFactor[2]*energy*energy) + mpvCorrectionFactor[4]);
+    //std::cout<<"After MPV correction: "<<energyCor<<std::endl;
+
+    // #####  energy leakage in cracks #####
+    
+    double tmpPhi = fmod(phi, 360./(moduleNumberPhi/2));
+
+    auto itPhiStart = std::lower_bound(std::begin(phiCorrectionAngleStart), std::end(phiCorrectionAngleStart), tmpPhi);
+    auto itPhiEnd = std::upper_bound(std::begin(phiCorrectionAngleEnd), std::end(phiCorrectionAngleEnd), tmpPhi);
+
+    int iPhiStart = std::distance(std::begin(phiCorrectionAngleStart), itPhiStart) - 1;
+    int jPhiEnd = std::distance(std::begin(phiCorrectionAngleEnd), itPhiEnd);
+
+    if(iPhiStart == jPhiEnd) {
+        
+        int binNumber = jPhiEnd*angleBinNumber + abs((tmpPhi-phiCorrectionAngleStart[iPhiStart])/(angleRangePhi/angleBinNumber));
+        //std::cout<<"   phi correction factor: "<<phiScale.at(binNumber)<<std::endl;
+        energyCor = energyCor/phiScale.at(binNumber);
+    }
+
+    //std::cout<<"After phi correction: "<<energyCor<<std::endl;
+    
+    double tmpTheta = theta;
+    if(tmpTheta<90) tmpTheta = 90 + (90-tmpTheta);
+
+    auto itThetaStart = std::lower_bound(std::begin(thetaCorrectionAngleStart), std::end(thetaCorrectionAngleStart), tmpTheta);
+    auto itThetaEnd = std::upper_bound(std::begin(thetaCorrectionAngleEnd), std::end(thetaCorrectionAngleEnd), tmpTheta);
+
+    int iThetaStart = std::distance(std::begin(thetaCorrectionAngleStart), itThetaStart) - 1;
+    int jThetaEnd = std::distance(std::begin(thetaCorrectionAngleEnd), itThetaEnd);
+
+    if(iThetaStart == jThetaEnd) {
+        
+        int binNumber = jThetaEnd*angleBinNumber + abs((tmpTheta-thetaCorrectionAngleStart[iThetaStart])/(angleRangeTheta/angleBinNumber));
+        //std::cout<<"   theta correction factor: "<<thetaScale.at(binNumber)<<std::endl;
+        energyCor = energyCor/thetaScale.at(binNumber);
+    }
+
+    //std::cout<<"After theta correction: "<<energyCor<<std::endl;
+    
+    //msg() << "corection done!" << endmsg;
+    return energyCor;
+}
+#endif
diff --git a/Service/CrystalEcalSvc/src/CrystalEcalEnergyCorrectionSvc.h b/Service/CrystalEcalSvc/src/CrystalEcalEnergyCorrectionSvc.h
new file mode 100755
index 00000000..71aa3922
--- /dev/null
+++ b/Service/CrystalEcalSvc/src/CrystalEcalEnergyCorrectionSvc.h
@@ -0,0 +1,44 @@
+#ifndef XTALECALENERGYCORRECTIONSvc_h
+#define XTALECALENERGYCORRECTIONSvc_h
+
+#include <GaudiKernel/Service.h>
+#include "CrystalEcalSvc/ICrystalEcalSvc.h"
+#include "TFile.h"
+#include "TTree.h"
+#include "TBranch.h"
+#include <vector>
+#include <string>
+#include <cfloat>
+#include <cmath>
+
+class CrystalEcalEnergyCorrectionSvc: public extends<Service, ICrystalEcalSvc> {
+public:
+    using extends::extends;
+
+    StatusCode initialize() override;
+    StatusCode finalize() override;
+
+    double energyCorrection(double energy, double phi, double theta) override;
+
+    static double thetaCorrectionAngleStart[7];
+    static double thetaCorrectionAngleEnd[7];
+    static double phiCorrectionAngleStart[2];
+    static double phiCorrectionAngleEnd[2];
+    static double mpvCorrectionFactor[5];
+    static int angleBinNumber;
+    static double angleRangePhi;
+    static double angleRangeTheta;
+    static int moduleNumberPhi;
+
+private:
+    mutable Gaudi::Property<std::string> _correctionFile{this, "CorrectionFile", "/cefs/higgs/songwz/summer24/CEPCSW_301/CEPCSW/workArea/scale.root", "position of energy correction file"};
+    
+    TFile* file;
+    TTree* treePhi;
+    TTree* treeTheta; 
+    double _phiAngle, _phiScale, _thetaAngle, _thetaScale;
+    std::vector<double> phiAngle, phiScale, thetaAngle, thetaScale; 
+      
+};
+
+#endif
diff --git a/Service/TrackSystemSvc/src/MarlinTrkUtils.cc b/Service/TrackSystemSvc/src/MarlinTrkUtils.cc
index bf2ab05d..2a4c9d8a 100644
--- a/Service/TrackSystemSvc/src/MarlinTrkUtils.cc
+++ b/Service/TrackSystemSvc/src/MarlinTrkUtils.cc
@@ -212,7 +212,16 @@ namespace MarlinTrk {
     ///////////////////////////////////////////////////////
     // add hits to IMarlinTrk  
     ///////////////////////////////////////////////////////
-    
+    std::vector<edm4hep::TrackerHit> hit_list_copy;
+    if (fit_backwards) {
+      std::reverse_copy(hit_list.begin(), hit_list.end(), std::back_inserter(hit_list_copy));
+    }
+    else {
+      hit_list_copy.reserve(hit_list.size());
+      hit_list_copy.insert(hit_list_copy.end(), hit_list.begin(), hit_list.end());
+    }
+
+    hit_list_copy.swap(hit_list);
     std::vector<edm4hep::TrackerHit>::iterator it = hit_list.begin();
     
     //  start by trying to add the hits to the track we want to finally use. 
@@ -262,9 +271,8 @@ namespace MarlinTrk {
       //std::cout << "ERROR<<<<<MarlinTrk::createFit : Cannot fit less with less than " << MIN_NDF << " degrees of freedom. Number of hits =  " << added_hits.size() << " ndof = " << ndof_added << std::endl;
       return IMarlinTrack::bad_intputs;
     }
-      
-    
-    
+
+    hit_list_copy.swap(hit_list);
     ///////////////////////////////////////////////////////
     // set the initial track parameters  
     ///////////////////////////////////////////////////////
@@ -347,7 +355,6 @@ namespace MarlinTrk {
       helixTrack.moveRefPoint(hit_list.front().getPosition()[0], hit_list.front().getPosition()[1], hit_list.front().getPosition()[2]);
     }
     
-    
     const float referencePoint[3] = { helixTrack.getRefPointX() , helixTrack.getRefPointY() , helixTrack.getRefPointZ() };
     
     pre_fit->D0 = helixTrack.getD0();
diff --git a/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.cpp b/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.cpp
index 9324df6e..7ac6e5bd 100644
--- a/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.cpp
@@ -113,7 +113,6 @@ Edm4hepWriterAnaElemTool::EndOfEventAction(const G4Event* anEvent) {
     auto sitcols = m_SITCol.createAndPut();
     auto tpccols = m_TPCCol.createAndPut();
     auto setcols = m_SETCol.createAndPut();
-    auto otkendcapcols = m_OTKEndCapCol.createAndPut();
 
     auto ecalbarrelcol            = m_EcalBarrelCol.createAndPut();
     auto ecalbarrelcontribcols    = m_EcalBarrelContributionCol.createAndPut();
@@ -189,8 +188,6 @@ Edm4hepWriterAnaElemTool::EndOfEventAction(const G4Event* anEvent) {
             tracker_col_ptr = setcols;
         } else if (collect->GetName() == "SETCollection") {
             tracker_col_ptr = setcols;
-        } else if (collect->GetName() == "OTKEndCapCollection") {
-            tracker_col_ptr = otkendcapcols;
         } else if (collect->GetName() == "CaloHitsCollection") {
             calo_col_ptr = calorimetercols;
             calo_contrib_col_ptr = calocontribcols;
diff --git a/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.h b/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.h
index 69798225..d3898994 100644
--- a/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.h
+++ b/Simulation/DetSimAna/src/Edm4hepWriterAnaElemTool.h
@@ -70,7 +70,9 @@ private:
             Gaudi::DataHandle::Writer, this};
     DataHandle<edm4hep::SimTrackerHitCollection> m_TPCCol{"TPCCollection", 
             Gaudi::DataHandle::Writer, this};
-    DataHandle<edm4hep::SimTrackerHitCollection> m_SETCol{"SETCollection", 
+    DataHandle<edm4hep::SimTrackerHitCollection> m_SETCol{"SETCollection",
+            Gaudi::DataHandle::Writer, this};
+    DataHandle<edm4hep::SimTrackerHitCollection> m_OTKBarrelCol{"OTKBarrelCollection",
             Gaudi::DataHandle::Writer, this};
     DataHandle<edm4hep::SimTrackerHitCollection> m_OTKEndCapCol{"OTKEndCapCollection",
             Gaudi::DataHandle::Writer, this};
diff --git a/Simulation/DetSimAna/src/ExampleAnaElemTool.cpp b/Simulation/DetSimAna/src/ExampleAnaElemTool.cpp
index 0d956651..e85666b8 100644
--- a/Simulation/DetSimAna/src/ExampleAnaElemTool.cpp
+++ b/Simulation/DetSimAna/src/ExampleAnaElemTool.cpp
@@ -44,6 +44,7 @@ ExampleAnaElemTool::EndOfEventAction(const G4Event* anEvent) {
     auto sitcols = m_SITCol.createAndPut();
     auto tpccols = m_TPCCol.createAndPut();
     auto setcols = m_SETCol.createAndPut();
+    auto otkbarrelcols = m_OTKbarrelCol.createAndPut();
     auto otkendcapcols = m_OTKEndCapCol.createAndPut();
 
     // readout defined in DD4hep
@@ -93,6 +94,8 @@ ExampleAnaElemTool::EndOfEventAction(const G4Event* anEvent) {
             tracker_col_ptr = setcols;
         } else if (collect->GetName() == "SETCollection") {
             tracker_col_ptr = setcols;
+        } else if (collect->GetName() == "OTKBarrelCollection") {
+            tracker_col_ptr = otkbarrelcols;
         } else if (collect->GetName() == "OTKEndCapCollection") {
             tracker_col_ptr = otkendcapcols;
         } else if (collect->GetName() == "CaloHitsCollection") {
diff --git a/Simulation/DetSimAna/src/ExampleAnaElemTool.h b/Simulation/DetSimAna/src/ExampleAnaElemTool.h
index a0d9bcaf..68c2ac65 100644
--- a/Simulation/DetSimAna/src/ExampleAnaElemTool.h
+++ b/Simulation/DetSimAna/src/ExampleAnaElemTool.h
@@ -61,6 +61,8 @@ private:
             Gaudi::DataHandle::Writer, this};
     DataHandle<plcio::SimTrackerHitCollection> m_SETCol{"SETCollection", 
             Gaudi::DataHandle::Writer, this};
+    DataHandle<plcio::SimTrackerHitCollection> m_OTKBarrelCol{"OTKBarrelCollection",
+            Gaudi::DataHandle::Writer, this};
     DataHandle<plcio::SimTrackerHitCollection> m_OTKEndCapCol{"OTKEndCapCollection",
             Gaudi::DataHandle::Writer, this};
 
-- 
GitLab