From 36b3be875dad9a9199b08a8b52a1e9d872aad1b7 Mon Sep 17 00:00:00 2001
From: Fang Wenxing <wxfang@lxslc609.ihep.ac.cn>
Date: Sat, 6 Jun 2020 13:21:25 +0800
Subject: [PATCH] update

---
 Examples/options/LCIO_read_pan.py             |  115 +
 Examples/options/tut_detsim_pan_matrix.py     |  217 ++
 Reconstruction/Digi_Calo/CMakeLists.txt       |   28 +
 Reconstruction/Digi_Calo/src/CaloDigiAlg.cpp  |  151 ++
 .../Digi_Calo/src/CaloDigiAlg.cpp_id          |   91 +
 .../Digi_Calo/src/CaloDigiAlg.cpp_v1          |  131 ++
 Reconstruction/Digi_Calo/src/CaloDigiAlg.h    |   88 +
 .../CED/.cepcenv/01-09-01/status/install.yml  |   20 +
 Reconstruction/PFA/Pandora/CED/CED/ced.cc     |  382 ++++
 Reconstruction/PFA/Pandora/CED/CED/ced.h      |  146 ++
 Reconstruction/PFA/Pandora/CED/CED/ced_cli.cc |  539 +++++
 Reconstruction/PFA/Pandora/CED/CED/ced_cli.h  |  432 ++++
 .../PFA/Pandora/CED/CED/ced_config.h          |  204 ++
 .../PFA/Pandora/GaudiPandora/CMakeLists.txt   |   51 +
 .../GaudiPandora/include/CaloHitCreator.h     |  282 +++
 .../GaudiPandora/include/GeometryCreator.h    |  158 ++
 .../GaudiPandora/include/MCParticleCreator.h  |  100 +
 .../GaudiPandora/include/PandoraPFAlg.h       |  320 +++
 .../Pandora/GaudiPandora/include/PfoCreator.h |  221 ++
 .../GaudiPandora/include/TrackCreator.h       |  378 ++++
 .../Pandora/GaudiPandora/include/Utility.h    |    6 +
 .../GaudiPandora/include/cellIDDecoder.h      |  127 ++
 .../GaudiPandora/src/CaloHitCreator.cpp       |  867 +++++++
 .../GaudiPandora/src/GeometryCreator.cpp      |  433 ++++
 .../GaudiPandora/src/MCParticleCreator.cpp    |  378 ++++
 .../Pandora/GaudiPandora/src/PandoraPFAlg.cpp |  910 ++++++++
 .../Pandora/GaudiPandora/src/PfoCreator.cpp   |  429 ++++
 .../Pandora/GaudiPandora/src/TrackCreator.cpp |  992 ++++++++
 .../PFA/Pandora/GaudiPandora/src/Utility.cpp  |    7 +
 .../MarlinUtil/01-08/source/ClusterShapes.cc  | 2005 +++++++++++++++++
 .../MarlinUtil/01-08/source/ClusterShapes.h   |  356 +++
 .../MarlinUtil/01-08/source/HelixClass.cc     |  768 +++++++
 .../MarlinUtil/01-08/source/HelixClass.h      |  302 +++
 .../MarlinUtil/01-08/source/LineClass.cc      |   73 +
 .../MarlinUtil/01-08/source/LineClass.h       |   32 +
 .../PFA/Pandora/MatrixPandora/CMakeLists.txt  |   57 +
 .../MatrixPandora/include/CaloHitCreator.h    |  298 +++
 .../MatrixPandora/include/GeometryCreator.h   |  166 ++
 .../MatrixPandora/include/MCParticleCreator.h |   98 +
 .../MatrixPandora/include/PandoraMatrixAlg.h  |  324 +++
 .../MatrixPandora/include/PfoCreator.h        |  219 ++
 .../MatrixPandora/include/TrackCreator.h      |  373 +++
 .../Pandora/MatrixPandora/include/Utility.h   |    6 +
 .../MatrixPandora/include/cellIDDecoder.h     |  127 ++
 .../MatrixPandora/src/CaloHitCreator.cpp      |  959 ++++++++
 .../MatrixPandora/src/GeometryCreator.cpp     |  488 ++++
 .../MatrixPandora/src/MCParticleCreator.cpp   |  372 +++
 .../MatrixPandora/src/PandoraMatrixAlg.cpp    |  932 ++++++++
 .../Pandora/MatrixPandora/src/PfoCreator.cpp  |  429 ++++
 .../MatrixPandora/src/TrackCreator.cpp        |  987 ++++++++
 .../PFA/Pandora/MatrixPandora/src/Utility.cpp |    7 +
 51 files changed, 17581 insertions(+)
 create mode 100644 Examples/options/LCIO_read_pan.py
 create mode 100644 Examples/options/tut_detsim_pan_matrix.py
 create mode 100644 Reconstruction/Digi_Calo/CMakeLists.txt
 create mode 100644 Reconstruction/Digi_Calo/src/CaloDigiAlg.cpp
 create mode 100644 Reconstruction/Digi_Calo/src/CaloDigiAlg.cpp_id
 create mode 100644 Reconstruction/Digi_Calo/src/CaloDigiAlg.cpp_v1
 create mode 100644 Reconstruction/Digi_Calo/src/CaloDigiAlg.h
 create mode 100644 Reconstruction/PFA/Pandora/CED/.cepcenv/01-09-01/status/install.yml
 create mode 100644 Reconstruction/PFA/Pandora/CED/CED/ced.cc
 create mode 100644 Reconstruction/PFA/Pandora/CED/CED/ced.h
 create mode 100644 Reconstruction/PFA/Pandora/CED/CED/ced_cli.cc
 create mode 100644 Reconstruction/PFA/Pandora/CED/CED/ced_cli.h
 create mode 100644 Reconstruction/PFA/Pandora/CED/CED/ced_config.h
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/CMakeLists.txt
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/include/CaloHitCreator.h
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/include/GeometryCreator.h
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/include/MCParticleCreator.h
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/include/PandoraPFAlg.h
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/include/PfoCreator.h
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/include/TrackCreator.h
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/include/Utility.h
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/include/cellIDDecoder.h
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/src/CaloHitCreator.cpp
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/src/GeometryCreator.cpp
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/src/MCParticleCreator.cpp
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/src/PandoraPFAlg.cpp
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/src/PfoCreator.cpp
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/src/TrackCreator.cpp
 create mode 100644 Reconstruction/PFA/Pandora/GaudiPandora/src/Utility.cpp
 create mode 100644 Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/ClusterShapes.cc
 create mode 100644 Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/ClusterShapes.h
 create mode 100644 Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/HelixClass.cc
 create mode 100644 Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/HelixClass.h
 create mode 100644 Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/LineClass.cc
 create mode 100644 Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/LineClass.h
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/CMakeLists.txt
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/include/CaloHitCreator.h
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/include/GeometryCreator.h
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/include/MCParticleCreator.h
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/include/PandoraMatrixAlg.h
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/include/PfoCreator.h
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/include/TrackCreator.h
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/include/Utility.h
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/include/cellIDDecoder.h
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/src/CaloHitCreator.cpp
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/src/GeometryCreator.cpp
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/src/MCParticleCreator.cpp
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/src/PandoraMatrixAlg.cpp
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/src/PfoCreator.cpp
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/src/TrackCreator.cpp
 create mode 100644 Reconstruction/PFA/Pandora/MatrixPandora/src/Utility.cpp

diff --git a/Examples/options/LCIO_read_pan.py b/Examples/options/LCIO_read_pan.py
new file mode 100644
index 00000000..97b5f9e9
--- /dev/null
+++ b/Examples/options/LCIO_read_pan.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+
+from Gaudi.Configuration import *
+from Configurables import K4DataSvc
+dsvc = K4DataSvc("EventDataSvc")
+
+# read LCIO files
+from Configurables import LCIOInput
+read = LCIOInput("read")
+read.inputs = [
+#"/cefs/data/FullSim/CEPC240/CEPC_v4/higgs/smart_final_states/E240.Pffh_invi.e0.p0.whizard195//ffh_inv.e0.p0.00001_1000_sim.slcio"
+#"/junofs/users/wxfang/CEPC/CEPCOFF/doReco/reco_output/nnh_aa.e0.p0.00010_000000_rec.slcio"
+"/cefs/higgs/wxfang/cepc/Pandora/CaloDigi/gamma/Digi_sim_0.slcio"
+]
+read.collections = {
+        #"COILCollection" : "SimTrackerHit",
+        #"EcalBarrelSiliconCollection" : "SimCalorimeterHit",
+        "MCParticle" : "MCParticle",
+        "ECALBarrel" : "CalorimeterHit",
+        "ECALEndcap" : "CalorimeterHit",
+        "ECALOther"  : "CalorimeterHit",
+        "HCALBarrel" : "CalorimeterHit",
+        "HCALEndcap" : "CalorimeterHit",
+        "HCALOther"  : "CalorimeterHit",
+        "MUON"       : "CalorimeterHit",
+        "LCAL"       : "CalorimeterHit",
+        "LHCAL"      : "CalorimeterHit",
+        "BCAL"       : "CalorimeterHit",
+        #"MarlinTrkTracks" : "Track"
+        #"TPCCollection" : "SimTrackerHit",
+        #"VXDCollection" : "SimTrackerHit"
+}
+##############################################################################
+from Configurables import GearSvc
+gearSvc  = GearSvc("GearSvc")
+gearSvc.GearXMLFile = "/junofs/users/wxfang/CEPC/CEPCOFF/doSim/fullDet/GearOutput.xml"
+##############################################################################
+from Configurables import PandoraPFAlg
+
+pandoralg = PandoraPFAlg("PandoraPFAlg")
+## KEEP same with lcioinput name for the ReadXXX ###########
+pandoralg.ReadMCParticle                       = "MCParticle"                   
+pandoralg.ReadECALBarrel                       = "ECALBarrel"                   
+pandoralg.ReadECALEndcap                       = "ECALEndcap"                   
+pandoralg.ReadECALOther                        = "ECALOther"                    
+pandoralg.ReadHCALBarrel                       = "HCALBarrel"                   
+pandoralg.ReadHCALEndcap                       = "HCALEndcap"                   
+pandoralg.ReadHCALOther                        = "HCALOther"                    
+pandoralg.ReadMUON                             = "MUON"                         
+pandoralg.ReadLCAL                             = "LCAL"                         
+pandoralg.ReadLHCAL                            = "LHCAL"                        
+pandoralg.ReadBCAL                             = "BCAL"                         
+pandoralg.ReadKinkVertices                     = "KinkVertices"                 
+pandoralg.ReadProngVertices                    = "ProngVertices"                
+pandoralg.ReadSplitVertices                    = "SplitVertices"                
+pandoralg.ReadV0Vertices                       = "V0Vertices"                   
+pandoralg.ReadTracks                           = "MarlinTrkTracks"                       
+pandoralg.WriteClusterCollection               = "PandoraClusters"              
+pandoralg.WriteReconstructedParticleCollection = "PandoraPFOs" 
+pandoralg.WriteVertexCollection                = "PandoraPFANewStartVertices"               
+pandoralg.AnaOutput = "/cefs/higgs/wxfang/cepc/Pandora/Ana/gamma/Ana_gamma_test.root"
+
+pandoralg.PandoraSettingsDefault_xml = "/junofs/users/wxfang/MyGit/MarlinPandora/scripts/PandoraSettingsDefault_wx.xml"
+#### Do not chage the collection name, only add or delete ###############
+pandoralg.TrackCollections      =  ["MarlinTrkTracks"]
+pandoralg.ECalCaloHitCollections=  ["ECALBarrel", "ECALEndcap", "ECALOther"]
+pandoralg.HCalCaloHitCollections=  ["HCALBarrel", "HCALEndcap", "HCALOther"]
+pandoralg.LCalCaloHitCollections=  ["LCAL"]
+pandoralg.LHCalCaloHitCollections= ["LHCAL"]
+pandoralg.MuonCaloHitCollections=  ["MUON"]
+pandoralg.MCParticleCollections =  ["MCParticle"]
+pandoralg.RelCaloHitCollections =  ["RecoCaloAssociation_ECALBarrel", "RecoCaloAssociation_ECALEndcap", "RecoCaloAssociation_ECALOther", "RecoCaloAssociation_HCALBarrel", "RecoCaloAssociation_HCALEndcap", "RecoCaloAssociation_HCALOther", "RecoCaloAssociation_LCAL", "RecoCaloAssociation_LHCAL", "RecoCaloAssociation_MUON"]
+pandoralg.RelTrackCollections   =  ["MarlinTrkTracksMCTruthLink"]
+pandoralg.KinkVertexCollections =  ["KinkVertices"]
+pandoralg.ProngVertexCollections=  ["ProngVertices"]
+pandoralg.SplitVertexCollections=  ["SplitVertices"]
+pandoralg.V0VertexCollections   =  ["V0Vertices"]
+pandoralg.ECalToMipCalibration  = 160.0 
+pandoralg.HCalToMipCalibration  = 34.8 
+pandoralg.ECalMipThreshold      = 0.5 
+pandoralg.HCalMipThreshold      = 0.3 
+pandoralg.ECalToEMGeVCalibration= 0.9 #for G2CD Digi, 1.007 for NewLDCaloDigi 
+pandoralg.HCalToEMGeVCalibration= 1.007 
+pandoralg.ECalToHadGeVCalibrationBarrel= 1.12 #very small effect 
+pandoralg.ECalToHadGeVCalibrationEndCap= 1.12 
+pandoralg.HCalToHadGeVCalibration= 1.07
+pandoralg.MuonToMipCalibration= 10.0 
+pandoralg.DigitalMuonHits= 0 
+pandoralg.MaxHCalHitHadronicEnergy   = 1.0 
+pandoralg.UseOldTrackStateCalculation= 0 
+pandoralg.AbsorberRadLengthECal= 0.2854 
+pandoralg.AbsorberIntLengthECal= 0.0101 
+pandoralg.AbsorberRadLengthHCal= 0.0569 
+pandoralg.AbsorberIntLengthHCal= 0.006  
+pandoralg.AbsorberRadLengthOther= 0.0569
+pandoralg.AbsorberIntLengthOther= 0.006 
+
+##############################################################################
+
+# write PODIO file
+from Configurables import PodioOutput
+write = PodioOutput("write")
+write.filename = "test.root"
+write.outputCommands = ["keep *"]
+
+# ApplicationMgr
+from Configurables import ApplicationMgr
+ApplicationMgr(
+        #TopAlg = [read, pandoralg, write],
+        TopAlg = [read, pandoralg],
+        EvtSel = 'NONE',
+        EvtMax = 10,
+        ExtSvc = [dsvc, gearSvc],
+        OutputLevel=INFO
+)
diff --git a/Examples/options/tut_detsim_pan_matrix.py b/Examples/options/tut_detsim_pan_matrix.py
new file mode 100644
index 00000000..50fdf5ba
--- /dev/null
+++ b/Examples/options/tut_detsim_pan_matrix.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python
+
+import os
+print(os.environ["DD4HEP_LIBRARY_PATH"])
+import sys
+# sys.exit(0)
+
+from Gaudi.Configuration import *
+
+##############################################################################
+# Random Number Svc
+##############################################################################
+from Configurables import RndmGenSvc, HepRndm__Engine_CLHEP__RanluxEngine_
+
+# rndmengine = HepRndm__Engine_CLHEP__RanluxEngine_() # The default engine in Gaudi
+rndmengine = HepRndm__Engine_CLHEP__HepJamesRandom_() # The default engine in Geant4
+rndmengine.SetSingleton = True
+rndmengine.Seeds = [42]
+
+# rndmgensvc = RndmGenSvc("RndmGenSvc")
+# rndmgensvc.Engine = rndmengine.name()
+
+
+##############################################################################
+# Event Data Svc
+##############################################################################
+#from Configurables import CEPCDataSvc
+#dsvc = CEPCDataSvc("EventDataSvc")
+from Configurables import K4DataSvc
+dsvc = K4DataSvc("EventDataSvc")
+
+
+##############################################################################
+# Geometry Svc
+##############################################################################
+
+# geometry_option = "CepC_v4-onlyTracker.xml"
+geometry_option = "CepC_v4-onlyVXD.xml"
+
+if not os.getenv("DETCEPCV4ROOT"):
+    print("Can't find the geometry. Please setup envvar DETCEPCV4ROOT." )
+    sys.exit(-1)
+
+geometry_path = os.path.join(os.getenv("DETCEPCV4ROOT"), "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 GeoSvc
+geosvc = GeoSvc("GeoSvc")
+#geosvc.compact = geometry_path
+geosvc.compact = "../Detector/DetEcalMatrix/compact/det.xml"
+
+##############################################################################
+# 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.Particles = ["gamma"]
+#gun.Energies =  [0.5, 1] # GeV
+#gun.EnergyMin = [0.1] # GeV
+gun.EnergyMin = [1] # GeV
+gun.EnergyMax = [1] # GeV
+gun.ThetaMins = [90] # rad; 45deg
+gun.ThetaMaxs = [90] # rad; 45deg
+gun.PhiMins   = [0] # rad; 0deg
+gun.PhiMaxs   = [0] # rad; 360deg
+
+stdheprdr = StdHepRdr("StdHepRdr")
+#stdheprdr.Input = "/cefs/data/stdhep/CEPC250/2fermions/E250.Pbhabha.e0.p0.whizard195/bhabha.e0.p0.00001.stdhep"
+#stdheprdr.Input = "/cefs/data/stdhep/CEPC250/2fermions/E250.Pbhabha.e0.p0.whizard195/bhabha.e0.p0.00001.stdhep"
+stdheprdr.Input = "/cefs/data/stdhep/CEPC250/higgs/E250.Pbbh.whizard195/E250.Pbbh_X.e0.p0.whizard195/Pbbh_X.e0.p0.00001.stdhep"
+
+# 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 = ["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 ExampleAnaElemTool
+# example_anatool = ExampleAnaElemTool("ExampleAnaElemTool")
+
+from Configurables import DetSimAlg
+
+detsimalg = DetSimAlg("DetSimAlg")
+
+# detsimalg.VisMacs = ["vis.mac"]
+
+detsimalg.RunCmds = [
+#    "/tracking/verbose 1",
+]
+detsimalg.AnaElems = [
+    # example_anatool.name()
+    # "ExampleAnaElemTool"
+    "Edm4hepWriterAnaElemTool"
+]
+detsimalg.RootDetElem = "WorldDetElemTool"
+
+from Configurables import AnExampleDetElemTool
+example_dettool = AnExampleDetElemTool("AnExampleDetElemTool")
+
+##############################################################################
+from Configurables import CaloDigiAlg
+example_CaloDigiAlg = CaloDigiAlg("CaloDigiAlg")
+example_CaloDigiAlg.Scale = 1
+example_CaloDigiAlg.SimCaloHitCollection = "SimCalorimeterCol"
+example_CaloDigiAlg.CaloHitCollection    = "ECALBarrel"
+##############################################################################
+from Configurables import GearSvc
+gearSvc  = GearSvc("GearSvc")
+gearSvc.GearXMLFile = "/junofs/users/wxfang/CEPC/CEPCOFF/doSim/fullDet/GearOutput.xml"
+##############################################################################
+#from Configurables import PandoraPFAlg
+from Configurables import PandoraMatrixAlg
+
+#pandoralg = PandoraPFAlg("PandoraPFAlg")
+pandoralg = PandoraMatrixAlg("PandoraMatrixAlg")
+## KEEP same with lcioinput name for the ReadXXX ###########
+pandoralg.ReadMCParticle                       = "MCParticle"                   
+pandoralg.ReadECALBarrel                       = "ECALBarrel"                   
+pandoralg.ReadECALEndcap                       = "ECALEndcap"                   
+pandoralg.ReadECALOther                        = "ECALOther"                    
+pandoralg.ReadHCALBarrel                       = "HCALBarrel"                   
+pandoralg.ReadHCALEndcap                       = "HCALEndcap"                   
+pandoralg.ReadHCALOther                        = "HCALOther"                    
+pandoralg.ReadMUON                             = "MUON"                         
+pandoralg.ReadLCAL                             = "LCAL"                         
+pandoralg.ReadLHCAL                            = "LHCAL"                        
+pandoralg.ReadBCAL                             = "BCAL"                         
+pandoralg.ReadKinkVertices                     = "KinkVertices"                 
+pandoralg.ReadProngVertices                    = "ProngVertices"                
+pandoralg.ReadSplitVertices                    = "SplitVertices"                
+pandoralg.ReadV0Vertices                       = "V0Vertices"                   
+pandoralg.ReadTracks                           = "MarlinTrkTracks"                       
+pandoralg.WriteClusterCollection               = "PandoraClusters"              
+pandoralg.WriteReconstructedParticleCollection = "PandoraPFOs" 
+pandoralg.WriteVertexCollection                = "PandoraPFANewStartVertices"               
+pandoralg.AnaOutput = "/cefs/higgs/wxfang/cepc/Pandora/Ana/gamma/Ana_gamma_Matrix_NewTest.root"
+
+pandoralg.PandoraSettingsDefault_xml = "/junofs/users/wxfang/MyGit/MarlinPandora/scripts/PandoraSettingsDefault_wx.xml"
+#### Do not chage the collection name, only add or delete ###############
+pandoralg.TrackCollections      =  ["MarlinTrkTracks"]
+pandoralg.ECalCaloHitCollections=  ["ECALBarrel", "ECALEndcap", "ECALOther"]
+pandoralg.HCalCaloHitCollections=  ["HCALBarrel", "HCALEndcap", "HCALOther"]
+pandoralg.LCalCaloHitCollections=  ["LCAL"]
+pandoralg.LHCalCaloHitCollections= ["LHCAL"]
+pandoralg.MuonCaloHitCollections=  ["MUON"]
+pandoralg.MCParticleCollections =  ["MCParticle"]
+pandoralg.RelCaloHitCollections =  ["RecoCaloAssociation_ECALBarrel", "RecoCaloAssociation_ECALEndcap", "RecoCaloAssociation_ECALOther", "RecoCaloAssociation_HCALBarrel", "RecoCaloAssociation_HCALEndcap", "RecoCaloAssociation_HCALOther", "RecoCaloAssociation_LCAL", "RecoCaloAssociation_LHCAL", "RecoCaloAssociation_MUON"]
+pandoralg.RelTrackCollections   =  ["MarlinTrkTracksMCTruthLink"]
+pandoralg.KinkVertexCollections =  ["KinkVertices"]
+pandoralg.ProngVertexCollections=  ["ProngVertices"]
+pandoralg.SplitVertexCollections=  ["SplitVertices"]
+pandoralg.V0VertexCollections   =  ["V0Vertices"]
+pandoralg.ECalToMipCalibration  = 112 #1000MeV/8.918 #CEPC :160.0 
+pandoralg.HCalToMipCalibration  = 34.8 
+pandoralg.ECalMipThreshold      = 0.225# 8.918*0.225=2.00655 #CEPC 0.5 
+pandoralg.HCalMipThreshold      = 0.3 
+pandoralg.ECalToEMGeVCalibration= 1.# BGO, to be tuned  # CEPC: 0.9 for G2CD Digi, 1.007 for NewLDCaloDigi 
+pandoralg.HCalToEMGeVCalibration= 1.007 
+pandoralg.ECalToHadGeVCalibrationBarrel= 1.12 #very small effect 
+pandoralg.ECalToHadGeVCalibrationEndCap= 1.12 
+pandoralg.HCalToHadGeVCalibration= 1.07
+pandoralg.MuonToMipCalibration= 10.0 
+pandoralg.DigitalMuonHits= 0 
+pandoralg.MaxHCalHitHadronicEnergy   = 1.0 
+pandoralg.UseOldTrackStateCalculation= 0 
+pandoralg.AbsorberRadLengthECal= 0.08945 #BG0: 1/11.18 mm , CEPC: 0.2854 = 1/3.504 mm 
+pandoralg.AbsorberIntLengthECal= 0.00448 #BG0: 1/223.2 mm , CEPC: 0.0101 = 1/99.46 mm 
+pandoralg.AbsorberRadLengthHCal= 0.0569 
+pandoralg.AbsorberIntLengthHCal= 0.006  
+pandoralg.AbsorberRadLengthOther= 0.0569
+pandoralg.AbsorberIntLengthOther= 0.006 
+
+##############################################################################
+
+# write PODIO file
+from Configurables import PodioOutput
+write = PodioOutput("write")
+write.filename = "test.root"
+write.outputCommands = ["keep *"]
+
+# ApplicationMgr
+from Configurables import ApplicationMgr
+ApplicationMgr(
+        TopAlg = [genalg, detsimalg, example_CaloDigiAlg, pandoralg],
+        #TopAlg = [genalg, detsimalg],
+        #TopAlg = [genalg, detsimalg,  write],
+        #TopAlg = [read, pandoralg],
+        EvtSel = 'NONE',
+        EvtMax = 100,
+        #ExtSvc = [rndmengine, dsvc, geosvc, gearSvc],
+        ExtSvc = [rndmengine, dsvc, geosvc, gearSvc,detsimsvc],
+        OutputLevel=INFO
+)
diff --git a/Reconstruction/Digi_Calo/CMakeLists.txt b/Reconstruction/Digi_Calo/CMakeLists.txt
new file mode 100644
index 00000000..5bec00f6
--- /dev/null
+++ b/Reconstruction/Digi_Calo/CMakeLists.txt
@@ -0,0 +1,28 @@
+gaudi_subdir(Digi_Calo v0r0)
+
+find_package(DD4hep COMPONENTS DDG4 REQUIRED)
+find_package(EDM4HEP REQUIRED )
+message("EDM4HEP_INCLUDE_DIRS: ${EDM4HEP_INCLUDE_DIR}")
+message("EDM4HEP_LIB: ${EDM4HEP_LIBRARIES}")
+include_directories(${EDM4HEP_INCLUDE_DIR})
+link_libraries("/cvmfs/cepcsw.ihep.ac.cn/prototype/releases/externals/97.0.0/EDM4hep/lib64/libedm4hep.so")
+
+find_package(CLHEP REQUIRED)
+find_package(podio REQUIRED )
+
+set(srcs
+    src/*.cpp
+)
+
+gaudi_depends_on_subdirs(
+    Detector/DetInterface
+)
+## Modules
+gaudi_add_module(Digi_Calo ${srcs}
+    INCLUDE_DIRS FWCore GaudiKernel GaudiAlgLib CLHEP DD4hep 
+    LINK_LIBRARIES FWCore GaudiKernel GaudiAlgLib CLHEP DD4hep ${DD4hep_COMPONENT_LIBRARIES} DDRec
+)
+#gaudi_add_module(Digi_Calo ${srcs}
+#    INCLUDE_DIRS FWCore GaudiKernel GaudiAlgLib CLHEP  
+#    LINK_LIBRARIES FWCore GaudiKernel GaudiAlgLib CLHEP
+#)
diff --git a/Reconstruction/Digi_Calo/src/CaloDigiAlg.cpp b/Reconstruction/Digi_Calo/src/CaloDigiAlg.cpp
new file mode 100644
index 00000000..1370dda8
--- /dev/null
+++ b/Reconstruction/Digi_Calo/src/CaloDigiAlg.cpp
@@ -0,0 +1,151 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+#include "CaloDigiAlg.h"
+
+
+#include "edm4hep/SimCalorimeterHit.h"
+#include "edm4hep/CalorimeterHit.h"
+#include "edm4hep/Vector3f.h"
+
+#include "DD4hep/Detector.h"
+#include <DD4hep/Objects.h>
+#include <DDRec/DetectorData.h>
+#include <DDRec/CellIDPositionConverter.h>
+
+
+#include <math.h>
+#include <cmath>
+#include <algorithm>
+
+DECLARE_COMPONENT( CaloDigiAlg )
+
+CaloDigiAlg::CaloDigiAlg(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");
+  
+   
+}
+
+StatusCode CaloDigiAlg::initialize()
+{
+
+  std::cout<<"CaloDigiAlg::m_scale="<<m_scale<<std::endl;
+  m_geosvc = service<IGeoSvc>("GeoSvc");
+  if ( !m_geosvc )  throw "CaloDigiAlg :Failed to find GeoSvc ...";
+  dd4hep::Detector* m_dd4hep = m_geosvc->lcdd();
+  if ( !m_dd4hep )  throw "CaloDigiAlg :Failed to get dd4hep::Detector ...";
+  m_cellIDConverter = new dd4hep::rec::CellIDPositionConverter(*m_dd4hep);
+  try{
+      const dd4hep::DetElement &detElement = m_dd4hep->detector("CaloDetector");
+      dd4hep::rec::LayeredCalorimeterData* Data = detElement.extension<dd4hep::rec::LayeredCalorimeterData>() ;
+      const std::vector<dd4hep::rec::LayeredCalorimeterData::Layer>& layerLayout = Data->layers;
+      assert(layerLayout.size()>=1);
+      m_layerLayout = &layerLayout;
+      std::cout<<"saved m_layerLayout"<<std::endl;
+  }
+  catch(...) 
+  {
+      throw "CaloDigiAlg :Failed to get LayeredCalorimeterData ...";
+  }
+  return GaudiAlgorithm::initialize();
+}
+
+StatusCode CaloDigiAlg::execute()
+{
+  std::map<std::string, std::vector<edm4hep::SimCalorimeterHit> > id_vhit_map;
+  edm4hep::CalorimeterHitCollection* caloVec   = w_DigiCaloCol.createAndPut();
+  const edm4hep::SimCalorimeterHitCollection* SimHitCol =  r_SimCaloCol.get();
+  if(SimHitCol == 0) 
+  {
+     std::cout<<"not found SimCalorimeterHitCollection"<< std::endl;
+     return StatusCode::SUCCESS;
+  }
+  std::cout<<"digi, input sim hit size="<< SimHitCol->size() <<std::endl;
+  float tot_sim_en = 0;
+  for( int i = 0; i < SimHitCol->size(); i++ ) 
+  {
+      edm4hep::SimCalorimeterHit SimHit = SimHitCol->at(i);
+      float en = SimHit.getEnergy();
+      tot_sim_en += en;
+      std::string id = GetCode(&SimHit);
+      //std::cout<<"sim i ="<< i<<",id="<< id<<",x="<<SimHit.getPosition().x<<",y="<<SimHit.getPosition().y<<",z="<<SimHit.getPosition().z <<std::endl;
+      if(id_vhit_map.find(id) != id_vhit_map.end()) id_vhit_map[id].push_back(SimHit);
+      else
+      {
+          std::vector<edm4hep::SimCalorimeterHit> vect;
+          vect.push_back(SimHit);
+          id_vhit_map[id] = vect;
+      }
+  }
+  float tot_en = 0;
+  for(std::map<std::string, std::vector <edm4hep::SimCalorimeterHit> >::iterator iter = id_vhit_map.begin(); iter != id_vhit_map.end(); iter++)
+  {
+    float energy = 0;
+    float x0 = 0;
+    float y0 = 0;
+    float z0 = 0;
+    for(int j=0; j < iter->second.size(); j++)
+    {
+        float hit_en = iter->second.at(j).getEnergy();
+        energy += hit_en;
+        x0     += hit_en*iter->second.at(j).getPosition().x ;
+        y0     += hit_en*iter->second.at(j).getPosition().y ;
+        z0     += hit_en*iter->second.at(j).getPosition().z ;
+    }
+    x0 = x0/energy;
+    y0 = y0/energy;
+    z0 = z0/energy;
+    edm4hep::Vector3f pos(x0, y0, z0);
+    energy = energy*m_scale;
+    auto caloHit = caloVec->create();
+    //caloHit.setCellID  (iter->second.getCellID());
+    caloHit.setEnergy  (energy);
+    caloHit.setPosition(pos);
+    tot_en += caloHit.getEnergy();
+  }
+    
+  std::cout<<"total sim e ="<< tot_sim_en <<std::endl;
+  std::cout<<"digi, output digi hit size="<< caloVec->size()<<",tot_en="<<tot_en <<std::endl;
+  _nEvt ++ ;
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode CaloDigiAlg::finalize()
+{
+  info() << "Processed " << _nEvt << " events " << endmsg;
+  return GaudiAlgorithm::finalize();
+}
+
+
+std::string CaloDigiAlg::GetCode(const edm4hep::SimCalorimeterHit *const pHit) const
+{
+    int layer = -1 ;   
+    for (unsigned int i = 0, iMax = m_layerLayout->size(); i < iMax; ++i)
+    {
+        const float distance(m_layerLayout->at(i).distance);
+        const float sensitive_thickness(m_layerLayout->at(i).sensitive_thickness);
+        if( (distance - 0.5*sensitive_thickness) <= pHit->getPosition().x && pHit->getPosition().x <= (distance + 0.5*sensitive_thickness)) {layer = i ; break;}
+    }
+    if(layer==-1)
+    {
+        int lmax = m_layerLayout->size()-1;
+        std::cout<<"Error BarrelLayer, set to default 1, Hit.x="<<pHit->getPosition().x<<", min_x="<<m_layerLayout->at(0).distance-0.5*m_layerLayout->at(0).sensitive_thickness<<", max_x="<<m_layerLayout->at(lmax).distance+0.5*m_layerLayout->at(lmax).sensitive_thickness<<std::endl;
+        layer = 0;
+    }
+    float cellSize0     = m_layerLayout->at(layer).cellSize0;
+    float cellSize1     = m_layerLayout->at(layer).cellSize1;
+    int cell0 = floor((pHit->getPosition().y+0.5*cellSize0)/cellSize0);
+    int cell1 = floor((pHit->getPosition().z+0.5*cellSize1)/cellSize1);
+    std::string sl = std::to_string(layer);
+    std::string s0 = std::to_string(cell0);
+    std::string s1 = std::to_string(cell1);
+    std::string id = sl+"_"+s0+"_"+s1 ;
+    return id;
+}
diff --git a/Reconstruction/Digi_Calo/src/CaloDigiAlg.cpp_id b/Reconstruction/Digi_Calo/src/CaloDigiAlg.cpp_id
new file mode 100644
index 00000000..e25ad95b
--- /dev/null
+++ b/Reconstruction/Digi_Calo/src/CaloDigiAlg.cpp_id
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+#include "CaloDigiAlg.h"
+
+
+#include "edm4hep/SimCalorimeterHit.h"
+#include "edm4hep/CalorimeterHit.h"
+
+#include "DD4hep/Detector.h"
+#include <DD4hep/Objects.h>
+
+#include <cmath>
+#include <algorithm>
+
+DECLARE_COMPONENT( CaloDigiAlg )
+
+CaloDigiAlg::CaloDigiAlg(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");
+  
+   
+}
+
+StatusCode CaloDigiAlg::initialize()
+{
+
+  m_geosvc = service<IGeoSvc>("GeoSvc");
+  if ( !m_geosvc )  throw "CaloDigiAlg :Failed to find GeoSvc ...";
+  dd4hep::Detector* m_dd4hep = m_geosvc->lcdd();
+  m_cellIDConverter = new dd4hep::rec::CellIDPositionConverter(*m_dd4hep);
+
+  std::cout<<"m_scale="<<m_scale<<std::endl;
+
+  return GaudiAlgorithm::initialize();
+}
+
+StatusCode CaloDigiAlg::execute()
+{
+  std::map<unsigned long long, edm4hep::SimCalorimeterHit> id_hit_map;
+  std::map<unsigned long long, int > test_map;
+  edm4hep::CalorimeterHitCollection* caloVec   = w_DigiCaloCol.createAndPut();
+  const edm4hep::SimCalorimeterHitCollection* SimHitCol =  r_SimCaloCol.get();
+  double tot_e = 0 ;
+  if(SimHitCol == 0) 
+  {
+     std::cout<<"not found SimCalorimeterHitCollection"<< std::endl;
+     return StatusCode::SUCCESS;
+  }
+  std::cout<<"digi, input sim hit size="<< SimHitCol->size() <<std::endl;
+  for( int i = 0; i < SimHitCol->size(); i++ ) 
+  {
+      edm4hep::SimCalorimeterHit SimHit = SimHitCol->at(i);
+      unsigned long long id = SimHit.getCellID();
+      /*
+      dd4hep::Position gpos(SimHit.getPosition().x, SimHit.getPosition().y, SimHit.getPosition().z);
+      long long int cell_id = m_cellIDConverter->cellID(gpos); 
+      signed long long id2 = (signed long long) SimHit.getCellID();
+      std::cout<<"sim gpos x="<<gpos.x()<<",y="<<gpos.y()<<",z="<<gpos.z()<<",id0="<<id<<",id1="<<cell_id<<",id2="<<id2<<std::endl;
+      */   
+      float en = SimHit.getEnergy();
+      tot_e += en;
+      test_map[id] = 1;
+      if ( id_hit_map.find(id) != id_hit_map.end()) id_hit_map[id].setEnergy(id_hit_map[id].getEnergy() + en);
+      else id_hit_map[id] = SimHit ;
+  }
+  for(std::map<unsigned long long, edm4hep::SimCalorimeterHit>::iterator iter = id_hit_map.begin(); iter != id_hit_map.end(); iter++)
+  {
+    auto caloHit = caloVec->create();
+    caloHit.setCellID((iter->second).getCellID());
+    caloHit.setEnergy((iter->second).getEnergy()*m_scale);
+    caloHit.setPosition((iter->second).getPosition());
+  }
+    
+  std::cout<<"total sim e ="<< tot_e <<std::endl;
+  std::cout<<"digi, output digi hit size="<< caloVec->size() <<std::endl;
+  _nEvt ++ ;
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode CaloDigiAlg::finalize()
+{
+  info() << "Processed " << _nEvt << " events " << endmsg;
+  return GaudiAlgorithm::finalize();
+}
diff --git a/Reconstruction/Digi_Calo/src/CaloDigiAlg.cpp_v1 b/Reconstruction/Digi_Calo/src/CaloDigiAlg.cpp_v1
new file mode 100644
index 00000000..d1d19a49
--- /dev/null
+++ b/Reconstruction/Digi_Calo/src/CaloDigiAlg.cpp_v1
@@ -0,0 +1,131 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+#include "CaloDigiAlg.h"
+
+
+#include "edm4hep/SimCalorimeterHit.h"
+#include "edm4hep/CalorimeterHit.h"
+#include "edm4hep/Vector3f.h"
+
+#include "DD4hep/Detector.h"
+#include <DD4hep/Objects.h>
+#include <DDRec/DetectorData.h>
+#include <DDRec/CellIDPositionConverter.h>
+
+
+#include <cmath>
+#include <algorithm>
+
+DECLARE_COMPONENT( CaloDigiAlg )
+
+CaloDigiAlg::CaloDigiAlg(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");
+  
+   
+}
+
+StatusCode CaloDigiAlg::initialize()
+{
+
+  std::cout<<"CaloDigiAlg::m_scale="<<m_scale<<std::endl;
+  m_geosvc = service<IGeoSvc>("GeoSvc");
+  if ( !m_geosvc )  throw "CaloDigiAlg :Failed to find GeoSvc ...";
+  dd4hep::Detector* m_dd4hep = m_geosvc->lcdd();
+  if ( !m_dd4hep )  throw "CaloDigiAlg :Failed to get dd4hep::Detector ...";
+  m_cellIDConverter = new dd4hep::rec::CellIDPositionConverter(*m_dd4hep);
+  try{
+      const dd4hep::DetElement &detElement = m_dd4hep->detector("CaloDetector");
+      dd4hep::rec::LayeredCalorimeterData* Data = detElement.extension<dd4hep::rec::LayeredCalorimeterData>() ;
+      const std::vector<dd4hep::rec::LayeredCalorimeterData::Layer>& layerLayout = Data->layers;
+      assert(layerLayout.size()>=1);
+      float cellSize0     = layerLayout.at(0).cellSize0;
+      float cellSize1     = layerLayout.at(0).cellSize1;
+      float cellThickness = layerLayout.at(0).sensitive_thickness;
+      m_length = sqrt(cellSize0*cellSize0 + cellSize1*cellSize1 + cellThickness*cellThickness);
+      std::cout<<"CaloDigiAlg::cellSize0="<<cellSize0<<",cellSize1="<<cellSize1<<",cellThickness="<<cellThickness<<",m_length="<<m_length<<std::endl;
+  }
+  catch(...) 
+  {
+      throw "CaloDigiAlg :Failed to get LayeredCalorimeterData ...";
+  }
+  return GaudiAlgorithm::initialize();
+}
+
+StatusCode CaloDigiAlg::execute()
+{
+  std::vector< edm4hep::SimCalorimeterHit > sim_vec;
+  std::map<unsigned long long, edm4hep::SimCalorimeterHit> id_hit_map;
+  std::map<unsigned long long, int > test_map;
+  edm4hep::CalorimeterHitCollection* caloVec   = w_DigiCaloCol.createAndPut();
+  const edm4hep::SimCalorimeterHitCollection* SimHitCol =  r_SimCaloCol.get();
+  if(SimHitCol == 0) 
+  {
+     std::cout<<"not found SimCalorimeterHitCollection"<< std::endl;
+     return StatusCode::SUCCESS;
+  }
+  std::cout<<"digi, input sim hit size="<< SimHitCol->size() <<std::endl;
+  float tot_sim_en = 0;
+  for( int i = 0; i < SimHitCol->size(); i++ ) 
+  {
+      int index = -1;
+      float dis_min = m_length;//10mm*10mm*10mm
+      bool found = false;
+      edm4hep::SimCalorimeterHit SimHit = SimHitCol->at(i);
+      float en = SimHit.getEnergy();
+      tot_sim_en += en;
+      float x0 = SimHit.getPosition().x ;
+      float y0 = SimHit.getPosition().y ;
+      float z0 = SimHit.getPosition().z ;
+      //std::cout<<"x0="<<x0<<",y0="<<y0<<",z0="<<z0<<std::endl;
+      for(int j=0; j<sim_vec.size(); j++)
+      {
+          float x = sim_vec.at(j).getPosition().x ;
+          float y = sim_vec.at(j).getPosition().y ;
+          float z = sim_vec.at(j).getPosition().z ;
+          float dist  = sqrt( (x-x0)*(x-x0) + (y-y0)*(y-y0) + (z-z0)*(z-z0) );
+          if( dist < dis_min ){ dis_min = dist; index = j ; }
+      }
+      if(index==-1) sim_vec.push_back(SimHit);
+      else  
+      {   
+          float en_ori = sim_vec.at(index).getEnergy();
+          float sum_en = en + en_ori ;          
+          sim_vec.at(index).setEnergy(sum_en);
+          //std::cout<<"ori_x="<<sim_vec.at(index).getPosition().x<<",ori_y="<<sim_vec.at(index).getPosition().y<<",ori_z="<<sim_vec.at(index).getPosition().z<<std::endl;
+          float new_x = sim_vec.at(index).getPosition().x * en_ori/sum_en + SimHit.getPosition().x * en/sum_en;
+          float new_y = sim_vec.at(index).getPosition().y * en_ori/sum_en + SimHit.getPosition().y * en/sum_en;
+          float new_z = sim_vec.at(index).getPosition().z * en_ori/sum_en + SimHit.getPosition().z * en/sum_en;
+          edm4hep::Vector3f pos(new_x, new_y, new_z);
+          sim_vec.at(index).setPosition(pos);
+          //std::cout<<"new_x="<<sim_vec.at(index).getPosition().x<<",new_y="<<sim_vec.at(index).getPosition().y<<",new_z="<<sim_vec.at(index).getPosition().z<<std::endl;
+      }
+  }
+  float tot_en = 0;
+  for(std::vector< edm4hep::SimCalorimeterHit >::iterator iter = sim_vec.begin(); iter != sim_vec.end(); iter++)
+  {
+    auto caloHit = caloVec->create();
+    caloHit.setCellID  (iter->getCellID());
+    caloHit.setEnergy  (iter->getEnergy()*m_scale);
+    caloHit.setPosition(iter->getPosition());
+    tot_en += caloHit.getEnergy();
+  }
+    
+  std::cout<<"total sim e ="<< tot_sim_en <<std::endl;
+  std::cout<<"digi, output digi hit size="<< caloVec->size()<<",tot_en="<<tot_en <<std::endl;
+  _nEvt ++ ;
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode CaloDigiAlg::finalize()
+{
+  info() << "Processed " << _nEvt << " events " << endmsg;
+  return GaudiAlgorithm::finalize();
+}
diff --git a/Reconstruction/Digi_Calo/src/CaloDigiAlg.h b/Reconstruction/Digi_Calo/src/CaloDigiAlg.h
new file mode 100644
index 00000000..5ecc0f68
--- /dev/null
+++ b/Reconstruction/Digi_Calo/src/CaloDigiAlg.h
@@ -0,0 +1,88 @@
+#ifndef Calo_DIGI_ALG_H
+#define Calo_DIGI_ALG_H
+
+#include "FWCore/DataHandle.h"
+#include "GaudiAlg/GaudiAlgorithm.h"
+#include "edm4hep/SimCalorimeterHitConst.h"
+#include "edm4hep/SimCalorimeterHit.h"
+#include "edm4hep/CalorimeterHit.h"
+#include "edm4hep/CalorimeterHitCollection.h"
+#include "edm4hep/SimCalorimeterHitCollection.h"
+
+#include <DDRec/DetectorData.h>
+#include <DDRec/CellIDPositionConverter.h>
+#include "DetInterface/IGeoSvc.h"
+
+/** ======= PlanarDigiProcessor / CaloDigiAlg ========== <br>
+ * Creates TrackerHits from SimTrackerHits, smearing them according to the input parameters. 
+ * The SimTrackerHits should come from a planar detector like VXD, SIT, SET or FTD.
+ * 
+ * WARNING: this processor depends on correctly set CellID0s and is NOT backwards compatible to 
+ * SimTrackerHit output with wrong CellID0s!!!
+ * 
+ * The positions of "digitized" TrackerHits are obtained by gaussian smearing positions
+ * of SimTrackerHits in u and v direction. 
+ * <h4>Input collections and prerequisites</h4> 
+ * Processor requires a collection of SimTrackerHits <br>
+ * <h4>Output</h4>
+ * Processor produces collection of smeared TrackerHits<br>
+ * @param SimTrackHitCollectionName The name of input collection of SimTrackerHits <br>
+ * (default name VXDCollection) <br>
+ * @param TrackerHitCollectionName The name of output collection of smeared TrackerHits <br>
+ * (default name VTXTrackerHits) <br>
+ * @param SimTrkHitRelCollection The name of the TrackerHit SimTrackerHit relation collection <br>
+ * (default name VTXTrackerHitRelations) <br>
+ * @param ResolutionU resolution in direction of u (in mm) <br>
+ * (default value 0.004) <br>
+ * @param ResolutionV Resolution in direction of v (in mm) <br>
+ * (default value 0.004) <br>
+ * @param IsStrip whether the hits are 1 dimensional strip measurements <br>
+ * (default value false)<br>
+ * <br>
+ * 
+ */
+
+
+
+class CaloDigiAlg : public GaudiAlgorithm
+{
+ 
+public:
+ 
+  CaloDigiAlg(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() ;
+ 
+  std::string GetCode(const edm4hep::SimCalorimeterHit *const pHit) const;
+ 
+protected:
+
+  const std::vector<dd4hep::rec::LayeredCalorimeterData::Layer>* m_layerLayout;
+ 
+  SmartIF<IGeoSvc> m_geosvc;
+  typedef std::vector<float> FloatVec;
+
+  int _nEvt ;
+  float m_length;
+  dd4hep::rec::CellIDPositionConverter* m_cellIDConverter;
+ 
+  Gaudi::Property<float> m_scale{ this, "Scale", 1 };
+
+  // Input collections
+  DataHandle<edm4hep::SimCalorimeterHitCollection> r_SimCaloCol{"SimCaloCol", Gaudi::DataHandle::Reader, this};
+  // Output collections
+  DataHandle<edm4hep::CalorimeterHitCollection>    w_DigiCaloCol{"DigiCaloCol", Gaudi::DataHandle::Writer, this};
+};
+
+#endif
diff --git a/Reconstruction/PFA/Pandora/CED/.cepcenv/01-09-01/status/install.yml b/Reconstruction/PFA/Pandora/CED/.cepcenv/01-09-01/status/install.yml
new file mode 100644
index 00000000..9f2056ab
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/CED/.cepcenv/01-09-01/status/install.yml
@@ -0,0 +1,20 @@
+clean:
+  end: 2018-09-04 10:38:21.535016
+  finished: true
+  start: 2018-09-04 10:38:21.503623
+compile:
+  end: 2018-09-04 10:38:21.449716
+  finished: true
+  start: 2018-09-04 10:38:07.382890
+download:
+  end: 2018-09-04 10:35:56.326883
+  finished: true
+  start: 2018-09-04 10:35:55.941740
+extract:
+  end: 2018-09-04 10:35:56.510664
+  finished: true
+  start: 2018-09-04 10:35:56.368258
+pre_compile:
+  end: 2018-09-04 10:37:33.127719
+  finished: true
+  start: 2018-09-04 10:37:33.110630
diff --git a/Reconstruction/PFA/Pandora/CED/CED/ced.cc b/Reconstruction/PFA/Pandora/CED/CED/ced.cc
new file mode 100644
index 00000000..65444465
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/CED/CED/ced.cc
@@ -0,0 +1,382 @@
+/* "C" event display.
+ * Communications related part. 
+ *
+*ik
+ * Alexey Zhelezov, DESY/ITEP, 2005 */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+
+#include <ced.h>
+
+//hauke
+//#include <stropts.h>
+#include <poll.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <sys/socket.h> /* for AF_INET */
+#include <iostream>
+
+
+//http://www.rhyolite.com/pipermail/dcc/2004/001986.html
+#ifndef POLLRDNORM //fg: should be defined in poll.h
+# define POLLRDNORM     0x040           /* Normal data may be read.  */
+# define POLLRDBAND     0x080           /* Priority data may be read.  */
+# define POLLWRNORM     0x100           /* Writing now will not block.  */
+# define POLLWRBAND     0x200           /* Priority data may be written.  */
+#endif
+//end hauke
+
+static int ced_fd=-1; // CED connection socket
+
+static unsigned short ced_port=7927; // port No of CED (assume localhost)
+static char ced_host[30];
+
+// Return 0 if can be connected, -1 otherwise.
+/*static*/ int ced_connect(void){
+  static time_t last_attempt=0;
+  time_t ct;
+  struct sockaddr_in addr;
+
+  if(ced_fd>=0){
+    return 0; // already connected;
+  }
+  time(&ct);
+  if(ct-last_attempt<5){
+    return -1; // don't try reconnect all the time
+  }
+  addr.sin_family=AF_INET;
+  addr.sin_port=htons(ced_port);
+  addr.sin_addr.s_addr=inet_addr(ced_host); 
+  memset(&addr.sin_zero, 0, sizeof(addr.sin_zero)); //not nessesary because sin_zero is not used!
+
+  ced_fd=socket(PF_INET,SOCK_STREAM,0);
+  if(connect(ced_fd,(struct sockaddr *)&addr,sizeof(addr)) != 0){
+    if(!last_attempt){
+        perror("WARNING:CED: can't connect to CED");
+    }
+    time(&last_attempt);
+    close(ced_fd);
+    ced_fd=-1;
+    return -1;
+  }
+  fprintf(stderr,"INFO:CED: connected to CED\n");
+  return 0;
+}
+
+
+typedef struct {
+  unsigned size;            // size of one item in bytes
+  unsigned char *b;         // "body" - data are stored here
+                            // (here is some trick :)
+  unsigned long count;      // number of usefull items
+  unsigned long alloced;    // number of allocated items
+  ced_draw_cb draw;         // draw fucation, NOT used in CED client
+} ced_element;
+
+typedef struct {
+  ced_element *e;
+  unsigned      e_count;
+} ced_event;
+
+//static ced_event eve = {0,0};
+
+static ced_event eve = {0,0};
+
+// NOT used in CED client
+static ced_event ceve = {0,0}; // current event on screen
+
+// we reserve this size just before ced_element.b data
+#define HDR_SIZE 8 
+
+unsigned ced_register_element(unsigned item_size,ced_draw_cb draw_func){
+  ced_element *pe;
+  if(!(eve.e_count&0xf)){
+    eve.e=(ced_element *) realloc(eve.e,(eve.e_count+0x10)*sizeof(ced_element));
+  }
+
+  pe=eve.e+eve.e_count;
+  memset(pe,0,sizeof(*pe));
+  pe->size=item_size;
+  pe->draw=draw_func;
+  return eve.e_count++;
+}
+
+static void ced_reset(void){
+  unsigned i;
+  
+  for(i=0;i<eve.e_count;i++){
+    eve.e[i].count=0;
+   // if( eve.e[i].alloced > 0){
+   //     eve.e[i].alloced=0; //hauke: 15.12.11
+   //     //free(eve.e[i].b-HDR_SIZE);
+   // }
+  }
+}
+
+static void ced_buf_alloc(ced_element *pe,unsigned count){
+  if(!pe->b){
+
+    //std::cout << "malloc  requestet: " << count*pe->size+HDR_SIZE << "bytes" << std::endl;
+    pe->b=(unsigned char *) malloc(count*pe->size+HDR_SIZE);
+    //printf("malloc: ask for NEW %lu bytes pointer: %p\n ", count*pe->size+HDR_SIZE, pe->b); //hauke
+    if(pe->b==NULL){ //hauke
+        printf("ERROR: malloc failed!\n");
+        exit(1);
+    }
+  }else{
+    //free(pe->b-HDR_SIZE);
+    //pe->b=(unsigned char *) malloc(count*pe->size+HDR_SIZE);
+
+    //std::cout << "realloc requestet: " << count*pe->size+HDR_SIZE << "bytes" << std::endl;
+    pe->b=(unsigned char *) realloc(pe->b-HDR_SIZE,count*pe->size+HDR_SIZE);
+
+    //printf("malloc: ask for %lu bytes, pointer: %p\n", count*pe->size+HDR_SIZE,pe->b);//hauke
+    if(pe->b==NULL){ //hauke
+        printf("ERROR: malloc failed!\n");
+        exit(1);
+    }
+  }
+  pe->b+=HDR_SIZE;
+  pe->alloced=count;
+}
+
+void *ced_add(unsigned id){
+  ced_element *pe;
+  if(id >= eve.e_count){
+    fprintf(stderr,"BUG:CED: attempt to access not registered element\n");
+    return 0;
+  }
+  pe=eve.e+id;
+
+  if(pe->count==pe->alloced){
+    ced_buf_alloc(pe,pe->alloced+256);
+  }
+
+  return (pe->b+(pe->count++)*pe->size);
+}
+
+static void ced_event_copy(ced_event *trg){
+  unsigned i;
+  ced_element *pe;
+  //std::cout << "trg->e_count: " << trg->e_count << std::endl;
+  //std::cout << "eve.e_count: " << eve.e_count << std::endl;
+
+  //eve.e_count = 0;
+  if(trg->e_count<eve.e_count){
+    //free(trg->e);
+    trg->e=(ced_element*) realloc(trg->e,eve.e_count*sizeof(ced_element));
+
+    //trg->e=(ced_element*) malloc(eve.e_count*sizeof(ced_element));
+  }
+
+
+  for(i=0;i<eve.e_count;i++){
+        pe=trg->e+i;
+        if(i<trg->e_count){
+            //if(pe->alloced > 0){
+            //  free(pe->b);
+            //  pe->b=NULL;
+            //  pe->alloced=0;
+            //}
+
+
+            //if(pe->alloced > 0){
+            //    std::cout << "try to free" << std::endl;
+            //    free(pe->b-HDR_SIZE);
+            //    pe->alloced = 0;
+            //    std::cout << "finished" << std::endl;
+            //}
+
+            if(pe->alloced<eve.e[i].alloced) {
+	          ced_buf_alloc(pe,eve.e[i].alloced);
+                //std::cout << "test1 " << std::endl;
+            }
+            pe->count=eve.e[i].count;
+
+        }else{
+            memcpy(pe,eve.e+i,sizeof(ced_element));
+            if(pe->b){
+	              pe->b=0;
+                  //  std::cout << "test2 " << std::endl;
+	              ced_buf_alloc(pe,pe->alloced);
+            }
+        }
+        if(pe->count){
+            memcpy(pe->b,eve.e[i].b,pe->count*pe->size);
+        }
+  }
+  trg->e_count=eve.e_count;
+}
+
+void ced_do_draw_event(void){
+  unsigned int i,j;
+  ced_element *pe;
+  unsigned char *pdata;
+  for(i=0;i<ceve.e_count;i++){
+    //printf("ceve.e_count: %i\n", ceve.e_count);
+    //for(i=ceve.e_count-1; i >=0;i--){ //quick hack, change order so that the detector is drawn at last
+    //printf("i = %i\n", i);
+
+    pe=ceve.e+i;
+    if(!pe->draw)
+      continue;
+    for(pdata=pe->b,j=0;j<pe->count;j++,pdata+=pe->size)
+      (*(pe->draw))(pdata);
+  }
+}
+
+typedef enum {
+  DRAW_EVENT=10000
+} MSG_TYPE;
+
+int ced_process_input(void *data){
+  struct _phdr{
+    unsigned size;
+    unsigned type;
+    unsigned char b[4];
+  } *hdr = (_phdr*) data;
+  unsigned count;
+  ced_element *pe;
+  
+  if(!data){ // new client is connected
+    ced_reset();
+    return 0;
+  }
+
+  if(hdr->type == DRAW_EVENT){
+    ced_event_copy(&ceve);
+    ced_reset();
+    return 1;
+  }
+  if(hdr->type>=eve.e_count){
+    fprintf(stderr,"WARNING:CED: undefined element type (%u), ignored\n",
+	    hdr->type);
+    return 0;
+  }
+  pe=eve.e+hdr->type;
+  if((hdr->size-HDR_SIZE)%pe->size){
+    fprintf(stderr,"BUG:CED: size alignment is wrong for element %u\n", hdr->type);
+    return 0;
+  }
+  count=(hdr->size-HDR_SIZE)/pe->size;
+  if(!count)
+    return 0;
+  if(count>=pe->alloced)
+    ced_buf_alloc(pe,count+256);
+  memcpy(pe->b,hdr->b,count*pe->size);
+  pe->count=count;
+  return 0;
+}
+
+void ced_send_event(void){
+  struct _phdr{
+    int size;
+    unsigned type;
+  } *hdr,draw_hdr;
+  unsigned i,problem=0;
+  int sent_sum;
+  char *buf;
+  int sent;
+  ced_element *pe;
+
+  if(ced_connect())
+    return;
+  for(i=0;i<eve.e_count && !problem;i++){
+    //printf("i=%i\n",i);
+    pe=eve.e+i;
+    if(!pe->count)
+      continue;
+    
+    //unsigned hauke;
+    //printf("size of unsigned %i\n", sizeof(hauke));
+    hdr=(struct _phdr *)(pe->b-HDR_SIZE); // !!! HERE is the trick :)
+    hdr->type=i;
+    //printf("pe->count %i, pe->size %i\n",pe->count, pe->size);
+    hdr->size=HDR_SIZE+pe->count*pe->size;
+    sent_sum=0;
+    //if(hdr->size > 10000000){printf("U P S!  This data set is realy big! (%f kB)(%i counts)\n",(hdr->size)/1024.0,pe->count);}
+    buf=(char *)hdr;
+    //printf("hdr->size=%i\n",hdr->size);
+    while(sent_sum<hdr->size){
+        //printf("sent_sum = %i, hdr->size=%i\n",sent_sum,hdr->size);
+	    sent=write(ced_fd,buf+sent_sum,hdr->size-sent_sum);
+        
+        //printf("byte: %u\n", buf[sent_sum]);
+	    if(sent<0){
+            printf("send < 0\n");
+	        problem=1;
+	        break;
+	    }
+	    sent_sum+=sent;
+    }
+  }
+  if(!problem){
+    draw_hdr.size=HDR_SIZE;
+    draw_hdr.type=DRAW_EVENT;
+    if(write(ced_fd,&draw_hdr,HDR_SIZE)!=HDR_SIZE)
+      problem=1;
+  }
+  if(problem){
+    perror("WARNING:CED: can't send event, till next time...");
+    close(ced_fd);
+    ced_fd=-1;
+  }
+}
+
+
+//hauke
+int ced_selected_id_noblock() {
+  int id=-1 ;
+  struct pollfd fds[1];
+  fds[0].fd=ced_fd;
+  fds[0].events = POLLRDNORM | POLLIN;
+  if(poll(fds,1,0) > 0){
+    if(recv(ced_fd, &id, sizeof(int) , 0 ) > 0){
+        return id;
+    }else{
+        return -1;
+    }
+  }else{
+   return -1;
+  }
+}
+
+int ced_selected_id() {
+  int id=-1 ;
+  if(recv(ced_fd, &id, sizeof(int) , 0 ) > 0){
+     return id;
+  }else{
+     return -1;
+  }
+}
+#include <signal.h>
+// API
+void ced_client_init(const char *hostname,unsigned short port){
+  struct hostent *host = gethostbyname(hostname);
+  snprintf(ced_host, 30, "%u.%u.%u.%u\n",(unsigned char)host->h_addr[0] ,(unsigned char)host->h_addr[1] ,(unsigned char)host->h_addr[2] ,(unsigned char)host->h_addr[3]); 
+
+
+  //printf("ip: %s\n",  ced_host);
+  //ced_host=host->h_addr;
+  ced_port=port;
+  signal(SIGPIPE,SIG_IGN);
+}
+
+void ced_new_event(void){
+  ced_reset();
+}
+
+void ced_draw_event(void){
+  ced_send_event();
+  ced_reset();
+}
diff --git a/Reconstruction/PFA/Pandora/CED/CED/ced.h b/Reconstruction/PFA/Pandora/CED/CED/ced.h
new file mode 100644
index 00000000..ccf355c2
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/CED/CED/ced.h
@@ -0,0 +1,146 @@
+/* "C" event display.
+ * Main part. 
+ *
+ * Alexey Zhelezov, DESY/ITEP, 2005 */
+
+/*
+ * This file is internal. It must not be
+ * included into enduser application.
+ */
+
+#ifndef __CED_H
+#define __CED_H
+
+#include "ced_cli.h"
+
+//#ifdef __cplusplus
+// extern "C" {
+//#endif
+		
+
+//char trusted_hosts[50];
+//extern static char testchar;
+
+typedef void (*ced_draw_cb)(void *data);
+
+
+/*
+ * Register new element type. Order is important!
+ * Appropriate code must be defined in both
+ * client and server parts.
+ *
+ *  item_size - size of one item in bytes.
+ *  draw_func - function to call to draw one item,
+ *              called from ced_do_draw_event()
+ *              not used on client side.
+ */
+unsigned ced_register_element(unsigned item_size,ced_draw_cb draw_func);
+
+/*
+ * To be called from element functions
+ * on client side.
+ * Return allocated space for one item
+ * with size, specified by ced_register_element()
+ *
+ * Example: assume struct Dummy { int i; }; is item.
+ *
+ *          DummyID=ced_register_element(sizeof(struct Dummy),0);
+ *          ...
+ *          struct Dummy *item=(struct Dummy *)ced_add_element(DummyID);
+ *            item->i=0;
+ * This will add one Dummy item to the event
+ */
+void *ced_add(unsigned id);
+
+/*
+ * To be called in paint function
+ *
+ * It calls user defined functions for
+ * each item of all elements types.
+ */
+void ced_do_draw_event(void);
+
+/*
+ * Server side function.
+ * Must be used to process all incoming
+ * messages from client.
+ *
+ * It return positive value when
+ * new event must be drawn.
+ *
+ * Example:
+ *      glut_tcp_server(7285,my_process_input)
+ *
+ *      my_process_input(x){
+ *        if(ced_process_input(x)>0)
+ *          <do redraw>
+ */
+int ced_process_input(void *data);
+
+//------------
+void addLayerDescriptionToMenu(int,char *);//glced.c
+void selectFromMenu(int id);//glced.c
+void toggleHelpWindow(void);//glced.c
+void updateLayerEntryInPopupMenu(int); //glced.c
+int buildMenuPopup(void);//glced.c
+
+
+#define VERSION_CONFIG 3
+struct CEDsettings{
+    bool trans;         //grid or surface view
+    bool persp;         //perspectivic view or flat projection 
+    bool antia;         //anti aliasing
+    bool light;         //light source 
+    bool picking_highlight; //marker at picking position
+    double detector_trans[NUMBER_DETECTOR_LAYER];
+    double detector_cut_angle[NUMBER_DETECTOR_LAYER];
+    double detector_cut_z[NUMBER_DETECTOR_LAYER];
+    bool detector_picking;
+//    double cut_angle; //deprecated
+//    double trans_value; //deprecated
+//    double z_cutting; //deprecated
+    bool layer[CED_MAX_LAYER];
+    bool phi_projection;
+    bool z_projection;
+    double view[3];
+    double va; //vertical angle of view
+    double ha; //horionzional angle of view
+    bool fixed_view;
+    int win_h; //height of the window (pixel)
+    int win_w; //wight of the window (pixel)
+    double zoom;
+    double fisheye_alpha;
+    double world_size;
+    double fisheye_world_size;
+    double bgcolor[4];
+    bool show_axes;
+    bool fps;
+    double screenshot_sections;
+    int font; //size of text (menu, shortcuts, text in ced window)
+    bool autoshot; // If true, generate screencapture in every new event
+    int autoshot_scale; // If true, generate screencapture in every new event
+};
+
+/*
+//important: 
+//          - sum of all layers must be smaler than max_layer!
+//          - number_popup_layer must be smaler than number_data_layer
+
+#define CED_MAX_LAYER       100 
+#define NUMBER_POPUP_LAYER      20
+#define NUMBER_DATA_LAYER       25
+#define NUMBER_DETECTOR_LAYER   20
+
+
+#define CED_MAX_LAYER_CHAR 400
+*/
+
+//-------------
+
+//#ifdef __cplusplus
+// }
+//#endif
+
+	
+
+#endif /* __CED_H  */
diff --git a/Reconstruction/PFA/Pandora/CED/CED/ced_cli.cc b/Reconstruction/PFA/Pandora/CED/CED/ced_cli.cc
new file mode 100644
index 00000000..8c063e12
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/CED/CED/ced_cli.cc
@@ -0,0 +1,539 @@
+/* "C" event display.
+ * Client side elements definitions.
+ *
+ * Alexey Zhelezov, DESY/ITEP, 2005 */
+#include <string.h>
+
+#include <ced_cli.h>
+#include <ced.h>
+#include <stdio.h>
+//#include <iostream>
+
+#include <math.h>
+
+
+/*
+ * Hit element
+ */
+
+static unsigned HIT_ID=0;
+
+void ced_hit(float x,float y,float z,unsigned type,unsigned size,unsigned color){
+  ced_hit_ID(x,y,z, type & 0xFF,( type >> CED_LAYER_SHIFT ) & 0xFF, size,color, 0);
+}
+
+//deprecated
+void ced_hit_ID_old(float x,float y,float z,unsigned type, unsigned size,unsigned color, unsigned lcioID){
+  ced_hit_ID(x,y,z,type & 0xFF,(type >> CED_LAYER_SHIFT) & 0xFF,size,color,lcioID);
+}
+
+void ced_hit_ID(float x,float y,float z,unsigned type,unsigned layer, unsigned size,unsigned color, unsigned lcioID){
+ CED_Hit *h=(CED_Hit *)ced_add(HIT_ID);
+ if(!h)
+   return;
+ h->p.x=x;
+ h->p.y=y;
+ h->p.z=z;
+ h->type=type;
+ // if(layer > 255){ //downward compability
+ //    h->layer=layer >> CED_LAYER_SHIFT;
+ // }else{
+ h->layer=layer;
+ // }
+ h->size=size;
+ h->color=color;
+ h->lcioID=lcioID;
+}
+
+/*
+ * Line element
+ */
+
+static unsigned LINE_ID=0;
+
+void ced_line(float x0,float y0,float z0,
+	      float x1,float y1,float z1,
+	      unsigned type, unsigned width,unsigned color){
+    ced_line_ID(x0,y0,z0,x1,y1,z1, type, width, color, 0);
+}
+
+void ced_line_ID(float x0,float y0,float z0,
+	      float x1,float y1,float z1,
+	      unsigned layer, unsigned width,unsigned color, unsigned lcioID){
+//  //test for picking
+//  //static int anz;
+//  float length=(x1-x0)*(x1-x0) + (y1-y0)*(y1-y0) + (z1-z0)*(z1-z0);
+//  //printf("CEDLINE length: %f\n x0 %f y0 %f z0 %f, x1 %f, y1 %f z1 %f", length, x0, y0, z0, x1, y1, z1);
+//
+//  if(length > 500.0){
+//      //std::cout << "devide line (length=" << length << ")" <<   std::endl;
+//      ced_line_ID(x0,y0,z0,
+//          (x1-x0)/2.0+x0,(y1-y0)/2.0+y0,(z1-z0)/2.0+z0,
+//          layer, width, color, lcioID);
+//              ced_line_ID(
+//          (x1-x0)/2.0+x0,(y1-y0)/2.0+y0,(z1-z0)/2.0+z0,
+//
+//          x1,y1,z1,
+//          layer, width, color, lcioID);
+//          //todo
+//          //printf("seperate line %i\n", anz++);
+//      return;
+//
+//  }
+
+/*
+//test
+  if( random()%100000 > 99990){
+    double size[]={10,100,50};
+    double position[]={x0,y0,z0};
+    double rotate[] = {20,50,70};
+    int color=1;
+    int lcio_id=5;
+    int layer=5;
+    //ced_geobox(size, position, color ); 
+    ced_geobox_ID(size, position, layer, 0xff00ff, 5); 
+    ced_geobox(size, position, color);
+
+  }
+
+//end test
+*/
+
+  CED_Line *l=(CED_Line *)ced_add(LINE_ID);
+  if(!l)
+    return;
+  l->p0.x=x0;
+  l->p0.y=y0;
+  l->p0.z=z0;
+  l->p1.x=x1;
+  l->p1.y=y1;
+  l->p1.z=z1;
+ if(layer > 255){ //downward compability
+    l->type=layer >> CED_LAYER_SHIFT;
+ }else{
+    l->type=layer;
+ }
+
+    //printf("layer pre: %i  after: %i\n" , layer, l->type); 
+
+//  l->type=type;
+  l->width=width;
+  l->color=color;
+  l->lcioID=lcioID;
+}
+/*
+ * GeoCylinder
+ */
+static unsigned GEOC_ID=0;
+
+void ced_geocylinder(float d,unsigned sides,float rotate,float z,float shift,
+		     unsigned color){
+  CED_GeoCylinder *c=(CED_GeoCylinder *)ced_add(GEOC_ID);
+  if(!c)
+    return;
+  c->d=d;
+  c->sides=sides;
+  c->rotate=rotate;
+  c->z=z;
+  c->shift=shift;
+  c->color=color;
+}
+
+/*
+ * Rotated Geocylinder
+ * Extension of the cylinder subject to a 3-DOF rotation
+ * @author: SD
+ * @date: 26.08.08
+ */
+static unsigned GEOCR_ID=0;
+
+void ced_geocylinder_r(float d, double z, double * center, double * rotate, unsigned sides, 
+		     unsigned int color, int layer){
+	int iDim;
+  CED_GeoCylinderR *c=(CED_GeoCylinderR *)ced_add(GEOCR_ID);
+  if(!c) return;
+    for (iDim = 0; iDim < 3; iDim ++ ) {
+   		c->center[iDim]  = center[iDim];
+   		c->rotate[iDim]   = rotate[iDim];
+    }
+  c->d=d;
+  c->sides=sides;
+  c->color=color;
+  c->z=z;
+ if(layer > 255){ //downward compability
+    c->layer=layer >> CED_LAYER_SHIFT;
+ }else{
+    c->layer=layer;
+ }
+
+  //c->layer=layer;
+}
+ 
+void ced_geocylinders(unsigned n,CED_GeoCylinder *all){
+  CED_GeoCylinder *c;
+  unsigned i;
+  for(i=0;i<n;i++){
+    c=(CED_GeoCylinder *)ced_add(GEOC_ID);
+    if(!c)
+      return;
+    memcpy(c,all+i,sizeof(CED_GeoCylinder));
+  }
+}
+
+static unsigned GEOT_ID=0;
+
+void ced_geotubes(unsigned n,CED_GeoTube *all){
+  CED_GeoTube *c;
+  unsigned i;
+  for(i=0;i<n;i++){
+    c=(CED_GeoTube *)ced_add(GEOT_ID);
+    if(!c)
+      return;
+    memcpy(c,all+i,sizeof(CED_GeoTube));
+  }
+}
+
+
+static unsigned GEOB_ID=0;
+
+void ced_geobox(double * sizes, double * center, unsigned int color ) {
+  int iDim;
+  CED_GeoBox * box = (CED_GeoBox*) ced_add(GEOB_ID);
+  if ( ! box ) return;
+  for ( iDim = 0; iDim < 3; iDim ++ ) {
+    box->sizes[iDim]   = sizes[iDim];
+    box->center[iDim]  = center[iDim];
+  }
+  box->color   = color;
+
+}
+
+void rotate3d(double *vektor, double *rotate){
+    //double cords2[3]; 
+    double r_rad[3]={rotate[0]/360*2*3.14159265358979323846, rotate[1]/360*2*3.14159265358979323846, rotate[2]/360*2*3.14159265358979323846};
+    double cords1[3] = {vektor[0], vektor[1], vektor[2]};
+
+    vektor[0] = ( cos(r_rad[1])*cos(r_rad[2]) )*cords1[0] + 
+                (-cos(r_rad[0])*sin(r_rad[2]) + sin(r_rad[0])*sin(r_rad[1])*cos(r_rad[2]) )*cords1[1] + 
+                ( sin(r_rad[0])*sin(r_rad[2]) + cos(r_rad[0])*sin(r_rad[1])*cos(r_rad[2]) )*cords1[2];
+    vektor[1] = ( cos(r_rad[1])*sin(r_rad[2]) ) * cords1[0] + 
+                ( cos(r_rad[0])*cos(r_rad[2]) + sin(r_rad[0])*sin(r_rad[1])*sin(r_rad[2])) * cords1[1] + 
+                ( -sin(r_rad[0])*cos(r_rad[2]) + cos(r_rad[0])*sin(r_rad[1])*sin(r_rad[2]) ) * cords1[2];
+    vektor[2] = (-sin(r_rad[1])) * cords1[0] + 
+                ( sin(r_rad[0])*cos(r_rad[1])) * cords1[1] + 
+                ( cos(r_rad[0])*cos(r_rad[1]))  * cords1[2];
+ 
+}
+
+void ced_geobox_r_ID(double *size, double *position, double *rotate, unsigned int layer, unsigned int color, unsigned int lcio_id) {
+
+    int i;
+    double vektor1[3], vektor2[3];
+    //unsigned int type = layer; //<< CED_LAYER_SHIFT;
+    //unsigned int type = layer;
+
+    double cubematrix[12][6] ={ {-1,-1,-1, +1,-1,-1},
+                                {-1,-1,-1, -1,+1,-1},
+                                {-1,-1,-1, -1,-1,+1},
+                                {+1,-1,-1, +1,+1,-1},
+                                {+1,-1,-1, +1,-1,+1},
+                                {+1,+1,-1, +1,+1,+1},
+                                {+1,+1,-1, -1,+1,-1},
+                                {+1,+1,+1, -1,+1,+1},
+                                {+1,+1,+1, +1,-1,+1},
+                                {-1,+1,+1, -1,-1,+1},
+                                {-1,+1,+1, -1,+1,-1},
+                                {-1,-1,+1, +1,-1,+1} };
+
+    for(i=0;i<12;i++){
+        vektor1[0] = cubematrix[i][0]*size[0]/2; 
+        vektor1[1] = cubematrix[i][1]*size[1]/2; 
+        vektor1[2] = cubematrix[i][2]*size[2]/2;
+ 
+        vektor2[0] = cubematrix[i][3]*size[0]/2;
+        vektor2[1] = cubematrix[i][4]*size[1]/2;
+        vektor2[2] = cubematrix[i][5]*size[2]/2;
+
+        rotate3d(vektor1,rotate);
+        rotate3d(vektor2,rotate);
+
+
+        ced_line_ID(position[0]+vektor1[0], position[1]+vektor1[1], position[2]+vektor1[2],
+                    position[0]+vektor2[0], position[1]+vektor2[1], position[2]+vektor2[2],
+                    layer, 1,color, lcio_id);
+    }
+}
+
+void ced_geobox_ID(double *size, double *position, unsigned int layer, unsigned int color, unsigned int lcio_id) {
+    double rotate[3]={0.0, 0.0, 0.0};
+    ced_geobox_r_ID(size, position, rotate, layer,  color, lcio_id);
+}
+
+
+void ced_geoboxes(unsigned int nBox, CED_GeoBox * allBoxes ) {
+  
+  CED_GeoBox * box;
+  unsigned int iBox;
+  for ( iBox = 0; iBox < nBox ; iBox++ ) {
+    box = (CED_GeoBox *) ced_add(GEOB_ID);
+    if ( ! box ) return;
+    memcpy( box, allBoxes + iBox, sizeof(CED_GeoBox) );
+  }
+}
+
+static unsigned GEOBR_ID=0;
+
+void ced_geobox_r(double * sizes, double * center, double * rotate, unsigned int color, unsigned int layer) {
+  int iDim;
+  CED_GeoBoxR * box = (CED_GeoBoxR*) ced_add(GEOBR_ID);
+  if ( ! box ) return;
+  for ( iDim = 0; iDim < 3; iDim ++ ) {
+    box->sizes[iDim]   = sizes[iDim];
+    box->center[iDim]  = center[iDim];
+    box->rotate[iDim] = rotate[iDim];
+  }
+  box->color = color;
+
+ if(layer > 255){ //downward compability
+    box->layer=layer >> CED_LAYER_SHIFT;
+ }else{
+    box->layer=layer;
+ }
+
+//  box->layer = layer;
+}
+
+static unsigned GEOBRS_ID=0;
+
+void ced_geobox_r_solid(double * sizes, double * center, double * rotate, unsigned int color, unsigned int layer) {
+  int iDim;
+  CED_GeoBoxR * box = (CED_GeoBoxR*) ced_add(GEOBRS_ID);
+  if ( ! box ) return;
+  for ( iDim = 0; iDim < 3; iDim ++ ) {
+    box->sizes[iDim]   = sizes[iDim];
+    box->center[iDim]  = center[iDim];
+    box->rotate[iDim] = rotate[iDim];
+  }
+  box->color = color;
+
+ if(layer > 255){ //downward compability
+    box->layer=layer >> CED_LAYER_SHIFT;
+ }else{
+    box->layer=layer;
+ }
+
+//  box->layer = layer;
+}
+
+static unsigned LEGEND_ID=0;
+
+void ced_legend(float ene_min, float ene_max, unsigned int color_steps, unsigned int ** rgb_matrix, unsigned int ticks, char scale) {
+	CED_Legend * legend = (CED_Legend*) ced_add(LEGEND_ID);
+	if ( ! legend ) return;
+	
+	legend->ene_min = ene_min;
+  	legend->ene_max = ene_max;
+ 	legend->color_steps = color_steps;
+ 	legend->ticks = ticks;
+ 	legend->scale = scale;
+ 	
+  	const unsigned int numberOfColours = 3;
+  	unsigned int i,j;
+  	for (i = 0; i < numberOfColours; i ++ ) {
+  		for (j = 0; j < color_steps; j ++ ) {
+  			legend->rgb_matrix[j][i] = rgb_matrix[j][i];
+  		}
+	}
+}
+//hauke 
+static unsigned TEXT_ID=0;
+
+void ced_describe_layer(const char *message, int id) {
+    //printf("ced_describe layer id=%i text: %s\n", id, message);
+
+    if(id >= CED_MAX_LAYER){
+        printf("WARNING: ced_describe_layer: Index out of range!\n");
+        return;
+    }
+
+	CED_TEXT *text = (CED_TEXT*) ced_add(TEXT_ID);
+	if(!text){
+        printf("WARNING: ced_describe_layer: cant register CED_TEXT");  
+        return;
+    }
+
+    strncpy(text->text,message,CED_MAX_LAYER_CHAR-1);
+    text->text[CED_MAX_LAYER_CHAR-1] = 0;
+    text->id=id;
+
+    //text->x=xCordinate;
+    //text->y=yCordinate;
+}
+
+/*
+static unsigned LAYER_TEXT_ID=0;
+
+void ced_layer_text(char *message, int id) {
+	LAYER_TEXT *obj = (LAYER_TEXT*) ced_add(LAYER_TEXT_ID);
+	if (!obj){ 
+        printf("ced_layer_text FAILED\n"); 
+        return;
+    }
+    strncpy(obj->str,message,CED_MAX_LAYER_CHAR-1);
+    obj->id=id;
+    //printf("ced_layer_text\n");
+}
+*/
+
+static unsigned PICKING_TEXT_ID=0;
+
+void ced_picking_text(const char *message, int id) {
+	CED_PICKING_TEXT *text = (CED_PICKING_TEXT*) ced_add(PICKING_TEXT_ID);
+	if(!text){
+        printf("WARNING: ced_picking_text: cant register CED_PICKING_TEXT");  
+        return;
+    }
+
+    strncpy(text->text,message,999);
+    text->id=id;
+    //text->text[CED_MAX_LAYER_CHAR-1] = 0;
+    //text->id=id;
+    //text->x=xCordinate;
+    //text->y=yCordinate;
+}
+//end hauke
+
+static unsigned CONER_ID=0;
+
+void ced_cone_r(float base, float height, double *center, double *rotate, unsigned int layer, float *RGBAcolor) {
+    ced_cone_r_ID(base,height,center,rotate,layer, RGBAcolor, 0);
+}
+
+void ced_cone_r_ID(float base, float height, double *center, double *rotate, unsigned int layer, float *RGBAcolor, int lcioid) {
+	CED_ConeR * cone = (CED_ConeR*) ced_add(CONER_ID);
+	if ( ! cone ) return;
+	
+	cone->base = base;
+  	cone->height = height;
+
+ if(layer > 255){ //downward compability
+    cone->layer=layer >> CED_LAYER_SHIFT;
+ }else{
+    cone->layer=layer;
+ }
+
+// 	cone->layer = layer;
+    cone->lcioid = lcioid;
+ 	
+  	const unsigned int dim = 3;
+  	const unsigned int channel = 4;
+  	unsigned int i, j;
+
+    // ced_line_ID(0,0,0, center[0], center[1], center[2], type, width, RGBAcolor, lcioid);
+
+    //ced_line_ID(center[0], center[1], center[2], rotate[0], rotate[1], rotate[2], layer, 1, RGBAcolor, lcioid);
+    //printf("CONE: from %f %f %f to %f %f %f\n", center[0], center[1], center[2], rotate[0], rotate[1], rotate[2]);
+
+  	for (i = 0; i < dim; i ++ ) {
+		cone->center[i] = center[i];
+		cone->rotate[i] = rotate[i];
+	}
+	for (j = 0; j < channel; j ++ ) {
+		cone->RGBAcolor[j] = RGBAcolor[j];
+	}
+}
+
+static unsigned ELLIPSOID_ID=0;
+
+void ced_ellipsoid_r(double *size, double *center, double *rotate, unsigned int layer, int color) {
+    ced_ellipsoid_r_ID(size, center, rotate, layer, color, 0);
+}
+
+void ced_ellipsoid_r_ID(double *size, double *center, double *rotate, unsigned int layer, int color, int lcioID) {
+	
+	CED_EllipsoidR * eli = (CED_EllipsoidR*) ced_add(ELLIPSOID_ID);
+	if ( ! eli ) return;	
+ 	
+  	const unsigned int dim = 3;
+  	unsigned int i;
+  	for (i = 0; i < dim; i ++ ) {
+		eli->center[i] = center[i];
+		eli->rotate[i] = rotate[i];
+		eli->size[i] = size[i];
+	}
+	eli->color = color;
+
+ if(layer > 255){ //downward compability
+    eli->layer=layer >> CED_LAYER_SHIFT;
+ }else{
+    eli->layer=layer;
+ }
+
+//	eli->layer = layer;
+    eli->lcioid = lcioID;
+}
+
+static unsigned CLUELLIPSE_ID=0;
+
+void ced_cluellipse_r(float radius, float height, float *center, double *rotate, unsigned int layer, int color) {
+    ced_cluellipse_r_ID(radius, height, center, rotate, layer, color, 0);
+}
+
+void ced_cluellipse_r_ID(float radius, float height, float *center, double *rotate, unsigned int layer, int color, int lcioid) { //hauke
+	
+	CED_CluEllipseR * eli = (CED_CluEllipseR*) ced_add(CLUELLIPSE_ID);
+	if ( ! eli ) return;	
+ 	
+  	const unsigned int dim = 3;
+  	unsigned int i;
+  	for (i = 0; i < dim; i ++ ) {
+		eli->center[i] = center[i];
+		eli->rotate[i] = rotate[i];
+	}
+	eli->radius = radius;
+	eli->height = height;
+
+ if(layer > 255){ //downward compability
+    eli->layer=layer >> CED_LAYER_SHIFT;
+ }else{
+    eli->layer=layer;
+ }
+
+//	eli->layer = layer;
+	eli->color = color;
+    eli->lcioid=lcioid;
+}
+
+
+void ced_register_elements(void){
+
+  //1:
+  GEOC_ID		=ced_register_element(sizeof(CED_GeoCylinder),0);
+  //2:
+  GEOCR_ID	    =ced_register_element(sizeof(CED_GeoCylinderR), 0);
+  //3:
+  LINE_ID		=ced_register_element(sizeof(CED_Line),0);
+  //4:
+  HIT_ID		=ced_register_element(sizeof(CED_Hit),0);
+  //5:
+  GEOB_ID		=ced_register_element(sizeof(CED_GeoBox), 0);
+  //6:
+  GEOBR_ID	    =ced_register_element(sizeof(CED_GeoBoxR), 0);
+  //7:
+  GEOBRS_ID	    =ced_register_element(sizeof(CED_GeoBoxR), 0);
+  //8:
+  CONER_ID	    =ced_register_element(sizeof(CED_ConeR), 0);
+  //9:
+  ELLIPSOID_ID	=ced_register_element(sizeof(CED_EllipsoidR), 0);
+  //10:
+  CLUELLIPSE_ID =ced_register_element(sizeof(CED_CluEllipseR), 0);
+  //11:
+  TEXT_ID       =ced_register_element(sizeof(CED_TEXT),0); //hauke: the order of this items is important
+  //12:
+  LEGEND_ID	    =ced_register_element(sizeof(CED_Legend), 0);
+  //13: 
+  GEOT_ID       =ced_register_element(sizeof(CED_GeoTube),0);
+  //14:
+  PICKING_TEXT_ID =ced_register_element(sizeof(CED_PICKING_TEXT),0);
+}
+
diff --git a/Reconstruction/PFA/Pandora/CED/CED/ced_cli.h b/Reconstruction/PFA/Pandora/CED/CED/ced_cli.h
new file mode 100644
index 00000000..a92164b0
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/CED/CED/ced_cli.h
@@ -0,0 +1,432 @@
+/* "C" event display.
+ * Enduser accessable API.
+ *
+ * Alexey Zhelezov, DESY/ITEP, 2005 
+ */
+
+#ifndef __CED_CLI_H
+#define __CED_CLI_H
+
+#include <ced_config.h>
+
+
+//important:
+//          - sum of all layers must be smaler than max_layer!
+//          - number_popup_layer must be smaler than number_data_layer
+
+//#define CED_MAX_LAYER       100
+//#define NUMBER_POPUP_LAYER      20
+//#define NUMBER_DATA_LAYER       25
+//#define NUMBER_DETECTOR_LAYER   20
+//
+//
+//#define CED_MAX_LAYER_CHAR 400
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * This is the first function to call (before any other).
+ *
+ *  host - host with CED (must be "localhost")
+ *  port - server port number (let say 7285 :)
+ *
+ * NOTE: ced_register_elements() must be called
+ *       separately.
+ */
+void ced_client_init(const char *host,unsigned short port);
+
+/*
+ * Cancel current event output. So, all elements
+ * queued will be discarded.
+ *
+ * Good to call at the begining of every event processing.
+ */
+void ced_new_event(void);
+
+
+/*
+ * This function really attempt to display event in CED.
+ * When CED is not available, this function discard
+ * current event information.
+ *
+ * NOTE: between ced_new_event() and ced_draw_event()
+ *       must be some element creation calls.
+ */
+void ced_draw_event(void);
+
+/*
+ * This function really attempt to display event in CED.
+ * Unlike ced_draw_event() does not reset the event.
+ *
+ * NOTE: between ced_new_event() and ced_draw_event()
+ *       must be some element creation calls.
+ */
+void ced_send_event(void);
+
+int ced_selected_id(void);
+
+//hauke
+int ced_selected_id_noblock(void);
+
+
+/*********************************************
+ *
+ * The following is elements API.
+ *
+ *********************************************/
+
+void ced_register_elements(void);
+
+typedef enum {
+  CED_TYPE_SHIFT=0x0,
+  CED_LAYER_SHIFT=0x8
+} CED_TYPE_BITS;
+
+/*
+typedef enum {
+  CED_TYPE_SHIFT=0x0,
+  CED_LAYER_SHIFT=0x0
+} CED_TYPE_BITS;
+*/
+
+
+typedef struct {
+  float x;
+  float y;
+  float z;
+} CED_Point;
+
+//class CED_Point{
+//  public:
+//  CED_Point(float _x, float _y, float _z){
+//       x=_x;
+//       y=_y;
+//       z=_z;
+//   }
+//
+//  CED_Point(void){
+//  }
+//  float x;
+//  float y;
+//  float z;
+//}; 
+
+
+/*
+ * Hit element
+ */
+
+typedef enum {
+    CED_HIT_POINT=0,
+    CED_HIT_CROSS,
+    CED_HIT_STAR,
+    CED_HIT_BOX,
+    CED_HIT_VXD
+} CED_HIT_TYPE;
+
+typedef struct {
+  CED_Point p;
+  unsigned type;  // point, star, etc
+  unsigned layer; //layer
+  unsigned color; // in ARGB form (so, 0xff0000 is RED)
+  unsigned size;  // size of point/size of cross
+  unsigned lcioID; // unique id of LICO object
+} CED_Hit;
+
+void ced_hit(float x,float y,float z,unsigned type,unsigned size,unsigned color);
+
+//to give a bit of downward compatibility
+void ced_hit_ID_old(float x,float y,float z,unsigned type, unsigned size,unsigned color, unsigned lcioID);
+
+
+void ced_hit_ID(float x,float y,float z,unsigned type,unsigned layer, unsigned size,unsigned color, unsigned lcioID);
+
+/*
+ * Line element
+ */
+
+typedef struct {
+  CED_Point p0;
+  CED_Point p1;
+  unsigned type;  // not yet defined...
+  unsigned width; // not yet defined...
+  unsigned color; // in ARGB form (so, 0xff0000 is RED)
+  unsigned lcioID; // unique id of LICO object
+
+} CED_Line;
+
+void ced_line(float x0,float y0,float z0,
+	      float x1,float y1,float z1,
+	      unsigned type,unsigned width,unsigned color);
+void ced_line_ID(float x0,float y0,float z0,
+	      float x1,float y1,float z1,
+	      unsigned type,unsigned width,unsigned color, unsigned lcioID);
+
+/*
+ * GeoCylinder
+ */
+typedef struct {
+  float d;       // radius
+  unsigned  sides;   // poligon order
+  float rotate;  // angle degree
+  float z;       // 1/2 length
+  float shift;   // in z
+  unsigned color;
+} CED_GeoCylinder;
+
+/*
+ * GeoTube
+ */
+typedef struct {
+  float r_o;            // outer radius
+  float r_i;            // inner radius
+  unsigned edges_o;     // edges outer
+  unsigned edges_i;     // edges inner
+  float rotate_o;       // angle degree, rotate outer cylinder
+  float rotate_i;       //rotate inner cylinder
+  float z;              // 1/2 length
+  float shift;          // shift in z
+  unsigned color;       // color
+  unsigned type;        //describes the layer where this element lies
+  bool classic_inner;   //draw the outer detector lines in classic view?
+  bool classic_outer;   //draw the inner detector lines in classic view?
+}  CED_GeoTube;
+
+
+/** Same as CED_GeoTube but here as C++ struct with contstructor. This is allows
+ *  to dynamically allocate the detector structure (in an std::vector using the constructor) 
+ *  without knowing the exact number of  detector elements a priori (such as the #layers in the FTD).
+ *  This wasn't poassible with the static allocation using the C type struct.
+ *  ( Used in MArlinCED::drawGearDetector ).
+ */
+struct CEDGeoTube{
+  CEDGeoTube(double  r_out,
+	     double  r_in,
+	     int edges_out,
+	     int edges_in,
+	     double  rotate_out,
+	     double  rotate_in,
+	     double  zlength,
+	     double  zshift,
+	     int col,
+	     int layer,
+	     bool classic_i,
+	     bool classic_o ) :
+    r_o(r_out),
+    r_i(r_in),
+    edges_o(edges_out),
+    edges_i (edges_in),
+    rotate_o (rotate_out),
+    rotate_i (rotate_in),
+    z( zlength),
+    shift (zshift),
+    color (col),
+    type(layer),
+    classic_inner(classic_i),
+    classic_outer(classic_o) {} 
+  float r_o;            // outer radius
+  float r_i;            // inner radius
+  unsigned edges_o;     // edges outer
+  unsigned edges_i;     // edges inner
+  float rotate_o;       // angle degree, rotate outer cylinder
+  float rotate_i;       //rotate inner cylinder
+  float z;              // 1/2 length
+  float shift;          // shift in z
+  unsigned color;       // color
+  unsigned type;        //describes the layer where this element lies
+  bool classic_inner;   //draw the outer detector lines in classic view?
+  bool classic_outer;   //draw the inner detector lines in classic view?
+} ;
+
+void ced_geotubes(unsigned n,CED_GeoTube *all);
+
+
+void ced_geocylinder(float d,unsigned sides,float rotate,float z,float shift,
+		     unsigned color);
+
+void ced_geocylinders(unsigned n,CED_GeoCylinder *all);
+		   
+/*
+ * GeoCylinder rotatable
+ * @author: S.Daraszewicz (UoE)
+ * @date: 01.09.09
+ */
+typedef struct {
+  float d;       	// radius
+  unsigned sides; 	// poligon order
+  float center[3];  // cylinder centre z,y,z
+  float rotate[3];  // rotation angles wrt x,y,z axis
+  float z;       	// length
+  unsigned color;	// colour
+  unsigned layer; 	// layer the Cylinder to be displayed onto
+} CED_GeoCylinderR;
+		   
+		     
+void ced_geocylinder_r(float d, double z, double * center, double * rotate, unsigned sides, 
+		     unsigned int color, int layer);
+		     
+
+  /** GeoBox structure
+   */
+  typedef struct {
+    /** The three box sizes in mm */
+    double sizes[3];
+    /** position of the center of the box*/
+    double center[3];
+    /** box color */
+    unsigned int color;
+  } CED_GeoBox;
+
+  /** Send/Draw a box at position center (x,y,z in mm) with lengths along the 
+   * axes specified in sizes.
+   * 
+   * @author A.Bulgheroni, INFN
+   */
+  void ced_geobox(double * sizes, double * center, unsigned int color );
+  void ced_geobox_ID(double *size, double *position, unsigned int layer, unsigned int color, unsigned int lcio_id);
+  void ced_geobox_r_ID(double *size, double *position, double *rotate, unsigned int layer, unsigned int color, unsigned int lcio_id);
+  void rotate3d(double *vektor, double *rotate);
+
+
+
+   /* 
+   * @author A.Bulgheroni, INFN
+   */
+  void ced_geoboxes( unsigned int nBox, CED_GeoBox * allBoxes);
+
+  typedef struct {
+    /** The three box sizes in mm */
+    double sizes[3];
+    /** position of the center of the box*/
+    double center[3];
+    /** box color */
+    unsigned int color;
+    /** rotation angle in degrees */
+    double rotate[3];
+    /** layer for toggling display */
+    unsigned int layer;
+  } CED_GeoBoxR;
+
+void ced_geobox_r(double * sizes, double * center, double * rotate, unsigned int color, unsigned int layer);
+void ced_geobox_r_solid(double * sizes, double * center, double * rotate, unsigned int color, unsigned int layer);
+
+//hauke
+  typedef struct{
+    char text[1000];
+    int id;
+  } CED_PICKING_TEXT; 
+
+void ced_picking_text(const char *, int number);
+
+//--------
+
+  typedef struct{
+    char text[400];
+    int id;
+  } CED_TEXT; 
+
+
+void ced_describe_layer(const char *, int); //, int, int);
+
+
+  typedef struct{
+    char str[400];
+    unsigned int id;
+  } LAYER_TEXT; 
+
+void ced_layer_text(char *, int);
+//end hauke
+
+
+/*
+ * Energy spectrum colour map legend.
+ * @author: S.Daraszewicz (UoE)
+ * @date: 01.09.09
+ */
+  typedef struct {  
+  	/** min energy on the legend */	
+  	float ene_max;
+  	/** max energy on the legend */
+  	float ene_min;
+  	/** number of ticks on the legend */
+  	unsigned int ticks;
+  	/** spectrum colour steps */
+  	unsigned int color_steps; 
+  	/** spectrum colour matrix */
+  	unsigned int rgb_matrix[512][3]; //FIX ME: 512 size not changed with color_steps
+  	/** LOG or LIN */
+  	char scale;
+  } CED_Legend;
+
+void ced_legend(float ene_min, float ene_max, unsigned int color_steps, unsigned int ** rgb_matrix, unsigned int ticks, char scale);
+
+  typedef struct {  
+  	/** position of the centre of the base */	
+  	double center[3];
+  	/** rotation matrix */
+  	double rotate[3];
+    /** layer for toggling display */
+    unsigned int layer;
+    /** base radius */
+    float base;
+    /** height */
+    float height;
+    /** RGBA color */
+    float RGBAcolor[4];
+    unsigned lcioid; //hauke
+  } CED_ConeR;
+
+
+void ced_cone_r(float base, float height, double *center, double *rotate, unsigned int layer, float *RGBAcolor);
+void ced_cone_r_ID(float base, float height, double *center, double *rotate, unsigned int layer, float *RGBAcolor, int lcioid); //hauke
+
+
+  typedef struct {  
+  	/** position of the centre of the base */	
+  	double center[3];
+  	/** rotation matrix */
+  	double rotate[3];
+    /** layer for toggling display */
+    unsigned int layer;
+    /** xyz size */
+	double size[3];
+    /** RGBA color */
+   	int color;
+    unsigned lcioid; //hauke
+  } CED_EllipsoidR;
+
+
+void ced_ellipsoid_r(double *size, double *center, double *rotate, unsigned int layer, int color);
+void ced_ellipsoid_r_ID(double *size, double *center, double *rotate, unsigned int layer, int color, int lcioid); //hauke
+
+
+  typedef struct {  
+  	/** position of the centre of the base */	
+  	double center[3];
+  	/** rotation matrix */
+  	double rotate[3];
+    /** layer for toggling display */
+    unsigned int layer;
+    /** base radius */
+	float radius;
+	/** half height */
+	float height;
+    /** RGBA color */
+    int color;
+    unsigned lcioid; //hauke
+  } CED_CluEllipseR;
+
+
+void ced_cluellipse_r(float radius, float height, float *center, double *rotate, unsigned int layer, int color);
+void ced_cluellipse_r_ID(float radius, float height, float *center, double *rotate, unsigned int layer, int color, int lcioid); //hauke
+
+
+#ifdef __cplusplus
+ }
+#endif
+	
+
+#endif /* __CED_CLI_H */
diff --git a/Reconstruction/PFA/Pandora/CED/CED/ced_config.h b/Reconstruction/PFA/Pandora/CED/CED/ced_config.h
new file mode 100644
index 00000000..66214ead
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/CED/CED/ced_config.h
@@ -0,0 +1,204 @@
+/********************************************************** 
+* ced_config.h, CED config file                           *    
+* Hauke Hoelbe, DESY, 2011                                *
+* Headerfile to adapt CED before the build.               *
+**********************************************************/
+
+#ifndef __CED_CONFIG
+#define __CED_CONFIG
+
+#include "ced_cli.h"
+
+
+/********************************************************** 
+* Handling                                                *
+**********************************************************/
+//enable zoom function by right click and pull
+#define ZOOM_RIGHT_CLICK                   0
+
+//time when 2 clicks should be a double click, in 1/1000000 secounds
+#define DOUBLE_CLICK_TIME                  300000 
+
+//data layer keys 
+#define DATALAYER_SHORTKEY_00       '0'
+#define DATALAYER_SHORTKEY_01       '1'
+#define DATALAYER_SHORTKEY_02       '2'
+#define DATALAYER_SHORTKEY_03       '3'
+#define DATALAYER_SHORTKEY_04       '4'
+#define DATALAYER_SHORTKEY_05       '5'
+#define DATALAYER_SHORTKEY_06       '6'
+#define DATALAYER_SHORTKEY_07       '7'
+#define DATALAYER_SHORTKEY_08       '8'
+#define DATALAYER_SHORTKEY_09       '9'
+#define DATALAYER_SHORTKEY_10       ')'
+#define DATALAYER_SHORTKEY_11       '!'
+#define DATALAYER_SHORTKEY_12       '@'
+#define DATALAYER_SHORTKEY_13       '#'
+#define DATALAYER_SHORTKEY_14       '$'
+#define DATALAYER_SHORTKEY_15       '%'
+#define DATALAYER_SHORTKEY_16       '^'
+#define DATALAYER_SHORTKEY_17       '&'
+#define DATALAYER_SHORTKEY_18       '*'
+#define DATALAYER_SHORTKEY_19       '('
+#define DATALAYER_SHORTKEY_20       't'
+#define DATALAYER_SHORTKEY_21       'y'
+#define DATALAYER_SHORTKEY_22       'u'
+#define DATALAYER_SHORTKEY_23       'i'
+#define DATALAYER_SHORTKEY_24       'o'
+
+//detector layer keys
+#define DETECTORLAYER_SHORTKEY_00   'j'
+#define DETECTORLAYER_SHORTKEY_01   'k'
+#define DETECTORLAYER_SHORTKEY_02   'l'
+#define DETECTORLAYER_SHORTKEY_03   ';'
+#define DETECTORLAYER_SHORTKEY_04   '\''
+#define DETECTORLAYER_SHORTKEY_05   'p'
+#define DETECTORLAYER_SHORTKEY_06   '['
+#define DETECTORLAYER_SHORTKEY_07   ']'
+#define DETECTORLAYER_SHORTKEY_08   '\\'
+#define DETECTORLAYER_SHORTKEY_09   'T'
+#define DETECTORLAYER_SHORTKEY_10   'Y'
+#define DETECTORLAYER_SHORTKEY_11   'U'
+#define DETECTORLAYER_SHORTKEY_12   'I'
+#define DETECTORLAYER_SHORTKEY_13   'O'
+#define DETECTORLAYER_SHORTKEY_14   'P'
+#define DETECTORLAYER_SHORTKEY_15   '{'
+#define DETECTORLAYER_SHORTKEY_16   '}'
+#define DETECTORLAYER_SHORTKEY_17   '|'
+#define DETECTORLAYER_SHORTKEY_18   'a'
+#define DETECTORLAYER_SHORTKEY_19   'e'
+
+
+/********************************************************** 
+* Colors and appearance                                   *
+**********************************************************/
+//size of the boarder line in filled (new view) of detector components.
+#define CED_GEOTUBE_LINE_WIDTH              0.3
+
+//maximal transparency of boarder lines 
+//#define CED_GEOTUBE_LINE_MAX_TRANS          0.2  
+#define CED_GEOTUBE_LINE_MAX_TRANS          1.0  
+
+
+//names and values of color apairs in popup menu
+#define CED_BGCOLOR_OPTION1_NAME            "Gainsboro" 
+#define CED_BGCOLOR_OPTION1_COLORCODE       0.862745,0.862745,0.862745,0 
+
+#define CED_BGCOLOR_OPTION2_NAME            "Lightgrey" 
+#define CED_BGCOLOR_OPTION2_COLORCODE       0.827451,0.827451,0.827451,0
+
+#define CED_BGCOLOR_OPTION3_NAME            "Darkgray" 
+#define CED_BGCOLOR_OPTION3_COLORCODE       0.662745,0.662745,0.662745,0
+
+#define CED_BGCOLOR_OPTION4_NAME            "Gray" 
+#define CED_BGCOLOR_OPTION4_COLORCODE       0.501961,0.501961,0.501961,0
+
+#define CED_BGCOLOR_OPTION5_NAME            "Silver" 
+#define CED_BGCOLOR_OPTION5_COLORCODE       0.7529,0.7529,0.7529,0
+
+#define CED_BGCOLOR_OPTION6_NAME            "Dimgray" 
+#define CED_BGCOLOR_OPTION6_COLORCODE       0.4118,0.4118,0.4118,0
+
+#define CED_BGCOLOR_OPTION7_NAME            "Lightsteelblue" 
+#define CED_BGCOLOR_OPTION7_COLORCODE       0.6902,0.7686 ,0.8706,0
+
+#define CED_BGCOLOR_OPTION8_NAME            "Steelblue" 
+#define CED_BGCOLOR_OPTION8_COLORCODE       0.2745,0.5098,0.70588,0
+
+#define CED_BGCOLOR_OPTION9_NAME            "Seagreen" 
+#define CED_BGCOLOR_OPTION9_COLORCODE       0.18039,0.54509,0.34117,0
+
+#define CED_BGCOLOR_OPTION10_NAME           "Orange" 
+#define CED_BGCOLOR_OPTION10_COLORCODE      1,0.647,0,0
+
+#define CED_BGCOLOR_OPTION11_NAME           "Yellow" 
+#define CED_BGCOLOR_OPTION11_COLORCODE      1,1,0,0
+
+#define CED_BGCOLOR_OPTION12_NAME           "Violett" 
+#define CED_BGCOLOR_OPTION12_COLORCODE      0.9333,0.5098,0.9333,0
+
+#define CED_BGCOLOR_OPTION13_NAME           "Black" 
+#define CED_BGCOLOR_OPTION13_COLORCODE      0,0,0,0
+
+#define CED_BGCOLOR_OPTION14_NAME           "Blue" 
+#define CED_BGCOLOR_OPTION14_COLORCODE      0,0.2,0.4,0
+
+#define CED_BGCOLOR_OPTION15_NAME           "White" 
+#define CED_BGCOLOR_OPTION15_COLORCODE      1,1,1,0
+
+
+
+
+//Color of xyz axes 
+#define AXES_COLOR                          0.2,0.2,0.8
+
+//Width of xyz axes 
+#define AXES_LINE_SIZE                      0.5
+
+
+//Help frame: Frame fill color, and transp
+#define HELP_FRAME_FILL_COLOR               0.5,1,1,0.8
+
+//Help frame: Frame boarder color and transp
+#define HELP_FRAME_BOARDER_COLOR            0.1,0.8,1.0,0.8 
+
+//Help frame: Frame boarder line width
+#define HELP_FRAME_BOARDER_LINE_SIZE        3. 
+
+//Help frame: Text color, and transp
+#define HELP_FRAME_TEXT_COLOR               0.0,0.0,0.0 
+
+
+
+
+/********************************************************** 
+* Layers                                                  *
+**********************************************************/
+//number of total number of layers 
+#define CED_MAX_LAYER                       100
+
+//number of layers shown in popup menu
+#define NUMBER_POPUP_LAYER                  20
+
+//number of layers reserved for data
+#define NUMBER_DATA_LAYER                   25
+
+//number of layers reserved for detector components 
+#define NUMBER_DETECTOR_LAYER               20
+
+//layer description text: maximal number of chars for one entry
+#define CED_MAX_LAYER_CHAR                  400
+
+/********************************************************** 
+* Graphics                                                *
+**********************************************************/
+
+//Camera field of view, in degree 
+#define CAMERA_FIELD_OF_VIEW                45
+
+//Camera min distance (hint: min and max should be close together)
+#define CAMERA_MIN_DISTANCE                 100
+
+//Camera max distance (hint: min and max should be close together)
+#define CAMERA_MAX_DISTANCE                 50000.0*mm.sf+50000/mm.sf 
+
+//Where the camera stands
+#define CAMERA_POSITION                     0,0,2000
+
+
+
+//Fisheye alpha factor
+#define FISHEYE_ALPHA                       1e-3
+#define FISHEYE_ZOOM                        8.
+
+/********************************************************** 
+* Debug                                                   *
+**********************************************************/
+
+//show pickable points:
+//#define DEBUG_PICKING 1
+
+
+
+
+#endif 
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/CMakeLists.txt b/Reconstruction/PFA/Pandora/GaudiPandora/CMakeLists.txt
new file mode 100644
index 00000000..c60217b9
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/CMakeLists.txt
@@ -0,0 +1,51 @@
+gaudi_subdir(GaudiPandora v0r0)
+
+find_package(CLHEP REQUIRED)
+#find_package(GSL REQUIRED )##don't use this,  use CEPC LCIO version one , due to the ClusterShape.cc file which is from LCIO
+#message("GSL: ${GSL_LIBRARIES} ")
+set (gsl_include "/cvmfs/cepc.ihep.ac.cn/software/cepcsoft/x86_64-sl6-gcc49/external/GSL/1.14/install/include")
+set (gsl_lib1 "/cvmfs/cepc.ihep.ac.cn/software/cepcsoft/x86_64-sl6-gcc49/external/GSL/1.14/install/lib/libgsl.so")
+set (gsl_lib2 "/cvmfs/cepc.ihep.ac.cn/software/cepcsoft/x86_64-sl6-gcc49/external/GSL/1.14/install/lib/libgslcblas.so")
+find_package(LCIO REQUIRED ) 
+find_package(GEAR REQUIRED)
+message("ENV GEAR: $ENV{GEAR}")
+
+
+find_package(EDM4HEP REQUIRED )
+include_directories(${EDM4HEP_INCLUDE_DIR})
+link_libraries("/cvmfs/cepcsw.ihep.ac.cn/prototype/releases/externals/97.0.0/EDM4hep/lib64/libedm4hep.so")
+find_package(PandoraSDK REQUIRED ) 
+include_directories(${PandoraSDK_INCLUDE_DIRS})
+link_libraries(${PandoraSDK_LIBRARIES})
+find_package(LCContent REQUIRED ) 
+include_directories(${LCContent_INCLUDE_DIRS})
+link_libraries(${LCContent_LIBRARIES})
+
+
+list(APPEND CMAKE_MODULE_PATH "$ENV{ROOTSYS}/etc/cmake/")
+find_package(ROOT 5.26.00 REQUIRED COMPONENTS Eve Geom RGL EG)
+
+include_directories("../CED/CED/")
+include_directories("../MarlinUtil/01-08/source/")
+
+gaudi_depends_on_subdirs(
+    Service/EventSeeder
+    Service/GearSvc
+)
+
+set(dir_srcs
+    src/PandoraPFAlg.cpp
+    src/MCParticleCreator.cpp
+    src/GeometryCreator.cpp
+    src/CaloHitCreator.cpp
+    src/TrackCreator.cpp
+    src/PfoCreator.cpp
+    ../CED/CED/*.cc
+    ../MarlinUtil/01-08/source/*.cc
+)
+set(dir_include include)
+# Modules
+gaudi_add_module(GaudiPandora ${dir_srcs}
+    INCLUDE_DIRS ${gsl_include} ${dir_include} GaudiKernel FWCore CLHEP  ${LCIO_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS} gear  
+    LINK_LIBRARIES ${gsl_lib1} ${gsl_lib2} GaudiKernel FWCore CLHEP ROOT ${LCIO_LIBRARIES} $ENV{GEAR}/lib/libgear.so $ENV{GEAR}/lib/libgearxml.so 
+)
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/include/CaloHitCreator.h b/Reconstruction/PFA/Pandora/GaudiPandora/include/CaloHitCreator.h
new file mode 100644
index 00000000..4ea4e9b0
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/include/CaloHitCreator.h
@@ -0,0 +1,282 @@
+/**
+ *  @file   MarlinPandora/include/CaloHitCreator.h
+ * 
+ *  @brief  Header file for the calo hit creator class.
+ * 
+ *  $Log: $
+ */
+
+#ifndef CALO_HIT_CREATOR_H
+#define CALO_HIT_CREATOR_H 1
+
+#include "GaudiKernel/ISvcLocator.h"
+#include "edm4hep/CalorimeterHit.h"
+
+#include "gear/LayerLayout.h"
+
+#include "Api/PandoraApi.h"
+
+#include <string>
+
+typedef std::vector<edm4hep::CalorimeterHit *> CalorimeterHitVector;
+
+namespace gear { class GearMgr; }
+
+class CollectionMaps;
+/**
+ *  @brief  CaloHitCreator class
+ */
+class CaloHitCreator
+{
+public:
+    typedef std::vector<std::string> StringVector;
+
+    /**
+     *  @brief  Settings class
+     */
+    class Settings
+    {
+    public:
+        /**
+         *  @brief  Default constructor
+         */
+        Settings();
+
+        StringVector    m_eCalCaloHitCollections;               ///< The ecal calorimeter hit collections
+        StringVector    m_hCalCaloHitCollections;               ///< The hcal calorimeter hit collections
+        StringVector    m_lCalCaloHitCollections;               ///< The lcal calorimeter hit collections
+        StringVector    m_lHCalCaloHitCollections;              ///< The lhcal calorimeter hit collections
+        StringVector    m_muonCaloHitCollections;               ///< The muon calorimeter hit collections
+
+        float           m_absorberRadLengthECal;                ///< The absorber radiation length in the ECal
+        float           m_absorberIntLengthECal;                ///< The absorber interaction length in the ECal
+        float           m_absorberRadLengthHCal;                ///< The absorber radiation length in the HCal
+        float           m_absorberIntLengthHCal;                ///< The absorber interaction length in the HCal
+        float           m_absorberRadLengthOther;               ///< The absorber radiation length in other detector regions
+        float           m_absorberIntLengthOther;               ///< The absorber interaction length in other detector regions
+
+        float           m_eCalToMip;                            ///< The calibration from deposited ECal energy to mip
+        float           m_hCalToMip;                            ///< The calibration from deposited HCal energy to mip
+        float           m_muonToMip;                            ///< The calibration from deposited Muon energy to mip
+        float           m_eCalMipThreshold;                     ///< Threshold for creating calo hits in the ECal, units mip
+        float           m_hCalMipThreshold;                     ///< Threshold for creating calo hits in the HCal, units mip
+        float           m_muonMipThreshold;                     ///< Threshold for creating calo hits in the HCal, units mip
+
+        float           m_eCalToEMGeV;                          ///< The calibration from deposited ECal energy to EM energy
+        float           m_eCalToHadGeVBarrel;                   ///< The calibration from deposited ECal barrel energy to hadronic energy
+        float           m_eCalToHadGeVEndCap;                   ///< The calibration from deposited ECal endcap energy to hadronic energy
+        float           m_hCalToEMGeV;                          ///< The calibration from deposited HCal energy to EM energy
+        float           m_hCalToHadGeV;                         ///< The calibration from deposited HCal energy to hadronic energy
+        int             m_muonDigitalHits;                      ///< Muon hits are treated as digital (energy from hit count)
+        float           m_muonHitEnergy;                        ///< The energy for a digital muon calorimeter hit, units GeV
+
+        float           m_maxHCalHitHadronicEnergy;             ///< The maximum hadronic energy allowed for a single hcal hit
+        int             m_nOuterSamplingLayers;                 ///< Number of layers from edge for hit to be flagged as an outer layer hit
+        float           m_layersFromEdgeMaxRearDistance;        ///< Maximum number of layers from candidate outer layer hit to rear of detector
+
+        int             m_hCalEndCapInnerSymmetryOrder;         ///< HCal end cap inner symmetry order (missing from ILD00 gear file)
+        float           m_hCalEndCapInnerPhiCoordinate;         ///< HCal end cap inner phi coordinate (missing from ILD00 gear file)
+
+        // For Strip Splitting method and hybrid ECAL.
+        int             m_stripSplittingOn;                     ///< To use SSA, this should be true (default is false)
+        int             m_useEcalScLayers;                      ///< To use scintillator layers ~ hybrid ECAL, this should be true (default is false)
+        float           m_eCalSiToMip;                          ///< The calibration from deposited Si-layer energy to mip
+        float           m_eCalScToMip;                          ///< The calibration from deposited Sc-layer energy to mip
+        float           m_eCalSiMipThreshold;                   ///< Threshold for creating calo hits in the Si-layers of ECAL, units mip
+        float           m_eCalScMipThreshold;                   ///< Threshold for creating calo hits in the Sc-layers of ECAL, units mip
+        float           m_eCalSiToEMGeV;                        ///< The calibration from deposited Si-layer energy to EM energy
+        float           m_eCalScToEMGeV;                        ///< The calibration from deposited Sc-layer energy to EM energy
+        float           m_eCalSiToHadGeVBarrel;                 ///< The calibration from deposited Si-layer energy on the enecaps to hadronic energy
+        float           m_eCalScToHadGeVBarrel;                 ///< The calibration from deposited Sc-layer energy on the endcaps to hadronic energy
+        float           m_eCalSiToHadGeVEndCap;                 ///< The calibration from deposited Si-layer energy on the enecaps to hadronic energy
+        float           m_eCalScToHadGeVEndCap;                 ///< The calibration from deposited Sc-layer energy on the endcaps to hadronic energy
+    };
+
+    /**
+     *  @brief  Constructor
+     * 
+     *  @param  settings the creator settings
+     *  @param  pPandora address of the relevant pandora instance
+     */
+     CaloHitCreator(const Settings &settings, const pandora::Pandora *const pPandora, ISvcLocator* svcloc, bool encoder_style);
+
+    /**
+     *  @brief  Destructor
+     */
+     ~CaloHitCreator();
+
+    /**
+     *  @brief  Create calo hits
+     * 
+     *  @param  pLCEvent the lcio event
+     */    
+    //pandora::StatusCode CreateCaloHits(const LCEvent *const pLCEvent);
+    pandora::StatusCode CreateCaloHits(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Get the calorimeter hit vector
+     * 
+     *  @return The calorimeter hit vector
+     */
+    const CalorimeterHitVector &GetCalorimeterHitVector() const;
+
+    /**
+     *  @brief  Reset the calo hit creator
+     */
+    void Reset();
+
+private:
+    /**
+     *  @brief  Create ecal calo hits
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    //pandora::StatusCode CreateECalCaloHits(const EVENT::LCEvent *const pLCEvent);
+    pandora::StatusCode CreateECalCaloHits(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Create hcal calo hits
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    pandora::StatusCode CreateHCalCaloHits(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Create muon calo hits
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    pandora::StatusCode CreateMuonCaloHits(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Create lcal calo hits
+     * 
+     *  @param  pLCEvent the lcio event
+     */    
+    pandora::StatusCode CreateLCalCaloHits(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Create lhcal calo hits
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    pandora::StatusCode CreateLHCalCaloHits(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Get common calo hit properties: position, parent address, input energy and time
+     * 
+     *  @param  pCaloHit the lcio calorimeter hit
+     *  @param  caloHitParameters the calo hit parameters to populate
+     */
+    void GetCommonCaloHitProperties(const edm4hep::CalorimeterHit *const pCaloHit, PandoraApi::CaloHit::Parameters &caloHitParameters) const;
+
+    /**
+     *  @brief  Get end cap specific calo hit properties: cell size, absorber radiation and interaction lengths, normal vector
+     * 
+     *  @param  pCaloHit the lcio calorimeter hit
+     *  @param  layerLayout the gear end cap layer layout
+     *  @param  caloHitParameters the calo hit parameters to populate
+     *  @param  absorberCorrection to receive the absorber thickness correction for the mip equivalent energy
+     */
+    void GetEndCapCaloHitProperties(const edm4hep::CalorimeterHit *const pCaloHit, const gear::LayerLayout &layerLayout,
+        PandoraApi::CaloHit::Parameters &caloHitParameters, float &absorberCorrection) const;
+
+    /**
+     *  @brief  Get barrel specific calo hit properties: cell size, absorber radiation and interaction lengths, normal vector
+     * 
+     *  @param  pCaloHit the lcio calorimeter hit
+     *  @param  layerLayout the gear barrel layer layout
+     *  @param  barrelSymmetryOrder the barrel order of symmetry
+     *  @param  barrelPhi0 the barrel orientation
+     *  @param  staveNumber the stave number
+     *  @param  caloHitParameters the calo hit parameters to populate
+     *  @param  absorberCorrection to receive the absorber thickness correction for the mip equivalent energy
+     */
+    void GetBarrelCaloHitProperties(const edm4hep::CalorimeterHit *const pCaloHit, const gear::LayerLayout &layerLayout,
+        unsigned int barrelSymmetryOrder, float barrelPhi0, unsigned int staveNumber, PandoraApi::CaloHit::Parameters &caloHitParameters,
+        float &absorberCorrection) const;
+
+    /**
+     *  @brief  Get number of active layers from position of a calo hit to the edge of the detector
+     * 
+     *  @param  pCaloHit the lcio calorimeter hit
+     */
+    int GetNLayersFromEdge(const edm4hep::CalorimeterHit *const pCaloHit) const;
+
+    /**
+     *  @brief  Get the maximum radius of a calo hit in a polygonal detector structure
+     * 
+     *  @param  pCaloHit the lcio calorimeter hit
+     *  @param  symmetryOrder the symmetry order
+     *  @param  phi0 the angular orientation
+     * 
+     *  @return the maximum radius
+     */
+    float GetMaximumRadius(const edm4hep::CalorimeterHit *const pCaloHit, const unsigned int symmetryOrder, const float phi0) const;
+
+    /**
+     *  @brief  Get the layer coding string from the provided cell id encoding string
+     * 
+     *  @param  encodingString the cell id encoding string
+     * 
+     *  @return the layer coding string
+     */
+    std::string GetLayerCoding(const std::string &encodingString) const;
+
+    /**
+     *  @brief  Get the stave coding string from the provided cell id encoding string
+     * 
+     *  @param  encodingString the cell id encoding string
+     * 
+     *  @return the stave coding string
+     */
+    std::string GetStaveCoding(const std::string &encodingString) const;
+
+    const Settings                      m_settings;                         ///< The calo hit creator settings
+
+    const pandora::Pandora             *m_pPandora;                         ///< Address of the pandora object to create calo hits
+
+    float                               m_eCalBarrelOuterZ;                 ///< ECal barrel outer z coordinate
+    float                               m_hCalBarrelOuterZ;                 ///< HCal barrel outer z coordinate
+    float                               m_muonBarrelOuterZ;                 ///< Muon barrel outer z coordinate
+    float                               m_coilOuterR;                       ///< Coil outer r coordinate
+
+    float                               m_eCalBarrelInnerPhi0;              ///< ECal barrel inner phi0 coordinate
+    unsigned int                        m_eCalBarrelInnerSymmetry;          ///< ECal barrel inner symmetry order
+    float                               m_hCalBarrelInnerPhi0;              ///< HCal barrel inner phi0 coordinate
+    unsigned int                        m_hCalBarrelInnerSymmetry;          ///< HCal barrel inner symmetry order
+    float                               m_muonBarrelInnerPhi0;              ///< Muon barrel inner phi0 coordinate
+    unsigned int                        m_muonBarrelInnerSymmetry;          ///< Muon barrel inner symmetry order
+
+    float                               m_hCalEndCapOuterR;                 ///< HCal endcap outer r coordinate
+    float                               m_hCalEndCapOuterZ;                 ///< HCal endcap outer z coordinate
+    float                               m_hCalBarrelOuterR;                 ///< HCal barrel outer r coordinate
+    float                               m_hCalBarrelOuterPhi0;              ///< HCal barrel outer phi0 coordinate
+    unsigned int                        m_hCalBarrelOuterSymmetry;          ///< HCal barrel outer symmetry order
+
+    float                               m_hCalBarrelLayerThickness;         ///< HCal barrel layer thickness
+    float                               m_hCalEndCapLayerThickness;         ///< HCal endcap layer thickness
+
+    CalorimeterHitVector                m_calorimeterHitVector;             ///< The calorimeter hit vector
+    std::string                         m_encoder_str;
+    std::string                         m_encoder_str_MUON ; 
+    std::string                         m_encoder_str_LCal ; 
+    std::string                         m_encoder_str_LHCal; 
+    gear::GearMgr* _GEAR;
+};
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+inline const CalorimeterHitVector &CaloHitCreator::GetCalorimeterHitVector() const
+{
+    return m_calorimeterHitVector;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+inline void CaloHitCreator::Reset()
+{
+    m_calorimeterHitVector.clear();
+}
+
+#endif // #ifndef CALO_HIT_CREATOR_H
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/include/GeometryCreator.h b/Reconstruction/PFA/Pandora/GaudiPandora/include/GeometryCreator.h
new file mode 100644
index 00000000..8be08e2f
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/include/GeometryCreator.h
@@ -0,0 +1,158 @@
+/**
+ *  @file   MarlinPandora/include/GeometryCreator.h
+ * 
+ *  @brief  Header file for the geometry creator class.
+ * 
+ *  $Log: $
+ */
+
+#ifndef GEOMETRY_CREATOR_H
+#define GEOMETRY_CREATOR_H 1
+
+#include "Api/PandoraApi.h"
+
+#include "GaudiKernel/ISvcLocator.h"
+//namespace gear { class CalorimeterParameters; }
+namespace gear { class CalorimeterParameters; class GearMgr; }
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+/**
+ *  @brief  GeometryCreator class
+ */
+class GeometryCreator
+{
+public:
+    /**
+     *  @brief  Settings class
+     */
+    class Settings
+    {
+    public:
+        /**
+         *  @brief  Default constructor
+         */
+        Settings();
+
+        float           m_absorberRadLengthECal;                ///< The absorber radiation length in the ECal
+        float           m_absorberIntLengthECal;                ///< The absorber interaction length in the ECal
+        float           m_absorberRadLengthHCal;                ///< The absorber radiation length in the HCal
+        float           m_absorberIntLengthHCal;                ///< The absorber interaction length in the HCal
+        float           m_absorberRadLengthOther;               ///< The absorber radiation length in other detector regions
+        float           m_absorberIntLengthOther;               ///< The absorber interaction length in other detector regions
+
+        int             m_eCalEndCapInnerSymmetryOrder;         ///< ECal end cap inner symmetry order (missing from ILD gear files)
+        float           m_eCalEndCapInnerPhiCoordinate;         ///< ECal end cap inner phi coordinate (missing from ILD gear files)
+        int             m_eCalEndCapOuterSymmetryOrder;         ///< ECal end cap outer symmetry order (missing from ILD gear files)
+        float           m_eCalEndCapOuterPhiCoordinate;         ///< ECal end cap outer phi coordinate (missing from ILD gear files)
+
+        int             m_hCalEndCapInnerSymmetryOrder;         ///< HCal end cap inner symmetry order (missing from ILD gear files)
+        float           m_hCalEndCapInnerPhiCoordinate;         ///< HCal end cap inner phi coordinate (missing from ILD gear files)
+        int             m_hCalEndCapOuterSymmetryOrder;         ///< HCal end cap outer symmetry order (missing from ILD gear files)
+        float           m_hCalEndCapOuterPhiCoordinate;         ///< HCal end cap outer phi coordinate (missing from ILD gear files)
+
+        int             m_hCalRingInnerSymmetryOrder;           ///< HCal ring inner symmetry order (missing from ILD gear files)
+        float           m_hCalRingInnerPhiCoordinate;           ///< HCal ring inner phi coordinate (missing from ILD gear files)
+        int             m_hCalRingOuterSymmetryOrder;           ///< HCal ring outer symmetry order (missing from ILD gear files)
+        float           m_hCalRingOuterPhiCoordinate;           ///< HCal ring outer phi coordinate (missing from ILD gear files)
+    };
+
+    /**
+     *  @brief  Constructor
+     * 
+     *  @param  settings the creator settings
+     *  @param  pPandora address of the relevant pandora instance
+     */
+     GeometryCreator(const Settings &settings, const pandora::Pandora *const pPandora);
+
+    /**
+     *  @brief  Destructor
+     */
+     ~GeometryCreator();
+
+    /**
+     *  @brief  Create geometry
+     */
+    //pandora::StatusCode CreateGeometry() const;
+    pandora::StatusCode CreateGeometry(ISvcLocator* svcloc);
+
+private:
+    typedef std::map<pandora::SubDetectorType, PandoraApi::Geometry::SubDetector::Parameters> SubDetectorTypeMap;
+    typedef std::map<std::string, PandoraApi::Geometry::SubDetector::Parameters> SubDetectorNameMap;
+
+    /**
+     *  @brief  Set mandatory sub detector parameters
+     * 
+     *  @param  subDetectorTypeMap the sub detector type map
+     */
+    void SetMandatorySubDetectorParameters(SubDetectorTypeMap &subDetectorTypeMap) const;
+
+    /**
+     *  @brief  Set additional sub detector parameters
+     * 
+     *  @param  subDetectorNameMap the sub detector name map (for smaller sub detectors, identified uniquely only by name)
+     */
+    void SetAdditionalSubDetectorParameters(SubDetectorNameMap &subDetectorNameMap) const;
+
+    /**
+     *  @brief  Set sub detector parameters to their gear default values
+     * 
+     *  @param  inputParameters input parameters, from gear
+     *  @param  subDetectorName the sub detector name
+     *  @param  subDetectorType the sub detector type
+     *  @param  parameters the sub detector parameters
+     */
+    void SetDefaultSubDetectorParameters(const gear::CalorimeterParameters &inputParameters, const std::string &subDetectorName,
+        const pandora::SubDetectorType subDetectorType, PandoraApi::Geometry::SubDetector::Parameters &parameters) const;
+
+    /**
+     *  @brief  Set positions of gaps in ILD detector and add information missing from GEAR parameters file
+     * 
+     *  @param  subDetectorTypeMap the sub detector type map
+     *  @param  subDetectorNameMap the sub detector name map (for smaller sub detectors, identified uniquely only by name)
+     */
+    pandora::StatusCode SetILDSpecificGeometry(SubDetectorTypeMap &subDetectorTypeMap, SubDetectorNameMap &subDetectorNameMap) const;
+
+    /**
+     *  @brief  Add information missing from GEAR parameters file for ILD SDHCAL detector
+     * 
+     *  @param  subDetectorTypeMap the sub detector type map
+     */
+    pandora::StatusCode SetILD_SDHCALSpecificGeometry(SubDetectorTypeMap &subDetectorTypeMap) const;
+
+    /**
+     *  @brief  Specify positions of hcal barrel box gaps - ILD specific
+     */
+    pandora::StatusCode CreateHCalBarrelBoxGaps() const;
+
+    /**
+     *  @brief  Specify positions of hcal end cap box gaps - ILD specific
+     */
+    pandora::StatusCode CreateHCalEndCapBoxGaps() const;
+
+    /**
+     *  @brief  Specify positions of hcal barrel concentric polygon gaps - ILD specific
+     */
+    pandora::StatusCode CreateHCalBarrelConcentricGaps() const;
+
+    /**
+     *  @brief  Create box gaps at regular positions on polygonal prism, oriented along main z axis - ILD specific
+     * 
+     *  @param  symmetryOrder the pandora geometry parameters
+     *  @param  phi0 the phi coordinate
+     *  @param  innerRadius the inner r coordinate
+     *  @param  outerRadius the outer r coordinate
+     *  @param  minZ the minimum z coordinate
+     *  @param  maxZ the maximum z coordinate
+     *  @param  gapWidth the gap width
+     *  @param  vertexOffset position offset for vertex that doesn't point back to origin of xy plane
+     */
+    pandora::StatusCode CreateRegularBoxGaps(unsigned int symmetryOrder, float phi0, float innerRadius, float outerRadius, float minZ,
+        float maxZ, float gapWidth, pandora::CartesianVector vertexOffset = pandora::CartesianVector(0, 0, 0)) const;
+
+    const Settings          m_settings;                     ///< The geometry creator settings
+    const pandora::Pandora *m_pPandora;                     ///< Address of the pandora object to create the geometry
+    gear::GearMgr* _GEAR;
+};
+
+#endif // #ifndef GEOMETRY_CREATOR_H
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/include/MCParticleCreator.h b/Reconstruction/PFA/Pandora/GaudiPandora/include/MCParticleCreator.h
new file mode 100644
index 00000000..46dbb55e
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/include/MCParticleCreator.h
@@ -0,0 +1,100 @@
+/**
+ *  @file   MarlinPandora/include/MCParticleCreator.h
+ * 
+ *  @brief  Header file for the mc particle creator class.
+ * 
+ *  $Log: $
+ */
+
+#ifndef MC_PARTICLE_CREATOR_H
+#define MC_PARTICLE_CREATOR_H 1
+
+#include "edm4hep/MCParticle.h"
+#include "Api/PandoraApi.h"
+
+#include "CaloHitCreator.h"
+#include "TrackCreator.h"
+/**
+ *  @brief  MCParticleCreator class
+ */
+//typedef std::map<unsigned int, edm4hep::MCParticle*> id_MCParticleMap;
+//typedef std::map<unsigned int, const edm4hep::MCParticle*> id_MCParticleMap;
+
+class CollectionMaps;
+
+class MCParticleCreator
+{
+public:
+    typedef std::vector<std::string> StringVector;
+
+    /**
+     *  @brief  Settings class
+     */
+    class Settings
+    {
+    public:
+        /**
+         *  @brief  Default constructor
+         */
+        Settings();
+
+        StringVector    m_mcParticleCollections;                ///< The mc particle collections
+        StringVector    m_CaloHitRelationCollections;         ///< The SimCaloHit to CaloHit particle relations
+        StringVector    m_TrackRelationCollections;           ///< The SimTrackerHit to TrackerHit particle relations
+        float           m_bField;                             ///< m_bField
+    };
+
+    /**
+     *  @brief  Constructor
+     * 
+     *  @param  settings the creator settings
+     *  @param  pPandora address of the relevant pandora instance
+     */
+     MCParticleCreator(const Settings &settings, const pandora::Pandora *const pPandora);
+
+    /**
+     *  @brief  Destructor
+     */
+     ~MCParticleCreator();
+
+    /**
+     *  @brief  Create MCParticles
+     * 
+     *  @param  pLCEvent the lcio event
+     */    
+    //pandora::StatusCode CreateMCParticles(const EVENT::LCEvent *const pLCEvent) const;
+    //pandora::StatusCode CreateMCParticles(const std::map<std::string, const podio::CollectionBase*>& collectionMap ) const;
+    pandora::StatusCode CreateMCParticles(const CollectionMaps& collectionMaps ) const;
+
+    /**
+     *  @brief  Create Track to mc particle relationships
+     *
+     *  @param  pLCEvent the lcio event
+     *  @param  trackVector the vector containing all tracks successfully passed to pandora
+     */
+ //   pandora::StatusCode CreateTrackToMCParticleRelationships(const EVENT::LCEvent *const pLCEvent, const TrackVector &trackVector) const;
+     pandora::StatusCode CreateTrackToMCParticleRelationships(const CollectionMaps& collectionMaps, const TrackVector &trackVector) const;
+
+     void Reset();
+    /**
+     *  @brief  Create calo hit to mc particle relationships
+     *
+     *  @param  pLCEvent the lcio event
+     *  @param  calorimeterHitVector the vector containing all calorimeter hits successfully passed to pandora
+     */
+//    pandora::StatusCode CreateCaloHitToMCParticleRelationships(const EVENT::LCEvent *const pLCEvent, const CalorimeterHitVector &calorimeterHitVector) const;
+      pandora::StatusCode CreateCaloHitToMCParticleRelationships(const CollectionMaps& collectionMaps, const CalorimeterHitVector &calorimeterHitVector) const;
+
+private:
+    const Settings          m_settings;                         ///< The mc particle creator settings
+    const pandora::Pandora *m_pPandora;                         ///< Address of the pandora object to create the mc particles
+    const float             m_bField;                           ///< The bfield
+    std::map<unsigned int, const edm4hep::MCParticle*>*  m_id_pMC_map;
+};
+
+inline void MCParticleCreator::Reset()
+{
+    m_id_pMC_map->clear();
+}
+
+#endif // #ifndef MC_PARTICLE_CREATOR_H
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/include/PandoraPFAlg.h b/Reconstruction/PFA/Pandora/GaudiPandora/include/PandoraPFAlg.h
new file mode 100644
index 00000000..8774006c
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/include/PandoraPFAlg.h
@@ -0,0 +1,320 @@
+#ifndef PandoraPFAlg_H
+#define PandoraPFAlg_H
+
+#include "FWCore/DataHandle.h"
+#include "GaudiAlg/GaudiAlgorithm.h"
+#include <gsl/gsl_rng.h>
+#include "edm4hep/ClusterCollection.h"
+#include "edm4hep/ReconstructedParticleCollection.h"
+#include "edm4hep/EventHeaderCollection.h"
+#include "edm4hep/SimTrackerHitCollection.h"
+#include "edm4hep/TrackerHitCollection.h"
+#include "edm4hep/CalorimeterHitCollection.h"
+#include "edm4hep/VertexCollection.h"
+#include "edm4hep/TrackCollection.h"
+#include "edm4hep/MCParticle.h" 
+#include "edm4hep/MCParticleCollection.h"
+#include "edm4hep/MCRecoCaloAssociation.h"
+#include "edm4hep/MCRecoTrackerAssociation.h"
+#include "edm4hep/MCRecoTrackerAssociationCollection.h"
+#include "edm4hep/MCRecoCaloAssociationCollection.h"
+#include "edm4hep/MCRecoParticleAssociation.h"
+#include "edm4hep/MCRecoParticleAssociationCollection.h"
+
+#include "Api/PandoraApi.h"
+
+#ifdef MONITORING
+#include "TApplication.h"
+#endif
+
+#include <iostream>
+#include <random>
+#include <string>
+#include <unistd.h>
+
+
+#include "CaloHitCreator.h"
+#include "GeometryCreator.h"
+#include "MCParticleCreator.h"
+#include "PfoCreator.h"
+#include "TrackCreator.h"
+
+#include "TROOT.h"
+#include "TTree.h"
+#include "TFile.h"
+
+
+/* PandoraPFAlg ========== <br>
+ * 
+ */
+namespace pandora {class Pandora;}
+
+class IEventSeeder;
+
+class CollectionMaps
+{
+public:
+    /**
+     *  @brief  Default constructor
+     */
+    CollectionMaps();
+    void clear();
+    std::map<std::string, const edm4hep::MCParticleCollection*> CollectionMap_MC;
+    std::map<std::string, const edm4hep::CalorimeterHitCollection*> CollectionMap_CaloHit;
+    std::map<std::string, const edm4hep::VertexCollection*> CollectionMap_Vertex;
+    std::map<std::string, const edm4hep::TrackCollection*> CollectionMap_Track;
+
+    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;
+};
+
+
+
+class PandoraPFAlg : public GaudiAlgorithm
+{
+  //friend class AlgFactory<PandoraPFAlg>;//gives error in 97 version
+ 
+public:
+ 
+  PandoraPFAlg(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 FinaliseSteeringParameters();
+  void FinaliseSteeringParameters(ISvcLocator* svcloc);
+  pandora::StatusCode RegisterUserComponents() const;
+  void Reset();
+  typedef std::vector<float> FloatVector;
+  typedef std::vector<std::string> StringVector;
+
+
+  class Settings
+  {
+  public:
+      /**
+       *  @brief  Default constructor
+       */
+      Settings();
+
+      std::string     m_pandoraSettingsXmlFile;           ///< The pandora settings xml file
+
+      float           m_innerBField;                      ///< The bfield in the main tracker, ecal and hcal, units Tesla
+      float           m_muonBarrelBField;                 ///< The bfield in the muon barrel, units Tesla
+      float           m_muonEndCapBField;                 ///< The bfield in the muon endcap, units Tesla
+
+      FloatVector     m_inputEnergyCorrectionPoints;      ///< The input energy points for non-linearity energy correction
+      FloatVector     m_outputEnergyCorrectionPoints;     ///< The output energy points for non-linearity energy correction
+  };
+
+
+    /**
+     *  @brief  Get address of the pandora instance
+     * 
+     *  @return address of the pandora instance
+     */
+    const pandora::Pandora *GetPandora() const;
+    StatusCode updateMap();
+    StatusCode updateMap(CollectionMaps & tmp_map);
+    StatusCode Ana();
+    StatusCode CreateMCRecoParticleAssociation();
+    //StatusCode Create_MC(); 
+protected:
+ 
+  typedef std::vector<float> FloatVec;
+
+  int _nEvt ;
+
+  IEventSeeder * _SEEDER;
+ 
+
+  Gaudi::Property< std::string >              m_PandoraSettingsXmlFile { this, "PandoraSettingsDefault_xml", "/junofs/users/wxfang/MyGit/MarlinPandora/scripts/PandoraSettingsDefault_wx.xml" };
+  Gaudi::Property<int>                        m_NEventsToSkip                   { this, "NEventsToSkip", 0 };
+
+  Gaudi::Property< std::vector<std::string> > m_TrackCollections{ this, "TrackCollections", {"MarlinTrkTracks"} };
+  Gaudi::Property< std::vector<std::string> > m_ECalCaloHitCollections{ this, "ECalCaloHitCollections", {"ECALBarrel","ECALEndcap","ECALOther"} };
+  Gaudi::Property< std::vector<std::string> > m_HCalCaloHitCollections{ this, "HCalCaloHitCollections", {"HCALBarrel","HCALEndcap","HCALOther"} };
+  Gaudi::Property< std::vector<std::string> > m_LCalCaloHitCollections{ this, "LCalCaloHitCollections", {"LCAL"} };
+  Gaudi::Property< std::vector<std::string> > m_LHCalCaloHitCollections{ this, "LHCalCaloHitCollections", {"LHCAL"} };
+  Gaudi::Property< std::vector<std::string> > m_MuonCaloHitCollections{ this, "MuonCaloHitCollections", {"MUON"} };
+  Gaudi::Property< std::vector<std::string> > m_MCParticleCollections{ this, "MCParticleCollections", {"MCParticle"} };
+  Gaudi::Property< std::vector<std::string> > m_RelCaloHitCollections{ this, "RelCaloHitCollections", {"RelationCaloHit","RelationMuonHit"} };
+  Gaudi::Property< std::vector<std::string> > m_RelTrackCollections{ this, "RelTrackCollections", {"MarlinTrkTracksMCTruthLink"} };
+  Gaudi::Property< std::vector<std::string> > m_KinkVertexCollections{ this, "KinkVertexCollections", {"KinkVertices"} };
+  Gaudi::Property< std::vector<std::string> > m_ProngVertexCollections{ this, "ProngVertexCollections", {"ProngVertices"} };
+  Gaudi::Property< std::vector<std::string> > m_SplitVertexCollections{ this, "SplitVertexCollections", {"SplitVertices"} };
+  Gaudi::Property< std::vector<std::string> > m_V0VertexCollections{ this, "V0VertexCollections", {"V0Vertices"} };
+  Gaudi::Property< std::string >              m_ClusterCollectionName { this, "ClusterCollectionName", "PandoraClusters" };
+  Gaudi::Property< std::string >              m_PFOCollectionName { this, "PFOCollectionName", "PandoraPFOs" };
+  Gaudi::Property<float>                      m_ECalToMipCalibration{ this, "ECalToMipCalibration", 160.0 };
+  Gaudi::Property<float>                      m_HCalToMipCalibration{ this, "HCalToMipCalibration", 34.8 };
+  Gaudi::Property<float>                      m_ECalMipThreshold{ this, "ECalMipThreshold", 0.5 };
+  Gaudi::Property<float>                      m_HCalMipThreshold{ this, "HCalMipThreshold", 0.3 };
+  Gaudi::Property<float>                      m_ECalToEMGeVCalibration{ this, "ECalToEMGeVCalibration", 1.007 };
+  Gaudi::Property<float>                      m_HCalToEMGeVCalibration{ this, "HCalToEMGeVCalibration", 1.007 };
+  Gaudi::Property<float>                      m_ECalToHadGeVCalibrationBarrel{ this, "ECalToHadGeVCalibrationBarrel", 1.12 };
+  Gaudi::Property<float>                      m_ECalToHadGeVCalibrationEndCap{ this, "ECalToHadGeVCalibrationEndCap", 1.12 };
+  Gaudi::Property<float>                      m_HCalToHadGeVCalibration{ this, "HCalToHadGeVCalibration", 1.07 };
+  Gaudi::Property<float>                      m_MuonToMipCalibration{ this, "MuonToMipCalibration", 10.0 };
+  Gaudi::Property<int>                        m_DigitalMuonHits{ this, "DigitalMuonHits", 0 };
+  Gaudi::Property<float>                      m_MaxHCalHitHadronicEnergy{ this, "MaxHCalHitHadronicEnergy", 1.0 };
+  Gaudi::Property<int>                        m_UseOldTrackStateCalculation{ this, "UseOldTrackStateCalculation", 0 };
+
+
+  Gaudi::Property<float>                      m_AbsorberRadLengthECal{ this, "AbsorberRadLengthECal", 0.2854 };
+  Gaudi::Property<float>                      m_AbsorberIntLengthECal{ this, "AbsorberIntLengthECal", 0.0101 };
+  Gaudi::Property<float>                      m_AbsorberRadLengthHCal{ this, "AbsorberRadLengthHCal", 0.0569 };
+  Gaudi::Property<float>                      m_AbsorberIntLengthHCal{ this, "AbsorberIntLengthHCal", 0.006  };
+  Gaudi::Property<float>                      m_AbsorberRadLengthOther{ this, "AbsorberRadLengthOther", 0.0569  };
+  Gaudi::Property<float>                      m_AbsorberIntLengthOther{ this, "AbsorberIntLengthOther", 0.006  };
+  Gaudi::Property< std::string >              m_StartVertexCollectionName { this, "StartVertexCollectionName", "PandoraPFANewStartVertices" };
+  Gaudi::Property< std::string >              m_StartVertexAlgorithmName { this, "StartVertexAlgorithmName", "PandoraPFANew" };
+  Gaudi::Property<float>                      m_EMStochasticTerm{ this, "EMStochasticTerm", 0.17  };
+  Gaudi::Property<float>                      m_HadStochasticTerm{ this, "HadStochasticTerm", 0.6  };
+  Gaudi::Property<float>                      m_EMConstantTerm{ this, "EMConstantTerm", 0.01  };
+  Gaudi::Property<float>                      m_HadConstantTerm{ this, "HadConstantTerm", 0.03  };
+  Gaudi::Property<float>                      m_MuonHitEnergy{ this, "MuonHitEnergy", 0.5 };
+  Gaudi::Property<int>                        m_NOuterSamplingLayers{ this, "NOuterSamplingLayers", 3 };
+  Gaudi::Property<float>                      m_LayersFromEdgeMaxRearDistance{ this, "LayersFromEdgeMaxRearDistance", 250.f };
+  Gaudi::Property<float>                      m_MuonBarrelBField{ this, "MuonBarrelBField", -1.5f };
+  Gaudi::Property<float>                      m_MuonEndCapBField{ this, "MuonEndCapBField", 0.01f };
+  Gaudi::Property<int>                        m_ShouldFormTrackRelationships{ this, "ShouldFormTrackRelationships", 1 };
+  Gaudi::Property<int>                        m_MinTrackHits{ this, "MinTrackHits", 5 };
+  Gaudi::Property<int>                        m_MinFtdTrackHits{ this, "MinFtdTrackHits", 0 };
+  Gaudi::Property<int>                        m_MaxTrackHits{ this, "MaxTrackHits", 5000 };
+  Gaudi::Property<float>                      m_D0TrackCut{ this, "D0TrackCut", 50. };
+  Gaudi::Property<float>                      m_Z0TrackCut{ this, "Z0TrackCut", 50. };
+  Gaudi::Property<int>                        m_UseNonVertexTracks{ this, "UseNonVertexTracks", 1 };
+  Gaudi::Property<int>                        m_UseUnmatchedNonVertexTracks{ this, "UseUnmatchedNonVertexTracks", 0 };
+  Gaudi::Property<int>                        m_UseUnmatchedVertexTracks{ this, "UseUnmatchedVertexTracks", 1 };
+  Gaudi::Property<float>                      m_UnmatchedVertexTrackMaxEnergy{ this, "UnmatchedVertexTrackMaxEnergy", 5. };
+  Gaudi::Property<float>                      m_D0UnmatchedVertexTrackCut{ this, "D0UnmatchedVertexTrackCut", 5. };
+  Gaudi::Property<float>                      m_Z0UnmatchedVertexTrackCut{ this, "Z0UnmatchedVertexTrackCut", 5. };
+  Gaudi::Property<float>                      m_ZCutForNonVertexTracks{ this, "ZCutForNonVertexTracks", 250. };
+  Gaudi::Property<int>                        m_ReachesECalNTpcHits{ this, "ReachesECalNTpcHits", 11 };
+  Gaudi::Property<int>                        m_ReachesECalNFtdHits{ this, "ReachesECalNFtdHits", 4 };
+  Gaudi::Property<float>                      m_ReachesECalTpcOuterDistance{ this, "ReachesECalTpcOuterDistance", -100. };
+  Gaudi::Property<int>                        m_ReachesECalMinFtdLayer{ this, "ReachesECalMinFtdLayer", 9 };
+  Gaudi::Property<float>                      m_ReachesECalTpcZMaxDistance{ this, "ReachesECalTpcZMaxDistance", -50. };
+  Gaudi::Property<float>                      m_ReachesECalFtdZMaxDistance{ this, "ReachesECalFtdZMaxDistance", -1. };
+  Gaudi::Property<float>                      m_CurvatureToMomentumFactor{ this, "CurvatureToMomentumFactor", 0.3 / 2000. };
+  Gaudi::Property<float>                      m_MinTrackECalDistanceFromIp{ this, "MinTrackECalDistanceFromIp", 100. };
+
+  Gaudi::Property<float>                      m_MaxTrackSigmaPOverP             { this, "MaxTrackSigmaPOverP", 0.15 };
+  Gaudi::Property<float>                      m_MinMomentumForTrackHitChecks    { this, "MinMomentumForTrackHitChecks", 1. };
+  Gaudi::Property<float>                      m_TpcMembraneMaxZ                 { this, "TpcMembraneMaxZ", 10. };
+  Gaudi::Property<float>                      m_MinTpcHitFractionOfExpected     { this, "MinTpcHitFractionOfExpected", 0.20 };
+  Gaudi::Property<int>                        m_MinFtdHitsForTpcHitFraction     { this, "MinFtdHitsForTpcHitFraction", 2 };
+  Gaudi::Property<float>                      m_MaxTpcInnerRDistance            { this, "MaxTpcInnerRDistance", 50.0 };
+  Gaudi::Property<int>                        m_ECalEndCapInnerSymmetryOrder    { this, "ECalEndCapInnerSymmetryOrder", 4 };
+  Gaudi::Property<float>                      m_ECalEndCapInnerPhiCoordinate    { this, "ECalEndCapInnerPhiCoordinate", 0. };
+  Gaudi::Property<int>                        m_ECalEndCapOuterSymmetryOrder    { this, "ECalEndCapOuterSymmetryOrder", 8 };
+  Gaudi::Property<float>                      m_ECalEndCapOuterPhiCoordinate    { this, "ECalEndCapOuterPhiCoordinate", 0. };
+  Gaudi::Property<int>                        m_HCalEndCapInnerSymmetryOrder    { this, "HCalEndCapInnerSymmetryOrder", 4 };
+  Gaudi::Property<float>                      m_HCalEndCapInnerPhiCoordinate    { this, "HCalEndCapInnerPhiCoordinate", 0. };
+  Gaudi::Property<int>                        m_HCalEndCapOuterSymmetryOrder    { this, "HCalEndCapOuterSymmetryOrder", 16 };
+  Gaudi::Property<float>                      m_HCalEndCapOuterPhiCoordinate    { this, "HCalEndCapOuterPhiCoordinate", 0. };
+  Gaudi::Property<int>                        m_HCalRingInnerSymmetryOrder      { this, "HCalRingInnerSymmetryOrder",   8  };
+  Gaudi::Property<float>                      m_HCalRingInnerPhiCoordinate      { this, "HCalRingInnerPhiCoordinate",   0. };
+  Gaudi::Property<int>                        m_HCalRingOuterSymmetryOrder      { this, "HCalRingOuterSymmetryOrder",   16 };
+  Gaudi::Property<float>                      m_HCalRingOuterPhiCoordinate      { this, "HCalRingOuterPhiCoordinate",   0. };
+  Gaudi::Property<bool>                       m_StripSplittingOn                { this, "StripSplittingOn", false };
+  Gaudi::Property<bool>                       m_UseEcalScLayers                 { this, "UseEcalScLayers", false };
+  Gaudi::Property<float>                      m_ECalSiToMipCalibration          { this, "ECalSiToMipCalibration", 1. };
+  Gaudi::Property<float>                      m_ECalScToMipCalibration          { this, "ECalScToMipCalibration", 1. };
+  Gaudi::Property<float>                      m_ECalSiMipThreshold              { this, "ECalSiMipThreshold", 0. };
+  Gaudi::Property<float>                      m_ECalScMipThreshold              { this, "ECalScMipThreshold", 0. };
+  Gaudi::Property<float>                      m_ECalSiToEMGeVCalibration        { this, "ECalSiToEMGeVCalibration", 1. };
+  Gaudi::Property<float>                      m_ECalScToEMGeVCalibration        { this, "ECalScToEMGeVCalibration", 1. };
+  Gaudi::Property<float>                      m_ECalSiToHadGeVCalibrationEndCap { this, "ECalSiToHadGeVCalibrationEndCap", 1. };
+  Gaudi::Property<float>                      m_ECalScToHadGeVCalibrationEndCap { this, "ECalScToHadGeVCalibrationEndCap", 1. };
+  Gaudi::Property<float>                      m_ECalSiToHadGeVCalibrationBarrel { this, "ECalSiToHadGeVCalibrationBarrel", 1. };
+  Gaudi::Property<float>                      m_ECalScToHadGeVCalibrationBarrel { this, "ECalScToHadGeVCalibrationBarrel", 1. };
+
+  Gaudi::Property<FloatVector>                m_InputEnergyCorrectionPoints { this, "InputEnergyCorrectionPoints", {} };
+  Gaudi::Property<FloatVector>                m_OutputEnergyCorrectionPoints { this, "OutputEnergyCorrectionPoints", {} };
+
+
+  static pandora::Pandora        *m_pPandora;
+  GeometryCreator                *m_pGeometryCreator;             ///< The geometry creator
+  CaloHitCreator                 *m_pCaloHitCreator;              ///< The calo hit creator
+  TrackCreator                   *m_pTrackCreator;                ///< The track creator
+  MCParticleCreator              *m_pMCParticleCreator;           ///< The mc particle creator
+  PfoCreator                     *m_pPfoCreator;                  ///< The pfo creator
+ 
+  Settings                        m_settings;                     ///< The settings for the pandora pfa new algo
+  CollectionMaps                  *m_CollectionMaps;               ///< The settings for the pandora pfa new algo
+  GeometryCreator::Settings       m_geometryCreatorSettings;      ///< The geometry creator settings
+  TrackCreator::Settings          m_trackCreatorSettings;         ///< The track creator settings
+  CaloHitCreator::Settings        m_caloHitCreatorSettings;       ///< The calo hit creator settings
+  MCParticleCreator::Settings     m_mcParticleCreatorSettings;    ///< The mc particle creator settings
+  PfoCreator::Settings            m_pfoCreatorSettings;           ///< The pfo creator settings
+
+  std::string                     m_detectorName;                 ///< The detector name
+  unsigned int                    m_nRun;                         ///< The run number
+  unsigned int                    m_nEvent;                       ///< The event number
+  //### For Ana #################
+  TFile* m_fout;
+  TTree* m_tree;
+  std::vector<int  > m_pReco_PID;    
+  std::vector<float> m_pReco_mass;
+  std::vector<float> m_pReco_energy;
+  std::vector<float> m_pReco_px;
+  std::vector<float> m_pReco_py;
+  std::vector<float> m_pReco_pz;
+  std::vector<float> m_pReco_charge;
+
+  std::vector<int>   m_mc_p_size;
+  std::vector<int>   m_mc_pid   ;
+  std::vector<float> m_mc_mass  ;
+  std::vector<float> m_mc_px    ;
+  std::vector<float> m_mc_py    ;
+  std::vector<float> m_mc_pz    ;
+  std::vector<float> m_mc_charge;
+  int m_hasConversion;
+
+  Gaudi::Property< std::string >              m_AnaOutput{ this, "AnaOutput", "/junofs/users/wxfang/MyGit/CEPCSW/Reconstruction/PFA/Pandora/GaudiPandora/Ana.root" };
+  //######################
+  
+  DataHandle<edm4hep::MCParticleCollection>     m_mcParCol_r  {"MCParticle", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_ECALBarrel_r{"ECALBarrel", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_ECALEndcap_r{"ECALEndcap", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_ECALOther_r {"ECALOther", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_HCALBarrel_r{"HCALBarrel", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_HCALEndcap_r{"HCALEndcap", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_HCALOther_r {"HCALOther", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_MUON_r      {"MUON", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_LCAL_r      {"LCAL", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_LHCAL_r     {"LHCAL", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_BCAL_r      {"BCAL", Gaudi::DataHandle::Reader, this};
+
+  DataHandle<edm4hep::VertexCollection> m_KinkVertices_r    {"KinkVertices",Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::VertexCollection> m_ProngVertices_r   {"ProngVertices",Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::VertexCollection> m_SplitVertices_r   {"SplitVertices",Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::VertexCollection> m_V0Vertices_r      {"V0Vertices",Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::TrackCollection>  m_MarlinTrkTracks_r {"MarlinTrkTracks",Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::MCRecoCaloAssociationCollection>  m_MCRecoCaloAssociation_r {"MCRecoCaloAssociation",Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::MCRecoTrackerAssociationCollection>  m_MCRecoTrackerAssociation_r {"MCRecoTrackerAssociation",Gaudi::DataHandle::Reader, this};
+
+  DataHandle<edm4hep::ClusterCollection>                m_ClusterCollection_w {"PandoraClusters",Gaudi::DataHandle::Writer, this};
+  DataHandle<edm4hep::ReconstructedParticleCollection>  m_ReconstructedParticleCollection_w {"PandoraPFOs"    ,Gaudi::DataHandle::Writer, this};
+  DataHandle<edm4hep::VertexCollection>                 m_VertexCollection_w {"PandoraPFANewStartVertices",Gaudi::DataHandle::Writer, this};
+  DataHandle<edm4hep::MCRecoParticleAssociationCollection>  m_MCRecoParticleAssociation_w {"pfoMCRecoParticleAssociation",Gaudi::DataHandle::Writer, this};
+
+};
+
+#endif
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/include/PfoCreator.h b/Reconstruction/PFA/Pandora/GaudiPandora/include/PfoCreator.h
new file mode 100644
index 00000000..85ba02dc
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/include/PfoCreator.h
@@ -0,0 +1,221 @@
+/**
+ *  @file   MarlinPandora/include/PfoCreator.h
+ * 
+ *  @brief  Header file for the pfo creator class.
+ * 
+ *  $Log: $
+ */
+
+#ifndef PFO_CREATOR_H
+#define PFO_CREATOR_H 1
+
+#include "FWCore/DataHandle.h"
+#include "edm4hep/Vector3f.h"
+#include "edm4hep/ClusterCollection.h"
+#include "edm4hep/Cluster.h"
+#include "edm4hep/ReconstructedParticleCollection.h"
+#include "edm4hep/ReconstructedParticle.h"
+#include "edm4hep/VertexCollection.h"
+#include "edm4hep/Vertex.h"
+#include "edm4hep/TrackCollection.h"
+#include "edm4hep/Track.h"
+#include "edm4hep/CalorimeterHitCollection.h"
+#include "edm4hep/CalorimeterHit.h"
+
+#include "ClusterShapes.h"
+#include "Api/PandoraApi.h"
+
+//namespace IMPL { class ClusterImpl; class ReconstructedParticleImpl; }
+//namespace EVENT { class LCEvent; }
+class CollectionMaps;
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+/**
+ *  @brief  PfoCreator class
+ */
+class PfoCreator
+{
+public:
+    /**
+     *  @brief  Settings class
+     */
+    class Settings
+    {
+    public:
+        /**
+         *  @brief  Default constructor
+         */
+        Settings();
+
+        std::string     m_clusterCollectionName;                ///< The name of the cluster output collection
+        std::string     m_pfoCollectionName;                    ///< The name of the pfo output collection
+        std::string     m_startVertexCollectionName;            ///< The name of the start vertex output collection
+        std::string     m_startVertexAlgName;                   ///< The name of the algorithm to fill the start vertex output collection
+        float           m_emStochasticTerm;                     ///< The stochastic term for EM shower energy resolution
+        float           m_hadStochasticTerm;                    ///< The stochastic term for Hadronic shower energy resolution
+        float           m_emConstantTerm;                       ///< The constant term for EM shower energy resolution
+        float           m_hadConstantTerm;                      ///< The constant term for Hadronic shower energy resolution
+    };
+
+    /**
+     *  @brief  Constructor
+     * 
+     *  @param  settings the creator settings
+     *  @param  pPandora address of the relevant pandora instance
+     */
+     PfoCreator(const Settings &settings, const pandora::Pandora *const pPandora);
+
+    /**
+     *  @brief  Destructor
+     */
+     ~PfoCreator();
+
+    /**
+     *  @brief  Create particle flow objects
+     * 
+     *  @param  pLCEvent the lcio event
+     */    
+    //pandora::StatusCode CreateParticleFlowObjects(EVENT::LCEvent *pLCEvent);
+    //pandora::StatusCode CreateParticleFlowObjects(const CollectionMaps& collectionMaps);
+    pandora::StatusCode CreateParticleFlowObjects(CollectionMaps& collectionMaps, DataHandle<edm4hep::ClusterCollection>& _pClusterCollection, DataHandle<edm4hep::ReconstructedParticleCollection>& _pReconstructedParticleCollection, DataHandle<edm4hep::VertexCollection>& _pStartVertexCollection);
+
+    CollectionMaps* m_collectionMaps;
+
+private:
+    /**
+     *  @brief  index for the subdetector
+     */
+    enum Index
+    {
+        ECAL_INDEX = 0,
+        HCAL_INDEX = 1,
+        YOKE_INDEX = 2,
+        LCAL_INDEX = 3,
+        LHCAL_INDEX = 4,
+        BCAL_INDEX = 5
+    };
+
+    /**
+     *  @brief  initialise sub detector name strings
+     * 
+     *  @param  subDetectorNames to receive the list of sub detector names
+     */
+    void InitialiseSubDetectorNames(pandora::StringVector &subDetectorNames) const;
+
+    /**
+     *  @brief  Set sub detector energies for a cluster
+     * 
+     *  @param  subDetectorNames the list of sub detector names
+     *  @param  pLcioCluster the address of the lcio cluster to be set sub detector energies
+     *  @param  pandoraCaloHitList the pandora calorimeter hit list
+     *  @param  hitE the vector to receive the energy of hits
+     *  @param  hitX the vector to receive the x position of hits
+     *  @param  hitY the vector to receive the y position of hits
+     *  @param  hitZ the vector to receive the z position of hits
+     */
+    void SetClusterSubDetectorEnergies(const pandora::StringVector &subDetectorNames, edm4hep::Cluster *const pLcioCluster,
+        const pandora::CaloHitList &pandoraCaloHitList, pandora::FloatVector &hitE, pandora::FloatVector &hitX, pandora::FloatVector &hitY,
+        pandora::FloatVector &hitZ) const;
+
+    /**
+     *  @brief  Set cluster energies and errors
+     * 
+     *  @param  pPandoraPfo the address of the pandora pfo
+     *  @param  pPandoraCluster the address of the pandora cluster
+     *  @param  pLcioCluster the address of the lcio cluster to be set energies and erros
+     *  @param  clusterCorrectEnergy a number to receive the cluster correct energy
+     */
+    void SetClusterEnergyAndError(const pandora::ParticleFlowObject *const pPandoraPfo, const pandora::Cluster *const pPandoraCluster, 
+        edm4hep::Cluster *const pLcioCluster, float &clusterCorrectEnergy) const;
+
+    /**
+     *  @brief  Set cluster position, errors and other shape info, by calculating culster shape first
+     * 
+     *  @param  nHitsInCluster number of hits in cluster
+     *  @param  hitE the vector of the energy of hits
+     *  @param  hitX the vector of the x position of hits
+     *  @param  hitY the vector of the y position of hits
+     *  @param  hitZ the vector of the z position of hits
+     *  @param  pLcioCluster the lcio cluster to be set positions and errors
+     *  @param  clusterPosition a CartesianVector to receive the cluster position
+     */
+    void SetClusterPositionAndError(const unsigned int nHitsInCluster, pandora::FloatVector &hitE, pandora::FloatVector &hitX, 
+        pandora::FloatVector &hitY, pandora::FloatVector &hitZ, edm4hep::Cluster *const pLcioCluster, pandora::CartesianVector &clusterPositionVec) const;
+
+    /**
+     *  @brief  Calculate reference point for pfo with tracks
+     * 
+     *  @param  pPandoraPfo the address of the pandora pfo
+     *  @param  referencePoint a CartesianVector to receive the reference point
+     */
+    pandora::StatusCode CalculateTrackBasedReferencePoint(const pandora::ParticleFlowObject *const pPandoraPfo, pandora::CartesianVector &referencePoint) const;
+
+    /**
+     *  @brief  Set reference point of the reconstructed particle
+     * 
+     *  @param  referencePoint a CartesianVector of the reference point
+     *  @param  pReconstructedParticle the address of the reconstructed particle to be reference point
+     */
+    void SetRecoParticleReferencePoint(const pandora::CartesianVector &referencePoint, edm4hep::ReconstructedParticle *const pReconstructedParticle) const;
+
+    /**
+     *  @brief  Add tracks to reconstructed particle
+     * 
+     *  @param  pPandoraPfo the address of the pandora pfo
+     *  @param  pReconstructedParticle the address of the reconstructed particle to be added tracks
+     */
+    void AddTracksToRecoParticle(const pandora::ParticleFlowObject *const pPandoraPfo, edm4hep::ReconstructedParticle *const pReconstructedParticle) const;
+
+    /**
+     *  @brief  Set properties of reconstructed particle from pandora pfo
+     * 
+     *  @param  pPandoraPfo the address of the pandora pfo 
+     *  @param  pReconstructedParticle the address of the reconstructed particle to be set properties
+     */
+    void SetRecoParticlePropertiesFromPFO(const pandora::ParticleFlowObject *const pPandoraPfo, edm4hep::ReconstructedParticle *const pReconstructedParticle) const;
+
+    /**
+     *  @brief  Whether parent and daughter tracks are associated with the same pfo
+     *
+     *  @param  pPandoraTrack the address of the pandora track
+     *  @param  allTrackList list of all tracks associated with reconstructed particle
+     * 
+     *  @return boolean
+     */
+    bool IsValidParentTrack(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const;
+
+    /**
+     *  @brief  Whether sibling tracks are associated with the same pfo
+     *
+     *  @param  pPandoraTrack the address of the pandora track
+     *  @param  allTrackList list of all tracks associated with reconstructed particle
+     * 
+     *  @return boolean
+     */
+    bool HasValidSiblingTrack(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const;
+
+    /**
+     *  @brief  Whether the track is the closest (of those associated with the same pfo) to the interaction point
+     *
+     *  @param  pPandoraTrack the address of the pandora track
+     *  @param  allTrackList list of all tracks associated to reconstructed particle
+     * 
+     *  @return boolean
+     */ 
+    bool IsClosestTrackToIP(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const;
+
+    /**
+     *  @brief  Whether at least one track sibling track is associated to the reconstructed particle 
+     *
+     *  @param  pPandoraTrack the address of the pandora track
+     *  @param  allTrackList list of all tracks associated to reconstructed particle
+     * 
+     *  @return boolean
+     */
+    bool AreAnyOtherSiblingsInList(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const;
+
+    const Settings              m_settings;                         ///< The pfo creator settings
+    const pandora::Pandora      *m_pPandora;                        ///< Address of the pandora object from which to extract the pfos
+};
+
+#endif // #ifndef PFO_CREATOR_H
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/include/TrackCreator.h b/Reconstruction/PFA/Pandora/GaudiPandora/include/TrackCreator.h
new file mode 100644
index 00000000..5e036715
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/include/TrackCreator.h
@@ -0,0 +1,378 @@
+/**
+ *  @file   MarlinPandora/include/TrackCreator.h
+ * 
+ *  @brief  Header file for the track creator class.
+ * 
+ *  $Log: $
+ */
+
+#ifndef TRACK_CREATOR_H
+#define TRACK_CREATOR_H 1
+
+//#include "lcio.h"
+
+//#include "IMPL/LCCollectionVec.h"
+//#include "IMPL/LCFlagImpl.h"
+
+//#include "EVENT/LCEvent.h"
+//#include "EVENT/Track.h"
+
+#include "GaudiKernel/ISvcLocator.h"
+
+#include "edm4hep/Track.h"
+#include "edm4hep/TrackConst.h"
+#include "edm4hep/TrackState.h"
+#include "edm4hep/ReconstructedParticleConst.h"
+
+#include "Api/PandoraApi.h"
+#include "Objects/Helix.h"
+
+namespace gear { class GearMgr; }
+
+class CollectionMaps;
+
+typedef std::vector<const edm4hep::Track *> TrackVector;
+//typedef std::set<const edm4hep::Track *> TrackList;
+typedef std::set<unsigned int> TrackList;
+//typedef std::map<edm4hep::Track *, int> TrackToPidMap;
+typedef std::map<edm4hep::ConstTrack, int> TrackToPidMap;
+/*
+inline LCCollectionVec *newTrkCol(const std::string &name, LCEvent *evt , bool isSubset)
+{
+    LCCollectionVec* col = new LCCollectionVec( LCIO::TRACK ) ;
+
+    LCFlagImpl hitFlag(0) ;
+    hitFlag.setBit( LCIO::TRBIT_HITS ) ;
+    col->setFlag( hitFlag.getFlag()  ) ;
+    evt->addCollection( col , name ) ;
+    col->setSubset( isSubset ) ;
+
+    return col ;
+}
+*/
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+/**
+ *  @brief  TrackCreator class
+ */
+class TrackCreator
+{
+public:
+    typedef std::vector<double> DoubleVector;
+    typedef std::vector<std::string> StringVector;
+
+    /**
+     *  @brief  Settings class
+     */
+    class Settings
+    {
+    public:
+        /**
+         *  @brief  Default constructor
+         */
+        Settings();
+
+        StringVector    m_trackCollections;                     ///< The reconstructed track collections
+        StringVector    m_kinkVertexCollections;                ///< The kink vertex collections
+        StringVector    m_prongVertexCollections;               ///< The prong vertex collections
+        StringVector    m_splitVertexCollections;               ///< The split vertex collections
+        StringVector    m_v0VertexCollections;                  ///< The v0 vertex collections
+
+        StringVector    m_prongSplitVertexCollections;          ///< Concatenated list of prong and split vertex collections
+        int             m_shouldFormTrackRelationships;         ///< Whether to form pandora track relationships using v0 and kink info
+
+        int             m_minTrackHits;                         ///< Track quality cut: the minimum number of track hits
+        int             m_minFtdTrackHits;                      ///< Track quality cut: the minimum number of FTD track hits for FTD only tracks
+        int             m_maxTrackHits;                         ///< Track quality cut: the maximum number of track hits
+
+        float           m_d0TrackCut;                           ///< Track d0 cut used to determine whether track can be used to form pfo
+        float           m_z0TrackCut;                           ///< Track z0 cut used to determine whether track can be used to form pfo
+
+        int             m_usingNonVertexTracks;                 ///< Whether can form pfos from tracks that don't start at vertex
+        int             m_usingUnmatchedNonVertexTracks;        ///< Whether can form pfos from unmatched tracks that don't start at vertex
+        int             m_usingUnmatchedVertexTracks;           ///< Whether can form pfos from unmatched tracks that start at vertex
+        float           m_unmatchedVertexTrackMaxEnergy;        ///< Maximum energy for unmatched vertex track
+
+        float           m_d0UnmatchedVertexTrackCut;            ///< d0 cut used to determine whether unmatched vertex track can form pfo
+        float           m_z0UnmatchedVertexTrackCut;            ///< z0 cut used to determine whether unmatched vertex track can form pfo
+        float           m_zCutForNonVertexTracks;               ///< Non vtx track z cut to determine whether track can be used to form pfo
+
+        int             m_reachesECalNTpcHits;                  ///< Minimum number of tpc hits to consider track as reaching ecal
+        int             m_reachesECalNFtdHits;                  ///< Minimum number of ftd hits to consider track as reaching ecal
+        float           m_reachesECalTpcOuterDistance;          ///< Max distance from track to tpc r max to id whether track reaches ecal
+        int             m_reachesECalMinFtdLayer;               ///< Min layer in Ftd for tracks to be considered to have reached decal
+        float           m_reachesECalTpcZMaxDistance;           ///< Max distance from track to tpc z max to id whether track reaches ecal
+        float           m_reachesECalFtdZMaxDistance;           ///< Max distance from track hit to ftd z position to identify ftd hits
+        float           m_curvatureToMomentumFactor;            ///< Constant relating track curvature in b field to momentum
+
+        float           m_minTrackECalDistanceFromIp;           ///< Sanity check on separation between ip and track projected ecal position
+        float           m_maxTrackSigmaPOverP;                  ///< Track fraction momentum error cut
+        float           m_minMomentumForTrackHitChecks;         ///< Min track momentum required to perform final quality checks on number of hits
+
+        float           m_tpcMembraneMaxZ;                      ///< Tpc membrane max z coordinate
+        float           m_maxTpcInnerRDistance;                 ///< Track cut on distance from tpc inner r to id whether track can form pfo
+        float           m_minTpcHitFractionOfExpected;          ///< Minimum fraction of TPC hits compared to expected
+        int             m_minFtdHitsForTpcHitFraction;          ///< Minimum number of FTD hits to ignore TPC hit fraction
+    };
+
+    /**
+     *  @brief  Constructor
+     * 
+     *  @param  settings the creator settings
+     *  @param  pPandora address of the relevant pandora instance
+     */
+     //TrackCreator(const Settings &settings, const pandora::Pandora *const pPandora);
+     TrackCreator(const Settings &settings, const pandora::Pandora *const pPandora, ISvcLocator* svcloc);
+
+    /**
+     *  @brief  Destructor
+     */
+     ~TrackCreator();
+
+    /**
+     *  @brief  Create associations between tracks, V0s, kinks, etc
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    //pandora::StatusCode CreateTrackAssociations(const EVENT::LCEvent *const pLCEvent);
+    pandora::StatusCode CreateTrackAssociations(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Create tracks, insert user code here
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    //pandora::StatusCode CreateTracks(EVENT::LCEvent *pLCEvent);
+    pandora::StatusCode CreateTracks(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Get the track vector
+     * 
+     *  @return The track vector
+     */
+    const TrackVector &GetTrackVector() const;
+
+    /**
+     *  @brief  Reset the track creator
+     */
+    void Reset();
+
+private:
+    /**
+     *  @brief  Extract kink information from specified lcio collections
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    //pandora::StatusCode ExtractKinks(const EVENT::LCEvent *const pLCEvent);
+    pandora::StatusCode ExtractKinks(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Extract prong and split information from specified lcio collections
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    //pandora::StatusCode ExtractProngsAndSplits(const EVENT::LCEvent *const pLCEvent);
+
+    /**
+     *  @brief  Extract v0 information from specified lcio collections
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+   //pandora::StatusCode ExtractV0s(const EVENT::LCEvent *const pLCEvent);
+
+    /**
+     *  @brief  Whether the track vertex conflicts with previously provided relationship information
+     * 
+     *  @param  trackVec the vector of tracks associated with the vertex
+     */
+    //bool IsConflictingRelationship(const EVENT::TrackVec &trackVec) const;
+    //bool IsConflictingRelationship(edm4hep::ConstTrack &trackVec) const;
+    bool IsConflictingRelationship(const edm4hep::ConstReconstructedParticle &Particle) const;
+
+    /**
+     *  @brief  Whether a track is a v0 track
+     * 
+     *  @param  pTrack the lcio track
+     * 
+     *  @return boolean
+     */
+    //bool IsV0(const EVENT::Track *const pTrack) const;
+    bool IsV0(unsigned int pTrack_id) const;
+
+    /**
+     *  @brief  Whether a track is a parent track
+     * 
+     *  @param  pTrack the lcio track
+     * 
+     *  @return boolean
+     */
+    //bool IsParent(const EVENT::Track *const pTrack) const;
+    bool IsParent(unsigned int pTrack_id) const;
+
+    /**
+     *  @brief  Whether a track is a daughter track
+     * 
+     *  @param  pTrack the lcio track
+     * 
+     *  @return boolean
+     */
+    //bool IsDaughter(const EVENT::Track *const pTrack) const;
+    bool IsDaughter(unsigned int pTrack_id) const;
+
+    /**
+     *  @brief  Copy track states stored in lcio tracks to pandora track parameters
+     * 
+     *  @param  pTrack the lcio track
+     *  @param  trackParameters the track parameters
+     */
+    //void GetTrackStates(const EVENT::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const;
+    void GetTrackStates(const edm4hep::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const;
+
+    /**
+     *  @brief  Copy track state from lcio track state instance to pandora input track state
+     * 
+     *  @param  pTrackState the lcio track state instance
+     *  @param  inputTrackState the pandora input track state
+     */
+    //void CopyTrackState(const TrackState *const pTrackState, pandora::InputTrackState &inputTrackState) const;
+    void CopyTrackState(const edm4hep::TrackState & pTrackState, pandora::InputTrackState &inputTrackState) const;
+
+    /**
+     *  @brief  Obtain track time when it reaches ECAL
+     * 
+     *  @param  pTrack the lcio track
+     */
+    //float CalculateTrackTimeAtCalorimeter(const EVENT::Track *const pTrack) const;
+    float CalculateTrackTimeAtCalorimeter(const edm4hep::Track *const pTrack) const;
+
+    /**
+     *  @brief  Decide whether track reaches the ecal surface
+     * 
+     *  @param  pTrack the lcio track
+     *  @param  trackParameters the track parameters
+     */
+    //void TrackReachesECAL(const EVENT::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const;
+    void TrackReachesECAL(const edm4hep::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const;
+    //void TrackReachesECAL(const edm4hep::Track& pTrack, PandoraApi::Track::Parameters &trackParameters) const;
+
+    /**
+     *  @brief  Determine whether a track can be used to form a pfo under the following conditions:
+     *          1) if the track proves to be associated with a cluster, OR
+     *          2) if the track proves to have no cluster associations
+     * 
+     *  @param  pTrack the lcio track
+     *  @param  trackParameters the track parameters
+     */
+    //void DefineTrackPfoUsage(const EVENT::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const;
+    void DefineTrackPfoUsage(const edm4hep::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const;
+
+    /**
+     *  @brief  Whether track passes the quality cuts required in order to be used to form a pfo
+     * 
+     *  @param  pTrack the lcio track
+     *  @param  trackParameters the track parameters
+     * 
+     *  @return boolean
+     */
+    //bool PassesQualityCuts(const EVENT::Track *const pTrack, const PandoraApi::Track::Parameters &trackParameters) const;
+    bool PassesQualityCuts(const edm4hep::Track *const pTrack, const PandoraApi::Track::Parameters &trackParameters) const;
+
+    /**
+     *  @brief  Get number of hits in TPC of a track
+     * 
+     *  @param  pTrack the lcio track
+     * 
+     *  @return number of hits in TPC of a track
+     */
+    //int GetNTpcHits(const EVENT::Track *const pTrack) const;
+    int GetNTpcHits(const edm4hep::Track *const pTrack) const;
+
+    /**
+     *  @brief  Get number of hits in FTD of a track
+     * 
+     *  @param  pTrack the lcio track
+     * 
+     *  @return number of hits in FTDof a track
+     */
+    //int GetNFtdHits(const EVENT::Track *const pTrack) const;
+    int GetNFtdHits(const edm4hep::Track *const pTrack) const;
+
+    const Settings          m_settings;                     ///< The track creator settings
+    const pandora::Pandora *m_pPandora;                     ///< Address of the pandora object to create tracks and track relationships
+
+    float             m_bField;                       ///< The bfield
+
+    float             m_tpcInnerR;                    ///< The tpc inner radius
+    float             m_tpcOuterR;                    ///< The tpc outer radius
+    unsigned int      m_tpcMaxRow;                    ///< The tpc maximum row number
+    float             m_tpcZmax;                      ///< The tpc maximum z coordinate
+    float                   m_cosTpc;                       ///< Cos(theta) value at end of tpc
+
+    DoubleVector            m_ftdInnerRadii;                ///< List of ftd inner radii
+    DoubleVector            m_ftdOuterRadii;                ///< List of ftd outer radii
+    DoubleVector            m_ftdZPositions;                ///< List of ftd z positions
+    unsigned int            m_nFtdLayers;                   ///< Number of ftd layers
+    float                   m_tanLambdaFtd;                 ///< Tan lambda for first ftd layer
+
+    int               m_eCalBarrelInnerSymmetry;      ///< ECal barrel inner symmetry order
+    float             m_eCalBarrelInnerPhi0;          ///< ECal barrel inner phi 0
+    float             m_eCalBarrelInnerR;             ///< ECal barrel inner radius
+    float             m_eCalEndCapInnerZ;             ///< ECal endcap inner z
+
+    float                   m_minEtdZPosition;              ///< Min etd z position
+    float                   m_minSetRadius;                 ///< Min set radius
+
+    TrackVector             m_trackVector;                  ///< The track vector
+    TrackList               m_v0TrackList;                  ///< The list of v0 tracks
+    TrackList               m_parentTrackList;              ///< The list of parent tracks
+    TrackList               m_daughterTrackList;            ///< The list of daughter tracks
+    TrackToPidMap           m_trackToPidMap;                ///< The map from track addresses to particle ids, where set by kinks/V0s
+    gear::GearMgr* _GEAR;
+};
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+inline const TrackVector &TrackCreator::GetTrackVector() const
+{
+    return m_trackVector;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+inline void TrackCreator::Reset()
+{
+    m_trackVector.clear();
+    m_v0TrackList.clear();
+    m_parentTrackList.clear();
+    m_daughterTrackList.clear();
+    m_trackToPidMap.clear();
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+//inline bool TrackCreator::IsV0(const Track *const pTrack) const
+inline bool TrackCreator::IsV0(unsigned int pTrack_id) const // should check here, if id is correct one to do this
+{
+    //return (m_v0TrackList.end() != m_v0TrackList.find(pTrack));
+    return (m_v0TrackList.end() != m_v0TrackList.find(pTrack_id));
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+//inline bool TrackCreator::IsParent(const Track *const pTrack) const
+inline bool TrackCreator::IsParent(unsigned int pTrack_id) const
+{
+    //return (m_parentTrackList.end() != m_parentTrackList.find(pTrack));
+    return (m_parentTrackList.end() != m_parentTrackList.find(pTrack_id));
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+//inline bool TrackCreator::IsDaughter(const Track *const pTrack) const
+inline bool TrackCreator::IsDaughter(unsigned int pTrack_id) const
+{
+    //return (m_daughterTrackList.end() != m_daughterTrackList.find(pTrack));
+    return (m_daughterTrackList.end() != m_daughterTrackList.find(pTrack_id));
+}
+
+#endif // #ifndef TRACK_CREATOR_H
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/include/Utility.h b/Reconstruction/PFA/Pandora/GaudiPandora/include/Utility.h
new file mode 100644
index 00000000..0bd75b5d
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/include/Utility.h
@@ -0,0 +1,6 @@
+#ifndef MYUTILITY
+#define MYUTILITY 1
+
+#include <sstream>
+std::string Convert (float number);
+#endif
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/include/cellIDDecoder.h b/Reconstruction/PFA/Pandora/GaudiPandora/include/cellIDDecoder.h
new file mode 100644
index 00000000..f1515996
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/include/cellIDDecoder.h
@@ -0,0 +1,127 @@
+#ifndef cellIDDecoder_h
+#define cellIDDecoder_h 1
+
+//#include <vector>
+//#include "EVENT/LCObject.h"
+#include "EVENT/LCCollection.h"
+//#include "EVENT/SimTrackerHit.h"
+#include "UTIL/BitField64.h"
+#include "lcio.h"
+#include <string>
+
+// fixes problem in gcc 4.0.3
+#include "EVENT/LCParameters.h"
+
+//#include <sstream>
+//#include <typeinfo>
+
+//##################### changed for EMD4HEP ########
+//need check !!
+
+namespace ID_UTIL{
+
+
+  /** Convenient class for decoding cellIDs from collection parameter LCIO::CellIDEncoding.
+   *  See UTIL::BitField64 for a description of the encoding string. 
+   * 
+   *  @see BitField64
+   *  @version $Id: CellIDDecoder.h,v 1.9.16.1 2011-03-04 14:09:07 engels Exp $
+   */
+  template <class T> 
+  class CellIDDecoder {
+    
+  public:  
+
+    CellIDDecoder() = default ;
+    CellIDDecoder(const CellIDDecoder& ) = delete ;
+    CellIDDecoder& operator=(const CellIDDecoder& ) = delete ;
+    
+
+    /** Constructor takes encoding string as argument.
+     */
+  CellIDDecoder( const std::string& encoder_str ) : _oldHit(0) {
+      
+      if( encoder_str.length() == 0 ){
+      	throw( lcio::Exception( "CellIDDecoder : string of length zero provided as encoder string" ) ) ;
+      }
+      _b = new UTIL::BitField64( encoder_str ) ; 
+      
+    }
+    
+    /** Constructor reads encoding string from collection parameter LCIO::CellIDEncoding.
+     */
+    CellIDDecoder( const EVENT::LCCollection* col ) : _oldHit(0) {
+      
+      std::string initString("") ; 
+
+      if( col !=0 ) 
+	initString = col->getParameters().getStringVal(  lcio::LCIO::CellIDEncoding ) ;
+      
+      if( initString.size() == 0 ) {
+	
+	initString = _defaultEncoding ;
+
+	std::cout << "    ----------------------------------------- " << std::endl  
+		  << "       WARNING: CellIDDecoder - no CellIDEncoding parameter in collection ! " 
+		  << std::endl 
+		  << "         -> using default : \"" << initString << "\"" 
+		  << std::endl 
+		  << "    ------------------------------------------ "  
+		  << std::endl ;
+      }
+      
+      _b = new UTIL::BitField64(  initString ) ; 
+    }
+    
+    ~CellIDDecoder(){ 
+      
+      delete _b ;
+    } 
+    
+    
+    /** Provides access to the bit fields, e.g. <br>
+     *   int layer =  myCellIDEncoding( hit )[ "layer" ] ;
+     * 
+     */
+    inline const UTIL::BitField64 & operator()( const T* hit ){  
+      
+      if( hit != _oldHit && hit ) {
+	auto id = hit->getCellID();
+        unsigned int id0 = id&0xFFFFFFFF;
+        unsigned int id1 = id>>32;
+	//lcio::long64 val = lcio::long64( hit->getCellID0() & 0xffffffff ) | ( lcio::long64( hit->getCellID1() ) << 32      ) ;
+	lcio::long64 val = lcio::long64( id0 & 0xffffffff ) | ( lcio::long64( id1 ) << 32      ) ;
+	
+	_b->setValue( val ) ;
+
+	_oldHit = hit ;
+      }
+      
+      return  *_b ;
+    }
+    
+
+    /** This can be used to set the default encoding that is used if no
+     *  CellIDEncoding parameter is set in the collection, e.g. in older lcio files.
+     */ 
+    static void setDefaultEncoding(const std::string& defaultEncoding ) {
+
+      _defaultEncoding = std::string( defaultEncoding ) ;
+    }
+    
+  protected:
+    UTIL::BitField64* _b{} ;
+    const T* _oldHit{NULL} ;
+    
+    static std::string _defaultEncoding;
+  } ; 
+  
+  template <class T>
+  std::string CellIDDecoder<T>::_defaultEncoding
+  = std::string("byte0:8,byte1:8,byte2:8,byte3:8,byte4:8,byte5:8,byte6:8,byte7:8") ;
+
+  
+} // namespace
+#endif
+
+
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/src/CaloHitCreator.cpp b/Reconstruction/PFA/Pandora/GaudiPandora/src/CaloHitCreator.cpp
new file mode 100644
index 00000000..e4dc0e2a
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/src/CaloHitCreator.cpp
@@ -0,0 +1,867 @@
+/**
+ *  @file   MarlinPandora/src/CaloHitCreator.cc
+ * 
+ *  @brief  Implementation of the calo hit creator class.
+ * 
+ *  $Log: $
+ */
+
+
+#include "gear/GearParameters.h"
+#include "gear/CalorimeterParameters.h"
+#include "gear/GearDistanceProperties.h"
+#include "gear/GearPointProperties.h"
+#include "gear/GEAR.h"
+#include "gear/TPCParameters.h"
+#include "gear/PadRowLayout2D.h"
+#include "gear/LayerLayout.h"
+
+#include "UTIL/CellIDDecoder.h"
+#include "cellIDDecoder.h"
+#include "GaudiKernel/IService.h"
+#include "GearSvc/IGearSvc.h"
+
+#include "PandoraPFAlg.h"
+#include "CaloHitCreator.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+/*
+template<typename T>
+static float* FloatFromFloatThree(T vec, float &){
+  float tmp[3];
+  for(unsigned i=0; i<3; i++){
+    tmp[i] = vec[i];
+  }
+  return tmp;
+}
+*/
+
+
+CaloHitCreator::CaloHitCreator(const Settings &settings, const pandora::Pandora *const pPandora, ISvcLocator* svcloc, bool encoder_style) :
+    m_settings(settings),
+    m_pPandora(pPandora)
+{
+    m_encoder_str = ""; 
+    m_encoder_str_MUON = ""; 
+    m_encoder_str_LCal = ""; 
+    m_encoder_str_LHCal = ""; 
+    if(encoder_style==0) 
+    {
+        m_encoder_str     = "M:3,S-1:3,I:9,J:9,K-1:6";// LCIO style
+        m_encoder_str_MUON="S-1:4,M:3,K-1:6,I:16,GRZone:3,J:32:16";
+        m_encoder_str_LCal="I:10,J:10,K:10,S-1:2";
+        m_encoder_str_LHCal=m_encoder_str;
+    }
+    IGearSvc*  iSvc = 0;
+    StatusCode sc = svcloc->service("GearSvc", iSvc, false);
+    if ( !sc ) 
+    {
+        throw "Failed to find GearSvc ...";
+    }
+    _GEAR = iSvc->getGearMgr();
+
+
+    m_eCalBarrelOuterZ        = (_GEAR->getEcalBarrelParameters().getExtent()[3]);
+    m_hCalBarrelOuterZ        = (_GEAR->getHcalBarrelParameters().getExtent()[3]);
+    m_muonBarrelOuterZ        = (_GEAR->getYokeBarrelParameters().getExtent()[3]);
+    m_coilOuterR              = (_GEAR->getGearParameters("CoilParameters").getDoubleVal("Coil_cryostat_outer_radius"));
+    m_eCalBarrelInnerPhi0     = (_GEAR->getEcalBarrelParameters().getPhi0());
+    m_eCalBarrelInnerSymmetry = (_GEAR->getEcalBarrelParameters().getSymmetryOrder());
+    m_hCalBarrelInnerPhi0     = (_GEAR->getHcalBarrelParameters().getPhi0());
+    m_hCalBarrelInnerSymmetry = (_GEAR->getHcalBarrelParameters().getSymmetryOrder());
+    m_muonBarrelInnerPhi0     = (_GEAR->getYokeBarrelParameters().getPhi0());
+    m_muonBarrelInnerSymmetry = (_GEAR->getYokeBarrelParameters().getSymmetryOrder());
+    m_hCalEndCapOuterR        = (_GEAR->getHcalEndcapParameters().getExtent()[1]);
+    m_hCalEndCapOuterZ        = (_GEAR->getHcalEndcapParameters().getExtent()[3]);
+    m_hCalBarrelOuterR        = (_GEAR->getHcalBarrelParameters().getExtent()[1]);
+    m_hCalBarrelOuterPhi0     =((std::find(_GEAR->getHcalBarrelParameters().getIntKeys().begin(),
+        _GEAR->getHcalBarrelParameters().getIntKeys().end(),
+        "Hcal_outer_polygon_phi0") != _GEAR->getHcalBarrelParameters().getIntKeys().end() ?
+        _GEAR->getHcalBarrelParameters().getIntVal("Hcal_outer_polygon_phi0")
+        : 0));
+    m_hCalBarrelOuterSymmetry = ((std::find(_GEAR->getHcalBarrelParameters().getIntKeys().begin(),
+        _GEAR->getHcalBarrelParameters().getIntKeys().end(),
+        "Hcal_outer_polygon_order") != _GEAR->getHcalBarrelParameters().getIntKeys().end() ?
+        _GEAR->getHcalBarrelParameters().getIntVal("Hcal_outer_polygon_order")
+        : 0));
+
+
+    const gear::LayerLayout &hCalEndCapLayerLayout(_GEAR->getHcalEndcapParameters().getLayerLayout());
+    const gear::LayerLayout &hCalBarrelLayerLayout(_GEAR->getHcalBarrelParameters().getLayerLayout()); 
+    m_hCalEndCapLayerThickness = hCalEndCapLayerLayout.getThickness(hCalEndCapLayerLayout.getNLayers() - 1);
+    m_hCalBarrelLayerThickness = hCalBarrelLayerLayout.getThickness(hCalBarrelLayerLayout.getNLayers() - 1);
+    if ((m_hCalEndCapLayerThickness < std::numeric_limits<float>::epsilon()) || (m_hCalBarrelLayerThickness < std::numeric_limits<float>::epsilon()))
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+
+
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+CaloHitCreator::~CaloHitCreator()
+{
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode CaloHitCreator::CreateCaloHits(const CollectionMaps& collectionMaps)
+{
+    
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateECalCaloHits (collectionMaps));
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateHCalCaloHits (collectionMaps));
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateMuonCaloHits (collectionMaps));
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateLCalCaloHits (collectionMaps));
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateLHCalCaloHits(collectionMaps));
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode CaloHitCreator::CreateECalCaloHits(const CollectionMaps& collectionMaps)
+{
+    std::cout<<"start CreateECalCaloHits:"<<std::endl;
+    for (StringVector::const_iterator iter = m_settings.m_eCalCaloHitCollections.begin(), iterEnd = m_settings.m_eCalCaloHitCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_CaloHit.find(*iter) == collectionMaps.collectionMap_CaloHit.end()) { std::cout<<"not find "<<(*iter)<<std::endl; continue;}
+        try
+        {
+            const std::vector<edm4hep::CalorimeterHit>& pCaloHitCollection = (collectionMaps.collectionMap_CaloHit.find(*iter))->second;
+            const int nElements(pCaloHitCollection.size());
+
+            std::cout<<(*iter)<<"has nElements="<<nElements<<std::endl;
+            if (0 == nElements)
+                continue;
+
+            const gear::LayerLayout &endcapLayerLayout(_GEAR->getEcalEndcapParameters().getLayerLayout());
+            const gear::LayerLayout &barrelLayerLayout(_GEAR->getEcalBarrelParameters().getLayerLayout()); 
+
+            //UTIL::CellIDDecoder<edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str);
+            ID_UTIL::CellIDDecoder<const edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str);
+            const std::string layerCodingString(m_encoder_str);
+            const std::string layerCoding(this->GetLayerCoding(layerCodingString));
+            const std::string staveCoding(this->GetStaveCoding(layerCodingString));
+
+            for (int i = 0; i < nElements; ++i)
+            {
+                try
+                {
+                    const edm4hep::CalorimeterHit& pCaloHit0 = pCaloHitCollection.at(i);
+                    const edm4hep::CalorimeterHit* pCaloHit = &(pCaloHit0);
+
+                    if (NULL == pCaloHit)
+                        throw ("CreateECalCaloHits pCaloHit Collection type mismatch");
+
+                    float eCalToMip(m_settings.m_eCalToMip), eCalMipThreshold(m_settings.m_eCalMipThreshold), eCalToEMGeV(m_settings.m_eCalToEMGeV),
+                        eCalToHadGeVBarrel(m_settings.m_eCalToHadGeVBarrel), eCalToHadGeVEndCap(m_settings.m_eCalToHadGeVEndCap);
+
+                    // Hybrid ECAL including pure ScECAL.
+                    if (m_settings.m_useEcalScLayers)
+                    {
+                        std::string collectionName(*iter);
+                        std::transform(collectionName.begin(), collectionName.end(), collectionName.begin(), ::tolower);
+
+                        if (collectionName.find("ecal", 0) == std::string::npos)
+                            std::cout << "WARNING: mismatching hybrid Ecal collection name. " << collectionName << std::endl;
+
+                        if (collectionName.find("si", 0) != std::string::npos)
+                        {
+                             eCalToMip = m_settings.m_eCalSiToMip;
+                             eCalMipThreshold = m_settings.m_eCalSiMipThreshold;
+                             eCalToEMGeV = m_settings.m_eCalSiToEMGeV;
+                             eCalToHadGeVBarrel = m_settings.m_eCalSiToHadGeVBarrel;
+                             eCalToHadGeVEndCap = m_settings.m_eCalSiToHadGeVEndCap;
+                        }
+                        else if (collectionName.find("sc", 0) != std::string::npos)
+                        {
+                             eCalToMip = m_settings.m_eCalScToMip;
+                             eCalMipThreshold = m_settings.m_eCalScMipThreshold;
+                             eCalToEMGeV = m_settings.m_eCalScToEMGeV;
+                             eCalToHadGeVBarrel = m_settings.m_eCalScToHadGeVBarrel;
+                             eCalToHadGeVEndCap = m_settings.m_eCalScToHadGeVEndCap;
+                        }
+                    }
+
+                    PandoraApi::CaloHit::Parameters caloHitParameters;
+                    caloHitParameters.m_hitType = pandora::ECAL;
+                    caloHitParameters.m_isDigital = false;//why false ?
+                    caloHitParameters.m_layer = cellIdDecoder(pCaloHit)[layerCoding.c_str()] + 1;//FIXME, should use + 1? because the decoded layer is start from 0.
+                    //std::cout << "ECAL layer=" << caloHitParameters.m_layer.Get() << std::endl;
+                    caloHitParameters.m_isInOuterSamplingLayer = false;
+                    this->GetCommonCaloHitProperties(pCaloHit, caloHitParameters);
+
+                    float absorberCorrection(1.);
+
+                    if (std::fabs(pCaloHit->getPosition()[2]) < m_eCalBarrelOuterZ)
+                    {
+                        this->GetBarrelCaloHitProperties(pCaloHit, barrelLayerLayout, m_eCalBarrelInnerSymmetry, m_eCalBarrelInnerPhi0,
+                            cellIdDecoder(pCaloHit)[ staveCoding], caloHitParameters, absorberCorrection);
+
+                        caloHitParameters.m_hadronicEnergy = eCalToHadGeVBarrel * pCaloHit->getEnergy();
+                        
+                        //std::cout << "ECAL: eCalToHadGeVBarrel="<<eCalToHadGeVBarrel<< std::endl;
+                    }
+                    else
+                    {
+                        this->GetEndCapCaloHitProperties(pCaloHit, endcapLayerLayout, caloHitParameters, absorberCorrection);
+                        caloHitParameters.m_hadronicEnergy = eCalToHadGeVEndCap * pCaloHit->getEnergy();
+                        //std::cout << "ECAL: eCalToHadGeVEndCap="<<eCalToHadGeVEndCap<< std::endl;
+                    }
+
+                    caloHitParameters.m_mipEquivalentEnergy = pCaloHit->getEnergy() * eCalToMip * absorberCorrection;
+
+                    if (caloHitParameters.m_mipEquivalentEnergy.Get() < eCalMipThreshold)
+                        continue;
+
+                    caloHitParameters.m_electromagneticEnergy = eCalToEMGeV * pCaloHit->getEnergy();
+                        //std::cout << "ECAL: eCalToEMGeV="<<eCalToEMGeV<< std::endl;
+
+                    // ATTN If using strip splitting, must correct cell sizes for use in PFA to minimum of strip width and strip length
+                    if (m_settings.m_stripSplittingOn)
+                    {
+                        const float splitCellSize(std::min(caloHitParameters.m_cellSize0.Get(), caloHitParameters.m_cellSize1.Get()));
+                        caloHitParameters.m_cellSize0 = splitCellSize;
+                        caloHitParameters.m_cellSize1 = splitCellSize;
+                    }
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::CaloHit::Create(*m_pPandora, caloHitParameters));
+                    m_calorimeterHitVector.push_back(const_cast<edm4hep::CalorimeterHit*>(pCaloHit));
+                    /*
+                    std::cout<<"i="<<i<<"\n"
+                    <<",layer="<<caloHitParameters.m_layer.Get()<<"\n"
+                    <<",m_hadronicEnergy="<<caloHitParameters.m_hadronicEnergy.Get()<<"\n"
+                    <<",m_electromagneticEnergy="<<caloHitParameters.m_electromagneticEnergy.Get()<<"\n"
+                    <<",m_mipEquivalentEnergy="<<caloHitParameters.m_mipEquivalentEnergy.Get()<<"\n"
+                    <<",m_inputEnergy="<<caloHitParameters.m_inputEnergy.Get()<<"\n"
+                    <<",m_positionVector0="<<caloHitParameters.m_positionVector.Get().GetX()<<"\n"
+                    <<",m_positionVector1="<<caloHitParameters.m_positionVector.Get().GetY()<<"\n"
+                    <<",m_positionVector2="<<caloHitParameters.m_positionVector.Get().GetZ()<<"\n"
+                    <<std::endl;
+                    */
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout<<"ERROR Failed to extract ecal calo hit: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout<<"WARNING Failed to extract ecal calo hit: " <<  std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout<< "Failed to extract ecal calo hit collection: " << *iter << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode CaloHitCreator::CreateHCalCaloHits(const CollectionMaps& collectionMaps)
+{
+    for (StringVector::const_iterator iter = m_settings.m_hCalCaloHitCollections.begin(), iterEnd = m_settings.m_hCalCaloHitCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_CaloHit.find(*iter) == collectionMaps.collectionMap_CaloHit.end()) { std::cout<<"not find "<<(*iter)<<std::endl; continue;}
+        try
+        {
+            const std::vector<edm4hep::CalorimeterHit>& pCaloHitCollection = (collectionMaps.collectionMap_CaloHit.find(*iter))->second;
+            const int nElements(pCaloHitCollection.size());
+
+            std::cout<<(*iter)<<"has nElements="<<nElements<<std::endl;
+            if (0 == nElements)
+                continue;
+
+            const gear::LayerLayout &endcapLayerLayout(_GEAR->getHcalEndcapParameters().getLayerLayout());
+            const gear::LayerLayout &barrelLayerLayout(_GEAR->getHcalBarrelParameters().getLayerLayout());
+
+            //UTIL::CellIDDecoder<edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str);
+            ID_UTIL::CellIDDecoder<const edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str);
+            const std::string layerCodingString(m_encoder_str);
+            const std::string layerCoding(this->GetLayerCoding(layerCodingString));
+            const std::string staveCoding(this->GetStaveCoding(layerCodingString));
+
+            for (int i = 0; i < nElements; ++i)
+            {
+                try
+                {
+                    const edm4hep::CalorimeterHit& pCaloHit0 = pCaloHitCollection.at(i);
+                    const edm4hep::CalorimeterHit* pCaloHit = &(pCaloHit0);
+
+                    if (NULL == pCaloHit)
+                        throw ("CreateHCalCaloHits Collection type mismatch");
+
+                    PandoraApi::CaloHit::Parameters caloHitParameters;
+                    caloHitParameters.m_hitType = pandora::HCAL;
+                    caloHitParameters.m_isDigital = false;
+                    caloHitParameters.m_layer = cellIdDecoder(pCaloHit)[layerCoding.c_str()];
+                    caloHitParameters.m_isInOuterSamplingLayer = (this->GetNLayersFromEdge(pCaloHit) <= m_settings.m_nOuterSamplingLayers);
+                    this->GetCommonCaloHitProperties(pCaloHit, caloHitParameters);
+
+                    float absorberCorrection(1.);
+
+                    if (std::fabs(pCaloHit->getPosition()[2]) < m_hCalBarrelOuterZ)
+                    {
+                        this->GetBarrelCaloHitProperties(pCaloHit, barrelLayerLayout, m_hCalBarrelInnerSymmetry, m_hCalBarrelInnerPhi0,
+                            m_hCalBarrelInnerSymmetry - int(cellIdDecoder(pCaloHit)[ staveCoding] / 2), caloHitParameters, absorberCorrection);
+                    }
+                    else
+                    {
+                        this->GetEndCapCaloHitProperties(pCaloHit, endcapLayerLayout, caloHitParameters, absorberCorrection);
+                    }
+
+                    caloHitParameters.m_mipEquivalentEnergy = pCaloHit->getEnergy() * m_settings.m_hCalToMip * absorberCorrection;
+
+                    if (caloHitParameters.m_mipEquivalentEnergy.Get() < m_settings.m_hCalMipThreshold)
+                        continue;
+
+                    caloHitParameters.m_hadronicEnergy = std::min(m_settings.m_hCalToHadGeV * pCaloHit->getEnergy(), m_settings.m_maxHCalHitHadronicEnergy);
+                    caloHitParameters.m_electromagneticEnergy = m_settings.m_hCalToEMGeV * pCaloHit->getEnergy();
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::CaloHit::Create(*m_pPandora, caloHitParameters));
+                    m_calorimeterHitVector.push_back(const_cast<edm4hep::CalorimeterHit*>(pCaloHit));
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout << "Error, Failed to extract hcal calo hit: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout<<"WARNING Failed to extract hcal calo hit: " << std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout << "Failed to extract hcal calo hit collection: " << *iter << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode CaloHitCreator::CreateMuonCaloHits(const CollectionMaps& collectionMaps)
+{
+    for (StringVector::const_iterator iter = m_settings.m_muonCaloHitCollections.begin(), iterEnd = m_settings.m_muonCaloHitCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_CaloHit.find(*iter) == collectionMaps.collectionMap_CaloHit.end()) { std::cout<<"not find "<<(*iter)<<std::endl; continue;}
+        try
+        {
+            const std::vector<edm4hep::CalorimeterHit>& pCaloHitCollection = (collectionMaps.collectionMap_CaloHit.find(*iter))->second;
+            const int nElements(pCaloHitCollection.size());
+
+            std::cout<<(*iter)<<"has nElements="<<nElements<<std::endl;
+            if (0 == nElements)
+                continue;
+
+            const gear::LayerLayout &endcapLayerLayout(_GEAR->getYokeEndcapParameters().getLayerLayout());
+            const gear::LayerLayout &barrelLayerLayout(_GEAR->getYokeBarrelParameters().getLayerLayout()); 
+            const gear::LayerLayout &plugLayerLayout(_GEAR->getYokePlugParameters().getLayerLayout());
+
+            //UTIL::CellIDDecoder<edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str_MUON);
+            ID_UTIL::CellIDDecoder<const edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str_MUON);
+            const std::string layerCodingString(m_encoder_str_MUON);
+            const std::string layerCoding(this->GetLayerCoding(layerCodingString));
+            const std::string staveCoding(this->GetStaveCoding(layerCodingString));
+
+            for (int i = 0; i < nElements; ++i)
+            {
+                try
+                {
+                    const edm4hep::CalorimeterHit& pCaloHit0 = pCaloHitCollection.at(i);
+                    const edm4hep::CalorimeterHit* pCaloHit = &(pCaloHit0);
+
+                    if (NULL == pCaloHit)
+                        throw ("Muon Collection type mismatch");
+
+                    PandoraApi::CaloHit::Parameters caloHitParameters;
+                    caloHitParameters.m_hitType = pandora::MUON;
+                    caloHitParameters.m_layer = cellIdDecoder(pCaloHit)[layerCoding.c_str()] + 1;//FIXME, should use +1? it starts from 0.
+                    caloHitParameters.m_isInOuterSamplingLayer = true;
+                    this->GetCommonCaloHitProperties(pCaloHit, caloHitParameters);
+
+                    const float radius(std::sqrt(pCaloHit->getPosition()[0] * pCaloHit->getPosition()[0] +
+                        pCaloHit->getPosition()[1] * pCaloHit->getPosition()[1]));
+
+                    const bool isWithinCoil(radius < m_coilOuterR);
+                    const bool isInBarrelRegion(std::fabs(pCaloHit->getPosition()[2]) < m_muonBarrelOuterZ);
+
+                    float absorberCorrection(1.);
+
+                    if (isInBarrelRegion && isWithinCoil)
+                    {
+                        this->GetEndCapCaloHitProperties(pCaloHit, plugLayerLayout, caloHitParameters, absorberCorrection);
+                    }
+                    else if (isInBarrelRegion)
+                    {
+                        this->GetBarrelCaloHitProperties(pCaloHit, barrelLayerLayout, m_muonBarrelInnerSymmetry, m_muonBarrelInnerPhi0,
+                            cellIdDecoder(pCaloHit)[ staveCoding ], caloHitParameters, absorberCorrection);
+                    }
+                    else
+                    {
+                        this->GetEndCapCaloHitProperties(pCaloHit, endcapLayerLayout, caloHitParameters, absorberCorrection);
+                    }
+
+                    if (m_settings.m_muonDigitalHits > 0)
+                    {
+                        caloHitParameters.m_isDigital = true;
+                        caloHitParameters.m_inputEnergy = m_settings.m_muonHitEnergy;
+                        caloHitParameters.m_hadronicEnergy = m_settings.m_muonHitEnergy;
+                        caloHitParameters.m_electromagneticEnergy = m_settings.m_muonHitEnergy;
+                        caloHitParameters.m_mipEquivalentEnergy = 1.f;
+                    }
+                    else
+                    {
+                        caloHitParameters.m_isDigital = false;
+                        caloHitParameters.m_inputEnergy = pCaloHit->getEnergy();
+                        caloHitParameters.m_hadronicEnergy = pCaloHit->getEnergy();
+                        caloHitParameters.m_electromagneticEnergy = pCaloHit->getEnergy();
+                        caloHitParameters.m_mipEquivalentEnergy = pCaloHit->getEnergy() * m_settings.m_muonToMip;
+                    }
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::CaloHit::Create(*m_pPandora, caloHitParameters));
+                    m_calorimeterHitVector.push_back(const_cast<edm4hep::CalorimeterHit*>(pCaloHit));
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout << "Failed to extract muon hit: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout << "Failed to extract muon hit: "  << std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout << "Failed to extract muon hit collection: " << *iter  << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode CaloHitCreator::CreateLCalCaloHits(const CollectionMaps& collectionMaps)
+{
+    for (StringVector::const_iterator iter = m_settings.m_lCalCaloHitCollections.begin(), iterEnd = m_settings.m_lCalCaloHitCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_CaloHit.find(*iter) == collectionMaps.collectionMap_CaloHit.end()) { std::cout<<"not find "<<(*iter)<<std::endl; continue;}
+        try
+        {
+            const std::vector<edm4hep::CalorimeterHit>& pCaloHitCollection = (collectionMaps.collectionMap_CaloHit.find(*iter))->second;
+            const int nElements(pCaloHitCollection.size());
+
+            std::cout<<(*iter)<<"has nElements="<<nElements<<std::endl;
+            if (0 == nElements)
+                continue;
+
+            const gear::LayerLayout &endcapLayerLayout(_GEAR->getLcalParameters().getLayerLayout()); 
+
+            //UTIL::CellIDDecoder<edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str_LCal);
+            ID_UTIL::CellIDDecoder<const edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str_LCal);
+            const std::string layerCodingString(m_encoder_str_LCal);
+            const std::string layerCoding(this->GetLayerCoding(layerCodingString));
+
+            for (int i = 0; i < nElements; ++i)
+            {
+                try
+                {
+                    const edm4hep::CalorimeterHit& pCaloHit0 = pCaloHitCollection.at(i);
+                    const edm4hep::CalorimeterHit* pCaloHit = &(pCaloHit0);
+
+                    if (NULL == pCaloHit)
+                        throw ("LCal Collection type mismatch");
+
+                    PandoraApi::CaloHit::Parameters caloHitParameters;
+                    caloHitParameters.m_hitType = pandora::ECAL;
+                    caloHitParameters.m_isDigital = false;
+                    caloHitParameters.m_layer = cellIdDecoder(pCaloHit)[layerCoding.c_str()];
+                    caloHitParameters.m_isInOuterSamplingLayer = false;
+                    this->GetCommonCaloHitProperties(pCaloHit, caloHitParameters);
+
+                    float absorberCorrection(1.);
+                    this->GetEndCapCaloHitProperties(pCaloHit, endcapLayerLayout, caloHitParameters, absorberCorrection);
+
+                    caloHitParameters.m_mipEquivalentEnergy = pCaloHit->getEnergy() * m_settings.m_eCalToMip * absorberCorrection;
+
+                    if (caloHitParameters.m_mipEquivalentEnergy.Get() < m_settings.m_eCalMipThreshold)
+                        continue;
+
+                    caloHitParameters.m_electromagneticEnergy = m_settings.m_eCalToEMGeV * pCaloHit->getEnergy();
+                    caloHitParameters.m_hadronicEnergy = m_settings.m_eCalToHadGeVEndCap * pCaloHit->getEnergy();
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::CaloHit::Create(*m_pPandora, caloHitParameters));
+                    m_calorimeterHitVector.push_back(const_cast<edm4hep::CalorimeterHit*>(pCaloHit));
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout << "Failed to extract lcal calo hit: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout << "Failed to extract lcal calo hit: " << std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout << "Failed to extract lcal calo hit collection: " << *iter << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode CaloHitCreator::CreateLHCalCaloHits(const CollectionMaps& collectionMaps)
+{
+    for (StringVector::const_iterator iter = m_settings.m_lHCalCaloHitCollections.begin(), iterEnd = m_settings.m_lHCalCaloHitCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_CaloHit.find(*iter) == collectionMaps.collectionMap_CaloHit.end()) { std::cout<<"not find "<<(*iter)<<std::endl; continue;}
+        try
+        {
+            const std::vector<edm4hep::CalorimeterHit>& pCaloHitCollection = (collectionMaps.collectionMap_CaloHit.find(*iter))->second;
+            const int nElements(pCaloHitCollection.size());
+
+            std::cout<<(*iter)<<"has nElements="<<nElements<<std::endl;
+            if (0 == nElements)
+                continue;
+
+            const gear::LayerLayout &endcapLayerLayout(_GEAR->getLHcalParameters().getLayerLayout());
+
+            //UTIL::CellIDDecoder<edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str_LHCal);
+            ID_UTIL::CellIDDecoder<const edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str_LHCal);
+            const std::string layerCodingString(m_encoder_str_LHCal);
+            const std::string layerCoding(this->GetLayerCoding(layerCodingString));
+
+            for (int i = 0; i < nElements; ++i)
+            {
+                try
+                {
+                    const edm4hep::CalorimeterHit& pCaloHit0 = pCaloHitCollection.at(i);
+                    const edm4hep::CalorimeterHit* pCaloHit = &(pCaloHit0);
+
+                    if (NULL == pCaloHit)
+                        throw ("LHCal Collection type mismatch");
+
+                    PandoraApi::CaloHit::Parameters caloHitParameters;
+                    caloHitParameters.m_hitType = pandora::HCAL;
+                    caloHitParameters.m_isDigital = false;
+                    caloHitParameters.m_layer = cellIdDecoder(pCaloHit)[layerCoding.c_str()];
+                    caloHitParameters.m_isInOuterSamplingLayer = (this->GetNLayersFromEdge(pCaloHit) <= m_settings.m_nOuterSamplingLayers);
+                    this->GetCommonCaloHitProperties(pCaloHit, caloHitParameters);
+
+                    float absorberCorrection(1.);
+                    this->GetEndCapCaloHitProperties(pCaloHit, endcapLayerLayout, caloHitParameters, absorberCorrection);
+
+                    caloHitParameters.m_mipEquivalentEnergy = pCaloHit->getEnergy() * m_settings.m_hCalToMip * absorberCorrection;
+
+                    if (caloHitParameters.m_mipEquivalentEnergy.Get() < m_settings.m_hCalMipThreshold)
+                        continue;
+
+                    caloHitParameters.m_hadronicEnergy = std::min(m_settings.m_hCalToHadGeV * pCaloHit->getEnergy(), m_settings.m_maxHCalHitHadronicEnergy);
+                    caloHitParameters.m_electromagneticEnergy = m_settings.m_hCalToEMGeV * pCaloHit->getEnergy();
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::CaloHit::Create(*m_pPandora, caloHitParameters));
+                    m_calorimeterHitVector.push_back(const_cast<edm4hep::CalorimeterHit*>(pCaloHit));
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout << "Failed to extract lhcal calo hit: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout << "Failed to extract lhcal calo hit: " << std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout << "Failed to extract lhcal calo hit collection: " << *iter << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void CaloHitCreator::GetCommonCaloHitProperties(const edm4hep::CalorimeterHit *const pCaloHit, PandoraApi::CaloHit::Parameters &caloHitParameters) const
+{
+    const float pCaloHitPosition[3]={pCaloHit->getPosition()[0], pCaloHit->getPosition()[1], pCaloHit->getPosition()[2]};
+    const pandora::CartesianVector positionVector(pCaloHitPosition[0], pCaloHitPosition[1], pCaloHitPosition[2]);
+
+    caloHitParameters.m_cellGeometry = pandora::RECTANGULAR;
+    caloHitParameters.m_positionVector = positionVector;
+    caloHitParameters.m_expectedDirection = positionVector.GetUnitVector();
+    //caloHitParameters.m_pParentAddress = (void*)pCaloHit;
+    caloHitParameters.m_pParentAddress = pCaloHit;
+    caloHitParameters.m_inputEnergy = pCaloHit->getEnergy();
+    caloHitParameters.m_time = pCaloHit->getTime();
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void CaloHitCreator::GetEndCapCaloHitProperties(const edm4hep::CalorimeterHit *const pCaloHit, const gear::LayerLayout &layerLayout,
+    PandoraApi::CaloHit::Parameters &caloHitParameters, float &absorberCorrection) const
+{
+    caloHitParameters.m_hitRegion = pandora::ENDCAP;
+
+    const int physicalLayer(std::min(static_cast<int>(caloHitParameters.m_layer.Get()), layerLayout.getNLayers() - 1));
+    caloHitParameters.m_cellSize0 = layerLayout.getCellSize0(physicalLayer);
+    caloHitParameters.m_cellSize1 = layerLayout.getCellSize1(physicalLayer);
+    caloHitParameters.m_cellThickness = layerLayout.getThickness(physicalLayer);
+
+    const float radiationLength((pandora::ECAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberRadLengthECal :
+        (pandora::HCAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberRadLengthHCal : m_settings.m_absorberRadLengthOther);
+    const float interactionLength((pandora::ECAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberIntLengthECal :
+        (pandora::HCAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberIntLengthHCal : m_settings.m_absorberIntLengthOther);
+
+    const float layerAbsorberThickness(layerLayout.getAbsorberThickness(physicalLayer));
+    caloHitParameters.m_nCellRadiationLengths = radiationLength * layerAbsorberThickness;
+    caloHitParameters.m_nCellInteractionLengths = interactionLength * layerAbsorberThickness;
+
+    if (caloHitParameters.m_nCellRadiationLengths.Get() < std::numeric_limits<float>::epsilon() || caloHitParameters.m_nCellInteractionLengths.Get() < std::numeric_limits<float>::epsilon())
+    {
+        //std::cout<<"interactionLength="<<interactionLength<<",layerAbsorberThickness="<<layerAbsorberThickness<<",radiationLength="<<radiationLength<<",physicalLayer="<<physicalLayer<<std::endl;
+        std::cout<<"WARNING CaloHitCreator::GetEndCapCaloHitProperties Calo hit has 0 radiation length or interaction length: \
+            not creating a Pandora calo hit." << std::endl;
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+    }
+
+    absorberCorrection = 1.;
+    for (unsigned int i = 0, iMax = layerLayout.getNLayers(); i < iMax; ++i)
+    {
+        const float absorberThickness(layerLayout.getAbsorberThickness(i));
+
+        if (absorberThickness < std::numeric_limits<float>::epsilon())
+            continue;
+
+        if (layerAbsorberThickness > std::numeric_limits<float>::epsilon())
+            absorberCorrection = absorberThickness / layerAbsorberThickness;
+
+        break;
+    }
+
+    caloHitParameters.m_cellNormalVector = (pCaloHit->getPosition()[2] > 0) ? pandora::CartesianVector(0, 0, 1) :
+        pandora::CartesianVector(0, 0, -1);
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void CaloHitCreator::GetBarrelCaloHitProperties(const edm4hep::CalorimeterHit *const pCaloHit, const gear::LayerLayout &layerLayout,
+    unsigned int barrelSymmetryOrder, float barrelPhi0, unsigned int staveNumber, PandoraApi::CaloHit::Parameters &caloHitParameters,
+    float &absorberCorrection) const
+{
+    caloHitParameters.m_hitRegion = pandora::BARREL;
+
+    const int physicalLayer(std::min(static_cast<int>(caloHitParameters.m_layer.Get()), layerLayout.getNLayers() - 1));
+    caloHitParameters.m_cellSize0 = layerLayout.getCellSize0(physicalLayer);
+    caloHitParameters.m_cellSize1 = layerLayout.getCellSize1(physicalLayer);
+    caloHitParameters.m_cellThickness = layerLayout.getThickness(physicalLayer);
+
+    const float radiationLength((pandora::ECAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberRadLengthECal :
+        (pandora::HCAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberRadLengthHCal : m_settings.m_absorberRadLengthOther);
+    const float interactionLength((pandora::ECAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberIntLengthECal :
+        (pandora::HCAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberIntLengthHCal : m_settings.m_absorberIntLengthOther);
+
+    const float layerAbsorberThickness(layerLayout.getAbsorberThickness(physicalLayer));
+    caloHitParameters.m_nCellRadiationLengths = radiationLength * layerAbsorberThickness;
+    caloHitParameters.m_nCellInteractionLengths = interactionLength * layerAbsorberThickness;
+
+    //std::cout<<"m_layer="<<caloHitParameters.m_layer.Get()<<",layerLayout.getNLayers() - 1="<<layerLayout.getNLayers() - 1<<",layerAbsorberThickness="<<layerAbsorberThickness<<std::endl;
+    if (caloHitParameters.m_nCellRadiationLengths.Get() < std::numeric_limits<float>::epsilon() || caloHitParameters.m_nCellInteractionLengths.Get() < std::numeric_limits<float>::epsilon())
+    {
+        std::cout<<"interactionLength="<<interactionLength<<",layerAbsorberThickness="<<layerAbsorberThickness<<",radiationLength="<<radiationLength<<",physicalLayer="<<physicalLayer<<",l1="<<caloHitParameters.m_layer.Get()<<",l2="<<layerLayout.getNLayers()-1<<std::endl;
+        std::cout<<"WARNIN CaloHitCreator::GetBarrelCaloHitProperties Calo hit has 0 radiation length or interaction length: \
+            not creating a Pandora calo hit." << std::endl;
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+    }
+
+    absorberCorrection = 1.;
+    for (unsigned int i = 0, iMax = layerLayout.getNLayers(); i < iMax; ++i)
+    {
+        const float absorberThickness(layerLayout.getAbsorberThickness(i));
+
+        if (absorberThickness < std::numeric_limits<float>::epsilon())
+            continue;
+
+        if (layerAbsorberThickness > std::numeric_limits<float>::epsilon())
+            absorberCorrection = absorberThickness / layerAbsorberThickness;
+
+        break;
+    }
+
+    if (barrelSymmetryOrder > 2)
+    {
+        const float phi = barrelPhi0 + (2. * M_PI * static_cast<float>(staveNumber) / static_cast<float>(barrelSymmetryOrder));
+        caloHitParameters.m_cellNormalVector = pandora::CartesianVector(-std::sin(phi), std::cos(phi), 0);
+    }
+    else
+    {
+        const float pCaloHitPosition[3]={pCaloHit->getPosition()[0], pCaloHit->getPosition()[1], pCaloHit->getPosition()[2]};
+
+        if (pCaloHitPosition[1] != 0)
+        {
+            const float phi = barrelPhi0 + std::atan(pCaloHitPosition[0] / pCaloHitPosition[1]);
+            caloHitParameters.m_cellNormalVector = pandora::CartesianVector(std::sin(phi), std::cos(phi), 0);
+        }
+        else
+        {
+            caloHitParameters.m_cellNormalVector = (pCaloHitPosition[0] > 0) ? pandora::CartesianVector(1, 0, 0) :
+                pandora::CartesianVector(-1, 0, 0);
+        }
+    }
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+int CaloHitCreator::GetNLayersFromEdge(const edm4hep::CalorimeterHit *const pCaloHit) const
+{
+    // Calo hit coordinate calculations
+    const float barrelMaximumRadius(this->GetMaximumRadius(pCaloHit, m_hCalBarrelOuterSymmetry, m_hCalBarrelOuterPhi0));
+    const float endCapMaximumRadius(this->GetMaximumRadius(pCaloHit, m_settings.m_hCalEndCapInnerSymmetryOrder, m_settings.m_hCalEndCapInnerPhiCoordinate));
+    const float caloHitAbsZ(std::fabs(pCaloHit->getPosition()[2]));
+
+    // Distance from radial outer
+    float radialDistanceToEdge(std::numeric_limits<float>::max());
+
+    if (caloHitAbsZ < m_eCalBarrelOuterZ)
+    {
+        radialDistanceToEdge = (m_hCalBarrelOuterR - barrelMaximumRadius) / m_hCalBarrelLayerThickness;
+    }
+    else
+    {
+        radialDistanceToEdge = (m_hCalEndCapOuterR - endCapMaximumRadius) / m_hCalEndCapLayerThickness;
+    }
+
+    // Distance from rear of endcap outer
+    float rearDistanceToEdge(std::numeric_limits<float>::max());
+
+    if (caloHitAbsZ >= m_eCalBarrelOuterZ)
+    {
+        rearDistanceToEdge = (m_hCalEndCapOuterZ - caloHitAbsZ) / m_hCalEndCapLayerThickness;
+    }
+    else
+    {
+        const float rearDistance((m_eCalBarrelOuterZ - caloHitAbsZ) / m_hCalBarrelLayerThickness);
+
+        if (rearDistance < m_settings.m_layersFromEdgeMaxRearDistance)
+        {
+            const float overlapDistance((m_hCalEndCapOuterR - endCapMaximumRadius) / m_hCalEndCapLayerThickness);
+            rearDistanceToEdge = std::max(rearDistance, overlapDistance);
+        }
+    }
+
+    return static_cast<int>(std::min(radialDistanceToEdge, rearDistanceToEdge));
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+float CaloHitCreator::GetMaximumRadius(const edm4hep::CalorimeterHit *const pCaloHit, const unsigned int symmetryOrder, const float phi0) const
+{
+    
+    const float pCaloHitPosition[3]={pCaloHit->getPosition()[0], pCaloHit->getPosition()[1], pCaloHit->getPosition()[2]};
+    if (symmetryOrder <= 2)
+        return std::sqrt((pCaloHitPosition[0] * pCaloHitPosition[0]) + (pCaloHitPosition[1] * pCaloHitPosition[1]));
+
+    float maximumRadius(0.f);
+    const float twoPi(2.f * M_PI);
+
+    for (unsigned int i = 0; i < symmetryOrder; ++i)
+    {
+        const float phi = phi0 + i * twoPi / static_cast<float>(symmetryOrder);
+        float radius = pCaloHitPosition[0] * std::cos(phi) + pCaloHitPosition[1] * std::sin(phi);
+
+        if (radius > maximumRadius)
+            maximumRadius = radius;
+    }
+
+    return maximumRadius;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+std::string CaloHitCreator::GetLayerCoding(const std::string &encodingString) const
+{
+    if (encodingString.find("layer") != std::string::npos)
+        return std::string("layer");
+
+    if (encodingString.find("K-1") != std::string::npos)
+        return std::string("K-1");
+
+    if (encodingString.find("K") != std::string::npos)
+        return std::string("K");
+
+    return std::string("unknown_layer_encoding");
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+std::string CaloHitCreator::GetStaveCoding(const std::string &encodingString) const
+{
+    if (encodingString.find("stave") != std::string::npos)
+        return std::string("stave");
+
+    if (encodingString.find("S-1") != std::string::npos)
+        return std::string("S-1");
+
+    return std::string("unknown_stave_encoding") ;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+CaloHitCreator::Settings::Settings() :
+    m_absorberRadLengthECal(1.f),
+    m_absorberIntLengthECal(1.f),
+    m_absorberRadLengthHCal(1.f),
+    m_absorberIntLengthHCal(1.f),
+    m_absorberRadLengthOther(1.f),
+    m_absorberIntLengthOther(1.f),
+    m_eCalToMip(1.f),
+    m_hCalToMip(1.f),
+    m_muonToMip(1.f),
+    m_eCalMipThreshold(0.f),
+    m_hCalMipThreshold(0.f),
+    m_muonMipThreshold(0.f),
+    m_eCalToEMGeV(1.f),
+    m_eCalToHadGeVBarrel(1.f),
+    m_eCalToHadGeVEndCap(1.f),
+    m_hCalToEMGeV(1.f),
+    m_hCalToHadGeV(1.f),
+    m_muonDigitalHits(1),
+    m_muonHitEnergy(0.5f),
+    m_maxHCalHitHadronicEnergy(10000.f),
+    m_nOuterSamplingLayers(3),
+    m_layersFromEdgeMaxRearDistance(250.f),
+    m_hCalEndCapInnerSymmetryOrder(4),
+    m_hCalEndCapInnerPhiCoordinate(0.f),
+    m_stripSplittingOn(0),
+    m_useEcalScLayers(0),
+    m_eCalSiToMip(1.f),
+    m_eCalScToMip(1.f),
+    m_eCalSiMipThreshold(0.f),
+    m_eCalScMipThreshold(0.f),
+    m_eCalSiToEMGeV(1.f),
+    m_eCalScToEMGeV(1.f),
+    m_eCalSiToHadGeVBarrel(1.f),
+    m_eCalScToHadGeVBarrel(1.f),
+    m_eCalSiToHadGeVEndCap(1.f),
+    m_eCalScToHadGeVEndCap(1.f)
+{
+}
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/src/GeometryCreator.cpp b/Reconstruction/PFA/Pandora/GaudiPandora/src/GeometryCreator.cpp
new file mode 100644
index 00000000..35e8fedb
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/src/GeometryCreator.cpp
@@ -0,0 +1,433 @@
+/**
+ *  @file   MarlinPandora/src/GeometryCreator.cc
+ * 
+ *  @brief  Implementation of the geometry creator class.
+ * 
+ *  $Log: $
+ */
+#include "GaudiKernel/IService.h"
+
+#include "GearSvc/IGearSvc.h"
+#include "gear/BField.h"
+#include "gear/GEAR.h"
+#include "gear/GearParameters.h"
+#include "gear/CalorimeterParameters.h"
+#include "gear/TPCParameters.h"
+#include "gear/PadRowLayout2D.h"
+#include "gear/LayerLayout.h"
+
+#include "GeometryCreator.h"
+//#include "PandoraPFANewProcessor.h"
+
+GeometryCreator::GeometryCreator(const Settings &settings, const pandora::Pandora *const pPandora) :
+    m_settings(settings),
+    m_pPandora(pPandora)
+{
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+GeometryCreator::~GeometryCreator()
+{
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+//pandora::StatusCode GeometryCreator::CreateGeometry() const
+//pandora::StatusCode GeometryCreator::CreateGeometry(ISvcLocator* svcloc) const
+pandora::StatusCode GeometryCreator::CreateGeometry(ISvcLocator* svcloc) 
+{
+    IGearSvc*  iSvc = 0;
+    StatusCode sc = svcloc->service("GearSvc", iSvc, false);
+    if ( !sc ) 
+    {
+        throw "Failed to find GearSvc ...";
+    }
+    _GEAR = iSvc->getGearMgr();
+    //auto _gear = service<IGearSvc>("GearSvc");
+    //if ( !_gear ) 
+    //{
+    //    throw "Failed to find GearSvc ...";
+    //}
+    //_GEAR = _gear->getGearMgr();
+    
+
+    try
+    {
+        SubDetectorTypeMap subDetectorTypeMap;
+        this->SetMandatorySubDetectorParameters(subDetectorTypeMap);
+
+        SubDetectorNameMap subDetectorNameMap;
+        this->SetAdditionalSubDetectorParameters(subDetectorNameMap);
+        
+        if (std::string::npos != _GEAR->getDetectorName().find("ILD"))
+            this->SetILDSpecificGeometry(subDetectorTypeMap, subDetectorNameMap);
+        
+        for (SubDetectorTypeMap::const_iterator iter = subDetectorTypeMap.begin(), iterEnd = subDetectorTypeMap.end(); iter != iterEnd; ++iter)
+            PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::Geometry::SubDetector::Create(*m_pPandora, iter->second));
+
+        for (SubDetectorNameMap::const_iterator iter = subDetectorNameMap.begin(), iterEnd = subDetectorNameMap.end(); iter != iterEnd; ++iter)
+            PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::Geometry::SubDetector::Create(*m_pPandora, iter->second));
+    }
+    catch (gear::Exception &exception)
+    {
+        std::cout << "Failure in marlin pandora geometry creator, gear exception: " << exception.what() << std::endl;
+        throw exception;
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void GeometryCreator::SetMandatorySubDetectorParameters(SubDetectorTypeMap &subDetectorTypeMap) const
+{
+    std::cout << "Begin SetMandatorySubDetectorParameters:" << std::endl;
+    PandoraApi::Geometry::SubDetector::Parameters eCalBarrelParameters, eCalEndCapParameters, hCalBarrelParameters, hCalEndCapParameters,
+        muonBarrelParameters, muonEndCapParameters;
+
+    this->SetDefaultSubDetectorParameters(_GEAR->getEcalBarrelParameters(), "ECalBarrel", pandora::ECAL_BARREL, eCalBarrelParameters);
+    this->SetDefaultSubDetectorParameters(_GEAR->getEcalEndcapParameters(), "ECalEndCap", pandora::ECAL_ENDCAP, eCalEndCapParameters);
+    this->SetDefaultSubDetectorParameters(_GEAR->getHcalBarrelParameters(), "HCalBarrel", pandora::HCAL_BARREL, hCalBarrelParameters);
+    this->SetDefaultSubDetectorParameters(_GEAR->getHcalEndcapParameters(), "HCalEndCap", pandora::HCAL_ENDCAP, hCalEndCapParameters);
+    this->SetDefaultSubDetectorParameters(_GEAR->getYokeBarrelParameters(), "MuonBarrel", pandora::MUON_BARREL, muonBarrelParameters);
+    this->SetDefaultSubDetectorParameters(_GEAR->getYokeEndcapParameters(), "MuonEndCap", pandora::MUON_ENDCAP, muonEndCapParameters);
+
+    subDetectorTypeMap[pandora::ECAL_BARREL] = eCalBarrelParameters;
+    subDetectorTypeMap[pandora::ECAL_ENDCAP] = eCalEndCapParameters;
+    subDetectorTypeMap[pandora::HCAL_BARREL] = hCalBarrelParameters;
+    subDetectorTypeMap[pandora::HCAL_ENDCAP] = hCalEndCapParameters;
+    subDetectorTypeMap[pandora::MUON_BARREL] = muonBarrelParameters;
+    subDetectorTypeMap[pandora::MUON_ENDCAP] = muonEndCapParameters;
+
+    PandoraApi::Geometry::SubDetector::Parameters trackerParameters;
+    const gear::TPCParameters &tpcParameters(_GEAR->getTPCParameters());
+    trackerParameters.m_subDetectorName = "Tracker";
+    trackerParameters.m_subDetectorType = pandora::INNER_TRACKER;
+    trackerParameters.m_innerRCoordinate = tpcParameters.getPadLayout().getPlaneExtent()[0];
+    trackerParameters.m_innerZCoordinate = 0.f;
+    trackerParameters.m_innerPhiCoordinate = 0.f;
+    trackerParameters.m_innerSymmetryOrder = 0;
+    trackerParameters.m_outerRCoordinate = tpcParameters.getPadLayout().getPlaneExtent()[1];
+    trackerParameters.m_outerZCoordinate = tpcParameters.getMaxDriftLength();
+    trackerParameters.m_outerPhiCoordinate = 0.f;
+    trackerParameters.m_outerSymmetryOrder = 0;
+    trackerParameters.m_isMirroredInZ = true;
+    trackerParameters.m_nLayers = 0;
+    subDetectorTypeMap[pandora::INNER_TRACKER] = trackerParameters;
+
+    PandoraApi::Geometry::SubDetector::Parameters coilParameters;
+    const gear::GearParameters &gearParameters(_GEAR->getGearParameters("CoilParameters"));
+    coilParameters.m_subDetectorName = "Coil";
+    coilParameters.m_subDetectorType = pandora::COIL;
+    coilParameters.m_innerRCoordinate = gearParameters.getDoubleVal("Coil_cryostat_inner_radius");
+    coilParameters.m_innerZCoordinate = 0.f;
+    coilParameters.m_innerPhiCoordinate = 0.f;
+    coilParameters.m_innerSymmetryOrder = 0;
+    coilParameters.m_outerRCoordinate = gearParameters.getDoubleVal("Coil_cryostat_outer_radius");
+    coilParameters.m_outerZCoordinate = gearParameters.getDoubleVal("Coil_cryostat_half_z");
+    coilParameters.m_outerPhiCoordinate = 0.f;
+    coilParameters.m_outerSymmetryOrder = 0;
+    coilParameters.m_isMirroredInZ = true;
+    coilParameters.m_nLayers = 0;
+    subDetectorTypeMap[pandora::COIL] = coilParameters;
+    std::cout << "End SetMandatorySubDetectorParameters" << std::endl;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void GeometryCreator::SetAdditionalSubDetectorParameters(SubDetectorNameMap &subDetectorNameMap) const
+{
+    std::cout << "Begin SetAdditionalSubDetectorParameters:" << std::endl;
+    try
+    {
+        PandoraApi::Geometry::SubDetector::Parameters parameters;
+        this->SetDefaultSubDetectorParameters(_GEAR->getEcalPlugParameters(), "ECalPlug", pandora::SUB_DETECTOR_OTHER, parameters);
+        subDetectorNameMap[parameters.m_subDetectorName.Get()] = parameters;
+    }
+    catch (gear::Exception &exception)
+    {
+        std::cout << " warning pandora geometry creator: " << exception.what() << std::endl;
+    }
+
+    try
+    {
+        PandoraApi::Geometry::SubDetector::Parameters parameters;
+        this->SetDefaultSubDetectorParameters(_GEAR->getHcalRingParameters(), "HCalRing", pandora::SUB_DETECTOR_OTHER, parameters);
+        subDetectorNameMap[parameters.m_subDetectorName.Get()] = parameters;
+    }
+    catch (gear::Exception &exception)
+    {
+         std::cout<< "warning pandora geometry creator: " << exception.what() << std::endl;
+    }
+
+    try
+    {
+        PandoraApi::Geometry::SubDetector::Parameters parameters;
+        this->SetDefaultSubDetectorParameters(_GEAR->getLcalParameters(), "LCal", pandora::SUB_DETECTOR_OTHER, parameters);
+        subDetectorNameMap[parameters.m_subDetectorName.Get()] = parameters;
+    }
+    catch (gear::Exception &exception)
+    {
+         std::cout << "warning pandora geometry creator: " << exception.what() << std::endl;
+    }
+
+    try
+    {
+        PandoraApi::Geometry::SubDetector::Parameters parameters;
+        this->SetDefaultSubDetectorParameters(_GEAR->getLHcalParameters(), "LHCal", pandora::SUB_DETECTOR_OTHER, parameters);
+        subDetectorNameMap[parameters.m_subDetectorName.Get()] = parameters;
+    }
+    catch (gear::Exception &exception)
+    {
+         std::cout << "warning pandora geometry creator: " << exception.what() << std::endl;
+    }
+    std::cout << "End SetAdditionalSubDetectorParameters" << std::endl;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void GeometryCreator::SetDefaultSubDetectorParameters(const gear::CalorimeterParameters &inputParameters, const std::string &subDetectorName,
+    const pandora::SubDetectorType subDetectorType, PandoraApi::Geometry::SubDetector::Parameters &parameters) const
+{
+    const gear::LayerLayout &layerLayout = inputParameters.getLayerLayout();
+
+    parameters.m_subDetectorName = subDetectorName;
+    parameters.m_subDetectorType = subDetectorType;
+    parameters.m_innerRCoordinate = inputParameters.getExtent()[0];
+    parameters.m_innerZCoordinate = inputParameters.getExtent()[2];
+    parameters.m_innerPhiCoordinate = inputParameters.getPhi0();
+    parameters.m_innerSymmetryOrder = inputParameters.getSymmetryOrder();
+    parameters.m_outerRCoordinate = inputParameters.getExtent()[1];
+    parameters.m_outerZCoordinate = inputParameters.getExtent()[3];
+    parameters.m_outerPhiCoordinate = inputParameters.getPhi0();
+    parameters.m_outerSymmetryOrder = inputParameters.getSymmetryOrder();
+    parameters.m_isMirroredInZ = true;
+    parameters.m_nLayers = layerLayout.getNLayers();
+    //std::cout << "m_nLayers="<<layerLayout.getNLayers() << std::endl;
+
+    // ATTN Not always going to be correct for any optional subdetectors, but impact of this is negligible for ILD
+    const float radiationLength(((pandora::ECAL_BARREL == subDetectorType) || (pandora::ECAL_ENDCAP == subDetectorType)) ? m_settings.m_absorberRadLengthECal :
+        ((pandora::HCAL_BARREL == subDetectorType) || (pandora::HCAL_ENDCAP == subDetectorType)) ? m_settings.m_absorberRadLengthHCal : m_settings.m_absorberRadLengthOther);
+    const float interactionLength(((pandora::ECAL_BARREL == subDetectorType) || (pandora::ECAL_ENDCAP == subDetectorType)) ? m_settings.m_absorberIntLengthECal :
+        ((pandora::HCAL_BARREL == subDetectorType) || (pandora::HCAL_ENDCAP == subDetectorType)) ? m_settings.m_absorberIntLengthHCal : m_settings.m_absorberIntLengthOther);
+
+    for (int i = 0; i < layerLayout.getNLayers(); ++i)
+    {
+        PandoraApi::Geometry::LayerParameters layerParameters;
+        layerParameters.m_closestDistanceToIp = layerLayout.getDistance(i) + (0.5 * (layerLayout.getThickness(i) + layerLayout.getAbsorberThickness(i)));
+        layerParameters.m_nRadiationLengths = radiationLength * layerLayout.getAbsorberThickness(i);
+        layerParameters.m_nInteractionLengths = interactionLength * layerLayout.getAbsorberThickness(i);
+        parameters.m_layerParametersVector.push_back(layerParameters);
+    }
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode GeometryCreator::SetILDSpecificGeometry(SubDetectorTypeMap &subDetectorTypeMap, SubDetectorNameMap &subDetectorNameMap) const
+{
+    // Set positions of gaps in ILD detector and add information missing from GEAR parameters file
+    try
+    {
+        const gear::CalorimeterParameters &hCalBarrelParameters = _GEAR->getHcalBarrelParameters();
+        subDetectorTypeMap[pandora::HCAL_BARREL].m_outerPhiCoordinate = hCalBarrelParameters.getIntVal("Hcal_outer_polygon_phi0");
+        subDetectorTypeMap[pandora::HCAL_BARREL].m_outerSymmetryOrder = hCalBarrelParameters.getIntVal("Hcal_outer_polygon_order");
+    }
+    catch (gear::Exception &)
+    {
+        // aLaVideauGeometry
+        return this->SetILD_SDHCALSpecificGeometry(subDetectorTypeMap);
+    }
+
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_innerSymmetryOrder = m_settings.m_eCalEndCapInnerSymmetryOrder;
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_innerPhiCoordinate = m_settings.m_eCalEndCapInnerPhiCoordinate;
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_outerSymmetryOrder = m_settings.m_eCalEndCapOuterSymmetryOrder;
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_outerPhiCoordinate = m_settings.m_eCalEndCapOuterPhiCoordinate;
+
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_innerSymmetryOrder = m_settings.m_hCalEndCapInnerSymmetryOrder;
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_innerPhiCoordinate = m_settings.m_hCalEndCapInnerPhiCoordinate;
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_outerSymmetryOrder = m_settings.m_hCalEndCapOuterSymmetryOrder;
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_outerPhiCoordinate = m_settings.m_hCalEndCapOuterPhiCoordinate;
+
+    subDetectorNameMap["HCalRing"].m_innerSymmetryOrder = m_settings.m_hCalRingInnerSymmetryOrder;
+    subDetectorNameMap["HCalRing"].m_innerPhiCoordinate = m_settings.m_hCalRingInnerPhiCoordinate;
+    subDetectorNameMap["HCalRing"].m_outerSymmetryOrder = m_settings.m_hCalRingOuterSymmetryOrder;
+    subDetectorNameMap["HCalRing"].m_outerPhiCoordinate = m_settings.m_hCalRingOuterPhiCoordinate;
+
+    // Gaps in detector active material
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateHCalBarrelBoxGaps());
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateHCalEndCapBoxGaps());
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateHCalBarrelConcentricGaps());
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode GeometryCreator::SetILD_SDHCALSpecificGeometry(SubDetectorTypeMap &subDetectorTypeMap) const
+{
+    // Non-default values (and those missing from GEAR parameters file)...
+    // The following 2 parameters have no sense for Videau Geometry, set them to 0
+    subDetectorTypeMap[pandora::HCAL_BARREL].m_outerPhiCoordinate = 0;
+    subDetectorTypeMap[pandora::HCAL_BARREL].m_outerSymmetryOrder = 0;
+
+    // Endcap is identical to standard ILD geometry, only HCAL barrel is different
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_innerSymmetryOrder = m_settings.m_eCalEndCapInnerSymmetryOrder;
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_innerPhiCoordinate = m_settings.m_eCalEndCapInnerPhiCoordinate;
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_outerSymmetryOrder = m_settings.m_eCalEndCapOuterSymmetryOrder;
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_outerPhiCoordinate = m_settings.m_eCalEndCapOuterPhiCoordinate;
+
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_innerSymmetryOrder = m_settings.m_hCalEndCapInnerSymmetryOrder;
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_innerPhiCoordinate = m_settings.m_hCalEndCapInnerPhiCoordinate;
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_outerSymmetryOrder = m_settings.m_hCalEndCapOuterSymmetryOrder;
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_outerPhiCoordinate = m_settings.m_hCalEndCapOuterPhiCoordinate;
+
+    // TODO implement gaps between modules
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode GeometryCreator::CreateHCalBarrelBoxGaps() const
+{
+    const std::string detectorName(_GEAR->getDetectorName());
+
+    const gear::CalorimeterParameters &hCalBarrelParameters = _GEAR->getHcalBarrelParameters();
+    const unsigned int innerSymmetryOrder(hCalBarrelParameters.getSymmetryOrder());
+    const unsigned int outerSymmetryOrder(hCalBarrelParameters.getIntVal("Hcal_outer_polygon_order"));
+
+    if ((0 == innerSymmetryOrder) || (2 != outerSymmetryOrder / innerSymmetryOrder))
+    {
+        std::cout << " Detector " << detectorName << " doesn't conform to expected ILD-specific geometry" << std::endl;
+        return pandora::STATUS_CODE_INVALID_PARAMETER;
+    }
+
+    const float innerRadius(hCalBarrelParameters.getExtent()[0]);
+    const float outerRadius(hCalBarrelParameters.getExtent()[1]);
+    const float outerZ(hCalBarrelParameters.getExtent()[3]);
+    const float phi0(hCalBarrelParameters.getPhi0());
+
+    const float staveGap(hCalBarrelParameters.getDoubleVal("Hcal_stave_gaps"));
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateRegularBoxGaps(innerSymmetryOrder, phi0, innerRadius, outerRadius,
+        -outerZ, outerZ, staveGap));
+
+    const float outerPseudoPhi0(M_PI / static_cast<float>(innerSymmetryOrder));
+    const float cosOuterPseudoPhi0(std::cos(outerPseudoPhi0));
+
+    if ((0 == outerPseudoPhi0) || (0.f == cosOuterPseudoPhi0))
+    {
+        std::cout << " Detector " << detectorName << " doesn't conform to expected ILD-specific geometry" << std::endl;
+        return pandora::STATUS_CODE_INVALID_PARAMETER;
+    }
+
+    const float middleStaveGap(hCalBarrelParameters.getDoubleVal("Hcal_middle_stave_gaps"));
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateRegularBoxGaps(innerSymmetryOrder, outerPseudoPhi0,
+        innerRadius / cosOuterPseudoPhi0, outerRadius, -outerZ, outerZ, middleStaveGap));
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode GeometryCreator::CreateHCalEndCapBoxGaps() const
+{
+    const gear::CalorimeterParameters &hCalEndCapParameters = _GEAR->getHcalEndcapParameters();
+
+    const float staveGap(hCalEndCapParameters.getDoubleVal("Hcal_stave_gaps"));
+    const float innerRadius(hCalEndCapParameters.getExtent()[0]);
+    const float outerRadius(hCalEndCapParameters.getExtent()[1]);
+    const float innerZ(hCalEndCapParameters.getExtent()[2]);
+    const float outerZ(hCalEndCapParameters.getExtent()[3]);
+
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateRegularBoxGaps(m_settings.m_hCalEndCapInnerSymmetryOrder,
+        m_settings.m_hCalEndCapInnerPhiCoordinate, innerRadius, outerRadius, innerZ, outerZ, staveGap,
+        pandora::CartesianVector(-innerRadius, 0, 0)));
+
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateRegularBoxGaps(m_settings.m_hCalEndCapInnerSymmetryOrder,
+        m_settings.m_hCalEndCapInnerPhiCoordinate, innerRadius, outerRadius, -outerZ, -innerZ, staveGap,
+        pandora::CartesianVector(innerRadius, 0, 0)));
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode GeometryCreator::CreateHCalBarrelConcentricGaps() const
+{
+    const gear::CalorimeterParameters &hCalBarrelParameters = _GEAR->getHcalBarrelParameters();
+    const float gapWidth(hCalBarrelParameters.getDoubleVal("Hcal_stave_gaps"));
+
+    PandoraApi::Geometry::ConcentricGap::Parameters gapParameters;
+
+    gapParameters.m_minZCoordinate = -0.5f * gapWidth;
+    gapParameters.m_maxZCoordinate =  0.5f * gapWidth;
+    gapParameters.m_innerRCoordinate = hCalBarrelParameters.getExtent()[0];
+    gapParameters.m_innerPhiCoordinate = hCalBarrelParameters.getPhi0();
+    gapParameters.m_innerSymmetryOrder = hCalBarrelParameters.getSymmetryOrder();
+    gapParameters.m_outerRCoordinate = hCalBarrelParameters.getExtent()[1];
+    gapParameters.m_outerPhiCoordinate = hCalBarrelParameters.getIntVal("Hcal_outer_polygon_phi0");
+    gapParameters.m_outerSymmetryOrder = hCalBarrelParameters.getIntVal("Hcal_outer_polygon_order");
+
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::Geometry::ConcentricGap::Create(*m_pPandora, gapParameters));
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode GeometryCreator::CreateRegularBoxGaps(unsigned int symmetryOrder, float phi0, float innerRadius, float outerRadius,
+    float minZ, float maxZ, float gapWidth, pandora::CartesianVector vertexOffset) const
+{
+    const pandora::CartesianVector basicGapVertex(pandora::CartesianVector(-0.5f * gapWidth, innerRadius, minZ) + vertexOffset);
+    const pandora::CartesianVector basicSide1(gapWidth, 0, 0);
+    const pandora::CartesianVector basicSide2(0, outerRadius - innerRadius, 0);
+    const pandora::CartesianVector basicSide3(0, 0, maxZ - minZ);
+
+    for (unsigned int i = 0; i < symmetryOrder; ++i)
+    {
+        const float phi = phi0 + (2. * M_PI * static_cast<float>(i) / static_cast<float>(symmetryOrder));
+        const float sinPhi(std::sin(phi));
+        const float cosPhi(std::cos(phi));
+
+        PandoraApi::Geometry::BoxGap::Parameters gapParameters;
+
+        gapParameters.m_vertex = pandora::CartesianVector(cosPhi * basicGapVertex.GetX() + sinPhi * basicGapVertex.GetY(),
+            -sinPhi * basicGapVertex.GetX() + cosPhi * basicGapVertex.GetY(), basicGapVertex.GetZ());
+        gapParameters.m_side1 = pandora::CartesianVector(cosPhi * basicSide1.GetX() + sinPhi * basicSide1.GetY(),
+            -sinPhi * basicSide1.GetX() + cosPhi * basicSide1.GetY(), basicSide1.GetZ());
+        gapParameters.m_side2 = pandora::CartesianVector(cosPhi * basicSide2.GetX() + sinPhi * basicSide2.GetY(),
+            -sinPhi * basicSide2.GetX() + cosPhi * basicSide2.GetY(), basicSide2.GetZ());
+        gapParameters.m_side3 = pandora::CartesianVector(cosPhi * basicSide3.GetX() + sinPhi * basicSide3.GetY(),
+            -sinPhi * basicSide3.GetX() + cosPhi * basicSide3.GetY(), basicSide3.GetZ());
+
+        PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::Geometry::BoxGap::Create(*m_pPandora, gapParameters));
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+GeometryCreator::Settings::Settings() :
+    m_absorberRadLengthECal(1.f),
+    m_absorberIntLengthECal(1.f),
+    m_absorberRadLengthHCal(1.f),
+    m_absorberIntLengthHCal(1.f),
+    m_absorberRadLengthOther(1.f),
+    m_absorberIntLengthOther(1.f),
+    m_eCalEndCapInnerSymmetryOrder(4),
+    m_eCalEndCapInnerPhiCoordinate(0.f),
+    m_eCalEndCapOuterSymmetryOrder(8),
+    m_eCalEndCapOuterPhiCoordinate(0.f),
+    m_hCalEndCapInnerSymmetryOrder(4),
+    m_hCalEndCapInnerPhiCoordinate(0.f),
+    m_hCalEndCapOuterSymmetryOrder(16),
+    m_hCalEndCapOuterPhiCoordinate(0.f),
+    m_hCalRingInnerSymmetryOrder(8),
+    m_hCalRingInnerPhiCoordinate(0.f),
+    m_hCalRingOuterSymmetryOrder(16),
+    m_hCalRingOuterPhiCoordinate(0.f)
+{
+}
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/src/MCParticleCreator.cpp b/Reconstruction/PFA/Pandora/GaudiPandora/src/MCParticleCreator.cpp
new file mode 100644
index 00000000..a19788b3
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/src/MCParticleCreator.cpp
@@ -0,0 +1,378 @@
+/**
+ *  @file   MarlinPandora/src/MCParticleCreator.cc
+ * 
+ *  @brief  Implementation of the mc particle creator class.
+ * 
+ *  $Log: $
+ */
+
+
+#include "edm4hep/MCParticleConst.h"
+#include "edm4hep/MCParticle.h" 
+#include "edm4hep/MCRecoCaloAssociation.h" 
+#include "edm4hep/SimCalorimeterHitConst.h" 
+#include "edm4hep/CaloHitContributionConst.h" 
+#include "edm4hep/Track.h" 
+#include "edm4hep/MCRecoTrackerAssociation.h" 
+#include "edm4hep/SimTrackerHitConst.h" 
+
+
+//#include "UTIL/LCRelationNavigator.h"
+
+//#include "gear/BField.h"
+
+#include "PandoraPFAlg.h"
+//#include "CaloHitCreator.h"
+//#include "TrackCreator.h"
+#include "MCParticleCreator.h"
+
+#include <cmath>
+#include <limits>
+#include <assert.h>
+
+MCParticleCreator::MCParticleCreator(const Settings &settings, const pandora::Pandora *const pPandora) :
+    m_settings(settings),
+    m_pPandora(pPandora),
+    //m_bField(marlin::Global::GEAR->getBField().at(gear::Vector3D(0., 0., 0.)).z())
+    m_bField(settings.m_bField)
+{
+m_id_pMC_map = new std::map<unsigned int, const edm4hep::MCParticle*>;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+MCParticleCreator::~MCParticleCreator()
+{
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode MCParticleCreator::CreateMCParticles(const CollectionMaps& collectionMaps ) const
+{
+    for (StringVector::const_iterator iter = m_settings.m_mcParticleCollections.begin(), iterEnd = m_settings.m_mcParticleCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_MC.find(*iter) == collectionMaps.collectionMap_MC.end()) continue;
+        try
+        {
+            const std::vector<edm4hep::MCParticle>& pMCParticleCollection = (collectionMaps.collectionMap_MC.find(*iter))->second;
+            std::cout<<"Do CreateMCParticles, collection:"<<(*iter)<<", size="<<pMCParticleCollection.size()<<std::endl;
+            for (int im = 0; im < pMCParticleCollection.size(); im++)
+            {
+                try
+                {
+                    const edm4hep::MCParticle& pMcParticle = pMCParticleCollection.at(im);
+                    PandoraApi::MCParticle::Parameters mcParticleParameters;
+                    mcParticleParameters.m_energy =   sqrt(pMcParticle.getMomentum()[0] * pMcParticle.getMomentum()[0] + pMcParticle.getMomentum()[1] * pMcParticle.getMomentum()[1] + pMcParticle.getMomentum()[2] * pMcParticle.getMomentum()[2] + pMcParticle.getMass() * pMcParticle.getMass());
+                    mcParticleParameters.m_particleId = pMcParticle.getPDG();
+                    mcParticleParameters.m_mcParticleType = pandora::MC_3D;
+                    mcParticleParameters.m_pParentAddress = &pMcParticle;
+                    unsigned int p_id = pMcParticle.id();
+                    const edm4hep::MCParticle* p_mc = &pMcParticle;
+                    //m_id_pMC_map [pMcParticle.id()]   = &pMcParticle;
+                    (*m_id_pMC_map) [p_id]   = p_mc;
+                    mcParticleParameters.m_momentum = pandora::CartesianVector(pMcParticle.getMomentum()[0], pMcParticle.getMomentum()[1],
+                        pMcParticle.getMomentum()[2]);
+                    mcParticleParameters.m_vertex = pandora::CartesianVector(pMcParticle.getVertex()[0], pMcParticle.getVertex()[1],
+                        pMcParticle.getVertex()[2]);
+                    mcParticleParameters.m_endpoint = pandora::CartesianVector(pMcParticle.getEndpoint()[0], pMcParticle.getEndpoint()[1],
+                        pMcParticle.getEndpoint()[2]);
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::MCParticle::Create(*m_pPandora, mcParticleParameters));
+
+                    // Create parent-daughter relationships
+                    for(std::vector<edm4hep::ConstMCParticle>::const_iterator itDaughter = pMcParticle.daughters_begin(),
+                        itDaughterEnd = pMcParticle.daughters_end(); itDaughter != itDaughterEnd; ++itDaughter)
+                    {   
+                        for (int ida = 0; ida < pMCParticleCollection.size(); ida++)
+                        {
+                           if(pMCParticleCollection.at(ida).id()==(*itDaughter).id())
+                           {
+                              
+                                const edm4hep::MCParticle& dMcParticle = pMCParticleCollection.at(ida);
+                                if(&pMcParticle == &dMcParticle){std::cout<< "error, mother and daughter are the same mc particle, don't save SetMCParentDaughterRelationship"<<std::endl;}
+                                else PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetMCParentDaughterRelationship(*m_pPandora, &pMcParticle, &dMcParticle));
+                                break;
+                           }
+                        }
+                    }
+                    
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout << "Failed to extract MCParticle: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout << "Failed to extract MCParticle: " <<  std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout << "Failed to extract MCParticles collection: " << *iter << ", " <<  std::endl;
+        }
+    }
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+/*
+pandora::StatusCode MCParticleCreator::CreateTrackToMCParticleRelationships(const EVENT::LCEvent *const pLCEvent, const TrackVector &trackVector) const
+{
+    for (StringVector::const_iterator iter = m_settings.m_lcTrackRelationCollections.begin(), iterEnd = m_settings.m_lcTrackRelationCollections.end();
+         iter != iterEnd; ++iter)
+    {
+        try
+        {
+            const EVENT::LCCollection *pMCRelationCollection = pLCEvent->getCollection(*iter);
+            UTIL::LCRelationNavigator navigate(pMCRelationCollection);
+
+            for (TrackVector::const_iterator trackIter = trackVector.begin(), trackIterEnd = trackVector.end();
+                trackIter != trackIterEnd; ++trackIter)
+            {
+                try
+                {
+                    EVENT::Track *pTrack = *trackIter;
+                    const EVENT::LCObjectVec &objectVec = navigate.getRelatedToObjects(*trackIter);
+
+                    // Get reconstructed momentum at dca
+                    const pandora::Helix helixFit(pTrack->getPhi(), pTrack->getD0(), pTrack->getZ0(), pTrack->getOmega(), pTrack->getTanLambda(), m_bField);
+                    const float recoMomentum(helixFit.GetMomentum().GetMagnitude());
+
+                    // Use momentum magnitude to identify best mc particle
+                    MCParticle *pBestMCParticle = NULL;
+                    float bestDeltaMomentum(std::numeric_limits<float>::max());
+
+                    for (EVENT::LCObjectVec::const_iterator itRel = objectVec.begin(), itRelEnd = objectVec.end(); itRel != itRelEnd; ++itRel)
+                    {
+                        EVENT::MCParticle *pMCParticle = NULL;
+                        pMCParticle = dynamic_cast<MCParticle *>(*itRel);
+
+                        if (NULL == pMCParticle)
+                            continue;
+
+                        const float trueMomentum(pandora::CartesianVector(pMCParticle->getMomentum()[0], pMCParticle->getMomentum()[1],
+                            pMCParticle->getMomentum()[2]).GetMagnitude());
+
+                        const float deltaMomentum(std::fabs(recoMomentum - trueMomentum));
+
+                        if (deltaMomentum < bestDeltaMomentum)
+                        {
+                            pBestMCParticle = pMCParticle;
+                            bestDeltaMomentum = deltaMomentum;
+                        }
+                    }
+
+                    if (NULL == pBestMCParticle)
+                        continue;
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackToMCParticleRelationship(*m_pPandora, pTrack,
+                        pBestMCParticle));
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    streamlog_out(ERROR) << "Failed to extract track to mc particle relationship: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (EVENT::Exception &exception)
+                {
+                    streamlog_out(WARNING) << "Failed to extract track to mc particle relationship: " << exception.what() << std::endl;
+                }
+            }
+        }
+        catch (EVENT::Exception &exception)
+        {
+            streamlog_out(DEBUG5) << "Failed to extract track to mc particle relationships collection: " << *iter << ", " << exception.what() << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+*/
+//------------------------------------------------------------------------------------------------------------------------------------------
+/*
+pandora::StatusCode MCParticleCreator::CreateCaloHitToMCParticleRelationships(const EVENT::LCEvent *const pLCEvent, const CalorimeterHitVector &calorimeterHitVector) const
+{
+    typedef std::map<MCParticle *, float> MCParticleToEnergyWeightMap;
+    MCParticleToEnergyWeightMap mcParticleToEnergyWeightMap;
+
+    for (StringVector::const_iterator iter = m_settings.m_lcCaloHitRelationCollections.begin(), iterEnd = m_settings.m_lcCaloHitRelationCollections.end();
+         iter != iterEnd; ++iter)
+    {
+        try
+        {
+            const EVENT::LCCollection *pMCRelationCollection = pLCEvent->getCollection(*iter);
+            UTIL::LCRelationNavigator navigate(pMCRelationCollection);
+
+            for (CalorimeterHitVector::const_iterator caloHitIter = calorimeterHitVector.begin(),
+                caloHitIterEnd = calorimeterHitVector.end(); caloHitIter != caloHitIterEnd; ++caloHitIter)
+            {
+                try
+                {
+                    mcParticleToEnergyWeightMap.clear();
+                    const EVENT::LCObjectVec &objectVec = navigate.getRelatedToObjects(*caloHitIter);
+
+                    for (EVENT::LCObjectVec::const_iterator itRel = objectVec.begin(), itRelEnd = objectVec.end(); itRel != itRelEnd; ++itRel)
+                    {
+                        EVENT::SimCalorimeterHit *pSimHit = dynamic_cast<SimCalorimeterHit *>(*itRel);
+
+                        if (NULL == pSimHit)
+                            continue;
+
+                        for (int iCont = 0, iEnd = pSimHit->getNMCContributions(); iCont < iEnd; ++iCont)
+                        {
+                            mcParticleToEnergyWeightMap[pSimHit->getParticleCont(iCont)] += pSimHit->getEnergyCont(iCont);
+                        }
+                    }
+
+                    for (MCParticleToEnergyWeightMap::const_iterator mcParticleIter = mcParticleToEnergyWeightMap.begin(),
+                        mcParticleIterEnd = mcParticleToEnergyWeightMap.end(); mcParticleIter != mcParticleIterEnd; ++mcParticleIter)
+                    {
+                        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetCaloHitToMCParticleRelationship(*m_pPandora,
+                            *caloHitIter, mcParticleIter->first, mcParticleIter->second));
+                    }
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    streamlog_out(ERROR) << "Failed to extract calo hit to mc particle relationship: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (EVENT::Exception &exception)
+                {
+                    streamlog_out(WARNING) << "Failed to extract calo hit to mc particle relationship: " << exception.what() << std::endl;
+                }
+            }
+        }
+        catch (EVENT::Exception &exception)
+        {
+            streamlog_out(DEBUG5) << "Failed to extract calo hit to mc particle relationships collection: " << *iter << ", " << exception.what() << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+*/
+
+//-------------- using sim calo hit and digi calo hit, no weight here---------------------------------------------------------------------------------------------------------------
+pandora::StatusCode MCParticleCreator::CreateCaloHitToMCParticleRelationships(const CollectionMaps& collectionMaps, const CalorimeterHitVector &calorimeterHitVector) const
+{
+    std::cout<<"Do CreateCaloHitToMCParticleRelationships"<<std::endl;
+    typedef std::map<const edm4hep::MCParticle *, float> MCParticleToEnergyWeightMap;
+    MCParticleToEnergyWeightMap mcParticleToEnergyWeightMap;
+
+    for (StringVector::const_iterator iter = m_settings.m_CaloHitRelationCollections.begin(), iterEnd = m_settings.m_CaloHitRelationCollections.end();
+         iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_CaloRel.find(*iter) == collectionMaps.collectionMap_CaloRel.end()) continue;
+        try
+        {
+            const std::vector<edm4hep::MCRecoCaloAssociation>& pMCRecoCaloAssociationCollection = (collectionMaps.collectionMap_CaloRel.find(*iter))->second;
+
+            for (unsigned i_calo=0; i_calo < calorimeterHitVector.size(); i_calo++)
+            {
+                try
+                {
+                    mcParticleToEnergyWeightMap.clear();
+                    for(unsigned ic=0; ic < pMCRecoCaloAssociationCollection.size(); ic++)
+                    {
+                        if( pMCRecoCaloAssociationCollection.at(ic).getRec().id() != (*(calorimeterHitVector.at(i_calo))).id() ) continue;
+                        
+                        const edm4hep::ConstSimCalorimeterHit pSimHit = pMCRecoCaloAssociationCollection.at(ic).getSim();
+                        for (int iCont = 0, iEnd = pSimHit.contributions_size(); iCont < iEnd; ++iCont)
+                        {
+                            edm4hep::ConstCaloHitContribution conb = pSimHit.getContributions(iCont);
+                            const edm4hep::ConstMCParticle ipa = conb.getParticle();
+                            float  ien = conb.getEnergy();
+                            if( m_id_pMC_map->find(ipa.id()) == m_id_pMC_map->end() ) continue;
+                            const edm4hep::MCParticle * p_tmp = (*m_id_pMC_map)[ipa.id()]; 
+                            mcParticleToEnergyWeightMap[p_tmp] += ien;
+                        }
+                        
+                    }
+
+                    for (MCParticleToEnergyWeightMap::const_iterator mcParticleIter = mcParticleToEnergyWeightMap.begin(),
+                        mcParticleIterEnd = mcParticleToEnergyWeightMap.end(); mcParticleIter != mcParticleIterEnd; ++mcParticleIter)
+                    {
+                        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetCaloHitToMCParticleRelationship(*m_pPandora,
+                            calorimeterHitVector.at(i_calo), mcParticleIter->first, mcParticleIter->second));
+                    }
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout<<"ERROR Failed to extract calo hit to mc particle relationship: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout<<"WARNING Failed to extract calo hit to mc particle relationship " << std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout<<"DEBUG5 Failed to extract calo hit to mc particle relationships collection: " << *iter << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+
+//-------------- using sim tracker hit and tracker hit, no weight here---------------------------------------------------------------------------------------------------------------
+pandora::StatusCode MCParticleCreator::CreateTrackToMCParticleRelationships(const CollectionMaps& collectionMaps, const TrackVector &trackVector) const
+{
+    std::cout<<"Do CreateTrackToMCParticleRelationships"<<std::endl;
+    for (unsigned ik = 0; ik < trackVector.size(); ik++)
+    {
+        const edm4hep::Track *pTrack = trackVector.at(ik);
+        // Get reconstructed momentum at dca
+        const pandora::Helix helixFit(pTrack->getTrackStates(0).phi, pTrack->getTrackStates(0).D0, pTrack->getTrackStates(0).Z0, pTrack->getTrackStates(0).omega, pTrack->getTrackStates(0).tanLambda, m_bField);
+        const float recoMomentum(helixFit.GetMomentum().GetMagnitude());
+        // Use momentum magnitude to identify best mc particle
+        edm4hep::MCParticle *pBestMCParticle = NULL;
+        float bestDeltaMomentum(std::numeric_limits<float>::max());
+        try
+        {
+            for (StringVector::const_iterator iter = m_settings.m_TrackRelationCollections.begin(), iterEnd = m_settings.m_TrackRelationCollections.end(); iter != iterEnd; ++iter)
+            {
+                if(collectionMaps.collectionMap_TrkRel.find(*iter) == collectionMaps.collectionMap_TrkRel.end()) continue;
+                const std::vector<edm4hep::MCRecoTrackerAssociation>& pMCRecoTrackerAssociationCollection = (collectionMaps.collectionMap_TrkRel.find(*iter))->second;
+                for(unsigned ith=0 ; ith<pTrack->trackerHits_size(); ith++)
+                {
+                    for(unsigned ic=0; ic < pMCRecoTrackerAssociationCollection.size(); ic++)
+                    {
+                        if( pMCRecoTrackerAssociationCollection.at(ic).getRec().id() != pTrack->getTrackerHits(ith).id() ) continue;
+                        const edm4hep::ConstSimTrackerHit pSimHit = pMCRecoTrackerAssociationCollection.at(ic).getSim();
+                        const edm4hep::ConstMCParticle ipa = pSimHit.getMCParticle();
+                        if( m_id_pMC_map->find(ipa.id()) == m_id_pMC_map->end() ) continue;
+                        const float trueMomentum(pandora::CartesianVector(ipa.getMomentum()[0], ipa.getMomentum()[1], ipa.getMomentum()[2]).GetMagnitude());
+                        const float deltaMomentum(std::fabs(recoMomentum - trueMomentum));
+                        if (deltaMomentum < bestDeltaMomentum)
+                        {
+                            pBestMCParticle =const_cast<edm4hep::MCParticle*>((*m_id_pMC_map)[ipa.id()]);
+                            bestDeltaMomentum = deltaMomentum;
+                        }
+                    }
+                }
+            }
+            
+            if (NULL == pBestMCParticle) continue;
+            PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackToMCParticleRelationship(*m_pPandora, pTrack, pBestMCParticle));
+        }
+        catch (pandora::StatusCodeException &statusCodeException)
+        {
+            std::cout<<"ERROR Failed to extract track to mc particle relationship: " << statusCodeException.ToString() << std::endl;
+        }
+        catch (...)
+        {
+            std::cout<<"WARNING Failed to extract track to mc particle relationship: " << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+MCParticleCreator::Settings::Settings()
+{
+}
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/src/PandoraPFAlg.cpp b/Reconstruction/PFA/Pandora/GaudiPandora/src/PandoraPFAlg.cpp
new file mode 100644
index 00000000..401a6e7a
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/src/PandoraPFAlg.cpp
@@ -0,0 +1,910 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+#include "GearSvc/IGearSvc.h"
+#include "PandoraPFAlg.h"
+#include "EventSeeder/IEventSeeder.h"
+#include "edm4hep/Vector3f.h"
+#include "edm4hep/Vector3d.h"
+#include "edm4hep/SimCalorimeterHit.h"
+#include "edm4hep/CaloHitContribution.h"
+#include "edm4hep/ClusterConst.h"
+#include "UTIL/ILDConf.h"
+#include <cmath>
+#include <algorithm>
+#include "gear/BField.h"
+#include <gear/GEAR.h>
+
+#include "LCContent.h"
+
+
+pandora::Pandora* PandoraPFAlg::m_pPandora=0;
+
+DECLARE_COMPONENT( PandoraPFAlg )
+
+template<typename T ,typename T1>
+StatusCode getCol(T & t, T1 & t1)
+{
+    try {
+        t1 = t.get();
+    }
+    catch ( GaudiException &e ) {
+        std::cout << "Collection " << t.fullKey() << " is unavailable in event "  << std::endl;
+    }
+    return StatusCode::SUCCESS;
+}
+
+
+
+PandoraPFAlg::PandoraPFAlg(const std::string& name, ISvcLocator* svcLoc)
+  : GaudiAlgorithm(name, svcLoc),
+    _nEvt(0)
+{
+ m_CollectionMaps = new CollectionMaps();
+  
+ declareProperty("ReadMCParticle"                      , m_mcParCol_r,                        "Handle of the MCParticle    input collection" );
+ declareProperty("ReadECALBarrel"                      , m_ECALBarrel_r,                      "Handle of the ECALBarrel    input collection" );
+ declareProperty("ReadECALEndcap"                      , m_ECALEndcap_r,                      "Handle of the ECALEndcap    input collection" );
+ declareProperty("ReadECALOther"                       , m_ECALOther_r,                       "Handle of the ECALOther     input collection" );
+ declareProperty("ReadHCALBarrel"                      , m_HCALBarrel_r,                      "Handle of the HCALBarrel    input collection" );
+ declareProperty("ReadHCALEndcap"                      , m_HCALEndcap_r,                      "Handle of the HCALEndcap    input collection" );
+ declareProperty("ReadHCALOther"                       , m_HCALOther_r,                       "Handle of the HCALOther     input collection" );
+ declareProperty("ReadMUON"                            , m_MUON_r,                            "Handle of the MUON          input collection" );
+ declareProperty("ReadLCAL"                            , m_LCAL_r,                            "Handle of the LCAL          input collection" );
+ declareProperty("ReadLHCAL"                           , m_LHCAL_r,                           "Handle of the LHCAL         input collection" );
+ declareProperty("ReadBCAL"                            , m_BCAL_r,                            "Handle of the BCAL          input collection" );
+ declareProperty("ReadKinkVertices"                    , m_KinkVertices_r,                    "Handle of the KinkVertices  input collection" );
+ declareProperty("ReadProngVertices"                   , m_ProngVertices_r,                   "Handle of the ProngVertices input collection" );
+ declareProperty("ReadSplitVertices"                   , m_SplitVertices_r,                   "Handle of the SplitVertices input collection" );
+ declareProperty("ReadV0Vertices"                      , m_V0Vertices_r,                      "Handle of the V0Vertices    input collection" );
+ declareProperty("ReadTracks"                          , m_MarlinTrkTracks_r,                 "Handle of the Tracks        input collection" );
+ declareProperty("MCRecoCaloAssociation"               , m_MCRecoCaloAssociation_r,           "Handle of the MCRecoCaloAssociation input collection" );
+ declareProperty("MCRecoTrackerAssociation"            , m_MCRecoTrackerAssociation_r,        "Handle of the MCRecoTrackerAssociation input collection" );
+ declareProperty("WriteClusterCollection"              , m_ClusterCollection_w,               "Handle of the ClusterCollection               output collection" );
+ declareProperty("WriteReconstructedParticleCollection", m_ReconstructedParticleCollection_w, "Handle of the ReconstructedParticleCollection output collection" );
+ declareProperty("WriteVertexCollection"               , m_VertexCollection_w,                "Handle of the VertexCollection                output collection" );
+
+}
+
+
+void PandoraPFAlg::FinaliseSteeringParameters(ISvcLocator* svcloc)
+{
+    // ATTN: This function seems to be necessary for operations that cannot easily be performed at construction of the processor,
+    // when the steering file is parsed e.g. the call to GEAR to get the inner bfield
+    
+    m_caloHitCreatorSettings.m_absorberRadLengthECal = m_geometryCreatorSettings.m_absorberRadLengthECal;
+    m_caloHitCreatorSettings.m_absorberIntLengthECal = m_geometryCreatorSettings.m_absorberIntLengthECal;
+    m_caloHitCreatorSettings.m_absorberRadLengthHCal = m_geometryCreatorSettings.m_absorberRadLengthHCal;
+    m_caloHitCreatorSettings.m_absorberIntLengthHCal = m_geometryCreatorSettings.m_absorberIntLengthHCal;
+    m_caloHitCreatorSettings.m_absorberRadLengthOther = m_geometryCreatorSettings.m_absorberRadLengthOther;
+    m_caloHitCreatorSettings.m_absorberIntLengthOther = m_geometryCreatorSettings.m_absorberIntLengthOther;
+
+    m_caloHitCreatorSettings.m_hCalEndCapInnerSymmetryOrder = m_geometryCreatorSettings.m_hCalEndCapInnerSymmetryOrder;
+    m_caloHitCreatorSettings.m_hCalEndCapInnerPhiCoordinate = m_geometryCreatorSettings.m_hCalEndCapInnerPhiCoordinate;
+    
+    m_trackCreatorSettings.m_prongSplitVertexCollections = m_trackCreatorSettings.m_prongVertexCollections;
+    m_trackCreatorSettings.m_prongSplitVertexCollections.insert(m_trackCreatorSettings.m_prongSplitVertexCollections.end(),
+        m_trackCreatorSettings.m_splitVertexCollections.begin(), m_trackCreatorSettings.m_splitVertexCollections.end());
+    
+    //m_settings.m_innerBField = marlin::Global::GEAR->getBField().at(gear::Vector3D(0., 0., 0.)).z();
+    IGearSvc*  iSvc = 0;
+    StatusCode sc = svcloc->service("GearSvc", iSvc, false);
+    if ( !sc ) 
+    {
+        throw "Failed to find GearSvc ...";
+    }
+    gear::GearMgr* _GEAR = iSvc->getGearMgr();
+    m_settings.m_innerBField = _GEAR->getBField().at(gear::Vector3D(0., 0., 0.)).z();
+    std::cout<<"m_innerBField="<<m_settings.m_innerBField<<std::endl;    
+    m_mcParticleCreatorSettings.m_bField = m_settings.m_innerBField;
+}
+
+
+
+
+StatusCode PandoraPFAlg::initialize()
+{
+
+  std::cout<<"hi init PandoraPFAlg"<<std::endl;
+
+  std::string s_output =m_AnaOutput; 
+  m_fout = new TFile(s_output.c_str(),"RECREATE"); 
+  m_tree = new TTree("evt","tree");
+  m_tree->Branch("m_pReco_PID"   , &m_pReco_PID);
+  m_tree->Branch("m_pReco_mass"  , &m_pReco_mass);
+  m_tree->Branch("m_pReco_energy", &m_pReco_energy);
+  m_tree->Branch("m_pReco_px"    , &m_pReco_px);
+  m_tree->Branch("m_pReco_py"    , &m_pReco_py);
+  m_tree->Branch("m_pReco_pz"    , &m_pReco_pz);
+  m_tree->Branch("m_pReco_charge", &m_pReco_charge);
+
+  m_tree->Branch("m_mc_p_size", &m_mc_p_size);
+  m_tree->Branch("m_mc_pid"   , &m_mc_pid   );
+  m_tree->Branch("m_mc_mass"  , &m_mc_mass  );
+  m_tree->Branch("m_mc_px"    , &m_mc_px    );
+  m_tree->Branch("m_mc_py"    , &m_mc_py    );
+  m_tree->Branch("m_mc_pz"    , &m_mc_pz    );
+  m_tree->Branch("m_mc_charge", &m_mc_charge);
+  m_tree->Branch("m_hasConversion", &m_hasConversion);
+
+  // XML file
+  m_settings.m_pandoraSettingsXmlFile =  m_PandoraSettingsXmlFile ; 
+  // Hadronic energy non-linearity correction
+  m_settings.m_inputEnergyCorrectionPoints = m_InputEnergyCorrectionPoints;
+  m_settings.m_outputEnergyCorrectionPoints = m_OutputEnergyCorrectionPoints;
+  // B-field parameters
+  m_settings.m_muonBarrelBField = m_MuonBarrelBField; 
+  m_settings.m_muonEndCapBField = m_MuonEndCapBField;
+  
+  m_trackCreatorSettings.m_trackCollections = m_TrackCollections ; 
+  m_trackCreatorSettings.m_kinkVertexCollections = m_KinkVertexCollections; 
+  m_trackCreatorSettings.m_prongVertexCollections = m_ProngVertexCollections;
+  m_trackCreatorSettings.m_splitVertexCollections = m_SplitVertexCollections;
+  m_trackCreatorSettings.m_v0VertexCollections = m_V0VertexCollections; 
+  
+  m_caloHitCreatorSettings.m_eCalCaloHitCollections = m_ECalCaloHitCollections;
+  m_caloHitCreatorSettings.m_hCalCaloHitCollections = m_HCalCaloHitCollections;
+  m_caloHitCreatorSettings.m_lCalCaloHitCollections = m_LCalCaloHitCollections;
+  m_caloHitCreatorSettings.m_lHCalCaloHitCollections = m_LHCalCaloHitCollections;
+  m_caloHitCreatorSettings.m_muonCaloHitCollections = m_MuonCaloHitCollections; 
+  m_mcParticleCreatorSettings.m_mcParticleCollections = m_MCParticleCollections;
+  m_mcParticleCreatorSettings.m_CaloHitRelationCollections = m_RelCaloHitCollections; 
+  m_mcParticleCreatorSettings.m_TrackRelationCollections = m_RelTrackCollections;
+  
+  
+  // Absorber properties
+  m_geometryCreatorSettings.m_absorberRadLengthECal = m_AbsorberRadLengthECal;
+  m_geometryCreatorSettings.m_absorberIntLengthECal = m_AbsorberIntLengthECal;
+  m_geometryCreatorSettings.m_absorberRadLengthHCal = m_AbsorberRadLengthHCal;
+  m_geometryCreatorSettings.m_absorberIntLengthHCal = m_AbsorberIntLengthHCal;
+  m_geometryCreatorSettings.m_absorberRadLengthOther = m_AbsorberRadLengthOther;
+  m_geometryCreatorSettings.m_absorberIntLengthOther = m_AbsorberIntLengthOther;
+  
+  // Name of PFO collection written by GaudiPandora
+  
+  m_pfoCreatorSettings.m_clusterCollectionName = m_ClusterCollectionName;// not used  
+  m_pfoCreatorSettings.m_pfoCollectionName = m_PFOCollectionName;//
+  m_pfoCreatorSettings.m_startVertexCollectionName = m_StartVertexCollectionName; //
+  m_pfoCreatorSettings.m_startVertexAlgName = m_StartVertexAlgorithmName;//
+   
+  m_pfoCreatorSettings.m_emStochasticTerm = m_EMStochasticTerm;
+  m_pfoCreatorSettings.m_hadStochasticTerm = m_HadStochasticTerm;
+  m_pfoCreatorSettings.m_emConstantTerm = m_EMConstantTerm;
+  m_pfoCreatorSettings.m_hadConstantTerm = m_HadConstantTerm;
+  
+  // Calibration constants
+  m_caloHitCreatorSettings.m_eCalToMip = m_ECalToMipCalibration; 
+  m_caloHitCreatorSettings.m_hCalToMip = m_HCalToMipCalibration;
+  m_caloHitCreatorSettings.m_eCalMipThreshold = m_ECalMipThreshold; 
+  m_caloHitCreatorSettings.m_muonToMip = m_MuonToMipCalibration;
+  m_caloHitCreatorSettings.m_hCalMipThreshold = m_HCalMipThreshold; 
+  m_caloHitCreatorSettings.m_eCalToEMGeV = m_ECalToEMGeVCalibration;
+  m_caloHitCreatorSettings.m_hCalToEMGeV = m_HCalToEMGeVCalibration;
+  m_caloHitCreatorSettings.m_eCalToHadGeVEndCap = m_ECalToHadGeVCalibrationEndCap;
+  m_caloHitCreatorSettings.m_eCalToHadGeVBarrel = m_ECalToHadGeVCalibrationBarrel; 
+  m_caloHitCreatorSettings.m_hCalToHadGeV = m_HCalToHadGeVCalibration;
+  m_caloHitCreatorSettings.m_muonDigitalHits = m_DigitalMuonHits; 
+  m_caloHitCreatorSettings.m_muonHitEnergy = m_MuonHitEnergy; 
+  m_caloHitCreatorSettings.m_maxHCalHitHadronicEnergy = m_MaxHCalHitHadronicEnergy; 
+  m_caloHitCreatorSettings.m_nOuterSamplingLayers = m_NOuterSamplingLayers; 
+  m_caloHitCreatorSettings.m_layersFromEdgeMaxRearDistance = m_LayersFromEdgeMaxRearDistance; 
+  
+  // Track relationship parameters
+  m_trackCreatorSettings.m_shouldFormTrackRelationships = m_ShouldFormTrackRelationships; 
+  // Initial track hit specifications
+  m_trackCreatorSettings.m_minTrackHits = m_MinTrackHits;
+  m_trackCreatorSettings.m_minFtdTrackHits = m_MinFtdTrackHits; 
+  m_trackCreatorSettings.m_maxTrackHits = m_MaxTrackHits; 
+  ////m_trackCreatorSettings.m_useOldTrackStateCalculation = m_UseOldTrackStateCalculation;
+  // Track PFO usage parameters
+  m_trackCreatorSettings.m_d0TrackCut = m_D0TrackCut; 
+  m_trackCreatorSettings.m_z0TrackCut = m_Z0TrackCut;
+  m_trackCreatorSettings.m_usingNonVertexTracks = m_UseNonVertexTracks;
+  m_trackCreatorSettings.m_usingUnmatchedNonVertexTracks = m_UseUnmatchedNonVertexTracks;
+  m_trackCreatorSettings.m_usingUnmatchedVertexTracks = m_UseUnmatchedVertexTracks;
+  m_trackCreatorSettings.m_unmatchedVertexTrackMaxEnergy = m_UnmatchedVertexTrackMaxEnergy; 
+  m_trackCreatorSettings.m_d0UnmatchedVertexTrackCut = m_D0UnmatchedVertexTrackCut; 
+  m_trackCreatorSettings.m_z0UnmatchedVertexTrackCut = m_Z0UnmatchedVertexTrackCut;
+  m_trackCreatorSettings.m_zCutForNonVertexTracks = m_ZCutForNonVertexTracks;
+  // Track "reaches ecal" parameters
+  m_trackCreatorSettings.m_reachesECalNTpcHits = m_ReachesECalNTpcHits;
+  m_trackCreatorSettings.m_reachesECalNFtdHits = m_ReachesECalNFtdHits;
+  m_trackCreatorSettings.m_reachesECalTpcOuterDistance = m_ReachesECalTpcOuterDistance;
+  m_trackCreatorSettings.m_reachesECalMinFtdLayer = m_ReachesECalMinFtdLayer;
+  m_trackCreatorSettings.m_reachesECalTpcZMaxDistance = m_ReachesECalTpcZMaxDistance;
+  m_trackCreatorSettings.m_reachesECalFtdZMaxDistance = m_ReachesECalFtdZMaxDistance;
+  m_trackCreatorSettings.m_curvatureToMomentumFactor = m_CurvatureToMomentumFactor;
+  m_trackCreatorSettings.m_minTrackECalDistanceFromIp = m_MinTrackECalDistanceFromIp;
+  // Final track quality parameters
+  m_trackCreatorSettings.m_maxTrackSigmaPOverP = m_MaxTrackSigmaPOverP;
+  m_trackCreatorSettings.m_minMomentumForTrackHitChecks = m_MinMomentumForTrackHitChecks;
+  m_trackCreatorSettings.m_tpcMembraneMaxZ = m_TpcMembraneMaxZ;
+  m_trackCreatorSettings.m_minTpcHitFractionOfExpected = m_MinTpcHitFractionOfExpected;
+  m_trackCreatorSettings.m_minFtdHitsForTpcHitFraction = m_MinFtdHitsForTpcHitFraction;
+  m_trackCreatorSettings.m_maxTpcInnerRDistance = m_MaxTpcInnerRDistance;
+  
+  
+  // Additional geometry parameters
+  m_geometryCreatorSettings.m_eCalEndCapInnerSymmetryOrder = m_ECalEndCapInnerSymmetryOrder;
+  m_geometryCreatorSettings.m_eCalEndCapInnerPhiCoordinate = m_ECalEndCapInnerPhiCoordinate;
+  m_geometryCreatorSettings.m_eCalEndCapOuterSymmetryOrder = m_ECalEndCapOuterSymmetryOrder;
+  m_geometryCreatorSettings.m_eCalEndCapOuterPhiCoordinate = m_ECalEndCapOuterPhiCoordinate;
+  m_geometryCreatorSettings.m_hCalEndCapInnerSymmetryOrder = m_HCalEndCapInnerSymmetryOrder;
+  m_geometryCreatorSettings.m_hCalEndCapInnerPhiCoordinate = m_HCalEndCapInnerPhiCoordinate;
+  m_geometryCreatorSettings.m_hCalEndCapOuterSymmetryOrder = m_HCalEndCapOuterSymmetryOrder;
+  m_geometryCreatorSettings.m_hCalEndCapOuterPhiCoordinate = m_HCalEndCapOuterPhiCoordinate;
+  m_geometryCreatorSettings.m_hCalRingInnerSymmetryOrder = m_HCalRingInnerSymmetryOrder;
+  m_geometryCreatorSettings.m_hCalRingInnerPhiCoordinate = m_HCalRingInnerPhiCoordinate;
+  m_geometryCreatorSettings.m_hCalRingOuterSymmetryOrder = m_HCalRingOuterSymmetryOrder; 
+  m_geometryCreatorSettings.m_hCalRingOuterPhiCoordinate = m_HCalRingOuterPhiCoordinate;
+  
+  // For Strip Splitting method and also for hybrid ECAL
+  m_caloHitCreatorSettings.m_stripSplittingOn = m_StripSplittingOn;
+  m_caloHitCreatorSettings.m_useEcalScLayers = m_UseEcalScLayers;
+  // Parameters for hybrid ECAL
+  // Energy to MIP for Si-layers and Sc-layers, respectively.
+  //Si
+  m_caloHitCreatorSettings.m_eCalSiToMip = m_ECalSiToMipCalibration;
+  //Sc
+  m_caloHitCreatorSettings.m_eCalScToMip = m_ECalScToMipCalibration;
+  // MipThreshold for Si-layers and Sc-layers, respectively.
+  // Si
+  m_caloHitCreatorSettings.m_eCalSiMipThreshold = m_ECalSiMipThreshold;
+  //Sc
+  m_caloHitCreatorSettings.m_eCalScMipThreshold = m_ECalScMipThreshold;
+  // EcalToEM for Si-layers and Sc-layers, respectively.
+  //Si
+  m_caloHitCreatorSettings.m_eCalSiToEMGeV = m_ECalSiToEMGeVCalibration;
+  //Sc
+  m_caloHitCreatorSettings.m_eCalScToEMGeV = m_ECalScToEMGeVCalibration;
+  // EcalToHad for Si-layers and Sc-layers of the endcaps, respectively.
+  //Si
+  m_caloHitCreatorSettings.m_eCalSiToHadGeVEndCap = m_ECalSiToHadGeVCalibrationEndCap;
+  //Sc
+  m_caloHitCreatorSettings.m_eCalScToHadGeVEndCap = m_ECalScToHadGeVCalibrationEndCap;
+  // EcalToHad for Si-layers and Sc-layers of the barrel, respectively.
+  //Si
+  m_caloHitCreatorSettings.m_eCalSiToHadGeVBarrel = m_ECalSiToHadGeVCalibrationBarrel;
+  //Sc
+  m_caloHitCreatorSettings.m_eCalScToHadGeVBarrel = m_ECalScToHadGeVCalibrationBarrel;
+
+  try
+  {
+      ISvcLocator* svcloc = serviceLocator();
+      this->FinaliseSteeringParameters(svcloc);
+      m_pPandora = new pandora::Pandora();
+      m_pMCParticleCreator = new MCParticleCreator(m_mcParticleCreatorSettings, m_pPandora);
+      m_pGeometryCreator = new GeometryCreator(m_geometryCreatorSettings, m_pPandora);
+      PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pGeometryCreator->CreateGeometry(svcloc));
+      m_pCaloHitCreator = new CaloHitCreator(m_caloHitCreatorSettings, m_pPandora, svcloc, 0);
+      m_pTrackCreator = new TrackCreator(m_trackCreatorSettings, m_pPandora, svcloc);
+      m_pPfoCreator = new PfoCreator(m_pfoCreatorSettings, m_pPandora);
+      PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->RegisterUserComponents());
+      PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::ReadSettings(*m_pPandora, m_settings.m_pandoraSettingsXmlFile));
+  }
+  catch (pandora::StatusCodeException &statusCodeException)
+  {
+      std::cout << "Failed to initialize gaudi pandora: " << statusCodeException.ToString() << std::endl;
+      throw statusCodeException;
+  }
+  catch (...)
+  {
+      std::cout << "Failed to initialize gaudi pandora: unrecognized exception" << std::endl;
+      throw;
+  }
+
+
+  return GaudiAlgorithm::initialize();
+}
+
+StatusCode PandoraPFAlg::execute()
+{
+    /*
+    if (_nEvt < m_settings.m_nEventsToSkip)
+    {
+        _nEvt++;
+        return StatusCode::SUCCESS;
+    }
+    */
+    
+    try
+    {
+        std::cout<<"execute PandoraPFAlg"<<std::endl;
+        
+        updateMap();
+        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pMCParticleCreator->CreateMCParticles(*m_CollectionMaps));
+        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pCaloHitCreator->CreateCaloHits(*m_CollectionMaps));
+        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pMCParticleCreator->CreateCaloHitToMCParticleRelationships(*m_CollectionMaps, m_pCaloHitCreator->GetCalorimeterHitVector() ));
+        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pTrackCreator->CreateTrackAssociations(*m_CollectionMaps));
+        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pTrackCreator->CreateTracks(*m_CollectionMaps));
+        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pMCParticleCreator->CreateTrackToMCParticleRelationships(*m_CollectionMaps, m_pTrackCreator->GetTrackVector() ));
+        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::ProcessEvent(*m_pPandora));
+        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pPfoCreator->CreateParticleFlowObjects(*m_CollectionMaps, m_ClusterCollection_w, m_ReconstructedParticleCollection_w, m_VertexCollection_w));
+        
+        StatusCode sc0 = CreateMCRecoParticleAssociation();
+        StatusCode sc = Ana();
+
+        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::Reset(*m_pPandora));
+        this->Reset();
+    }
+    catch (pandora::StatusCodeException &statusCodeException)
+    {
+        std::cout << "Gaudi pandora failed to process event: " << statusCodeException.ToString() << std::endl;
+        throw statusCodeException;
+    }
+    catch (...)
+    {
+        std::cout << "Gaudi pandora failed to process event: unrecognized exception" << std::endl;
+        throw;
+    }
+  
+  info() << "PandoraPFAlg Processed " << _nEvt << " events " << endmsg;
+  _nEvt ++ ;
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode PandoraPFAlg::finalize()
+{
+  info() << "Finalized. Processed " << _nEvt << " events " <<",saved tree with entries="<<m_tree->GetEntries()<< endmsg;
+  m_fout->cd();
+  m_tree->Write();
+  m_fout->Close();
+  delete m_pPandora;
+  delete m_pGeometryCreator;
+  delete m_pCaloHitCreator;
+  delete m_pTrackCreator;
+  delete m_pMCParticleCreator;
+  delete m_pPfoCreator;
+  return GaudiAlgorithm::finalize();
+}
+
+
+
+
+pandora::StatusCode PandoraPFAlg::RegisterUserComponents() const
+{
+    
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, LCContent::RegisterAlgorithms(*m_pPandora));
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, LCContent::RegisterBasicPlugins(*m_pPandora));
+
+    
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, LCContent::RegisterBFieldPlugin(*m_pPandora,
+        m_settings.m_innerBField, m_settings.m_muonBarrelBField, m_settings.m_muonEndCapBField));
+
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, LCContent::RegisterNonLinearityEnergyCorrection(*m_pPandora,
+        "NonLinearity", pandora::HADRONIC, m_settings.m_inputEnergyCorrectionPoints, m_settings.m_outputEnergyCorrectionPoints));
+    
+    
+    /*
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::RegisterAlgorithmFactory(*m_pPandora,
+        "ExternalClustering", new ExternalClusteringAlgorithm::Factory));
+    */
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+
+void PandoraPFAlg::Reset()
+{
+    m_pCaloHitCreator->Reset();
+    m_pTrackCreator->Reset();
+    m_pMCParticleCreator->Reset();
+
+    std::vector<int>()  .swap(m_pReco_PID   );
+    std::vector<float>().swap(m_pReco_mass);
+    std::vector<float>().swap(m_pReco_energy);
+    std::vector<float>().swap(m_pReco_px);
+    std::vector<float>().swap(m_pReco_py);
+    std::vector<float>().swap(m_pReco_pz);
+    std::vector<float>().swap(m_pReco_charge);
+
+    std::vector<int>()  .swap(m_mc_p_size);
+    std::vector<int>()  .swap(m_mc_pid   );
+    std::vector<float>().swap(m_mc_mass  );
+    std::vector<float>().swap(m_mc_px    );
+    std::vector<float>().swap(m_mc_py    );
+    std::vector<float>().swap(m_mc_pz    );
+    std::vector<float>().swap(m_mc_charge);
+    m_hasConversion = 0;
+
+    m_CollectionMaps->clear();
+}
+
+const pandora::Pandora *PandoraPFAlg::GetPandora() const
+{
+    if (NULL == m_pPandora)
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_NOT_INITIALIZED);
+
+    return m_pPandora;
+}
+PandoraPFAlg::Settings::Settings() :
+    m_innerBField(3.5f),
+    m_muonBarrelBField(-1.5f),
+    m_muonEndCapBField(0.01f)
+{
+}
+CollectionMaps::CollectionMaps()
+{
+}
+void CollectionMaps::clear()
+{
+CollectionMap_MC.clear();
+CollectionMap_CaloHit.clear();
+CollectionMap_Vertex.clear();
+CollectionMap_Track.clear();
+collectionMap_MC.clear();
+collectionMap_CaloHit.clear();
+collectionMap_Vertex.clear();
+collectionMap_Track.clear();
+collectionMap_CaloRel.clear();
+collectionMap_TrkRel.clear();
+}
+
+StatusCode PandoraPFAlg::updateMap()
+{
+        const edm4hep::MCParticleCollection*     MCParticle = nullptr;
+        const edm4hep::CalorimeterHitCollection* ECALBarrel = nullptr;        
+        const edm4hep::CalorimeterHitCollection* ECALEndcap = nullptr; 
+        const edm4hep::CalorimeterHitCollection* ECALOther  = nullptr; 
+        const edm4hep::CalorimeterHitCollection* HCALBarrel = nullptr; 
+        const edm4hep::CalorimeterHitCollection* HCALEndcap = nullptr; 
+        const edm4hep::CalorimeterHitCollection* HCALOther  = nullptr; 
+        const edm4hep::CalorimeterHitCollection* MUON       = nullptr; 
+        const edm4hep::CalorimeterHitCollection* LCAL       = nullptr; 
+        const edm4hep::CalorimeterHitCollection* LHCAL      = nullptr; 
+        const edm4hep::CalorimeterHitCollection* BCAL       = nullptr; 
+        const edm4hep::VertexCollection* KinkVertices       = nullptr; 
+        const edm4hep::VertexCollection* ProngVertices      = nullptr; 
+        const edm4hep::VertexCollection* SplitVertices      = nullptr; 
+        const edm4hep::VertexCollection* V0Vertices         = nullptr; 
+        const edm4hep::TrackCollection*  MarlinTrkTracks    = nullptr; 
+        const edm4hep::MCRecoCaloAssociationCollection*  mcRecoCaloAssociation    = nullptr; 
+        const edm4hep::MCRecoTrackerAssociationCollection*  mcRecoTrackerAssociation    = nullptr; 
+        StatusCode sc = StatusCode::SUCCESS;
+        sc =  getCol(m_mcParCol_r  , MCParticle );
+        sc =  getCol(m_ECALBarrel_r, ECALBarrel );
+        sc =  getCol(m_ECALEndcap_r, ECALEndcap );
+        sc =  getCol(m_ECALOther_r , ECALOther  );
+        sc =  getCol(m_HCALBarrel_r, HCALBarrel );
+        sc =  getCol(m_HCALEndcap_r, HCALEndcap );
+        sc =  getCol(m_HCALOther_r , HCALOther  );
+        sc =  getCol(m_MUON_r      , MUON       );
+        sc =  getCol(m_LCAL_r      , LCAL       );
+        sc =  getCol(m_LHCAL_r     , LHCAL      );
+        sc =  getCol(m_BCAL_r      , BCAL       );        
+        sc =  getCol(m_KinkVertices_r  , KinkVertices );        
+        sc =  getCol(m_ProngVertices_r , ProngVertices);        
+        sc =  getCol(m_SplitVertices_r , SplitVertices);        
+        sc =  getCol(m_V0Vertices_r    , V0Vertices   );        
+        sc =  getCol(m_MarlinTrkTracks_r , MarlinTrkTracks   );        
+        sc =  getCol(m_MCRecoCaloAssociation_r , mcRecoCaloAssociation   );        
+        sc =  getCol(m_MCRecoTrackerAssociation_r , mcRecoTrackerAssociation);        
+
+        if (NULL != MCParticle   )  
+        {
+            std::vector<edm4hep::MCParticle> v_mc;
+            m_CollectionMaps->CollectionMap_MC ["MCParticle"] = MCParticle ;
+            m_CollectionMaps->collectionMap_MC ["MCParticle"] = v_mc;
+            for(unsigned int i=0 ; i< MCParticle->size(); i++) m_CollectionMaps->collectionMap_MC ["MCParticle"].push_back(MCParticle->at(i));
+        }
+        if (NULL != ECALBarrel   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["ECALBarrel"] = ECALBarrel ;
+            m_CollectionMaps->collectionMap_CaloHit["ECALBarrel"] = v_cal ;
+            for(unsigned int i=0 ; i< ECALBarrel->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["ECALBarrel"].push_back(ECALBarrel->at(i));
+        }
+        if (NULL != ECALEndcap   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["ECALEndcap"] = ECALEndcap ;
+            m_CollectionMaps->collectionMap_CaloHit["ECALEndcap"] = v_cal ;
+            for(unsigned int i=0 ; i< ECALEndcap->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["ECALEndcap"].push_back(ECALEndcap->at(i));
+        }
+        if (NULL != ECALOther   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["ECALOther"] = ECALOther ;
+            m_CollectionMaps->collectionMap_CaloHit["ECALOther"] = v_cal ;
+            for(unsigned int i=0 ; i< ECALOther->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["ECALOther"].push_back(ECALOther->at(i));
+        }
+        if (NULL != HCALBarrel   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["HCALBarrel"] = HCALBarrel ;
+            m_CollectionMaps->collectionMap_CaloHit["HCALBarrel"] = v_cal ;
+            for(unsigned int i=0 ; i< HCALBarrel->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["HCALBarrel"].push_back(HCALBarrel->at(i));
+        }
+        if (NULL != HCALEndcap   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["HCALEndcap"] = HCALEndcap ;
+            m_CollectionMaps->collectionMap_CaloHit["HCALEndcap"] = v_cal ;
+            for(unsigned int i=0 ; i< HCALEndcap->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["HCALEndcap"].push_back(HCALEndcap->at(i));
+        }
+        if (NULL != HCALOther   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["HCALOther"] = HCALOther ;
+            m_CollectionMaps->collectionMap_CaloHit["HCALOther"] = v_cal ;
+            for(unsigned int i=0 ; i< HCALOther->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["HCALOther"].push_back(HCALOther->at(i));
+        }
+        if (NULL != MUON   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["MUON"] = MUON ;
+            m_CollectionMaps->collectionMap_CaloHit["MUON"] = v_cal ;
+            for(unsigned int i=0 ; i< MUON->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["MUON"].push_back(MUON->at(i));
+        }
+        if (NULL != LCAL   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["LCAL"] = LCAL ;
+            m_CollectionMaps->collectionMap_CaloHit["LCAL"] = v_cal ;
+            for(unsigned int i=0 ; i< LCAL->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["LCAL"].push_back(LCAL->at(i));
+        }
+        if (NULL != LHCAL   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["LHCAL"] = LHCAL ;
+            m_CollectionMaps->collectionMap_CaloHit["LHCAL"] = v_cal ;
+            for(unsigned int i=0 ; i< LHCAL->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["LHCAL"].push_back(LHCAL->at(i));
+        }
+        if (NULL != BCAL   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["BCAL"] = BCAL ;
+            m_CollectionMaps->collectionMap_CaloHit["BCAL"] = v_cal ;
+            for(unsigned int i=0 ; i< BCAL->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["BCAL"].push_back(BCAL->at(i));
+        }
+        if (NULL != KinkVertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            m_CollectionMaps->CollectionMap_Vertex["KinkVertices"] = KinkVertices ;
+            m_CollectionMaps->collectionMap_Vertex["KinkVertices"] = v_cal ;
+            for(unsigned int i=0 ; i< KinkVertices->size(); i++) m_CollectionMaps->collectionMap_Vertex ["KinkVertices"].push_back(KinkVertices->at(i));
+        }
+        if (NULL != ProngVertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            m_CollectionMaps->CollectionMap_Vertex["ProngVertices"] = ProngVertices ;
+            m_CollectionMaps->collectionMap_Vertex["ProngVertices"] = v_cal ;
+            for(unsigned int i=0 ; i< ProngVertices->size(); i++) m_CollectionMaps->collectionMap_Vertex ["ProngVertices"].push_back(ProngVertices->at(i));
+        }
+        if (NULL != SplitVertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            m_CollectionMaps->CollectionMap_Vertex["SplitVertices"] = SplitVertices ;
+            m_CollectionMaps->collectionMap_Vertex["SplitVertices"] = v_cal ;
+            for(unsigned int i=0 ; i< SplitVertices->size(); i++) m_CollectionMaps->collectionMap_Vertex ["SplitVertices"].push_back(SplitVertices->at(i));
+        }
+        if (NULL != V0Vertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            m_CollectionMaps->CollectionMap_Vertex["V0Vertices"] = V0Vertices ;
+            m_CollectionMaps->collectionMap_Vertex["V0Vertices"] = v_cal ;
+            for(unsigned int i=0 ; i< V0Vertices->size(); i++) m_CollectionMaps->collectionMap_Vertex ["V0Vertices"].push_back(V0Vertices->at(i));
+        }
+        if (NULL != MarlinTrkTracks   )
+        {
+            std::vector<edm4hep::Track> v_cal;
+            m_CollectionMaps->CollectionMap_Track["MarlinTrkTracks"] = MarlinTrkTracks ;
+            m_CollectionMaps->collectionMap_Track["MarlinTrkTracks"] = v_cal ;
+            for(unsigned int i=0 ; i< MarlinTrkTracks->size(); i++) m_CollectionMaps->collectionMap_Track ["MarlinTrkTracks"].push_back(MarlinTrkTracks->at(i));
+        }
+        if (NULL != mcRecoCaloAssociation )
+        {
+            std::vector<edm4hep::MCRecoCaloAssociation> v_cal;
+            m_CollectionMaps->collectionMap_CaloRel["RecoCaloAssociation"] = v_cal ;
+            for(unsigned int i=0 ; i< mcRecoCaloAssociation->size(); i++) m_CollectionMaps->collectionMap_CaloRel ["RecoCaloAssociation"].push_back(mcRecoCaloAssociation->at(i));
+        }
+        else
+        {
+            if (NULL != MCParticle   )
+            {
+                for(unsigned int i=0 ; i< MCParticle->size(); i++)
+                {
+                    if(MCParticle->at(i).parents_size()==0)
+                    {
+                        std::cout<<"create recoCaloAssociation by hand now"<<std::endl;
+                        for(std::map<std::string, std::vector<edm4hep::CalorimeterHit> >::iterator iter = m_CollectionMaps->collectionMap_CaloHit.begin(); iter != m_CollectionMaps->collectionMap_CaloHit.end(); iter++)
+                        {
+                            std::string prefix = "RecoCaloAssociation_";
+                            std::string key = prefix + iter->first;
+                            std::cout<<"create for "<<key<<std::endl;
+                            std::vector<edm4hep::MCRecoCaloAssociation> v_cal;
+                            m_CollectionMaps->collectionMap_CaloRel[key] = v_cal ;
+                            for(std::vector<edm4hep::CalorimeterHit>::iterator it=iter->second.begin(); it != iter->second.end(); it ++)
+                            {
+                                edm4hep::SimCalorimeterHit sim_hit( it->getCellID(), it->getEnergy(), it->getPosition() );
+                                edm4hep::CaloHitContribution conb ( MCParticle->at(i).getPDG(), it->getEnergy(), 0, it->getPosition() ); 
+                                conb.setParticle( MCParticle->at(i) );
+                                sim_hit.addContribution(conb);
+                                edm4hep::MCRecoCaloAssociation calo_association;
+                                calo_association.setRec(*it);
+                                calo_association.setSim(sim_hit);
+                                m_CollectionMaps->collectionMap_CaloRel[key].push_back(calo_association); 
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        if (NULL != mcRecoTrackerAssociation )
+        {
+            std::vector<edm4hep::MCRecoTrackerAssociation> v_cal;
+            m_CollectionMaps->collectionMap_TrkRel["RecoTrackerAssociation"] = v_cal ;
+            for(unsigned int i=0 ; i< mcRecoTrackerAssociation->size(); i++) m_CollectionMaps->collectionMap_TrkRel ["RecoTrackerAssociation"].push_back(mcRecoTrackerAssociation->at(i));
+        }
+    return StatusCode::SUCCESS;
+}
+
+
+StatusCode PandoraPFAlg::updateMap(CollectionMaps & tmp_map)
+{
+        const edm4hep::MCParticleCollection*     MCParticle = nullptr;
+        const edm4hep::CalorimeterHitCollection* ECALBarrel = nullptr;        
+        const edm4hep::CalorimeterHitCollection* ECALEndcap = nullptr; 
+        const edm4hep::CalorimeterHitCollection* ECALOther  = nullptr; 
+        const edm4hep::CalorimeterHitCollection* HCALBarrel = nullptr; 
+        const edm4hep::CalorimeterHitCollection* HCALEndcap = nullptr; 
+        const edm4hep::CalorimeterHitCollection* HCALOther  = nullptr; 
+        const edm4hep::CalorimeterHitCollection* MUON       = nullptr; 
+        const edm4hep::CalorimeterHitCollection* LCAL       = nullptr; 
+        const edm4hep::CalorimeterHitCollection* LHCAL      = nullptr; 
+        const edm4hep::CalorimeterHitCollection* BCAL       = nullptr; 
+        const edm4hep::VertexCollection* KinkVertices       = nullptr; 
+        const edm4hep::VertexCollection* ProngVertices      = nullptr; 
+        const edm4hep::VertexCollection* SplitVertices      = nullptr; 
+        const edm4hep::VertexCollection* V0Vertices         = nullptr; 
+        const edm4hep::TrackCollection*  MarlinTrkTracks    = nullptr; 
+        const edm4hep::MCRecoCaloAssociationCollection*  mcRecoCaloAssociation    = nullptr; 
+        const edm4hep::MCRecoTrackerAssociationCollection*  mcRecoTrackerAssociation    = nullptr; 
+        StatusCode sc = StatusCode::SUCCESS;
+        sc =  getCol(m_mcParCol_r  , MCParticle );
+        sc =  getCol(m_ECALBarrel_r, ECALBarrel );
+        sc =  getCol(m_ECALEndcap_r, ECALEndcap );
+        sc =  getCol(m_ECALOther_r , ECALOther  );
+        sc =  getCol(m_HCALBarrel_r, HCALBarrel );
+        sc =  getCol(m_HCALEndcap_r, HCALEndcap );
+        sc =  getCol(m_HCALOther_r , HCALOther  );
+        sc =  getCol(m_MUON_r      , MUON       );
+        sc =  getCol(m_LCAL_r      , LCAL       );
+        sc =  getCol(m_LHCAL_r     , LHCAL      );
+        sc =  getCol(m_BCAL_r      , BCAL       );        
+        sc =  getCol(m_KinkVertices_r  , KinkVertices );        
+        sc =  getCol(m_ProngVertices_r , ProngVertices);        
+        sc =  getCol(m_SplitVertices_r , SplitVertices);        
+        sc =  getCol(m_V0Vertices_r    , V0Vertices   );        
+        sc =  getCol(m_MarlinTrkTracks_r , MarlinTrkTracks   );        
+        sc =  getCol(m_MCRecoCaloAssociation_r , mcRecoCaloAssociation   );        
+        sc =  getCol(m_MCRecoTrackerAssociation_r , mcRecoTrackerAssociation  );        
+
+        if (NULL != MCParticle   )  
+        {
+            std::vector<edm4hep::MCParticle> v_mc;
+            tmp_map.CollectionMap_MC ["MCParticle"] = MCParticle ;
+            tmp_map.collectionMap_MC ["MCParticle"] = v_mc;
+            for(unsigned int i=0 ; i< MCParticle->size(); i++) tmp_map.collectionMap_MC ["MCParticle"].push_back(MCParticle->at(i));
+        }
+        if (NULL != ECALBarrel   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["ECALBarrel"] = ECALBarrel ;
+            tmp_map.collectionMap_CaloHit["ECALBarrel"] = v_cal ;
+            for(unsigned int i=0 ; i< ECALBarrel->size(); i++) tmp_map.collectionMap_CaloHit ["ECALBarrel"].push_back(ECALBarrel->at(i));
+        }
+        if (NULL != ECALEndcap   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["ECALEndcap"] = ECALEndcap ;
+            tmp_map.collectionMap_CaloHit["ECALEndcap"] = v_cal ;
+            for(unsigned int i=0 ; i< ECALEndcap->size(); i++) tmp_map.collectionMap_CaloHit ["ECALEndcap"].push_back(ECALEndcap->at(i));
+        }
+        if (NULL != ECALOther   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["ECALOther"] = ECALOther ;
+            tmp_map.collectionMap_CaloHit["ECALOther"] = v_cal ;
+            for(unsigned int i=0 ; i< ECALOther->size(); i++) tmp_map.collectionMap_CaloHit ["ECALOther"].push_back(ECALOther->at(i));
+        }
+        if (NULL != HCALBarrel   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["HCALBarrel"] = HCALBarrel ;
+            tmp_map.collectionMap_CaloHit["HCALBarrel"] = v_cal ;
+            for(unsigned int i=0 ; i< HCALBarrel->size(); i++) tmp_map.collectionMap_CaloHit ["HCALBarrel"].push_back(HCALBarrel->at(i));
+        }
+        if (NULL != HCALEndcap   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["HCALEndcap"] = HCALEndcap ;
+            tmp_map.collectionMap_CaloHit["HCALEndcap"] = v_cal ;
+            for(unsigned int i=0 ; i< HCALEndcap->size(); i++) tmp_map.collectionMap_CaloHit ["HCALEndcap"].push_back(HCALEndcap->at(i));
+        }
+        if (NULL != HCALOther   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["HCALOther"] = HCALOther ;
+            tmp_map.collectionMap_CaloHit["HCALOther"] = v_cal ;
+            for(unsigned int i=0 ; i< HCALOther->size(); i++) tmp_map.collectionMap_CaloHit ["HCALOther"].push_back(HCALOther->at(i));
+        }
+        if (NULL != MUON   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["MUON"] = MUON ;
+            tmp_map.collectionMap_CaloHit["MUON"] = v_cal ;
+            for(unsigned int i=0 ; i< MUON->size(); i++) tmp_map.collectionMap_CaloHit ["MUON"].push_back(MUON->at(i));
+        }
+        if (NULL != LCAL   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["LCAL"] = LCAL ;
+            tmp_map.collectionMap_CaloHit["LCAL"] = v_cal ;
+            for(unsigned int i=0 ; i< LCAL->size(); i++) tmp_map.collectionMap_CaloHit ["LCAL"].push_back(LCAL->at(i));
+        }
+        if (NULL != LHCAL   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["LHCAL"] = LHCAL ;
+            tmp_map.collectionMap_CaloHit["LHCAL"] = v_cal ;
+            for(unsigned int i=0 ; i< LHCAL->size(); i++) tmp_map.collectionMap_CaloHit ["LHCAL"].push_back(LHCAL->at(i));
+        }
+        if (NULL != BCAL   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["BCAL"] = BCAL ;
+            tmp_map.collectionMap_CaloHit["BCAL"] = v_cal ;
+            for(unsigned int i=0 ; i< BCAL->size(); i++) tmp_map.collectionMap_CaloHit ["BCAL"].push_back(BCAL->at(i));
+        }
+        if (NULL != KinkVertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            tmp_map.CollectionMap_Vertex["KinkVertices"] = KinkVertices ;
+            tmp_map.collectionMap_Vertex["KinkVertices"] = v_cal ;
+            for(unsigned int i=0 ; i< KinkVertices->size(); i++) tmp_map.collectionMap_Vertex ["KinkVertices"].push_back(KinkVertices->at(i));
+        }
+        if (NULL != ProngVertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            tmp_map.CollectionMap_Vertex["ProngVertices"] = ProngVertices ;
+            tmp_map.collectionMap_Vertex["ProngVertices"] = v_cal ;
+            for(unsigned int i=0 ; i< ProngVertices->size(); i++) tmp_map.collectionMap_Vertex ["ProngVertices"].push_back(ProngVertices->at(i));
+        }
+        if (NULL != SplitVertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            tmp_map.CollectionMap_Vertex["SplitVertices"] = SplitVertices ;
+            tmp_map.collectionMap_Vertex["SplitVertices"] = v_cal ;
+            for(unsigned int i=0 ; i< SplitVertices->size(); i++) tmp_map.collectionMap_Vertex ["SplitVertices"].push_back(SplitVertices->at(i));
+        }
+        if (NULL != V0Vertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            tmp_map.CollectionMap_Vertex["V0Vertices"] = V0Vertices ;
+            tmp_map.collectionMap_Vertex["V0Vertices"] = v_cal ;
+            for(unsigned int i=0 ; i< V0Vertices->size(); i++) tmp_map.collectionMap_Vertex ["V0Vertices"].push_back(V0Vertices->at(i));
+        }
+        if (NULL != MarlinTrkTracks   )
+        {
+            std::vector<edm4hep::Track> v_cal;
+            tmp_map.CollectionMap_Track["MarlinTrkTracks"] = MarlinTrkTracks ;
+            tmp_map.collectionMap_Track["MarlinTrkTracks"] = v_cal ;
+            for(unsigned int i=0 ; i< MarlinTrkTracks->size(); i++) tmp_map.collectionMap_Track ["MarlinTrkTracks"].push_back(MarlinTrkTracks->at(i));
+        }
+        if (NULL != mcRecoCaloAssociation )
+        {
+            std::vector<edm4hep::MCRecoCaloAssociation> v_cal;
+            tmp_map.collectionMap_CaloRel["RecoCaloAssociation"] = v_cal ;
+            for(unsigned int i=0 ; i< mcRecoCaloAssociation->size(); i++) tmp_map.collectionMap_CaloRel ["RecoCaloAssociation"].push_back(mcRecoCaloAssociation->at(i));
+        }
+        if (NULL != mcRecoTrackerAssociation )
+        {
+            std::vector<edm4hep::MCRecoTrackerAssociation> v_cal;
+            tmp_map.collectionMap_TrkRel["RecoTrackerAssociation"] = v_cal ;
+            for(unsigned int i=0 ; i< mcRecoTrackerAssociation->size(); i++) tmp_map.collectionMap_TrkRel ["RecoTrackerAssociation"].push_back(mcRecoTrackerAssociation->at(i));
+        }
+    return StatusCode::SUCCESS;
+}
+
+
+StatusCode PandoraPFAlg::Ana()
+{
+    int n_current = m_tree->GetEntries()+1;
+    const edm4hep::ReconstructedParticleCollection* reco_col = m_ReconstructedParticleCollection_w.get();
+    const edm4hep::MCRecoParticleAssociationCollection* reco_associa_col = m_MCRecoParticleAssociation_w.get();
+    std::cout<<"reco_col size="<<reco_col->size()<<std::endl;
+    for(int i=0; i<reco_col->size();i++)
+    {
+        std::cout<<"reco="<<i<<std::endl;
+        const edm4hep::ReconstructedParticle pReco = reco_col->at(i);
+        const float px = pReco.getMomentum()[0];
+        const float py = pReco.getMomentum()[1];
+        const float pz = pReco.getMomentum()[2];
+        const float energy = pReco.getEnergy();
+        const float mass = pReco.getMass();
+        const float charge = pReco.getCharge();
+        const int type = pReco.getType();
+        //std::cout<<"MYDBUG evt="<<n_current<<",rec i="<<i<<",particleId="<<type<<",mass="<<mass<<",charge="<<charge<<",energy="<<energy<<",px="<<px<<",py="<<py<<",pz="<<pz<<std::endl;
+        m_pReco_PID.push_back(type);
+        m_pReco_mass.push_back(mass);
+        m_pReco_charge.push_back(charge);
+        m_pReco_energy.push_back(energy);
+        m_pReco_px.push_back(px);
+        m_pReco_py.push_back(py);
+        m_pReco_pz.push_back(pz);
+        for(int j=0; j < reco_associa_col->size(); j++)
+        {
+            if(reco_associa_col->at(j).getRec().id() != pReco.id() ) continue;
+            std::cout<<"MC pid ="<<reco_associa_col->at(j).getSim().getPDG()<<", px="<<reco_associa_col->at(j).getSim().getMomentum()[0]<<", py="<<reco_associa_col->at(j).getSim().getMomentum()[1]<<",pz="<<reco_associa_col->at(j).getSim().getMomentum()[2]<<std::endl;
+        }
+    }
+    const edm4hep::MCParticleCollection*     MCParticle = nullptr;
+    StatusCode sc = StatusCode::SUCCESS;
+    sc =  getCol(m_mcParCol_r  , MCParticle );
+    if (NULL != MCParticle   )  
+    { 
+        for(unsigned int i=0 ; i< MCParticle->size(); i++)
+        {
+            m_mc_p_size.push_back(MCParticle->at(i).parents_size());
+            m_mc_pid   .push_back(MCParticle->at(i).getPDG());
+            m_mc_mass  .push_back(MCParticle->at(i).getMass());
+            m_mc_px    .push_back(MCParticle->at(i).getMomentum()[0]);
+            m_mc_py    .push_back(MCParticle->at(i).getMomentum()[1]);
+            m_mc_pz    .push_back(MCParticle->at(i).getMomentum()[2]);
+            m_mc_charge.push_back(MCParticle->at(i).getCharge());
+            //for(unsigned int j =0 ; j< MCParticle->at(i).daughters_size(); j++) da_pids.push_back( MCParticle->at(i).getDaughters(j).getPDG());
+            if(MCParticle->at(i).parents_size()==0) std::cout<<"MYDBUG evt="<<n_current<<", mc i="<<i<<",px="<<MCParticle->at(i).getMomentum()[0]<<",py="<<MCParticle->at(i).getMomentum()[1]<<",pz="<<MCParticle->at(i).getMomentum()[2]<<std::endl;
+            if (MCParticle->at(i).getPDG() != 22) continue;
+            int hasEm = 0;
+            int hasEp = 0;
+            for(unsigned int j =0 ; j< MCParticle->at(i).daughters_size(); j++)
+            {
+                if      (MCParticle->at(i).getDaughters(j).getPDG() ==  11 ) hasEm=1;
+                else if (MCParticle->at(i).getDaughters(j).getPDG() == -11 ) hasEp=1;
+            }
+            if(hasEm && hasEp) m_hasConversion=1;
+        }
+    }
+    m_tree->Fill();
+    return StatusCode::SUCCESS;
+}
+
+// create simple MCRecoParticleAssociation using calorimeter hit only
+StatusCode PandoraPFAlg::CreateMCRecoParticleAssociation()
+{
+    edm4hep::MCRecoParticleAssociationCollection* pMCRecoParticleAssociationCollection  = m_MCRecoParticleAssociation_w.createAndPut();
+    const edm4hep::ReconstructedParticleCollection* reco_col = m_ReconstructedParticleCollection_w.get();
+    std::cout<<"CreateMCRecoParticleAssociation, reco_col size="<<reco_col->size()<<std::endl;
+    for(int i=0; i<reco_col->size();i++)
+    {
+        std::map<int, edm4hep::ConstMCParticle> mc_map;
+        const edm4hep::ReconstructedParticle pReco = reco_col->at(i);
+        for(int j=0; j < pReco.clusters_size(); j++)
+        {
+            edm4hep::ConstCluster cluster = pReco.getClusters(j);
+            for(int k=0; k < cluster.hits_size(); k++)
+            {
+                edm4hep::ConstCalorimeterHit hit = cluster.getHits(k);
+                for(std::map<std::string, std::vector<edm4hep::MCRecoCaloAssociation> >::iterator iter = m_CollectionMaps->collectionMap_CaloRel.begin(); iter != m_CollectionMaps->collectionMap_CaloRel.end(); iter++)
+                {
+                    for(std::vector<edm4hep::MCRecoCaloAssociation>::iterator it = iter->second.begin(); it != iter->second.end(); it ++)
+                    {
+                        if(it->getRec().id() != hit.id()) continue;
+                        for(std::vector<edm4hep::ConstCaloHitContribution>::const_iterator itc = it->getSim().contributions_begin(); itc != it->getSim().contributions_end(); itc++)
+                        {
+                            if(mc_map.find(itc->getParticle().id()) == mc_map.end()) mc_map[itc->getParticle().id()] = itc->getParticle() ;
+                        }
+                    }
+                }
+            }
+        }
+        for(std::map<int, edm4hep::ConstMCParticle>::iterator it = mc_map.begin(); it != mc_map.end(); it ++)
+        {      
+            edm4hep::MCRecoParticleAssociation association = pMCRecoParticleAssociationCollection->create();
+            association.setRec(pReco);
+            association.setSim(it->second);
+        }
+    }
+    return StatusCode::SUCCESS;
+}
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/src/PfoCreator.cpp b/Reconstruction/PFA/Pandora/GaudiPandora/src/PfoCreator.cpp
new file mode 100644
index 00000000..a3d35da1
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/src/PfoCreator.cpp
@@ -0,0 +1,429 @@
+/**
+ *  @file   MarlinPandora/src/PfoCreator.cc
+ * 
+ *  @brief  Implementation of the pfo creator class.
+ * 
+ *  $Log: $
+ */
+
+//#include "CalorimeterHitType.h"
+
+#include "Api/PandoraApi.h"
+
+#include "Objects/Cluster.h"
+#include "Objects/ParticleFlowObject.h"
+#include "Objects/Track.h"
+
+#include "Pandora/PdgTable.h"
+#include "PfoCreator.h"
+#include "PandoraPFAlg.h"
+
+#include <algorithm>
+#include <cmath>
+
+PfoCreator::PfoCreator(const Settings &settings, const pandora::Pandora *const pPandora) :
+    m_settings(settings),
+    m_pPandora(pPandora)
+{
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+PfoCreator::~PfoCreator()
+{
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode PfoCreator::CreateParticleFlowObjects(CollectionMaps& collectionMaps, DataHandle<edm4hep::ClusterCollection>& _pClusterCollection, DataHandle<edm4hep::ReconstructedParticleCollection>& _pReconstructedParticleCollection, DataHandle<edm4hep::VertexCollection>& _pStartVertexCollection)
+{
+    m_collectionMaps = &collectionMaps;
+    edm4hep::ClusterCollection* pClusterCollection                              = _pClusterCollection.createAndPut();
+    edm4hep::ReconstructedParticleCollection* pReconstructedParticleCollection  = _pReconstructedParticleCollection.createAndPut();
+    edm4hep::VertexCollection* pStartVertexCollection                           = _pStartVertexCollection.createAndPut();
+ 
+    const pandora::PfoList *pPandoraPfoList = NULL;
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::GetCurrentPfoList(*m_pPandora, pPandoraPfoList));
+
+
+    //IMPL::LCFlagImpl lcFlagImpl(pClusterCollection->getFlag());
+    //lcFlagImpl.setBit(LCIO::CLBIT_HITS);
+    //pClusterCollection->setFlag(lcFlagImpl.getFlag());
+
+    pandora::StringVector subDetectorNames;
+    this->InitialiseSubDetectorNames(subDetectorNames);
+    //pClusterCollection->parameters().setValues("ClusterSubdetectorNames", subDetectorNames);
+
+    // Create lcio "reconstructed particles" from the pandora "particle flow objects"
+    std::cout<<"pPandoraPfoList size="<<pPandoraPfoList->size()<<std::endl;
+    for (pandora::PfoList::const_iterator pIter = pPandoraPfoList->begin(), pIterEnd = pPandoraPfoList->end(); pIter != pIterEnd; ++pIter)
+    {
+        const pandora::ParticleFlowObject *const pPandoraPfo(*pIter);
+        //IMPL::ReconstructedParticleImpl *const pReconstructedParticle(new ReconstructedParticleImpl());
+        edm4hep::ReconstructedParticle pReconstructedParticle0 = pReconstructedParticleCollection->create();
+        edm4hep::ReconstructedParticle* pReconstructedParticle = &pReconstructedParticle0;
+
+        const bool hasTrack(!pPandoraPfo->GetTrackList().empty());
+        const pandora::ClusterList &clusterList(pPandoraPfo->GetClusterList());
+
+        //std::cout<<"ClusterList size="<<clusterList.size()<<std::endl;
+        float clustersTotalEnergy(0.f);
+        pandora::CartesianVector referencePoint(0.f, 0.f, 0.f), clustersWeightedPosition(0.f, 0.f, 0.f);
+        for (pandora::ClusterList::const_iterator cIter = clusterList.begin(), cIterEnd = clusterList.end(); cIter != cIterEnd; ++cIter)
+        {
+            const pandora::Cluster *const pPandoraCluster(*cIter);
+            pandora::CaloHitList pandoraCaloHitList;
+            pPandoraCluster->GetOrderedCaloHitList().FillCaloHitList(pandoraCaloHitList);
+            pandoraCaloHitList.insert(pandoraCaloHitList.end(), pPandoraCluster->GetIsolatedCaloHitList().begin(), pPandoraCluster->GetIsolatedCaloHitList().end());
+
+            pandora::FloatVector hitE, hitX, hitY, hitZ;
+            //IMPL::ClusterImpl *const p_Cluster(new ClusterImpl());
+            edm4hep::Cluster p_Cluster0 = pClusterCollection->create();
+            edm4hep::Cluster* p_Cluster = &p_Cluster0;
+            this->SetClusterSubDetectorEnergies(subDetectorNames, p_Cluster, pandoraCaloHitList, hitE, hitX, hitY, hitZ);
+
+            float clusterCorrectEnergy(0.f);
+            this->SetClusterEnergyAndError(pPandoraPfo, pPandoraCluster, p_Cluster, clusterCorrectEnergy);
+
+            pandora::CartesianVector clusterPosition(0.f, 0.f, 0.f);
+            const unsigned int nHitsInCluster(pandoraCaloHitList.size());
+            this->SetClusterPositionAndError(nHitsInCluster, hitE, hitX, hitY, hitZ, p_Cluster, clusterPosition);
+
+            if (!hasTrack)
+            {
+                clustersWeightedPosition += clusterPosition * clusterCorrectEnergy;
+                clustersTotalEnergy += clusterCorrectEnergy;
+            }
+
+            //pClusterCollection->addElement(p_Cluster);
+            edm4hep::ConstCluster p_ClusterCon = *p_Cluster;
+            pReconstructedParticle->addCluster(p_ClusterCon);
+        }
+
+        if (!hasTrack)
+        {
+            if (clustersTotalEnergy < std::numeric_limits<float>::epsilon())
+            {
+                std::cout<<"WARNING PfoCreator::CreateParticleFlowObjects: invalid cluster energy " << clustersTotalEnergy << std::endl;
+                throw pandora::StatusCodeException(pandora::STATUS_CODE_FAILURE);
+            }
+            else
+            {
+                referencePoint = clustersWeightedPosition * (1.f / clustersTotalEnergy);
+            }
+        }
+        else
+        {
+            PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CalculateTrackBasedReferencePoint(pPandoraPfo, referencePoint));
+        }
+
+        this->SetRecoParticleReferencePoint(referencePoint, pReconstructedParticle);
+        this->AddTracksToRecoParticle(pPandoraPfo, pReconstructedParticle);
+        this->SetRecoParticlePropertiesFromPFO(pPandoraPfo, pReconstructedParticle);
+
+        edm4hep::Vertex pStartVertex0 = pStartVertexCollection->create();
+        edm4hep::Vertex* pStartVertex = &pStartVertex0;
+        //pStartVertex->setAlgorithmType(m_settings.m_startVertexAlgName.c_str());
+        pStartVertex->setAlgorithmType(0);
+        const float ref_value[3] = {referencePoint.GetX(),referencePoint.GetY(),referencePoint.GetZ()};
+        pStartVertex->setPosition(edm4hep::Vector3f(ref_value));
+        pStartVertex->setAssociatedParticle(*pReconstructedParticle);
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void PfoCreator::InitialiseSubDetectorNames(pandora::StringVector &subDetectorNames) const
+{
+    subDetectorNames.push_back("ecal");
+    subDetectorNames.push_back("hcal");
+    subDetectorNames.push_back("yoke");
+    subDetectorNames.push_back("lcal");
+    subDetectorNames.push_back("lhcal");
+    subDetectorNames.push_back("bcal");
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void PfoCreator::SetClusterSubDetectorEnergies(const pandora::StringVector &subDetectorNames, edm4hep::Cluster *const p_Cluster,
+    const pandora::CaloHitList &pandoraCaloHitList, pandora::FloatVector &hitE, pandora::FloatVector &hitX, pandora::FloatVector &hitY,
+    pandora::FloatVector &hitZ) const
+{
+    for (pandora::CaloHitList::const_iterator hIter = pandoraCaloHitList.begin(), hIterEnd = pandoraCaloHitList.end(); hIter != hIterEnd; ++hIter)
+    {
+        const pandora::CaloHit *const pPandoraCaloHit(*hIter);
+        edm4hep::CalorimeterHit *const pCalorimeterHit0 = (edm4hep::CalorimeterHit*)(pPandoraCaloHit->GetParentAddress());
+        const edm4hep::CalorimeterHit pCalorimeterHit = *pCalorimeterHit0;
+        
+        p_Cluster->addHit(pCalorimeterHit);
+
+        const float caloHitEnergy(pCalorimeterHit.getEnergy());
+        hitE.push_back(caloHitEnergy);
+        hitX.push_back(pCalorimeterHit.getPosition()[0]);
+        hitY.push_back(pCalorimeterHit.getPosition()[1]);
+        hitZ.push_back(pCalorimeterHit.getPosition()[2]);
+        /*
+        std::vector<float> &subDetectorEnergies = p_Cluster->subdetectorEnergies();
+        subDetectorEnergies.resize(subDetectorNames.size());
+
+        switch (CHT(pCalorimeterHit->getType()).caloID())
+        {
+            case CHT::ecal:  subDetectorEnergies[ECAL_INDEX ] += caloHitEnergy; break;
+            case CHT::hcal:  subDetectorEnergies[HCAL_INDEX ] += caloHitEnergy; break;
+            case CHT::yoke:  subDetectorEnergies[YOKE_INDEX ] += caloHitEnergy; break;
+            case CHT::lcal:  subDetectorEnergies[LCAL_INDEX ] += caloHitEnergy; break;
+            case CHT::lhcal: subDetectorEnergies[LHCAL_INDEX] += caloHitEnergy; break;
+            case CHT::bcal:  subDetectorEnergies[BCAL_INDEX ] += caloHitEnergy; break;
+            default: streamlog_out(WARNING) << "PfoCreator::SetClusterSubDetectorEnergies: no subdetector found for hit with type: " << pCalorimeterHit->getType() << std::endl;
+        }
+        */
+    }
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void PfoCreator::SetClusterEnergyAndError(const pandora::ParticleFlowObject *const pPandoraPfo, const pandora::Cluster *const pPandoraCluster, 
+    edm4hep::Cluster *const p_Cluster, float &clusterCorrectEnergy) const
+{
+    const bool isEmShower((pandora::PHOTON == pPandoraPfo->GetParticleId()) || (pandora::E_MINUS == std::abs(pPandoraPfo->GetParticleId())));
+    clusterCorrectEnergy = (isEmShower ? pPandoraCluster->GetCorrectedElectromagneticEnergy(*m_pPandora) : pPandoraCluster->GetCorrectedHadronicEnergy(*m_pPandora));
+
+    if (clusterCorrectEnergy < std::numeric_limits<float>::epsilon())
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_FAILURE);
+
+    const float stochasticTerm(isEmShower ? m_settings.m_emStochasticTerm : m_settings.m_hadStochasticTerm); 
+    const float constantTerm(isEmShower ? m_settings.m_emConstantTerm : m_settings.m_hadConstantTerm);
+    const float energyError(std::sqrt(stochasticTerm * stochasticTerm / clusterCorrectEnergy + constantTerm * constantTerm) * clusterCorrectEnergy);
+
+    p_Cluster->setEnergy(clusterCorrectEnergy);
+    p_Cluster->setEnergyError(energyError);
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void PfoCreator::SetClusterPositionAndError(const unsigned int nHitsInCluster, pandora::FloatVector &hitE, pandora::FloatVector &hitX, 
+    pandora::FloatVector &hitY, pandora::FloatVector &hitZ, edm4hep::Cluster *const p_Cluster, pandora::CartesianVector &clusterPositionVec) const
+{
+    ClusterShapes *const pClusterShapes(new ClusterShapes(nHitsInCluster, hitE.data(), hitX.data(), hitY.data(), hitZ.data()));
+
+    try
+    {
+        p_Cluster->setPhi(std::atan2(pClusterShapes->getEigenVecInertia()[1], pClusterShapes->getEigenVecInertia()[0]));
+        p_Cluster->setITheta(std::acos(pClusterShapes->getEigenVecInertia()[2]));
+        p_Cluster->setPosition(pClusterShapes->getCentreOfGravity());
+        //ATTN these two lines below would only compile with ilcsoft HEAD V2015-10-13 and above
+        //p_Cluster->setPositionError(pClusterShapes->getCenterOfGravityErrors());
+        //p_Cluster->setDirectionError(pClusterShapes->getEigenVecInertiaErrors());
+        clusterPositionVec.SetValues(pClusterShapes->getCentreOfGravity()[0], pClusterShapes->getCentreOfGravity()[1], pClusterShapes->getCentreOfGravity()[2]);
+    }
+    catch (...)
+    {
+        std::cout<<"WARNING PfoCreator::SetClusterPositionAndError: unidentified exception caught." << std::endl;
+    }
+
+    delete pClusterShapes;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode PfoCreator::CalculateTrackBasedReferencePoint(const pandora::ParticleFlowObject *const pPandoraPfo, pandora::CartesianVector &referencePoint) const
+{
+    const pandora::TrackList &trackList(pPandoraPfo->GetTrackList());
+
+    float totalTrackMomentumAtDca(0.f), totalTrackMomentumAtStart(0.f);
+    pandora::CartesianVector referencePointAtDCAWeighted(0.f, 0.f, 0.f), referencePointAtStartWeighted(0.f, 0.f, 0.f);
+
+    bool hasSiblings(false);
+    for (pandora::TrackList::const_iterator tIter = trackList.begin(), tIterEnd = trackList.end(); tIter != tIterEnd; ++tIter)
+    {
+        const pandora::Track *const pPandoraTrack(*tIter);
+
+        if (!this->IsValidParentTrack(pPandoraTrack, trackList))
+            continue;
+
+        if (this->HasValidSiblingTrack(pPandoraTrack, trackList))
+        {
+            // Presence of sibling tracks typically represents a conversion
+            const pandora::CartesianVector &trackStartPoint((pPandoraTrack->GetTrackStateAtStart()).GetPosition());
+            const float trackStartMomentum(((pPandoraTrack->GetTrackStateAtStart()).GetMomentum()).GetMagnitude());
+            referencePointAtStartWeighted += trackStartPoint * trackStartMomentum;
+            totalTrackMomentumAtStart += trackStartMomentum;
+            hasSiblings = true;
+        }
+        else
+        {
+            const edm4hep::Track *const pLcioTrack0 = (edm4hep::Track*)(pPandoraTrack->GetParentAddress());
+            const edm4hep::Track pLcioTrack = *pLcioTrack0;
+
+            const float z0(pPandoraTrack->GetZ0());
+            pandora::CartesianVector intersectionPoint(0.f, 0.f, 0.f);
+
+            //intersectionPoint.SetValues(pLcioTrack->getD0() * std::cos(pLcioTrack->getPhi()), pLcioTrack->getD0() * std::sin(pLcioTrack->getPhi()), z0);
+            if(pLcioTrack.trackStates_size()==0) throw "zero trackStates size find";
+            intersectionPoint.SetValues(pLcioTrack.getTrackStates(0).D0 * std::cos(pLcioTrack.getTrackStates(0).phi), pLcioTrack.getTrackStates(0).D0 * std::sin(pLcioTrack.getTrackStates(0).phi), z0);
+            const float trackMomentumAtDca((pPandoraTrack->GetMomentumAtDca()).GetMagnitude());
+            referencePointAtDCAWeighted += intersectionPoint * trackMomentumAtDca;
+            totalTrackMomentumAtDca += trackMomentumAtDca;
+        }
+    }
+
+    if (hasSiblings)
+    {
+        if (totalTrackMomentumAtStart < std::numeric_limits<float>::epsilon())
+        {
+            std::cout<<" WARNING PfoCreator::CalculateTrackBasedReferencePoint: invalid track momentum " << totalTrackMomentumAtStart << std::endl;
+            throw pandora::StatusCodeException(pandora::STATUS_CODE_FAILURE);
+        }
+        else
+        {
+            referencePoint = referencePointAtStartWeighted * (1.f / totalTrackMomentumAtStart);
+        }
+    }
+    else
+    {
+        if (totalTrackMomentumAtDca < std::numeric_limits<float>::epsilon())
+        {
+            std::cout<<"WARNING PfoCreator::CalculateTrackBasedReferencePoint: invalid track momentum " << totalTrackMomentumAtDca << std::endl;
+            throw pandora::StatusCodeException(pandora::STATUS_CODE_FAILURE);
+        }
+        else
+        {
+            referencePoint = referencePointAtDCAWeighted * (1.f / totalTrackMomentumAtDca);
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+bool PfoCreator::IsValidParentTrack(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const
+{
+    const pandora::TrackList &parentTrackList(pPandoraTrack->GetParentList());
+
+    for (pandora::TrackList::const_iterator iter = parentTrackList.begin(), iterEnd = parentTrackList.end(); iter != iterEnd; ++iter)
+    {
+        if (allTrackList.end() != std::find(allTrackList.begin(), allTrackList.end(), *iter))
+            continue;
+
+        // ATTN This track must have a parent not in the all track list; still use it if it is the closest to the ip
+        std::cout<<"WARNING PfoCreator::IsValidParentTrack: mismatch in track relationship information, use information as available " << std::endl;
+
+        if (this->IsClosestTrackToIP(pPandoraTrack, allTrackList))
+            return true;
+
+        return false;
+    }
+
+    // Ideal case: All parents are associated to same pfo
+    return true;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+bool PfoCreator::HasValidSiblingTrack(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const
+{
+    const pandora::TrackList &siblingTrackList(pPandoraTrack->GetSiblingList());
+
+    for (pandora::TrackList::const_iterator iter = siblingTrackList.begin(), iterEnd = siblingTrackList.end(); iter != iterEnd; ++iter)
+    {
+        if (allTrackList.end() != std::find(allTrackList.begin(), allTrackList.end(), *iter))
+            continue;
+
+        // ATTN This track must have a sibling not in the all track list; still use it if it has a second sibling that is in the list
+        std::cout<<"WARNING PfoCreator::HasValidSiblingTrack: mismatch in track relationship information, use information as available " << std::endl;
+
+        if (this->AreAnyOtherSiblingsInList(pPandoraTrack, allTrackList))
+            return true;
+
+        return false;
+    }
+
+    // Ideal case: All siblings associated to same pfo
+    return true;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+bool PfoCreator::IsClosestTrackToIP(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const 
+{
+    const pandora::Track *pClosestTrack(NULL);
+    float closestTrackDisplacement(std::numeric_limits<float>::max()); 
+
+    for (pandora::TrackList::const_iterator iter = allTrackList.begin(), iterEnd = allTrackList.end(); iter != iterEnd; ++iter)
+    {
+        const pandora::Track *const pTrack(*iter);
+        const float trialTrackDisplacement(pTrack->GetTrackStateAtStart().GetPosition().GetMagnitude());
+
+        if (trialTrackDisplacement < closestTrackDisplacement)
+        {
+            closestTrackDisplacement = trialTrackDisplacement;
+            pClosestTrack = pTrack;
+        }
+    }
+
+    return (pPandoraTrack == pClosestTrack);
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+bool PfoCreator::AreAnyOtherSiblingsInList(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const
+{
+    const pandora::TrackList &siblingTrackList(pPandoraTrack->GetSiblingList());
+
+    for (pandora::TrackList::const_iterator iter = siblingTrackList.begin(), iterEnd = siblingTrackList.end(); iter != iterEnd; ++iter)
+    {
+        if (allTrackList.end() != std::find(allTrackList.begin(), allTrackList.end(), *iter))
+            return true;
+    }
+
+    return false;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void PfoCreator::SetRecoParticleReferencePoint(const pandora::CartesianVector &referencePoint, edm4hep::ReconstructedParticle *const pReconstructedParticle) const
+{
+    const float referencePointArray[3] = {referencePoint.GetX(), referencePoint.GetY(), referencePoint.GetZ()};
+    pReconstructedParticle->setReferencePoint(referencePointArray);
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void PfoCreator::AddTracksToRecoParticle(const pandora::ParticleFlowObject *const pPandoraPfo, edm4hep::ReconstructedParticle *const pReconstructedParticle) const
+{
+    const pandora::TrackList &trackList(pPandoraPfo->GetTrackList());
+
+    for (pandora::TrackList::const_iterator tIter = trackList.begin(), tIterEnd = trackList.end(); tIter != tIterEnd; ++tIter)
+    {
+        const pandora::Track *const pTrack(*tIter);
+        const edm4hep::Track *const pLcioTrack0 = (edm4hep::Track*)(pTrack->GetParentAddress());
+        const edm4hep::Track pLcioTrack = *pLcioTrack0;
+        pReconstructedParticle->addTrack(pLcioTrack);
+
+    }
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void PfoCreator::SetRecoParticlePropertiesFromPFO(const pandora::ParticleFlowObject *const pPandoraPfo, edm4hep::ReconstructedParticle *const pReconstructedParticle) const
+{
+    const float momentum[3] = {pPandoraPfo->GetMomentum().GetX(), pPandoraPfo->GetMomentum().GetY(), pPandoraPfo->GetMomentum().GetZ()};
+    pReconstructedParticle->setMomentum(momentum);
+    pReconstructedParticle->setEnergy(pPandoraPfo->GetEnergy());
+    pReconstructedParticle->setMass(pPandoraPfo->GetMass());
+    pReconstructedParticle->setCharge(pPandoraPfo->GetCharge());
+    pReconstructedParticle->setType(pPandoraPfo->GetParticleId());
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+PfoCreator::Settings::Settings():
+    m_emStochasticTerm(0.17f),
+    m_hadStochasticTerm(0.6f),
+    m_emConstantTerm(0.01f),
+    m_hadConstantTerm(0.03f)
+{
+}
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/src/TrackCreator.cpp b/Reconstruction/PFA/Pandora/GaudiPandora/src/TrackCreator.cpp
new file mode 100644
index 00000000..a57bd9b2
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/src/TrackCreator.cpp
@@ -0,0 +1,992 @@
+/**
+ *  @file   MarlinPandora/src/TrackCreator.cc
+ * 
+ *  @brief  Implementation of the track creator class.
+ * 
+ *  $Log: $
+ */
+
+//#include "marlin/Global.h"
+//#include "marlin/Processor.h"
+
+//#include "EVENT/LCCollection.h"
+//#include "EVENT/ReconstructedParticle.h"
+//#include "EVENT/Vertex.h"
+#include "UTIL/ILDConf.h"
+
+#include "edm4hep/Vertex.h"
+#include "edm4hep/ReconstructedParticle.h"
+
+#include "gear/BField.h"
+#include "gear/CalorimeterParameters.h"
+#include "gear/PadRowLayout2D.h"
+#include "gear/TPCParameters.h"
+#include "gear/FTDParameters.h"
+#include "gear/FTDLayerLayout.h"
+
+#include "GaudiKernel/IService.h"
+#include "GearSvc/IGearSvc.h"
+#include "PandoraPFAlg.h"
+
+#include "TrackCreator.h"
+#include "Pandora/PdgTable.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+TrackCreator::TrackCreator(const Settings &settings, const pandora::Pandora *const pPandora, ISvcLocator* svcloc) :
+    m_settings(settings),
+    m_pPandora(pPandora)
+{
+
+    IGearSvc*  iSvc = 0;
+    StatusCode sc = svcloc->service("GearSvc", iSvc, false);
+    if ( !sc ) 
+    {
+        throw "Failed to find GearSvc ...";
+    }
+    _GEAR = iSvc->getGearMgr();
+
+
+    m_bField                  = (_GEAR->getBField().at(gear::Vector3D(0., 0., 0.)).z());
+    m_tpcInnerR               = (_GEAR->getTPCParameters().getPadLayout().getPlaneExtent()[0]);
+    m_tpcOuterR               = (_GEAR->getTPCParameters().getPadLayout().getPlaneExtent()[1]);
+    m_tpcMaxRow               = (_GEAR->getTPCParameters().getPadLayout().getNRows());
+    m_tpcZmax                 = (_GEAR->getTPCParameters().getMaxDriftLength());
+    m_eCalBarrelInnerSymmetry = (_GEAR->getEcalBarrelParameters().getSymmetryOrder());
+    m_eCalBarrelInnerPhi0     = (_GEAR->getEcalBarrelParameters().getPhi0());
+    m_eCalBarrelInnerR        = (_GEAR->getEcalBarrelParameters().getExtent()[0]);
+    m_eCalEndCapInnerZ        = (_GEAR->getEcalEndcapParameters().getExtent()[2]);
+    // fg: FTD description in GEAR has changed ...
+    try
+    {
+        m_ftdInnerRadii = _GEAR->getGearParameters("FTD").getDoubleVals("FTDInnerRadius");
+        m_ftdOuterRadii = _GEAR->getGearParameters("FTD").getDoubleVals("FTDOuterRadius");
+        m_ftdZPositions = _GEAR->getGearParameters("FTD").getDoubleVals("FTDZCoordinate");
+        m_nFtdLayers = m_ftdZPositions.size();
+    }
+    catch (gear::UnknownParameterException &)
+    {
+        const gear::FTDLayerLayout &ftdLayerLayout(_GEAR->getFTDParameters().getFTDLayerLayout());
+        std::cout << " Filling FTD parameters from gear::FTDParameters - n layers: " << ftdLayerLayout.getNLayers() << std::endl;
+
+        for(unsigned int i = 0, N = ftdLayerLayout.getNLayers(); i < N; ++i)
+        {
+            // Create a disk to represent even number petals front side
+            m_ftdInnerRadii.push_back(ftdLayerLayout.getSensitiveRinner(i));
+            m_ftdOuterRadii.push_back(ftdLayerLayout.getMaxRadius(i));
+
+            // Take the mean z position of the staggered petals
+            const double zpos(ftdLayerLayout.getZposition(i));
+            m_ftdZPositions.push_back(zpos);
+            std::cout << "     layer " << i << " - mean z position = " << zpos << std::endl;
+        }
+
+        m_nFtdLayers = m_ftdZPositions.size() ;
+    }
+
+    // Check tpc parameters
+    if ((std::fabs(m_tpcZmax) < std::numeric_limits<float>::epsilon()) || (std::fabs(m_tpcInnerR) < std::numeric_limits<float>::epsilon())
+        || (std::fabs(m_tpcOuterR - m_tpcInnerR) < std::numeric_limits<float>::epsilon()))
+    {
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+    }
+
+    m_cosTpc = m_tpcZmax / std::sqrt(m_tpcZmax * m_tpcZmax + m_tpcInnerR * m_tpcInnerR);
+
+    // Check ftd parameters
+    if ((0 == m_nFtdLayers) || (m_nFtdLayers != m_ftdInnerRadii.size()) || (m_nFtdLayers != m_ftdOuterRadii.size()))
+    {
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+    }
+
+    for (unsigned int iFtdLayer = 0; iFtdLayer < m_nFtdLayers; ++iFtdLayer)
+    {
+        if ((std::fabs(m_ftdOuterRadii[iFtdLayer]) < std::numeric_limits<float>::epsilon()) ||
+            (std::fabs(m_ftdInnerRadii[iFtdLayer]) < std::numeric_limits<float>::epsilon()))
+        {
+            throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+        }
+    }
+
+    m_tanLambdaFtd = m_ftdZPositions[0] / m_ftdOuterRadii[0];
+
+    // Calculate etd and set parameters
+    // fg: make SET and ETD optional - as they might not be in the model ...
+    try
+    {
+        const DoubleVector &etdZPositions(_GEAR->getGearParameters("ETD").getDoubleVals("ETDLayerZ"));
+        const DoubleVector &setInnerRadii(_GEAR->getGearParameters("SET").getDoubleVals("SETLayerRadius"));
+
+        if (etdZPositions.empty() || setInnerRadii.empty())
+            throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+
+        m_minEtdZPosition = *(std::min_element(etdZPositions.begin(), etdZPositions.end()));
+        m_minSetRadius = *(std::min_element(setInnerRadii.begin(), setInnerRadii.end()));
+    }
+    catch(gear::UnknownParameterException &)
+    {
+        std::cout << "Warnning, ETDLayerZ or SETLayerRadius parameters missing from GEAR parameters!" << std::endl
+                               << "     -> both will be set to " << std::numeric_limits<float>::quiet_NaN() << std::endl;
+
+        //fg: Set them to NAN, so that they cannot be used to set   trackParameters.m_reachesCalorimeter = true;
+        m_minEtdZPosition = std::numeric_limits<float>::quiet_NaN();
+        m_minSetRadius = std::numeric_limits<float>::quiet_NaN();
+    }
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+TrackCreator::~TrackCreator()
+{
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode TrackCreator::CreateTrackAssociations(const CollectionMaps& collectionMaps)
+{
+//    Don't use it now, because the Vertex.getAssociatedParticle() doesn't work for LCIO to plcio transfer
+//    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->ExtractKinks(collectionMaps));
+//    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->ExtractProngsAndSplits(const CollectionMaps& collectionMaps));
+//    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->ExtractV0s(const CollectionMaps& collectionMaps));
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+/*
+pandora::StatusCode TrackCreator::ExtractKinks(const CollectionMaps& collectionMaps)
+{
+    std::cout<<"start TrackCreator::ExtractKinks:"<<std::endl;
+    for (StringVector::const_iterator iter = m_settings.m_kinkVertexCollections.begin(), iterEnd = m_settings.m_kinkVertexCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.CollectionMap_Vertex.find(*iter) == collectionMaps.CollectionMap_Vertex.end()) { std::cout<<"not find "<<(*iter)<<std::endl; continue;}
+        try
+        {
+            const edm4hep::VertexCollection *pKinkCollection = (collectionMaps.CollectionMap_Vertex.find(*iter))->second;
+
+            for (int i = 0, iMax = pKinkCollection->size(); i < iMax; ++i)
+            {
+                try
+                {
+                    const edm4hep::Vertex  pVertex0 = pKinkCollection->at(i);
+                    const edm4hep::Vertex* pVertex  = &(pVertex0);
+
+                    if (NULL == pVertex) throw ("Collection type mismatch");
+
+                    std::cout<<"pVertex0 getChi2="<<pVertex0.getChi2()<<std::endl;
+                    std::cout<<"pVertex getChi2="<<pVertex->getChi2()<<std::endl;
+                    std::cout<<"Hi 0 "<<std::endl;
+                    const plcio::ConstReconstructedParticle pReconstructedParticle0 = pVertex0.getAssociatedParticle();
+                    std::cout<<"Hi 1"<<std::endl;
+                    //EVENT::ReconstructedParticle *pReconstructedParticle = pVertex->getAssociatedParticle();
+                    const plcio::ConstReconstructedParticle pReconstructedParticle = pVertex->getAssociatedParticle();
+                    std::cout<<"Hi 2:"<<&pReconstructedParticle<<std::endl;
+                    //const EVENT::TrackVec &trackVec(pReconstructedParticle->getTracks());
+                    //plcio::ConstTrack trackVec = pReconstructedParticle.getTracks();
+
+                    std::cout<<"pReconstructedParticle0 en="<<pReconstructedParticle0.getEnergy()<<std::endl;
+                    std::cout<<"pReconstructedParticle en="<<pReconstructedParticle.getEnergy()<<std::endl;
+                    //if (this->IsConflictingRelationship(trackVec))continue;
+                    if (this->IsConflictingRelationship(pReconstructedParticle))continue;
+
+                    //const int vertexPdgCode(pReconstructedParticle->getType());
+                    const int vertexPdgCode(pReconstructedParticle.getType());
+
+                    // Extract the kink vertex information
+                    //for (unsigned int iTrack = 0, nTracks = trackVec.size(); iTrack < nTracks; ++iTrack)
+                    //for (unsigned int iTrack = 0, nTracks = trackVec.tracks_size(); iTrack < nTracks; ++iTrack)
+                    for (unsigned int iTrack = 0, nTracks = pReconstructedParticle.tracks_size(); iTrack < nTracks; ++iTrack)
+                    {
+                        //EVENT::Track *pTrack = trackVec[iTrack];
+                        //plcio::ConstTrack pTrack = trackVec.getTracks(iTrack);
+                        plcio::ConstTrack pTrack = pReconstructedParticle.getTracks(iTrack);
+                        //(0 == iTrack) ? m_parentTrackList.insert(pTrack) : m_daughterTrackList.insert(pTrack);
+                        (0 == iTrack) ? m_parentTrackList.insert(pTrack.id()) : m_daughterTrackList.insert(pTrack.id());
+                       //std::cout << "KinkTrack " << iTrack << ", nHits " << pTrack.getTrackerHits().size() << std::endl;
+                       std::cout << "KinkTrack " << iTrack << ", nHits " << pTrack.trackerHits_size() << std::endl;
+
+                        int trackPdgCode = pandora::UNKNOWN_PARTICLE_TYPE;
+
+                        if (0 == iTrack)
+                        {
+                            trackPdgCode = vertexPdgCode;
+                        }
+                        else
+                        {
+                            switch (vertexPdgCode)
+                            {
+                            case pandora::PI_PLUS :
+                            case pandora::K_PLUS :
+                                trackPdgCode = pandora::MU_PLUS;
+                                break;
+                            case pandora::PI_MINUS :
+                            case pandora::K_MINUS :
+                                trackPdgCode = pandora::MU_MINUS;
+                                break;
+                            case pandora::HYPERON_MINUS_BAR :
+                            case pandora::SIGMA_PLUS :
+                                trackPdgCode = pandora::PI_PLUS;
+                                break;
+                            case pandora::SIGMA_MINUS :
+                            case pandora::HYPERON_MINUS :
+                                trackPdgCode = pandora::PI_PLUS;
+                                break;
+                            default :
+                                //(pTrack->getOmega() > 0) ? trackPdgCode = pandora::PI_PLUS : trackPdgCode = pandora::PI_MINUS;
+                                (pTrack.getTrackStates(0).omega > 0) ? trackPdgCode = pandora::PI_PLUS : trackPdgCode = pandora::PI_MINUS;
+                                break;
+                            }
+                        }
+
+                        m_trackToPidMap.insert(TrackToPidMap::value_type(pTrack, trackPdgCode));
+
+                        if (0 == m_settings.m_shouldFormTrackRelationships)
+                            continue;
+
+                        // Make track parent-daughter relationships
+                        if (0 == iTrack)
+                        {
+                            for (unsigned int jTrack = iTrack + 1; jTrack < nTracks; ++jTrack)
+                            {
+                                //PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackParentDaughterRelationship(*m_pPandora, pTrack, trackVec[jTrack]));
+                                //PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackParentDaughterRelationship(*m_pPandora, (int*)(pTrack.id()), (int*)(trackVec.getTracks(jTrack).id()) ) );
+                                PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackParentDaughterRelationship(*m_pPandora, (int*)(pTrack.id()), (int*)(pReconstructedParticle.getTracks(jTrack).id()) ) );
+                            }
+                        }
+
+                        // Make track sibling relationships
+                        else
+                        {
+                            for (unsigned int jTrack = iTrack + 1; jTrack < nTracks; ++jTrack)
+                            {
+                                //PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackSiblingRelationship(*m_pPandora, pTrack, trackVec[jTrack]));
+                                //PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackSiblingRelationship(*m_pPandora, (int*)(pTrack.id()), (int*)(trackVec.getTracks(jTrack).id()) ));
+                                PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackSiblingRelationship(*m_pPandora, (int*)(pTrack.id()), (int*)(pReconstructedParticle.getTracks(jTrack).id()) ));
+                            }
+                        }
+                    }
+                }
+                catch (...)
+                {
+                    std::cout << "Failed to extract kink vertex: " <<  std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout << "Failed to extract kink vertex collection: " << *iter <<  std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+*/
+//------------------------------------------------------------------------------------------------------------------------------------------
+/*
+pandora::StatusCode TrackCreator::ExtractProngsAndSplits(const EVENT::LCEvent *const pLCEvent)
+{
+    for (StringVector::const_iterator iter = m_settings.m_prongSplitVertexCollections.begin(), iterEnd = m_settings.m_prongSplitVertexCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        try
+        {
+            const EVENT::LCCollection *pProngOrSplitCollection = pLCEvent->getCollection(*iter);
+
+            for (int i = 0, iMax = pProngOrSplitCollection->getNumberOfElements(); i < iMax; ++i)
+            {
+                try
+                {
+                    EVENT::Vertex *pVertex = dynamic_cast<Vertex*>(pProngOrSplitCollection->getElementAt(i));
+
+                    if (NULL == pVertex)
+                        throw EVENT::Exception("Collection type mismatch");
+
+                    EVENT::ReconstructedParticle *pReconstructedParticle = pVertex->getAssociatedParticle();
+                    const EVENT::TrackVec &trackVec(pReconstructedParticle->getTracks());
+
+                    if (this->IsConflictingRelationship(trackVec))
+                        continue;
+
+                    // Extract the prong/split vertex information
+                    for (unsigned int iTrack = 0, nTracks = trackVec.size(); iTrack < nTracks; ++iTrack)
+                    {
+                        EVENT::Track *pTrack = trackVec[iTrack];
+                        (0 == iTrack) ? m_parentTrackList.insert(pTrack) : m_daughterTrackList.insert(pTrack);
+                        streamlog_out(DEBUG) << "Prong or Split Track " << iTrack << ", nHits " << pTrack->getTrackerHits().size() << std::endl;
+
+                        if (0 == m_settings.m_shouldFormTrackRelationships)
+                            continue;
+
+                        // Make track parent-daughter relationships
+                        if (0 == iTrack)
+                        {
+                            for (unsigned int jTrack = iTrack + 1; jTrack < nTracks; ++jTrack)
+                            {
+                                PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackParentDaughterRelationship(*m_pPandora,
+                                    pTrack, trackVec[jTrack]));
+                            }
+                        }
+
+                        // Make track sibling relationships
+                        else
+                        {
+                            for (unsigned int jTrack = iTrack + 1; jTrack < nTracks; ++jTrack)
+                            {
+                                PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackSiblingRelationship(*m_pPandora,
+                                    pTrack, trackVec[jTrack]));
+                            }
+                        }
+                    }
+                }
+                catch (EVENT::Exception &exception)
+                {
+                    streamlog_out(WARNING) << "Failed to extract prong/split vertex: " << exception.what() << std::endl;
+                }
+            }
+        }
+        catch (EVENT::Exception &exception)
+        {
+            streamlog_out(DEBUG5) << "Failed to extract prong/split vertex collection: " << *iter << ", " << exception.what() << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode TrackCreator::ExtractV0s(const EVENT::LCEvent *const pLCEvent)
+{
+    for (StringVector::const_iterator iter = m_settings.m_v0VertexCollections.begin(), iterEnd = m_settings.m_v0VertexCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        try
+        {
+            const EVENT::LCCollection *pV0Collection = pLCEvent->getCollection(*iter);
+
+            for (int i = 0, iMax = pV0Collection->getNumberOfElements(); i < iMax; ++i)
+            {
+                try
+                {
+                    EVENT::Vertex *pVertex = dynamic_cast<Vertex*>(pV0Collection->getElementAt(i));
+
+                    if (NULL == pVertex)
+                        throw EVENT::Exception("Collection type mismatch");
+
+                    EVENT::ReconstructedParticle *pReconstructedParticle = pVertex->getAssociatedParticle();
+                    const EVENT::TrackVec &trackVec(pReconstructedParticle->getTracks());
+
+                    if (this->IsConflictingRelationship(trackVec))
+                        continue;
+
+                    // Extract the v0 vertex information
+                    const int vertexPdgCode(pReconstructedParticle->getType());
+
+                    for (unsigned int iTrack = 0, nTracks = trackVec.size(); iTrack < nTracks; ++iTrack)
+                    {
+                        EVENT::Track *pTrack = trackVec[iTrack];
+                        m_v0TrackList.insert(pTrack);
+                        streamlog_out(DEBUG) << "V0Track " << iTrack << ", nHits " << pTrack->getTrackerHits().size() << std::endl;
+
+                        int trackPdgCode = pandora::UNKNOWN_PARTICLE_TYPE;
+
+                        switch (vertexPdgCode)
+                        {
+                        case pandora::PHOTON :
+                            (pTrack->getOmega() > 0) ? trackPdgCode = pandora::E_PLUS : trackPdgCode = pandora::E_MINUS;
+                            break;
+                        case pandora::LAMBDA :
+                            (pTrack->getOmega() > 0) ? trackPdgCode = pandora::PROTON : trackPdgCode = pandora::PI_MINUS;
+                            break;
+                        case pandora::LAMBDA_BAR :
+                            (pTrack->getOmega() > 0) ? trackPdgCode = pandora::PI_PLUS : trackPdgCode = pandora::PROTON_BAR;
+                            break;
+                        case pandora::K_SHORT :
+                            (pTrack->getOmega() > 0) ? trackPdgCode = pandora::PI_PLUS : trackPdgCode = pandora::PI_MINUS;
+                            break;
+                        default :
+                            (pTrack->getOmega() > 0) ? trackPdgCode = pandora::PI_PLUS : trackPdgCode = pandora::PI_MINUS;
+                            break;
+                        }
+
+                        m_trackToPidMap.insert(TrackToPidMap::value_type(pTrack, trackPdgCode));
+
+                        if (0 == m_settings.m_shouldFormTrackRelationships)
+                            continue;
+
+                        // Make track sibling relationships
+                        for (unsigned int jTrack = iTrack + 1; jTrack < nTracks; ++jTrack)
+                        {
+                            PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackSiblingRelationship(*m_pPandora,
+                                pTrack, trackVec[jTrack]));
+                        }
+                    }
+                }
+                catch (EVENT::Exception &exception)
+                {
+                    streamlog_out(WARNING) << "Failed to extract v0 vertex: " << exception.what() << std::endl;
+                }
+            }
+        }
+        catch (EVENT::Exception &exception)
+        {
+            streamlog_out(DEBUG5) << "Failed to extract v0 vertex collection: " << *iter << ", " << exception.what() << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+*/
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+bool TrackCreator::IsConflictingRelationship(const edm4hep::ConstReconstructedParticle &Particle) const
+{
+    //for (unsigned int iTrack = 0, nTracks = trackVec.size(); iTrack < nTracks; ++iTrack)
+    //for (unsigned int iTrack = 0, nTracks = trackVec.tracks_size(); iTrack < nTracks; ++iTrack)
+    std::cout<<"Particle en="<<Particle.getEnergy()<<std::endl;
+    for (unsigned int iTrack = 0, nTracks = Particle.tracks_size(); iTrack < nTracks; ++iTrack)
+    {
+        //EVENT::Track *pTrack = trackVec[iTrack];
+        edm4hep::ConstTrack pTrack = Particle.getTracks(iTrack) ;
+        unsigned int pTrack_id = pTrack.id() ;
+
+        //if (this->IsDaughter(pTrack) || this->IsParent(pTrack) || this->IsV0(pTrack))
+        if (this->IsDaughter(pTrack_id) || this->IsParent(pTrack_id) || this->IsV0(pTrack_id))
+            return true;
+    }
+
+    return false;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode TrackCreator::CreateTracks(const CollectionMaps& collectionMaps)
+{
+    std::cout<<"start TrackCreator::CreateTracks:"<<std::endl;
+    for (StringVector::const_iterator iter = m_settings.m_trackCollections.begin(), iterEnd = m_settings.m_trackCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_Track.find(*iter) == collectionMaps.collectionMap_Track.end()) { std::cout<<"not find "<<(*iter)<<std::endl; continue;}
+        try
+        {
+            const std::vector<edm4hep::Track>& pTrackCollection = (collectionMaps.collectionMap_Track.find(*iter))->second;
+            std::cout<<(*iter)<<" size="<<pTrackCollection.size()<<std::endl;
+
+            for (int i = 0, iMax = pTrackCollection.size(); i < iMax; ++i)
+            {
+                try
+                {
+                    const edm4hep::Track& pTrack0 = pTrackCollection.at(i);
+                    const edm4hep::Track* pTrack  = (const edm4hep::Track*)(&pTrack0);
+
+                    if (NULL == pTrack) throw ("Collection type mismatch");
+
+                    int minTrackHits = m_settings.m_minTrackHits;
+                    //const float tanLambda(std::fabs(pTrack->getTanLambda()));
+                    //std::cout<<"track states size="<<pTrack->trackStates_size()<<std::endl;
+                    const float tanLambda(std::fabs(pTrack->getTrackStates(0).tanLambda));
+
+                    if (tanLambda > m_tanLambdaFtd)
+                    {
+                        int expectedFtdHits(0);
+
+                        for (unsigned int iFtdLayer = 0; iFtdLayer < m_nFtdLayers; ++iFtdLayer)
+                        {
+                            if ((tanLambda > m_ftdZPositions[iFtdLayer] / m_ftdOuterRadii[iFtdLayer]) &&
+                                (tanLambda < m_ftdZPositions[iFtdLayer] / m_ftdInnerRadii[iFtdLayer]))
+                            {
+                                expectedFtdHits++;
+                            }
+                        }
+
+                        minTrackHits = std::max(m_settings.m_minFtdTrackHits, expectedFtdHits);
+                    }
+
+                    const int nTrackHits(static_cast<int>(pTrack->trackerHits_size()));
+
+                    if ((nTrackHits < minTrackHits) || (nTrackHits > m_settings.m_maxTrackHits))
+                        continue;
+
+                    // Proceed to create the pandora track
+                    PandoraApi::Track::Parameters trackParameters;
+                    //trackParameters.m_d0 = pTrack->getD0();
+                    trackParameters.m_d0 = pTrack->getTrackStates(0).D0;
+                    //trackParameters.m_z0 = pTrack->getZ0();
+                    trackParameters.m_z0 = pTrack->getTrackStates(0).Z0;
+                    trackParameters.m_pParentAddress = pTrack;
+                    // By default, assume tracks are charged pions
+                    const float signedCurvature(pTrack->getTrackStates(0).omega);
+                    trackParameters.m_particleId = (signedCurvature > 0) ? pandora::PI_PLUS : pandora::PI_MINUS;
+                    trackParameters.m_mass = pandora::PdgTable::GetParticleMass(pandora::PI_PLUS);
+
+                    // Use particle id information from V0 and Kink finders
+                    TrackToPidMap::const_iterator iter_t = m_trackToPidMap.find(*pTrack);
+
+                    if(iter_t != m_trackToPidMap.end())
+                    {
+                        trackParameters.m_particleId = (*iter_t).second;
+                        trackParameters.m_mass = pandora::PdgTable::GetParticleMass((*iter_t).second);
+                    }
+
+                    if (std::numeric_limits<float>::epsilon() < std::fabs(signedCurvature))
+                        trackParameters.m_charge = static_cast<int>(signedCurvature / std::fabs(signedCurvature));
+
+                    this->GetTrackStates(pTrack, trackParameters);
+                    this->TrackReachesECAL(pTrack, trackParameters);
+                    this->DefineTrackPfoUsage(pTrack, trackParameters);
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::Track::Create(*m_pPandora, trackParameters));
+                    m_trackVector.push_back(pTrack);
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout<<"ERROR Failed to extract a track: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout << "WARNNING Failed to extract a track "<< std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout<<"WARNING Failed to extract track collection: " << *iter << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void TrackCreator::GetTrackStates(const edm4hep::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const
+{
+    //const TrackState *pTrackState = pTrack->getTrackState(TrackState::AtIP);
+    edm4hep::TrackState pTrackState = pTrack->getTrackStates(1); // ref  /cvmfs/cepcsw.ihep.ac.cn/prototype/LCIO/include/EVENT/TrackState.h 
+
+    //if (!pTrackState)  throw pandora::StatusCodeException(pandora::STATUS_CODE_NOT_INITIALIZED);
+
+    //const double pt(m_bField * 2.99792e-4 / std::fabs(pTrackState->getOmega()));
+    const double pt(m_bField * 2.99792e-4 / std::fabs(pTrackState.omega));
+    //trackParameters.m_momentumAtDca = pandora::CartesianVector(std::cos(pTrackState->getPhi()), std::sin(pTrackState->getPhi()), pTrackState->getTanLambda()) * pt;
+    trackParameters.m_momentumAtDca = pandora::CartesianVector(std::cos(pTrackState.phi), std::sin(pTrackState.phi), pTrackState.tanLambda) * pt;
+    //this->CopyTrackState(pTrack->getTrackState(TrackState::AtFirstHit), trackParameters.m_trackStateAtStart);
+    this->CopyTrackState(pTrack->getTrackStates(2), trackParameters.m_trackStateAtStart);
+
+    //fg: curling TPC tracks have pointers to track segments stored -> need to get track states from last segment!
+    //const EVENT::Track *pEndTrack = (pTrack->getTracks().empty() ?  pTrack  :  pTrack->getTracks().back());
+    auto pEndTrack = (pTrack->tracks_size() ==0 ) ?  *pTrack  :  pTrack->getTracks(pTrack->tracks_size()-1);
+
+    //std::cout<<"GetTrackStates 1.6"<<", end track trackStates_size()="<<pEndTrack.trackStates_size()<<std::endl;
+    
+    //this->CopyTrackState(pEndTrack->getTrackState(TrackState::AtLastHit), trackParameters.m_trackStateAtEnd);
+    //this->CopyTrackState(pEndTrack->getTrackState(TrackState::AtCalorimeter), trackParameters.m_trackStateAtCalorimeter);
+    
+    this->CopyTrackState(pEndTrack.getTrackStates(3), trackParameters.m_trackStateAtEnd);
+    //this->CopyTrackState(pEndTrack.getTrackStates(4), trackParameters.m_trackStateAtCalorimeter);
+    //FIXME ? LCIO input only has 4 states, so 4 can't be used.
+    this->CopyTrackState(pEndTrack.getTrackStates(3), trackParameters.m_trackStateAtCalorimeter);
+    
+    
+    trackParameters.m_isProjectedToEndCap = ((std::fabs(trackParameters.m_trackStateAtCalorimeter.Get().GetPosition().GetZ()) < m_eCalEndCapInnerZ) ? false : true);
+
+    // Convert generic time (length from reference point to intersection, divided by momentum) into nanoseconds
+    const float minGenericTime(this->CalculateTrackTimeAtCalorimeter(pTrack));
+    const float particleMass(trackParameters.m_mass.Get());
+    const float particleEnergy(std::sqrt(particleMass * particleMass + trackParameters.m_momentumAtDca.Get().GetMagnitudeSquared()));
+    trackParameters.m_timeAtCalorimeter = minGenericTime * particleEnergy / 299.792f;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+float TrackCreator::CalculateTrackTimeAtCalorimeter(const edm4hep::Track *const pTrack) const
+{
+    //const pandora::Helix helix(pTrack->getPhi(), pTrack->getD0(), pTrack->getZ0(), pTrack->getOmega(), pTrack->getTanLambda(), m_bField);
+    const pandora::Helix helix(pTrack->getTrackStates(0).phi, pTrack->getTrackStates(0).D0, pTrack->getTrackStates(0).Z0, pTrack->getTrackStates(0).omega, pTrack->getTrackStates(0).tanLambda, m_bField);
+    const pandora::CartesianVector &referencePoint(helix.GetReferencePoint());
+
+    // First project to endcap
+    float minGenericTime(std::numeric_limits<float>::max());
+
+    pandora::CartesianVector bestECalProjection(0.f, 0.f, 0.f);
+    const int signPz((helix.GetMomentum().GetZ() > 0.f) ? 1 : -1);
+    (void) helix.GetPointInZ(static_cast<float>(signPz) * m_eCalEndCapInnerZ, referencePoint, bestECalProjection, minGenericTime);
+
+    // Then project to barrel surface(s)
+    pandora::CartesianVector barrelProjection(0.f, 0.f, 0.f);
+    if (m_eCalBarrelInnerSymmetry > 0)
+    {
+        // Polygon
+        float twopi_n = 2. * M_PI / (static_cast<float>(m_eCalBarrelInnerSymmetry));
+
+        for (int i = 0; i < m_eCalBarrelInnerSymmetry; ++i)
+        {
+            float genericTime(std::numeric_limits<float>::max());
+            const float phi(twopi_n * static_cast<float>(i) + m_eCalBarrelInnerPhi0);
+
+            const pandora::StatusCode statusCode(helix.GetPointInXY(m_eCalBarrelInnerR * std::cos(phi), m_eCalBarrelInnerR * std::sin(phi),
+                std::cos(phi + 0.5 * M_PI), std::sin(phi + 0.5 * M_PI), referencePoint, barrelProjection, genericTime));
+
+            if ((pandora::STATUS_CODE_SUCCESS == statusCode) && (genericTime < minGenericTime))
+            {
+                minGenericTime = genericTime;
+                bestECalProjection = barrelProjection;
+            }
+        }
+    }
+    else
+    {
+        // Cylinder
+        float genericTime(std::numeric_limits<float>::max());
+        const pandora::StatusCode statusCode(helix.GetPointOnCircle(m_eCalBarrelInnerR, referencePoint, barrelProjection, genericTime));
+
+        if ((pandora::STATUS_CODE_SUCCESS == statusCode) && (genericTime < minGenericTime))
+        {
+            minGenericTime = genericTime;
+            bestECalProjection = barrelProjection;
+        }
+    }
+
+    if (bestECalProjection.GetMagnitudeSquared() < std::numeric_limits<float>::epsilon())
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_NOT_INITIALIZED);
+
+    return minGenericTime;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void TrackCreator::CopyTrackState(const edm4hep::TrackState & pTrackState, pandora::InputTrackState &inputTrackState) const
+{
+    //if (!pTrackState)  throw pandora::StatusCodeException(pandora::STATUS_CODE_NOT_INITIALIZED);
+    //const double pt(m_bField * 2.99792e-4 / std::fabs(pTrackState->getOmega()));
+    const double pt(m_bField * 2.99792e-4 / std::fabs(pTrackState.omega));
+
+    //const double px(pt * std::cos(pTrackState->getPhi()));
+    const double px(pt * std::cos(pTrackState.phi));
+    //const double py(pt * std::sin(pTrackState->getPhi()));
+    const double py(pt * std::sin(pTrackState.phi));
+    //const double pz(pt * pTrackState->getTanLambda());
+    const double pz(pt * pTrackState.tanLambda);
+
+    const double xs(pTrackState.referencePoint[0]);
+    const double ys(pTrackState.referencePoint[1]);
+    const double zs(pTrackState.referencePoint[2]);
+
+    inputTrackState = pandora::TrackState(xs, ys, zs, px, py, pz);
+}
+
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void TrackCreator::TrackReachesECAL(const edm4hep::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const
+{
+    
+    // Calculate hit position information
+    float hitZMin(std::numeric_limits<float>::max());
+    float hitZMax(-std::numeric_limits<float>::max());
+    float hitOuterR(-std::numeric_limits<float>::max());
+
+    int maxOccupiedFtdLayer=0;
+    
+    
+    const unsigned int nTrackHits(pTrack->trackerHits_size());
+    for (unsigned int i = 0; i < nTrackHits; ++i)
+    {
+        
+        const edm4hep::ConstTrackerHit Hit ( pTrack->getTrackerHits(i) );
+        const edm4hep::Vector3d pos = Hit.getPosition();
+        
+        float x = float(pos[0]);
+        float y = float(pos[1]);
+        float z = float(pos[2]);
+        float r = std::sqrt(x * x + y * y);
+
+        if (z > hitZMax) hitZMax = z;
+
+        if (z < hitZMin) hitZMin = z;
+
+        if (r > hitOuterR) hitOuterR = r;
+
+        if ((r > m_tpcInnerR) && (r < m_tpcOuterR) && (std::fabs(z) <= m_tpcZmax))  continue;
+
+        for (unsigned int j = 0; j < m_nFtdLayers; ++j)
+        {
+            if ((r > m_ftdInnerRadii[j]) && (r < m_ftdOuterRadii[j]) &&
+                (std::fabs(z) - m_settings.m_reachesECalFtdZMaxDistance < m_ftdZPositions[j]) &&
+                (std::fabs(z) + m_settings.m_reachesECalFtdZMaxDistance > m_ftdZPositions[j]))
+            {
+                //if (static_cast<int>(j) > maxOccupiedFtdLayer) maxOccupiedFtdLayer = static_cast<int>(j);
+                if ( j > maxOccupiedFtdLayer) maxOccupiedFtdLayer = j;
+                break;
+            }
+        }
+    }
+    const int nTpcHits(this->GetNTpcHits(pTrack));
+    const int nFtdHits(this->GetNFtdHits(pTrack));
+
+    // Look to see if there are hits in etd or set, implying track has reached edge of ecal
+    if ((hitOuterR > m_minSetRadius) || (hitZMax > m_minEtdZPosition))
+    {
+        trackParameters.m_reachesCalorimeter = true;
+        return;
+    }
+
+    // Require sufficient hits in tpc or ftd, then compare extremal hit positions with tracker dimensions
+    if ((nTpcHits >= m_settings.m_reachesECalNTpcHits) || (nFtdHits >= m_settings.m_reachesECalNFtdHits))
+    {
+        if ((hitOuterR - m_tpcOuterR > m_settings.m_reachesECalTpcOuterDistance) ||
+            (std::fabs(hitZMax) - m_tpcZmax > m_settings.m_reachesECalTpcZMaxDistance) ||
+            (std::fabs(hitZMin) - m_tpcZmax > m_settings.m_reachesECalTpcZMaxDistance) ||
+            (maxOccupiedFtdLayer >= m_settings.m_reachesECalMinFtdLayer))
+        {
+            trackParameters.m_reachesCalorimeter = true;
+            return;
+        }
+    }
+
+    // If track is lowpt, it may curl up and end inside tpc inner radius
+    const pandora::CartesianVector &momentumAtDca(trackParameters.m_momentumAtDca.Get());
+    const float cosAngleAtDca(std::fabs(momentumAtDca.GetZ()) / momentumAtDca.GetMagnitude());
+    const float pX(momentumAtDca.GetX()), pY(momentumAtDca.GetY());
+    const float pT(std::sqrt(pX * pX + pY * pY));
+
+    if ((cosAngleAtDca > m_cosTpc) || (pT < m_settings.m_curvatureToMomentumFactor * m_bField * m_tpcOuterR))
+    {
+        trackParameters.m_reachesCalorimeter = true;
+        return;
+    }
+    
+    trackParameters.m_reachesCalorimeter = false;
+    
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void TrackCreator::DefineTrackPfoUsage(const edm4hep::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const
+{
+    bool canFormPfo(false);
+    bool canFormClusterlessPfo(false);
+
+    //if (trackParameters.m_reachesCalorimeter.Get() && !this->IsParent(pTrack))
+    if (trackParameters.m_reachesCalorimeter.Get() && !this->IsParent(pTrack->id()))
+    {
+        //const float d0(std::fabs(pTrack->getD0())), z0(std::fabs(pTrack->getZ0()));
+        const float d0(std::fabs(pTrack->getTrackStates(0).D0)), z0(std::fabs(pTrack->getTrackStates(0).Z0));
+
+        //EVENT::TrackerHitVec trackerHitvec(pTrack->getTrackerHits());
+        float rInner(std::numeric_limits<float>::max()), zMin(std::numeric_limits<float>::max());
+
+        //for (EVENT::TrackerHitVec::const_iterator iter = trackerHitvec.begin(), iterEnd = trackerHitvec.end(); iter != iterEnd; ++iter)
+        for (std::vector<edm4hep::ConstTrackerHit>::const_iterator iter = pTrack->trackerHits_begin(), iterEnd = pTrack->trackerHits_end(); iter != iterEnd; ++iter)
+        {
+            //const double *pPosition((*iter)->getPosition());
+            const edm4hep::Vector3d pPosition = (*iter).getPosition(); 
+            const float x(pPosition[0]), y(pPosition[1]), absoluteZ(std::fabs(pPosition[2]));
+            const float r(std::sqrt(x * x + y * y));
+
+            if (r < rInner)
+                rInner = r;
+
+            if (absoluteZ < zMin)
+                zMin = absoluteZ;
+        }
+
+        if (this->PassesQualityCuts(pTrack, trackParameters))
+        {
+            const pandora::CartesianVector &momentumAtDca(trackParameters.m_momentumAtDca.Get());
+            const float pX(momentumAtDca.GetX()), pY(momentumAtDca.GetY()), pZ(momentumAtDca.GetZ());
+            const float pT(std::sqrt(pX * pX + pY * pY));
+
+            const float zCutForNonVertexTracks(m_tpcInnerR * std::fabs(pZ / pT) + m_settings.m_zCutForNonVertexTracks);
+            const bool passRzQualityCuts((zMin < zCutForNonVertexTracks) && (rInner < m_tpcInnerR + m_settings.m_maxTpcInnerRDistance));
+
+            const bool isV0(this->IsV0(pTrack->id()));
+            const bool isDaughter(this->IsDaughter(pTrack->id()));
+
+            // Decide whether track can be associated with a pandora cluster and used to form a charged PFO
+            if ((d0 < m_settings.m_d0TrackCut) && (z0 < m_settings.m_z0TrackCut) && (rInner < m_tpcInnerR + m_settings.m_maxTpcInnerRDistance))
+            {
+                canFormPfo = true;
+            }
+            else if (passRzQualityCuts && (0 != m_settings.m_usingNonVertexTracks))
+            {
+                canFormPfo = true;
+            }
+            else if (isV0 || isDaughter)
+            {
+                canFormPfo = true;
+            }
+
+            // Decide whether track can be used to form a charged PFO, even if track fails to be associated with a pandora cluster
+            const float particleMass(trackParameters.m_mass.Get());
+            const float trackEnergy(std::sqrt(momentumAtDca.GetMagnitudeSquared() + particleMass * particleMass));
+
+            if ((0 != m_settings.m_usingUnmatchedVertexTracks) && (trackEnergy < m_settings.m_unmatchedVertexTrackMaxEnergy))
+            {
+                if ((d0 < m_settings.m_d0UnmatchedVertexTrackCut) && (z0 < m_settings.m_z0UnmatchedVertexTrackCut) &&
+                    (rInner < m_tpcInnerR + m_settings.m_maxTpcInnerRDistance))
+                {
+                    canFormClusterlessPfo = true;
+                }
+                else if (passRzQualityCuts && (0 != m_settings.m_usingNonVertexTracks) && (0 != m_settings.m_usingUnmatchedNonVertexTracks))
+                {
+                    canFormClusterlessPfo = true;
+                }
+                else if (isV0 || isDaughter)
+                {
+                    canFormClusterlessPfo = true;
+                }
+            }
+        }
+        else if (this->IsDaughter(pTrack->id()) || this->IsV0(pTrack->id()))
+        {
+            std::cout<<"WARNING Recovering daughter or v0 track " << trackParameters.m_momentumAtDca.Get().GetMagnitude() << std::endl;
+            canFormPfo = true;
+        }
+    }
+
+    trackParameters.m_canFormPfo = canFormPfo;
+    trackParameters.m_canFormClusterlessPfo = canFormClusterlessPfo;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+bool TrackCreator::PassesQualityCuts(const edm4hep::Track *const pTrack, const PandoraApi::Track::Parameters &trackParameters) const
+{
+    // First simple sanity checks
+    if (trackParameters.m_trackStateAtCalorimeter.Get().GetPosition().GetMagnitude() < m_settings.m_minTrackECalDistanceFromIp)
+        return false;
+
+    //if (std::fabs(pTrack->getOmega()) < std::numeric_limits<float>::epsilon())
+    if (std::fabs(pTrack->getTrackStates(0).omega) < std::numeric_limits<float>::epsilon())
+    {
+        std::cout<<"ERROR Track has Omega = 0 " << std::endl;
+        return false;
+    }
+
+    // Check momentum uncertainty is reasonable to use track
+    const pandora::CartesianVector &momentumAtDca(trackParameters.m_momentumAtDca.Get());
+    //const float sigmaPOverP(std::sqrt(pTrack->getCovMatrix()[5]) / std::fabs(pTrack->getOmega()));
+    const float sigmaPOverP(std::sqrt(pTrack->getTrackStates(0).covMatrix[5]) / std::fabs(pTrack->getTrackStates(0).omega));
+
+    if (sigmaPOverP > m_settings.m_maxTrackSigmaPOverP)
+    {
+        std::cout<<"WARNING Dropping track : " << momentumAtDca.GetMagnitude() << "+-" << sigmaPOverP * (momentumAtDca.GetMagnitude())
+                               << " chi2 = " <<  pTrack->getChi2() << " " << pTrack->getNdf()
+                               << " from " << pTrack->trackerHits_size() << std::endl;
+        return false;
+    }
+
+    // Require reasonable number of TPC hits 
+    if (momentumAtDca.GetMagnitude() > m_settings.m_minMomentumForTrackHitChecks)
+    {
+        const float pX(fabs(momentumAtDca.GetX()));
+        const float pY(fabs(momentumAtDca.GetY()));
+        const float pZ(fabs(momentumAtDca.GetZ()));
+        const float pT(std::sqrt(pX * pX + pY * pY));
+        const float rInnermostHit(pTrack->getRadiusOfInnermostHit());
+
+        if ((std::numeric_limits<float>::epsilon() > std::fabs(pT)) || (std::numeric_limits<float>::epsilon() > std::fabs(pZ)) || (rInnermostHit == m_tpcOuterR))
+        {
+            std::cout<<"ERROR Invalid track parameter, pT " << pT << ", pZ " << pZ << ", rInnermostHit " << rInnermostHit << std::endl;
+            return false;
+        }
+
+        float nExpectedTpcHits(0.);
+
+        if (pZ < m_tpcZmax / m_tpcOuterR * pT)
+        {
+            const float innerExpectedHitRadius(std::max(m_tpcInnerR, rInnermostHit));
+            const float frac((m_tpcOuterR - innerExpectedHitRadius) / (m_tpcOuterR - m_tpcInnerR));
+            nExpectedTpcHits = m_tpcMaxRow * frac;
+        }
+
+        if ((pZ <= m_tpcZmax / m_tpcInnerR * pT) && (pZ >= m_tpcZmax / m_tpcOuterR * pT))
+        {
+            const float innerExpectedHitRadius(std::max(m_tpcInnerR, rInnermostHit));
+            const float frac((m_tpcZmax * pT / pZ - innerExpectedHitRadius) / (m_tpcOuterR - innerExpectedHitRadius));
+            nExpectedTpcHits = frac * m_tpcMaxRow;
+        }
+
+        // TODO Get TPC membrane information from GEAR when available
+        if (std::fabs(pZ) / momentumAtDca.GetMagnitude() < m_settings.m_tpcMembraneMaxZ / m_tpcInnerR)
+            nExpectedTpcHits = 0;
+
+        const int nTpcHits(this->GetNTpcHits(pTrack));
+        const int nFtdHits(this->GetNFtdHits(pTrack));
+
+        const int minTpcHits = static_cast<int>(nExpectedTpcHits * m_settings.m_minTpcHitFractionOfExpected);
+
+        if ((nTpcHits < minTpcHits) && (nFtdHits < m_settings.m_minFtdHitsForTpcHitFraction))
+        {
+            std::cout<<"WARNING Dropping track : " << momentumAtDca.GetMagnitude() << " Number of TPC hits = " << nTpcHits
+                                   << " < " << minTpcHits << " nftd = " << nFtdHits  << std::endl;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+int TrackCreator::GetNTpcHits(const edm4hep::Track *const pTrack) const
+{
+    // ATTN
+    //fg: hit numbers are now given in different order wrt LOI:  
+    // trk->subdetectorHitNumbers()[ 2 * ILDDetID::TPC - 1 ] =  hitsInFit ;  
+    // trk->subdetectorHitNumbers()[ 2 * ILDDetID::TPC - 2 ] =  hitCount ;  
+    // ---- use hitsInFit :
+    //return pTrack->getSubdetectorHitNumbers()[ 2 * lcio::ILDDetID::TPC - 1 ];
+    return pTrack->getSubDetectorHitNumbers(2 * lcio::ILDDetID::TPC - 1);// still use LCIO code now
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+int TrackCreator::GetNFtdHits(const edm4hep::Track *const pTrack) const
+{
+    // ATTN
+    //fg: hit numbers are now given in different order wrt LOI:  
+    // trk->subdetectorHitNumbers()[ 2 * ILDDetID::TPC - 1 ] =  hitsInFit ;  
+    // trk->subdetectorHitNumbers()[ 2 * ILDDetID::TPC - 2 ] =  hitCount ;  
+    // ---- use hitsInFit :
+    //return pTrack->getSubdetectorHitNumbers()[ 2 * lcio::ILDDetID::FTD - 1 ];
+    return pTrack->getSubDetectorHitNumbers( 2 * lcio::ILDDetID::FTD - 1 );
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+TrackCreator::Settings::Settings() :
+    m_shouldFormTrackRelationships(1),
+    m_minTrackHits(5),
+    m_minFtdTrackHits(0),
+    m_maxTrackHits(5000.f),
+    m_d0TrackCut(50.f),
+    m_z0TrackCut(50.f),
+    m_usingNonVertexTracks(1),
+    m_usingUnmatchedNonVertexTracks(0),
+    m_usingUnmatchedVertexTracks(1),
+    m_unmatchedVertexTrackMaxEnergy(5.f),
+    m_d0UnmatchedVertexTrackCut(5.f),
+    m_z0UnmatchedVertexTrackCut(5.f),
+    m_zCutForNonVertexTracks(250.f),
+    m_reachesECalNTpcHits(11),
+    m_reachesECalNFtdHits(4),
+    m_reachesECalTpcOuterDistance(-100.f),
+    m_reachesECalMinFtdLayer(9),
+    m_reachesECalTpcZMaxDistance(-50.f),
+    m_reachesECalFtdZMaxDistance(1.f),
+    m_curvatureToMomentumFactor(0.3f / 2000.f),
+    m_minTrackECalDistanceFromIp(100.f),
+    m_maxTrackSigmaPOverP(0.15f),
+    m_minMomentumForTrackHitChecks(1.f),
+    m_tpcMembraneMaxZ(10.f),
+    m_maxTpcInnerRDistance(50.f),
+    m_minTpcHitFractionOfExpected(0.2f),
+    m_minFtdHitsForTpcHitFraction(2)
+{
+}
diff --git a/Reconstruction/PFA/Pandora/GaudiPandora/src/Utility.cpp b/Reconstruction/PFA/Pandora/GaudiPandora/src/Utility.cpp
new file mode 100644
index 00000000..819fa15d
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/GaudiPandora/src/Utility.cpp
@@ -0,0 +1,7 @@
+#include "Utility.h"
+
+std::string Convert (float number){
+    std::ostringstream buff;
+    buff<<number;
+    return buff.str();   
+}
diff --git a/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/ClusterShapes.cc b/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/ClusterShapes.cc
new file mode 100644
index 00000000..e7e8d4fc
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/ClusterShapes.cc
@@ -0,0 +1,2005 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+#include "ClusterShapes.h"
+
+#include <gsl/gsl_vector.h>
+#include <gsl/gsl_matrix.h>
+//#include <gsl/gsl_blas.h>
+#include <gsl/gsl_linalg.h>
+#include <gsl/gsl_eigen.h>
+#include <gsl/gsl_multifit_nlin.h>
+#include <gsl/gsl_sf_gamma.h>
+#include <gsl/gsl_integration.h>
+//#include <gsl/gsl_rng.h>
+//#include <gsl/gsl_sf_pow_int.h>
+
+
+
+// #################################################
+// #####                                       #####
+// #####  Additional Structures and Functions  #####
+// #####                                       #####
+// #################################################
+//=============================================================================
+
+struct data {
+  int n;
+  float* x;
+  float* y;
+  float* z;
+  float* ex;
+  float* ey;
+  float* ez;
+};
+
+//=============================================================================
+
+// Gammafunction
+double G(double x) {
+
+  return gsl_sf_gamma(x);
+  
+}
+
+//=============================================================================
+
+// inverse Gammafunction
+double invG(double x) {
+  
+ return gsl_sf_gammainv(x);
+    
+}
+
+//=============================================================================
+
+// Integral needed for deriving the derivative of the Gammafunction
+double Integral_G(double x, void* params) {
+  double a = *(double*)params;
+  double f = exp(-x) * pow(x,a-1) * log(x);
+  return f;
+}
+    
+//=============================================================================
+
+double DinvG(double x) {
+
+  int workspace_size = 1000;
+  double abs_error = 0;
+  double rel_error = 1e-6;
+  double result = 0.0;
+  double error = 0.0;
+  int status = 0;
+  
+  gsl_integration_workspace* w  = gsl_integration_workspace_alloc(workspace_size);
+  gsl_function F;
+  F.function = &Integral_G;
+  F.params = &x;
+    
+  status = gsl_integration_qagiu(&F,0,abs_error,rel_error,workspace_size,w,
+      			   &result,&error); 
+
+  // debug  
+  /*  
+  printf ("Numeric Integration : \n");
+  printf ("parameter of integration = % .18f\n", x);
+  printf ("status of integration    = %d \n"   , status);
+  printf ("result                   = % .18f\n", result);
+  printf ("estimated error          = % .18f\n", error);
+  printf ("intervals                =  %d\n\n", w->size);
+  */
+  
+  double G2 = pow(gsl_sf_gamma(x),2); 
+  double DG = result;
+  
+  gsl_integration_workspace_free(w);
+  
+  return -DG/G2;
+
+}
+
+//=============================================================================
+
+int ShapeFitFunct(const gsl_vector* par, void* d, gsl_vector* f) {
+
+  // Used for shape fitting. Function to fit: 
+  //
+  // a[i](t[i],s[i]) =
+  // 
+  // E0 * b * 1/Gamma(a) * ( b * (t[i] - t0) )^(a-1) * exp(-b*(t[i] - t0)) * exp(-d*s[i])
+  //
+  // Function to minimise:
+  //
+  // f0[i] =  E0 * b * 1/Gamma(a) * 
+  //        ( b * (t[i] - t0) )^(a-1) * exp(-b*(t[i] - t0)) * exp(-d*s[i]) - a[i]
+  //
+
+
+  //  float E0   = gsl_vector_get(par,0);
+  float A    = gsl_vector_get(par,0);
+  float B    = gsl_vector_get(par,1);
+  float D    = gsl_vector_get(par,2);
+  float t0   = gsl_vector_get(par,3);
+  int n      = ((struct data*)d)->n;
+  float* t   = ((struct data*)d)->x;
+  float* s   = ((struct data*)d)->y;
+  float* a   = ((struct data*)d)->z; // amplitude stored in z[i]
+  float fi   = 0.0;
+
+
+  for (int i(0); i < n; i++) {
+    fi = /*E0 * */ B * invG(A) * pow(B*(t[i]-t0),A-1) * exp(-B*(t[i]-t0)) * exp(-D*s[i]) 
+         - a[i];
+    gsl_vector_set(f,i,fi);
+  }
+
+  return GSL_SUCCESS;
+}
+
+//=============================================================================
+
+int dShapeFitFunct(const gsl_vector* par, void* d, gsl_matrix* J) {
+
+  // Used for shape fitting
+  //float E0  = gsl_vector_get(par,0);
+  float A   = gsl_vector_get(par,0);
+  float B   = gsl_vector_get(par,1);
+  float D   = gsl_vector_get(par,2);
+  float t0  = gsl_vector_get(par,3);
+  int n     = ((struct data*)d)->n;
+  float* t  = ((struct data*)d)->x;
+  float* s  = ((struct data*)d)->y;
+
+
+  // calculate Jacobi's matrix J[i][j] = dfi/dparj, but here only one dimension
+
+  for (int i(0); i < n; i++) {
+
+    /*    
+    gsl_matrix_set(J,i,0,B * invG(A) * pow(B*(t[i]-t0),A-1) * exp(-B*(t[i]-t0)) 
+		                                            * exp(-D*s[i]) );
+    */
+
+    gsl_matrix_set(J,i,0,( /* E0 * */ B * invG(A) * log(B*(t[i]-t0))*pow(B*(t[i]-t0),A-1) * 
+			   exp(-B*(t[i]-t0)) + DinvG(A) * /* E0 * */ B * pow(B*(t[i]-t0),A-1) *
+			                       exp(-B*(t[i]-t0))
+			 ) * exp(-D*s[i]));
+		         
+    gsl_matrix_set(J,i,1,( /* E0 * */ invG(A) * pow(B*(t[i]-t0),A-1) * exp(-B*(t[i]-t0)) +
+			   /* E0 * */ invG(A) * (A-1) * B * (t[i]-t0) * pow(B*(t[i]-t0),A-2) * 
+			                                exp(-B*(t[i]-t0)) -
+			   /* E0 * */ B * invG(A) * (t[i]-t0) * pow(B*(t[i]-t0),A-1) * 
+			                                exp(-B*(t[i]-t0)) 
+			 ) * exp(-D*s[i]));
+                         
+    gsl_matrix_set(J,i,2,-/* E0 * */ B * invG(A) * s[i] * pow(B*(t[i]-t0),A-1) *
+		         exp(-B*(t[i]-t0)) * exp(-D*s[i]));
+       		         
+    gsl_matrix_set(J,i,3,(-/* E0 * */ pow(B,2) * invG(A) * (A-1) * pow(B*(t[i]-t0),A-2) * 
+			                                     exp(-B*(t[i]-t0)) +
+			   /* E0 * */ pow(B,2) * invG(A) * pow(B*(t[i]-t0),A-1) * 
+			                             exp(-B*(t[i]-t0))
+			 ) * exp(-D*s[i]));
+ 		         
+
+  }
+  
+  return GSL_SUCCESS;
+}
+
+//=============================================================================
+
+int fdfShapeFitFunct(const gsl_vector* par, void* d, gsl_vector* f, gsl_matrix* J) {
+
+  //     For helix fitting
+  ShapeFitFunct(par, d, f);
+  dShapeFitFunct(par, d, J);
+
+  return GSL_SUCCESS;
+
+}
+
+//=============================================================================
+
+int signum(float x) {
+
+  // computes the signum of x. Needed for the 3. parametrisation
+
+  if ( x >= 0 ) return 1; // x == 0 is taken as positive
+  else return -1;
+
+}
+
+//=============================================================================
+
+int functParametrisation1(const gsl_vector* par, void* d, gsl_vector* f) {
+
+  //     For helix fitting
+  // calculate fit function f0[i] = 
+  // ( (x0 + R*cos(b*z[i] + phi0)) - x[i] ) for i = 0 to n-1
+  //                    and f1[i] = 
+  // ( (y0 + R*sin(b*z[i] + phi0)) - y[i] ) for i = n to dim*n - 1
+  // That means, minimise the two functions f0[i] and f1[i]
+
+  float x0   = gsl_vector_get(par,0);
+  float y0   = gsl_vector_get(par,1);
+  float R    = gsl_vector_get(par,2);
+  float b    = gsl_vector_get(par,3);
+  float phi0 = gsl_vector_get(par,4);
+  int n    = ((struct data*)d)->n;
+  float* x = ((struct data*)d)->x;
+  float* y = ((struct data*)d)->y;
+  float* z = ((struct data*)d)->z;
+//float* ex = ((struct data*)d)->ex;
+//float* ey = ((struct data*)d)->ey;
+//float* ez = ((struct data*)d)->ez;
+  float fi = 0.0;
+
+  // first dimension
+  for (int i(0); i < n; i++) {
+    fi = (x0 + R*cos(b*z[i] + phi0)) - x[i];
+    //    float err = sqrt(ex[i]*ex[i]+R*b*R*b*sin(b*z[i] + phi0)*R*b*R*b*sin(b*z[i] + phi0)*ez[i]*ez[i]);
+    //    fi = fi/err;
+    gsl_vector_set(f,i,fi);
+  }
+  // second dimension
+  for (int i(0); i < n; i++) {
+    fi = (y0 + R*sin(b*z[i] + phi0)) - y[i];
+    //    float err = sqrt(ey[i]*ey[i]+R*b*R*b*cos(b*z[i] + phi0)*R*b*R*b*cos(b*z[i] + phi0)*ez[i]*ez[i]);
+    //    fi = fi/err;  
+    gsl_vector_set(f,i+n,fi);
+  }
+
+  return GSL_SUCCESS;
+}
+
+//=============================================================================
+
+int dfunctParametrisation1(const gsl_vector* par, void* d, gsl_matrix* J) {
+
+  //     For helix fitting
+  float R    = gsl_vector_get(par,2);
+  float b    = gsl_vector_get(par,3);
+  float phi0 = gsl_vector_get(par,4);
+
+  int n    = ((struct data*)d)->n;
+  float* z = ((struct data*)d)->z;
+//float* ex = ((struct data*)d)->ex;
+//float* ey = ((struct data*)d)->ey;
+//float* ez = ((struct data*)d)->ez;
+
+
+  // calculate Jacobi's matrix J[i][j] = dfi/dparj
+
+  // part of Jacobi's matrix corresponding to first dimension
+  for (int i(0); i < n; i++) {
+    //    float err = sqrt(ex[i]*ex[i]+R*b*R*b*sin(b*z[i] + phi0)*R*b*R*b*sin(b*z[i] + phi0)*ez[i]*ez[i]);
+    gsl_matrix_set(J,i,0,1);
+    gsl_matrix_set(J,i,1,0);
+    gsl_matrix_set(J,i,2,cos(b*z[i]+phi0));
+    gsl_matrix_set(J,i,3,-z[i]*R*sin(b*z[i]+phi0));
+    gsl_matrix_set(J,i,4,-R*sin(b*z[i]+phi0));
+
+  }
+
+  // part of Jacobi's matrix corresponding to second dimension
+  for (int i(0); i < n; i++) {
+    //    float err = sqrt(ey[i]*ey[i]+R*b*R*b*cos(b*z[i] + phi0)*R*b*R*b*cos(b*z[i] + phi0)*ez[i]*ez[i]);
+    gsl_matrix_set(J,i+n,0,0);
+    gsl_matrix_set(J,i+n,1,1);
+    gsl_matrix_set(J,i+n,2,sin(b*z[i]+phi0));
+    gsl_matrix_set(J,i+n,3,z[i]*R*cos(b*z[i]+phi0));
+    gsl_matrix_set(J,i+n,4,R*cos(b*z[i]+phi0));
+    
+  }
+  
+  return GSL_SUCCESS;
+}
+
+//=============================================================================
+
+int fdfParametrisation1(const gsl_vector* par, void* d, gsl_vector* f, gsl_matrix* J) {
+
+  //     For helix fitting
+  functParametrisation1(par, d, f);
+  dfunctParametrisation1(par, d, J);
+
+  return GSL_SUCCESS;
+
+}
+
+//=============================================================================
+
+int functParametrisation2(const gsl_vector* par, void* d, gsl_vector* f) {
+
+  //     For helix fitting
+  // calculate fit function f0[i] = 
+  // ( (x0 + R*cos(phi)) - x[i] ) for i = 0 to n-1
+  //                        f1[i] = 
+  // ( (y0 + R*sin(phi)) - y[i] ) for i = n to dim*n - 1
+  //                    and f2[i] =
+  // ( (z0 + b*phi     ) - z[i] )
+  // That means, minimise the three functions f0[i], f1[i] and f2[i]
+
+  float x0   = gsl_vector_get(par,0);
+  float y0   = gsl_vector_get(par,1);
+  float z0   = gsl_vector_get(par,2);
+  float R    = gsl_vector_get(par,3);
+  float b    = gsl_vector_get(par,4);
+  int n    = ((struct data*)d)->n;
+  float* x = ((struct data*)d)->x;
+  float* y = ((struct data*)d)->y;
+  float* z = ((struct data*)d)->z;
+  float fi = 0.0;
+  float phii = 0.0;
+
+  // first dimension
+  for (int i(0); i < n; i++) {
+    phii = atan2( y[i]-y0, x[i]-x0 );
+    fi = (x0 + R*cos(phii)) - x[i];
+    gsl_vector_set(f,i,fi);
+  }
+  // second dimension
+  for (int i(0); i < n; i++) {
+    phii = atan2( y[i]-y0, x[i]-x0 );
+    fi = (y0 + R*sin(phii)) - y[i];  
+    gsl_vector_set(f,i+n,fi);
+  }
+  // third dimension
+  for (int i(0); i < n; i++) {
+    phii = atan2( y[i]-y0, x[i]-x0 );
+    fi = (z0 + b*phii) - z[i];  
+    gsl_vector_set(f,i+2*n,fi);
+  }
+
+  return GSL_SUCCESS;
+}
+
+//=============================================================================
+
+int dfunctParametrisation2(const gsl_vector* par, void* d, gsl_matrix* J) {
+
+  //     For helix fitting
+  float x0   = gsl_vector_get(par,0);
+  float y0   = gsl_vector_get(par,1);
+  float R    = gsl_vector_get(par,3);
+  float b    = gsl_vector_get(par,4);
+  int n    = ((struct data*)d)->n;
+  float* x = ((struct data*)d)->x;
+  float* y = ((struct data*)d)->y;
+  float phii = 0.0;
+
+  // calculate Jacobi's matrix J[i][j] = dfi/dparj
+
+  // part of Jacobi's matrix corresponding to first dimension
+  for (int i(0); i < n; i++) {
+    
+    phii = atan2( y[i]-y0, x[i]-x0 );
+
+    gsl_matrix_set(J,i,0,1 - R*sin(phii)*
+		   ( (y[i]-y0)/( (x[i]-x0)*(x[i]-x0) + (y[i]-y0)*(y[i]-y0) ) ) );
+    gsl_matrix_set(J,i,1,R*sin(phii)*
+		   ( (x[i]-x0)/( (x[i]-x0)*(x[i]-x0) + (y[i]-y0)*(y[i]-y0) ) ) );
+    gsl_matrix_set(J,i,2,0);
+    gsl_matrix_set(J,i,3,cos(phii));
+    gsl_matrix_set(J,i,4,0);
+
+  }
+  
+  // part of Jacobi's matrix corresponding to second dimension
+  for (int i(0); i < n; i++) {
+
+    phii = atan2( y[i]-y0, x[i]-x0 );
+    
+    gsl_matrix_set(J,i+n,0,R*cos(phii)*
+		   ( (y[i]-y0)/( (x[i]-x0)*(x[i]-x0) + (y[i]-y0)*(y[i]-y0) ) ) );
+    gsl_matrix_set(J,i+n,1,1 + R*cos(phii)*
+		   ( (x[i]-x0)/( (x[i]-x0)*(x[i]-x0) + (y[i]-y0)*(y[i]-y0) ) ) );
+    gsl_matrix_set(J,i+n,2,0);
+    gsl_matrix_set(J,i+n,3,sin(phii));
+    gsl_matrix_set(J,i+n,4,0);
+    
+  }
+
+  // part of Jacobi's matrix corresponding to third dimension
+  for (int i(0); i < n; i++) {
+
+    phii = atan2( y[i]-y0, x[i]-x0 );
+    
+    gsl_matrix_set(J,i+2*n,0,b*
+		   ( (y[i]-y0)/( (x[i]-x0)*(x[i]-x0) + (y[i]-y0)*(y[i]-y0) ) ) );
+    gsl_matrix_set(J,i+2*n,1,b*
+		   ( (x[i]-x0)/( (x[i]-x0)*(x[i]-x0) + (y[i]-y0)*(y[i]-y0) ) ) );
+    gsl_matrix_set(J,i+2*n,2,1);
+    gsl_matrix_set(J,i+2*n,3,0);
+    gsl_matrix_set(J,i+2*n,4,phii);
+    
+  }
+  
+  return GSL_SUCCESS;
+}
+
+//=============================================================================
+
+int fdfParametrisation2(const gsl_vector* par, void* d, gsl_vector* f, gsl_matrix* J) {
+
+  //     For helix fitting
+  functParametrisation2(par, d, f);
+  dfunctParametrisation2(par, d, J);
+
+  return GSL_SUCCESS;
+
+}
+
+//=============================================================================
+
+int functParametrisation3(const gsl_vector* par, void* d, gsl_vector* f) {
+
+  //     For helix fitting
+  // calculate fit function f0[i] = 
+  // ( ( ( (1/omega) - d0 )*sin(Phi0) + ( 1/fabs(omega) )*cos( ( -omega/sqrt(1+tanL^2) )*s + Phi0 +( (omega*pi)/(2*fabs(omega)) ) ) ) - x[i] ) for i = 0 to n-1
+  //                        f1[i] = 
+  // ( ( (-1.0)*( (1/omega) - d0 )*cos(Phi0) + ( 1/fabs(omega) )*sin( ( -omega/sqrt(1+tanL^2) )*s + Phi0 +( (omega*pi)/(2*fabs(omega)) ) ) ) - y[i] ) for i = n to dim*n - 1
+  //                    and f2[i] =
+  // ( ( z0 + (tanL/sqrt(1+tanL^2))*s ) - z[i] )
+  // That means, minimise the three functions f0[i], f1[i] and f2[i]
+
+  double z0    = gsl_vector_get(par,0);
+  double Phi0  = gsl_vector_get(par,1);
+  double omega = gsl_vector_get(par,2);
+  double d0    = gsl_vector_get(par,3);
+  double tanL  = gsl_vector_get(par,4);
+  int n    = ((struct data*)d)->n;
+  float* x = ((struct data*)d)->x;
+  float* y = ((struct data*)d)->y;
+  float* z = ((struct data*)d)->z;
+  double phii = 0.0;
+  double fi = 0.0;
+  double si = 0.0;
+
+  // first dimension
+  for (int i(0); i < n; i++) {
+    phii = atan2( ( ((double)y[i]) + ((1/omega) - d0 )*cos(Phi0) ), ( ((double)x[i]) - ((1/omega) - d0 )*sin(Phi0) ) );
+    fi = ( ( (1/omega) - d0 )*sin(Phi0) + ( 1/fabs(omega) )*cos(phii) ) - ((double)x[i]);
+    gsl_vector_set(f,i,fi);
+  }
+  // second dimension
+  for (int i(0); i < n; i++) {
+    phii = atan2( ( ((double)y[i]) + ((1/omega) - d0 )*cos(Phi0) ), ( ((double)x[i]) - ((1/omega) - d0 )*sin(Phi0) ) );
+    fi = ( (-1.0)*( (1/omega) - d0 )*cos(Phi0) + ( 1/fabs(omega) )*sin(phii) ) - ((double)y[i]);
+    gsl_vector_set(f,i+n,fi);
+  }
+  // third dimension
+  for (int i(0); i < n; i++) {
+    phii = atan2( ( ((double)y[i]) + ((1/omega) - d0 )*cos(Phi0) ), ( ((double)x[i]) - ((1/omega) - d0 )*sin(Phi0) ) );
+    si = (-1.0)*( (sqrt(1+pow(tanL,2)))/omega )*(phii - Phi0 - (omega*M_PI)/(2*fabs(omega)));
+    fi = ( z0 + (tanL/sqrt(1+pow(tanL,2)))*si ) - ((double)z[i]);
+    gsl_vector_set(f,i+2*n,fi);
+  }
+  
+  return GSL_SUCCESS;
+
+}
+
+//=============================================================================
+
+int dfunctParametrisation3(const gsl_vector* par, void* d, gsl_matrix* J) {
+
+ 
+  //     For helix fitting
+  // double z0    = gsl_vector_get(par,0); // not needed
+  double Phi0  = gsl_vector_get(par,1);
+  double omega = gsl_vector_get(par,2);
+  double d0    = gsl_vector_get(par,3);
+  double tanL  = gsl_vector_get(par,4);
+  int n    = ((struct data*)d)->n;
+  float* x = ((struct data*)d)->x;
+  float* y = ((struct data*)d)->y;
+  // float* z = ((struct data*)d)->z; // not needed
+  double phii = 0.0;
+  double si = 0.0;
+
+  // calculate Jacobi's matrix J[i][j] = dfi/dparj
+
+  // part of Jacobi's matrix corresponding to first dimension
+  for (int i(0); i < n; i++) {
+    phii = atan2( ( ((double)y[i]) + ((1/omega) - d0 ) * cos(Phi0) ), ( ((double)x[i]) - ((1/omega) - d0 )*sin(Phi0) ) );
+    si = (-1.0)*( (sqrt(1+pow(tanL,2)))/omega )*(phii - Phi0 - (omega*M_PI)/(2*fabs(omega)));
+
+    gsl_matrix_set(J,i,0,0);
+    gsl_matrix_set(J,i,1,((1/omega) - d0) * cos(Phi0) - (1/fabs(omega)) * sin(phii) );
+    gsl_matrix_set(J,i,2,((-1.0)*sin(Phi0))/pow(omega,2) - ( (signum(omega))/(pow(fabs(omega),2)) ) * cos(phii) - 
+                   (1/fabs(omega)) * sin(phii) * ( ( ((-1.0)/sqrt(1+pow(tanL,2)))*si) + (M_PI)/(2*fabs(omega)) - (signum(omega)*omega*M_PI)/(2*pow(fabs(omega),2)) ) );
+    gsl_matrix_set(J,i,3,(-1.0)*sin(Phi0));
+    gsl_matrix_set(J,i,4,((-1.0)/fabs(omega))*sin(phii) * ( (omega*tanL*si)/sqrt(pow(1+pow(tanL,2),3)) ) );
+
+  }
+  
+  // part of Jacobi's matrix corresponding to second dimension
+  for (int i(0); i < n; i++) {
+    phii = atan2( ( ((double)y[i]) + ((1/omega) - d0 )*cos(Phi0) ), ( ((double)x[i]) - ((1/omega) - d0 )*sin(Phi0) ) );
+    si = (-1.0)*( (sqrt(1+pow(tanL,2)))/omega )*(phii - Phi0 - (omega*M_PI)/(2*fabs(omega)));
+
+    gsl_matrix_set(J,i+n,0,0);
+    gsl_matrix_set(J,i+n,1,((1/omega) - d0)*sin(Phi0) + (1/fabs(omega)) * cos(phii) );
+    gsl_matrix_set(J,i+n,2,cos(Phi0)/pow(omega,2) + ( (signum(omega))/(pow(fabs(omega),2)) ) * sin(phii) + 
+                   (1/fabs(omega))*cos(phii) * ( ( ((-1.0)/sqrt(1+pow(tanL,2)))*si) + (M_PI)/(2*fabs(omega)) - (signum(omega)*omega*M_PI)/(2*pow(fabs(omega),2)) ) );
+    gsl_matrix_set(J,i+n,3,cos(Phi0));
+    gsl_matrix_set(J,i+n,4,(1/fabs(omega))*cos(phii) * ( (omega*tanL*si)/sqrt(pow(1+pow(tanL,2),3)) ) );
+    
+  }
+
+  // part of Jacobi's matrix corresponding to third dimension
+  for (int i(0); i < n; i++) {
+    phii = atan2( ( ((double)y[i]) + ((1/omega) - d0 )*cos(Phi0) ), ( ((double)x[i]) - ((1/omega) - d0 )*sin(Phi0) ) );
+    si = (-1.0)*( (sqrt(1+pow(tanL,2)))/omega )*(phii - Phi0 - (omega*M_PI)/(2*fabs(omega)));
+
+    gsl_matrix_set(J,i+2*n,0,1.0);
+    gsl_matrix_set(J,i+2*n,1,0);
+    gsl_matrix_set(J,i+2*n,2,0);
+    gsl_matrix_set(J,i+2*n,3,0);
+    gsl_matrix_set(J,i+2*n,4,si/sqrt(1+pow(tanL,2)) - (pow(tanL,2)*si)/sqrt(pow(1+pow(tanL,2),3)) );
+    
+  }
+  
+  return GSL_SUCCESS;
+
+}
+
+//=============================================================================
+
+int fdfParametrisation3(const gsl_vector* par, void* d, gsl_vector* f, gsl_matrix* J) {
+  
+  //     For helix fitting
+  functParametrisation3(par, d, f);
+  dfunctParametrisation3(par, d, J);
+  
+  return GSL_SUCCESS;
+
+}
+
+//=============================================================================
+
+
+
+
+// ##########################################
+// #####                                #####
+// #####   Constructor and Destructor   #####
+// #####                                #####
+// ##########################################
+
+//=============================================================================
+
+ClusterShapes::ClusterShapes(int nhits, float* a, float* x, float* y, float* z){
+
+  _nHits = nhits;
+  _aHit = new float[_nHits];
+  _xHit = new float[_nHits];
+  _yHit = new float[_nHits];
+  _zHit = new float[_nHits];
+  _exHit = new float[_nHits];   
+  _eyHit = new float[_nHits];
+  _ezHit = new float[_nHits];         
+  _types = new int[_nHits];
+
+  _xl = new float[_nHits];
+  _xt = new float[_nHits];
+
+  _t = new float[_nHits];
+  _s = new float[_nHits]; 
+
+  for (int i(0); i < nhits; ++i) {
+    _aHit[i] = a[i];
+    _xHit[i] = x[i];
+    _yHit[i] = y[i];
+    _zHit[i] = z[i];
+    _exHit[i] = 1.0;
+    _eyHit[i] = 1.0;
+    _ezHit[i] = 1.0;
+    _types[i] = 1; // all hits are assumed to be "cyllindrical"
+  }
+
+  _ifNotGravity     = 1;
+  _ifNotWidth       = 1;
+  _ifNotInertia     = 1;
+  _ifNotEigensystem = 1;
+
+}
+
+
+//=============================================================================
+
+ClusterShapes::~ClusterShapes() {
+
+  delete[] _aHit;
+  delete[] _xHit;
+  delete[] _yHit;
+  delete[] _zHit;
+  delete[] _exHit;
+  delete[] _eyHit;
+  delete[] _ezHit;
+  delete[] _types;
+  delete[] _xl;
+  delete[] _xt;
+  delete[] _t;
+  delete[] _s;
+}
+
+//=============================================================================
+
+void ClusterShapes::setErrors(float *ex, float *ey, float *ez) {
+
+  for (int i=0; i<_nHits; ++i) {
+    _exHit[i] = ex[i];
+    _eyHit[i] = ey[i];
+    _ezHit[i] = ez[i];
+  }	
+
+}
+
+void ClusterShapes::setHitTypes(int* typ) {
+  for (int i=0; i<_nHits; ++i) {
+    _types[i] = typ[i];
+
+  }
+  
+}
+
+
+
+// ##########################################
+// #####                                #####
+// #####        public methods          #####
+// #####                                #####
+// ##########################################
+
+//=============================================================================
+
+int ClusterShapes::getNumberOfHits() {
+  return _nHits;
+}
+
+//=============================================================================
+
+float ClusterShapes::getTotalAmplitude() {
+  if (_ifNotGravity == 1) findGravity();
+  return _totAmpl;
+}
+
+//=============================================================================
+
+float* ClusterShapes::getCentreOfGravity() {
+  if (_ifNotGravity == 1) findGravity() ;
+  return &_analogGravity[0] ;
+}
+
+//=============================================================================
+
+float* ClusterShapes::getEigenValInertia() {
+  if (_ifNotInertia == 1) findInertia();
+  return &_ValAnalogInertia[0] ;
+}
+
+//=============================================================================
+
+float* ClusterShapes::getEigenVecInertia() {
+  if (_ifNotInertia == 1) findInertia();
+  return &_VecAnalogInertia[0] ;
+}
+
+//=============================================================================
+
+float ClusterShapes::getWidth() {
+  if (_ifNotWidth == 1) findWidth();
+  return _analogWidth;
+}
+
+//=============================================================================
+
+int ClusterShapes::getEigenSytemCoordinates(float* xlong, float* xtrans) {
+
+  float xStart[3];
+  int index_xStart;
+
+
+  // NOT SAVE, change to class variables !!!!!
+  float X0 = 7.0;
+  float Rm = 13.5;
+
+  if (_ifNotEigensystem == 1) transformToEigensystem(xStart,index_xStart,X0,Rm);
+
+  for (int i = 0; i < _nHits; ++i) {
+    xlong[i]  = _xl[i];
+    xtrans[i] = _xt[i];
+  }
+
+  return 0; // no error messages at the moment
+
+}
+
+//=============================================================================
+
+int ClusterShapes::getEigenSytemCoordinates(float* xlong, float* xtrans, float* a) {
+
+  float xStart[3];
+  int index_xStart;
+
+  // NOT SAVE, change to class variables !!!!!
+  float X0 = 7.0;
+  float Rm = 13.5;
+
+  if (_ifNotEigensystem == 1) transformToEigensystem(xStart,index_xStart,X0,Rm);
+
+  for (int i = 0; i < _nHits; ++i) {
+    xlong[i]  = _xl[i];
+    xtrans[i] = _xt[i];
+    a[i] = _aHit[i];
+  }
+
+  return 0; // no error messages at the moment
+
+}
+
+//=============================================================================
+
+int ClusterShapes::fit3DProfile(float& chi2, float& E0, float& A, float& B, float& D,
+				float& xl0, float* xStart, int& index_xStart,
+				float X0, float Rm) {
+
+  const int npar = 4;
+
+
+  if (_ifNotEigensystem == 1) transformToEigensystem(xStart,index_xStart,X0,Rm);
+
+  float* E = new float[_nHits];
+
+
+  double par_init[npar];
+  for (int i = 0; i < npar; ++i) par_init[i] = 0.0; // initialise
+
+  float E0_init = 0.0;
+  float A_init  = 0.0;
+  float B_init  = 0.5; // empirically
+  float D_init  = 0.5; // empirically
+  float t0_init = -10.0/X0; // shift xl0_ini is assumed to be -10.0 without a reason ????
+  
+  float E0_tmp = 0.0;
+  int i_max = 0;
+  float t_max = 0.0;
+  for (int i = 0; i < _nHits; ++i) {
+    if (E0_tmp < _aHit[i]) {
+      E0_tmp = _aHit[i]; 
+      i_max = i;
+    }
+    E0_init += _aHit[i];
+  }
+
+  // first definition
+  //t_max = _xl[i_max]/X0;
+  //A_init = t_max*B_init + 1.0;
+
+  // second definition
+  //t_max = (1.0/3.0) * (t[_nHits-1] - t[0]);
+  //A_init = t_max*B_init + 1.0;
+
+  // third definition
+  float Ec = X0 * 0.021/Rm;
+  A_init =  B_init * log(E0_init/Ec) + 0.5 * B_init + 1.0; // (+0.5 for Photons initiated showers)
+
+
+
+  // par_init[0] = E0_init;
+  E0 = E0_init;
+  for (int i = 0; i < _nHits; ++i) E[i] = _aHit[i]/E0_init;
+  par_init[0] = A_init; // 2.0
+  par_init[1] = B_init;
+  par_init[2] = D_init;
+  par_init[3] = t0_init;
+
+  // debug
+
+  std::cout << "E0_init : " <<  E0_init << "\t" << "A_init : " << A_init << "\t" 
+	    << "B_init : " <<  B_init << "\t" << "D_init : " << D_init << "\t" 
+	    << "xl0_init : "  << t0_init*X0 << "\t" << "X0 : " << X0 
+	    << "\t" << "t_max : " << t_max << std::endl << std::endl;
+
+
+  double t0 = xl0/X0;
+  double par[npar];
+  par[0] = A;
+  par[1] = B;
+  par[2] = D;
+  par[3] = t0;
+
+  fit3DProfileAdvanced(chi2,par_init,par,npar,_t,_s,E,E0);
+
+  A   = par[0];
+  B   = par[1];
+  D   = par[2];
+  xl0 = par[3] * X0;
+
+  delete[] E;
+
+  int result = 0;  // no error handling at the moment
+  return result;
+}
+
+//=============================================================================
+
+float ClusterShapes::getChi2Fit3DProfileSimple(float a, float b, float c, float d,
+					       float X0, float Rm) {
+
+  float chi2 = 0.0;
+
+  float xStart[3];
+  int index_xStart;
+
+  if (_ifNotEigensystem == 1) transformToEigensystem(xStart,index_xStart,X0,Rm);
+
+  chi2 = calculateChi2Fit3DProfileSimple(a,b,c,d);
+  
+  return chi2;
+  
+}
+
+//=============================================================================
+
+float ClusterShapes::getChi2Fit3DProfileAdvanced(float E0, float a, float b, float d,
+						 float t0, float X0, float Rm) {
+
+  float chi2 = 0.0;
+
+  float xStart[3];
+  int index_xStart;
+
+  if (_ifNotEigensystem == 1) transformToEigensystem(xStart,index_xStart,X0,Rm);
+
+  chi2 = calculateChi2Fit3DProfileAdvanced(E0,a,b,d,t0);
+
+  return chi2;
+
+}
+
+//=============================================================================
+
+int ClusterShapes::FitHelix(int max_iter, int status_out, int parametrisation,
+			    float* parameter, float* dparameter, float& chi2, 
+			    float& distmax, int direction) {
+
+
+  // Modified by Hengne Li @ LAL
+  
+  double parameterdb[5];
+  double dparameterdb[5];
+  double chi2db;
+  double distmaxdb;
+  for ( int i=0; i<5; i++ ){
+    parameterdb[i] = double(parameter[i]);
+    dparameterdb[i] = double(dparameter[i]);
+  }
+  chi2db = double(chi2);
+  distmaxdb = double(distmax);
+  
+  int returnvalue = FitHelix(max_iter,status_out,parametrisation,parameterdb,dparameterdb,chi2db,distmaxdb,direction);
+  
+  for ( int i=0; i<5; i++ ){
+    parameter[i] = float(parameterdb[i]);
+    dparameter[i] = float(dparameterdb[i]);
+  }
+  chi2 = float(chi2db);
+  distmax = float(distmaxdb);
+  
+  return returnvalue ;
+
+}
+
+//=============================================================================
+
+int ClusterShapes::FitHelix(int max_iter, int status_out, int parametrisation,
+			    double* parameter, double* dparameter, double& chi2, 
+                            double& distmax, int direction) {
+
+  // FIXME: version with double typed parameters needed 2006/06/10 OW
+  
+  if (_nHits < 3) {
+      std::cout << "ClusterShapes : helix fit impossible, two few points" ;
+      std::cout << std::endl;
+      for (int i = 0; i < 5; ++i) {
+	  parameter[i] = 0.0;
+	  dparameter[i] = 0.0;
+      }
+      return 1;
+  }
+
+  // find initial parameters
+
+  double Rmin = 1.0e+10;
+  double Rmax = -1.0;
+  int i1 = -1;
+
+  // 1st loop  
+  for (int i(0); i < _nHits; ++i) {
+      double Rz = sqrt(_xHit[i]*_xHit[i] + _yHit[i]*_yHit[i]);
+      if (Rz < Rmin) {
+        Rmin = Rz;
+        i1 = i;
+      }
+      if (Rz > Rmax) {
+        Rmax = Rz;
+      }
+
+  }
+
+
+
+  // debug
+  /*
+  for (int i(0); i < _nHits; ++i) std::cout << i << "  " << _xHit[i] << "  " << _yHit[i] << "  " << _zHit[i] << std::endl;
+  std::cout << std::endl << Rmin << "  " <<  Rmax << "  " << i1 << std::endl;
+  */
+
+
+
+
+
+  // 2nd loop
+  double Upper = Rmin + 1.1*(Rmax-Rmin);
+  double Lower = Rmin + 0.9*(Rmax-Rmin);
+  double dZmin  = 1.0e+20;
+
+  int i3 = -1 ;
+
+  for (int i(0); i < _nHits; ++i) {
+    double Rz = sqrt(_xHit[i]*_xHit[i] + _yHit[i]*_yHit[i]);
+    if ((Rz > Lower) && (Rz < Upper)) {
+      double dZ = fabs(_zHit[i]-_zHit[i1]);
+      if (dZ < dZmin) {
+        dZmin = dZ;
+        i3 = i;
+      }
+    }
+  }
+
+  // debug
+  //std::cout << std::endl << Upper << "  " << Lower << "  " << dZmin << "  " << i3 << std::endl;
+
+
+
+
+  double z1 = std::min(_zHit[i1],_zHit[i3]);
+  double z3 = std::max(_zHit[i1],_zHit[i3]);
+
+
+  int i2 = -1;
+  double dRmin = 1.0e+20;
+  double Rref = 0.5 * ( Rmax + Rmin );
+
+  // 3d loop
+
+  for (int i(0); i < _nHits; ++i) {
+      if (_zHit[i] >= z1 && _zHit[i] <= z3) {
+        double Rz = sqrt(_xHit[i]*_xHit[i] + _yHit[i]*_yHit[i]);
+        double dRz = fabs(Rz - Rref);
+        if (dRz < dRmin) {
+          i2 = i;
+          dRmin = dRz;
+        }
+      }
+  }
+
+  int problematic = 0;
+
+  if (i2 < 0 || i2 == i1 || i2 == i3) {
+    problematic = 1;
+    // std::cout << "here we are " << std::endl;
+    for (int i(0); i < _nHits; ++i) {
+      if (i != i1 && i != i3) {
+        i2 = i;
+        if (_zHit[i2] < z1) {
+          int itemp = i1;
+          i1 = i2;
+          i2 = itemp;
+        }
+        else if (_zHit[i2] > z3) {
+          int itemp = i3;
+          i3 = i2;
+          i2 = itemp;
+        }        
+        break;
+      }
+    }      
+    // std::cout << i1 << " " << i2 << " " << i3 << std::endl;
+  }
+
+
+  double x0  = 0.5*(_xHit[i2]+_xHit[i1]);
+  double y0  = 0.5*(_yHit[i2]+_yHit[i1]);
+  double x0p = 0.5*(_xHit[i3]+_xHit[i2]);
+  double y0p = 0.5*(_yHit[i3]+_yHit[i2]);
+  double ax  = _yHit[i2] - _yHit[i1];
+  double ay  = _xHit[i1] - _xHit[i2];
+  double axp = _yHit[i3] - _yHit[i2];
+  double ayp = _xHit[i2] - _xHit[i3];
+  double det = ax * ayp - axp * ay;
+  double time;
+
+  if (det == 0.) {
+      time = 500.;
+  }
+  else {
+      gsl_matrix* A = gsl_matrix_alloc(2,2);
+      gsl_vector* B = gsl_vector_alloc(2);
+      gsl_vector* T = gsl_vector_alloc(2);     
+      gsl_matrix_set(A,0,0,ax);
+      gsl_matrix_set(A,0,1,-axp);
+      gsl_matrix_set(A,1,0,ay);
+      gsl_matrix_set(A,1,1,-ayp);
+      gsl_vector_set(B,0,x0p-x0);
+      gsl_vector_set(B,1,y0p-y0);
+      gsl_linalg_HH_solve(A,B,T);
+      time = gsl_vector_get(T,0); 
+      gsl_matrix_free(A);
+      gsl_vector_free(B);
+      gsl_vector_free(T);
+  }
+
+  double X0 = x0 + ax*time;
+  double Y0 = y0 + ay*time;
+
+  double dX = _xHit[i1] - X0;
+  double dY = _yHit[i1] - Y0;
+
+  double R0 = sqrt(dX*dX + dY*dY);
+
+  /*
+   if (problematic == 1) {
+     std::cout << i1 << " " << i2 << " " << i3 << std::endl;
+     std::cout << _xHit[i1] << " " << _yHit[i1] << " " << _zHit[i1] << std::endl;
+     std::cout << _xHit[i2] << " " << _yHit[i2] << " " << _zHit[i2] << std::endl;
+     std::cout << _xHit[i3] << " " << _yHit[i3] << " " << _zHit[i3] << std::endl;
+     std::cout << "R0 = " << R0 << std::endl;
+   }
+  */
+
+  double phi1 = (double)atan2(_yHit[i1]-Y0,_xHit[i1]-X0);
+  double phi2 = (double)atan2(_yHit[i2]-Y0,_xHit[i2]-X0);
+  double phi3 = (double)atan2(_yHit[i3]-Y0,_xHit[i3]-X0);
+
+// testing bz > 0 hypothesis
+
+  if ( phi1 > phi2 ) 
+      phi2 = phi2 + 2.0*M_PI;
+  if ( phi1 > phi3 )
+      phi3 = phi3 + 2.0*M_PI;
+  if ( phi2 > phi3 )
+      phi3 = phi3 + 2.0*M_PI;
+
+  double bz_plus = (phi3 - phi1) / (_zHit[i3]-_zHit[i1]);
+  double phi0_plus = phi1 - bz_plus * _zHit[i1];
+  double dphi_plus = fabs( bz_plus * _zHit[i2] + phi0_plus - phi2 );
+
+// testing bz < 0 hypothesis
+
+  phi1 = (double)atan2(_yHit[i1]-Y0,_xHit[i1]-X0);
+  phi2 = (double)atan2(_yHit[i2]-Y0,_xHit[i2]-X0);
+  phi3 = (double)atan2(_yHit[i3]-Y0,_xHit[i3]-X0);
+
+  if ( phi1 < phi2 ) 
+      phi2 = phi2 - 2.0*M_PI;
+  if ( phi1 < phi3 )
+      phi3 = phi3 - 2.0*M_PI;
+  if ( phi2 < phi3 )
+      phi3 = phi3 - 2.0*M_PI;
+
+  double bz_minus = (phi3 - phi1) / (_zHit[i3]-_zHit[i1]);
+  double phi0_minus = phi1 - bz_minus * _zHit[i1];
+  double dphi_minus = fabs( bz_minus * _zHit[i2] + phi0_minus - phi2 );
+
+  double bz;
+  double phi0;
+
+  if (dphi_plus < dphi_minus) {
+      bz = bz_plus;
+      phi0 = phi0_plus;
+  }
+  else {
+      bz = bz_minus;
+      phi0 = phi0_minus;
+
+  }
+
+  double par_init[5];
+
+  if (parametrisation == 1) {
+    par_init[0] = (double)X0;
+    par_init[1] = (double)Y0;
+    par_init[2] = (double)R0;
+    par_init[3] = (double)bz;
+    par_init[4] = (double)phi0;
+  }
+  else if (parametrisation == 2) {
+    par_init[0] = (double)X0;
+    par_init[1] = (double)Y0;
+    par_init[2] = (double)(-phi0/bz);
+    par_init[3] = (double)R0;
+    par_init[4] = (double)(1/bz);
+  }
+
+  else if (parametrisation == 3) {  // parameter vector: (z0,Phi0,omega,d0,tanL)
+
+    // debug
+    // std::cout << std::setprecision(6) << "InitFit (X0,Y0,R0,bz,phi0) = " << "(" << X0 << "," << Y0 << "," << R0 << "," << bz << "," << phi0 << ")" << std::endl;
+
+    
+    // debug
+    /*    
+    X0 = -1205.28;
+    Y0 = 175.317;
+    R0 = 1217.97;
+    bz = 0.00326074;
+    phi0 = -0.144444;
+    */ 
+
+
+    // debug
+    // std::cout << std::setprecision(6) << "InitUsed (X0,Y0,R0,bz,phi0) = " << "(" << X0 << "," << Y0 << "," << R0 << "," << bz << "," << phi0 << ")" << std::endl;
+        
+    double omega = 1/R0*direction;
+    double tanL  = (-1.0)*omega/bz;
+
+    double Phi0  = (-1.0)*atan2(X0,Y0);
+    
+    if (direction == 1) {
+
+      if (tanL >= 0.0) Phi0 += M_PI; // add pi (see LC-DET-2006-004) //  >= or > ?
+      else Phi0 -= M_PI; // < or <= ?
+      
+    }
+
+    //double d0 = R0 - X0/sin(Phi0);
+    //double d0 = R0 + Y0/cos(Phi0);
+
+    double d0 = 0.0;
+    if (true /*direction != 1*/) d0 = R0 - sqrt(X0*X0 + Y0*Y0);
+    // else d0 = R0 + sqrt(X0*X0 + Y0*Y0);
+
+
+
+    // double d0 = R0 - ( (X0-Y0)/(sqrt(2.0)*cos(pi/4 - Phi0)) );    
+    // double d0 = R0 - ((X0-Y0)/(sin(Phi0)+cos(Phi0)));
+
+    // double Phi0 = asin(X0/(R0-d0));
+
+    
+    double z0 = (1/bz)*((-1.0)*phi0+Phi0+(omega*M_PI)/(2.0*fabs(omega)));
+
+
+    // debug
+    /*
+    std::cout << std::setprecision(6) << "InitFitCalculated (d0,z0,phi0,omega,tanL) = " << "(" << d0 << "," << z0 << "," << Phi0 << "," << omega << "," << tanL << ")" 
+              << "  " << "sign(omega) = " << direction << std::endl;
+    */
+
+    // debug        
+    /*
+    d0    = 0.00016512;
+    z0    = 0.000853511;
+    Phi0  = 1.11974;
+    omega = -4.22171e-05;
+    tanL  = -0.33436;
+    */
+
+
+    
+
+    // debug
+    // std::cout << std::setprecision(6) << "InitFitUsed (d0,z0,phi0,omega,tanL) = " << "(" << d0 << "," << z0 << "," << Phi0 << "," << omega << "," << tanL << ")" << std::endl;
+
+    par_init[0] = z0;
+    par_init[1] = Phi0;
+    par_init[2] = omega;
+    par_init[3] = d0;
+    par_init[4] = tanL;
+  }
+  else return 1;
+
+
+  // local variables
+  int status = 0;
+  int iter = 0;
+
+  int npar = 5; // five parameters to fit
+  int ndim = 0;
+  if (parametrisation == 1) ndim = 2; // two dependent dimensions 
+  else if (parametrisation == 2) ndim = 3; // three dependent dimensions
+  else if (parametrisation == 3) ndim = 3; // three dependent dimensions
+
+  else return 1;
+
+
+
+  double chi2_nofit = 0.0;
+  int iFirst = 1;
+  for (int ipoint(0); ipoint < _nHits; ipoint++) {
+    double distRPZ[2];
+    double Dist = DistanceHelix(_xHit[ipoint],_yHit[ipoint],_zHit[ipoint],
+			       X0,Y0,R0,bz,phi0,distRPZ);
+    double chi2rphi = distRPZ[0]/_exHit[ipoint];
+    chi2rphi = chi2rphi*chi2rphi;
+    double chi2z = distRPZ[1]/_ezHit[ipoint];
+    chi2z = chi2z*chi2z;
+    chi2_nofit = chi2_nofit + chi2rphi + chi2z;
+    if (Dist > distmax || iFirst == 1) {
+      distmax = Dist;
+      iFirst = 0;
+    }
+  }      
+  chi2_nofit = chi2_nofit/double(_nHits);
+
+  if ( status_out == 1 ) {
+    for (int i(0); i < 5; ++i) {
+      parameter[i] = (double)par_init[i];
+      dparameter[i] = 0.0;      
+    }
+    chi2 = chi2_nofit;
+    return 0;
+  }
+
+  // converging criteria
+  const double abs_error = 1e-4;
+  const double rel_error = 1e-4;
+
+  gsl_multifit_function_fdf fitfunct;
+
+  const gsl_multifit_fdfsolver_type* T = gsl_multifit_fdfsolver_lmsder;
+
+  gsl_multifit_fdfsolver* s = gsl_multifit_fdfsolver_alloc(T,ndim*_nHits,npar);
+
+  gsl_matrix* covar = gsl_matrix_alloc(npar,npar);   // covariance matrix
+
+  data d;
+  d.n = _nHits;
+  d.x = &_xHit[0];
+  d.y = &_yHit[0];
+  d.z = &_zHit[0];
+  d.ex = &_exHit[0];
+  d.ey = &_eyHit[0];
+  d.ez = &_ezHit[0];
+
+
+  if (parametrisation == 1) {
+    fitfunct.f = &functParametrisation1;
+    fitfunct.df = &dfunctParametrisation1;
+    fitfunct.fdf = &fdfParametrisation1;
+    fitfunct.n = ndim*_nHits;
+    fitfunct.p = npar;
+    fitfunct.params = &d;
+  }
+  else if (parametrisation == 2) {
+    fitfunct.f = &functParametrisation2;
+    fitfunct.df = &dfunctParametrisation2;
+    fitfunct.fdf = &fdfParametrisation2;
+    fitfunct.n = ndim*_nHits;
+    fitfunct.p = npar;
+    fitfunct.params = &d;
+  }
+  else if (parametrisation == 3) {
+    fitfunct.f = &functParametrisation3;
+    fitfunct.df = &dfunctParametrisation3;
+    fitfunct.fdf = &fdfParametrisation3;
+    fitfunct.n = ndim*_nHits;
+    fitfunct.p = npar;
+    fitfunct.params = &d;
+  }
+  else return 1;
+
+  gsl_vector_view pinit = gsl_vector_view_array(par_init,npar);
+  gsl_multifit_fdfsolver_set(s,&fitfunct,&pinit.vector);
+
+  // perform fit
+  do {
+    iter++;
+    status = gsl_multifit_fdfsolver_iterate(s);
+
+    if (status) break;
+    status = gsl_multifit_test_delta (s->dx, s->x,abs_error,rel_error);
+
+  } while ( status==GSL_CONTINUE && iter < max_iter);
+
+  gsl_multifit_covar (s->J, rel_error, covar);
+
+
+
+  chi2 = 0.0;
+
+  if (parametrisation == 1) {
+    X0   = (double)gsl_vector_get(s->x,0);
+    Y0   = (double)gsl_vector_get(s->x,1);
+    R0   = (double)gsl_vector_get(s->x,2);
+    bz   = (double)gsl_vector_get(s->x,3);
+    phi0 = (double)gsl_vector_get(s->x,4);
+  }
+  else if (parametrisation == 2) {
+    X0   = (double)gsl_vector_get(s->x,0);
+    Y0   = (double)gsl_vector_get(s->x,1);
+    R0   = (double)gsl_vector_get(s->x,3);
+    bz   = (double)(1/gsl_vector_get(s->x,4));
+    phi0 = (double)(-gsl_vector_get(s->x,2)/gsl_vector_get(s->x,4));
+  }
+  else if (parametrisation == 3) { // (parameter vector: (z0,phi0,omega,d0,tanL)
+
+    double z0    = gsl_vector_get(s->x,0);
+    double Phi0  = gsl_vector_get(s->x,1);
+    double omega = gsl_vector_get(s->x,2);
+    double d0    = gsl_vector_get(s->x,3);
+    double tanL  = gsl_vector_get(s->x,4);
+
+    X0   = (double)( ( (1/omega) - d0 )*sin(Phi0) );
+    Y0   = (double)( (-1)*( (1/omega) - d0 )*cos(Phi0) );
+    R0   = (double)( 1/fabs(omega) );
+    bz   = (double)( (-1)*(omega/tanL) );
+    phi0 = (double)( ( (z0*omega)/tanL ) + Phi0 + ( (omega*M_PI)/(2*fabs(omega)) ) );
+  }
+  else return 1;
+
+  iFirst = 1;
+  double ddmax = 0.0;
+  for (int ipoint(0); ipoint < _nHits; ipoint++) {
+    double distRPZ[2];
+    double Dist = DistanceHelix(_xHit[ipoint],_yHit[ipoint],_zHit[ipoint],
+			       X0,Y0,R0,bz,phi0,distRPZ);
+    double chi2rphi = distRPZ[0]/_exHit[ipoint];
+    chi2rphi = chi2rphi*chi2rphi;
+    double chi2z = distRPZ[1]/_ezHit[ipoint];
+    chi2z = chi2z*chi2z;
+    chi2 = chi2 + chi2rphi + chi2z;
+    if (Dist > ddmax || iFirst == 1) {
+      iFirst = 0;
+      ddmax = Dist;
+    }
+  }
+      
+
+  chi2 = chi2/double(_nHits);
+  if (chi2 < chi2_nofit) {
+    for (int i = 0; i < npar; i++) {
+      parameter[i]  = gsl_vector_get(s->x,i);
+      dparameter[i] = sqrt(gsl_matrix_get(covar,i,i));
+    }    
+    distmax = ddmax;
+  }
+  else {
+    chi2 = chi2_nofit;
+    for (int i = 0; i < npar; i++) {
+      parameter[i] = (double)par_init[i];
+      dparameter[i] = 0.0;
+    }
+  }
+
+  //  if (problematic == 1)
+  //    std::cout << "chi2 = " << chi2 << std::endl;
+
+  gsl_multifit_fdfsolver_free(s);
+  gsl_matrix_free(covar);
+  return 0; 
+
+}
+
+
+
+// ##########################################
+// #####                                #####
+// #####        private methods         #####
+// #####                                #####
+// ##########################################
+
+//=============================================================================
+
+void ClusterShapes::findElipsoid() {
+
+  /**   Elipsoid parameter calculations see cluster_proper.f  */
+  float cx,cy,cz ;
+  float dx,dy,dz ;
+  float r_hit_max, d_begn, d_last, r_max, proj;
+  if (_ifNotInertia == 1) findInertia() ;
+  //   Normalize the eigen values of inertia tensor
+  float wr1 = sqrt(_ValAnalogInertia[0]/_totAmpl);
+  float wr2 = sqrt(_ValAnalogInertia[1]/_totAmpl);
+  float wr3 = sqrt(_ValAnalogInertia[2]/_totAmpl);
+  _r1 = sqrt(wr2*wr3);                // spatial axis length -- the largest
+  _r2 = sqrt(wr1*wr3);                // spatial axis length -- less
+  _r3 = sqrt(wr1*wr2);                // spatial axis length -- even more less
+  _vol = 4.*M_PI*_r1*_r2*_r3/3.;      // ellipsoid volume
+  _r_ave = pow(_vol,1/3);             // average radius  (quibc root)
+  _density = _totAmpl/_vol;           // density
+  //    _eccentricity = _r_ave/_r1;   // Cluster Eccentricity
+  _eccentricity =_analogWidth/_r1;   // Cluster Eccentricity
+
+  // Find Minumal and Maximal Lenght for Principal axis
+  r_hit_max = -100000.;
+  d_begn    =  100000.;
+  d_last    = -100000.;
+  cx = _VecAnalogInertia[0] ;
+  cy = _VecAnalogInertia[1] ;
+  cz = _VecAnalogInertia[2] ;
+  for (int i(0); i < _nHits; ++i) {
+    dx = _xHit[i] - _xgr;
+    dy = _yHit[i] - _ygr;
+    dz = _zHit[i] - _zgr;
+    r_max = sqrt(dx*dx + dy*dy + dz*dz);;
+    if(r_max > r_hit_max) r_hit_max = r_max;
+    proj = dx*cx + dy*cy + dz*cz;
+    if(proj < d_begn)
+      d_begn = proj;
+    //            lad_begn = ladc(L)
+    if(proj > d_last)
+      d_last = proj;
+    //            lad_last = ladc(L)
+  }
+  //        if (r_hit_max > 0.0)
+  //	  _r1 = 1.05*r_hit_max; // + 5% of length
+  _r1_forw = fabs(d_last);
+  _r1_back = fabs(d_begn);
+}
+
+//=============================================================================
+
+void ClusterShapes::findGravity() {
+
+    _totAmpl = 0. ;
+    for (int i(0); i < 3; ++i) {
+	_analogGravity[i] = 0.0 ;
+    }
+    for (int i(0); i < _nHits; ++i) {
+	_totAmpl+=_aHit[i] ;
+	_analogGravity[0]+=_aHit[i]*_xHit[i] ;
+	_analogGravity[1]+=_aHit[i]*_yHit[i] ;
+	_analogGravity[2]+=_aHit[i]*_zHit[i] ;
+    }
+    for (int i(0); i < 3; ++i) {
+	_analogGravity[i]/=_totAmpl ;
+    }
+    _xgr = _analogGravity[0];
+    _ygr = _analogGravity[1];
+    _zgr = _analogGravity[2];
+    _ifNotGravity = 0;
+}
+
+//=============================================================================
+
+void ClusterShapes::findInertia() {
+
+  double aIne[3][3];
+  //  float radius1;
+  float radius2 = 0.0;
+
+  findGravity();
+
+  for (int i(0); i < 3; ++i) {
+      for (int j(0); j < 3; ++j) {
+	  aIne[i][j] = 0.0;
+      }
+  }
+
+  for (int i(0); i < _nHits; ++i) {
+      float dX = _xHit[i] - _analogGravity[0];
+      float dY = _yHit[i] - _analogGravity[1];
+      float dZ = _zHit[i] - _analogGravity[2];
+      aIne[0][0] += _aHit[i]*(dY*dY+dZ*dZ);
+      aIne[1][1] += _aHit[i]*(dX*dX+dZ*dZ);
+      aIne[2][2] += _aHit[i]*(dX*dX+dY*dY);
+      aIne[0][1] -= _aHit[i]*dX*dY;
+      aIne[0][2] -= _aHit[i]*dX*dZ;
+      aIne[1][2] -= _aHit[i]*dY*dZ;
+  }
+
+  for (int i(0); i < 2; ++i) {
+      for (int j = i+1; j < 3; ++j) {
+	  aIne[j][i] = aIne[i][j];
+      }
+  }
+  //****************************************
+  // analog Inertia
+  //****************************************
+
+  gsl_matrix_view aMatrix = gsl_matrix_view_array((double*)aIne,3,3);
+  gsl_vector* aVector = gsl_vector_alloc(3);
+  gsl_matrix* aEigenVec = gsl_matrix_alloc(3,3);
+  gsl_eigen_symmv_workspace* wa = gsl_eigen_symmv_alloc(3);
+  gsl_eigen_symmv(&aMatrix.matrix,aVector,aEigenVec,wa);
+  gsl_eigen_symmv_free(wa);
+  gsl_eigen_symmv_sort(aVector,aEigenVec,GSL_EIGEN_SORT_ABS_ASC);
+
+  for (int i(0); i < 3; i++) {
+    _ValAnalogInertia[i] = gsl_vector_get(aVector,i);
+    for (int j(0); j < 3; j++) {
+      _VecAnalogInertia[i+3*j] = gsl_matrix_get(aEigenVec,i,j);
+    }
+  }
+
+  // Main principal points away from IP
+
+  _radius = 0.;
+  radius2 = 0.;
+
+  for (int i(0); i < 3; ++i) {
+      _radius += _analogGravity[i]*_analogGravity[i];
+      radius2 += (_analogGravity[i]+_VecAnalogInertia[i])*(_analogGravity[i]+_VecAnalogInertia[i]);
+  }
+
+  if ( radius2 < _radius) {
+      for (int i(0); i < 3; ++i)
+	  _VecAnalogInertia[i] = - _VecAnalogInertia[i];
+  }
+
+  _radius = sqrt(_radius);
+  _ifNotInertia = 0;
+
+  // The final job
+  findWidth();
+  findElipsoid();
+
+  gsl_vector_free(aVector);
+  gsl_matrix_free(aEigenVec);
+
+}
+
+//=============================================================================
+
+void ClusterShapes::findWidth() {
+
+  float dist = 0.0;
+  if (_ifNotInertia == 1)  findInertia() ;
+  _analogWidth  = 0.0 ;
+  for (int i(0); i < _nHits; ++i) {
+    dist = findDistance(i) ;
+    _analogWidth+=_aHit[i]*dist*dist ;
+  }
+  _analogWidth  = sqrt(_analogWidth / _totAmpl) ;
+  _ifNotWidth = 0 ;
+}
+
+//=============================================================================
+
+float ClusterShapes::findDistance(int i) {
+
+    float cx = 0.0;
+    float cy = 0.0;
+    float cz = 0.0;
+    float dx = 0.0;
+    float dy = 0.0;
+    float dz = 0.0;
+    cx = _VecAnalogInertia[0] ;
+    cy = _VecAnalogInertia[1] ;
+    cz = _VecAnalogInertia[2] ;
+    dx = _analogGravity[0] - _xHit[i] ;
+    dy = _analogGravity[1] - _yHit[i] ;
+    dz = _analogGravity[2] - _zHit[i] ;
+    float tx = cy*dz - cz*dy ;
+    float ty = cz*dx - cx*dz ;
+    float tz = cx*dy - cy*dx ;
+    float tt = sqrt(tx*tx+ty*ty+tz*tz) ;
+    float ti = sqrt(cx*cx+cy*cy+cz*cz) ;
+    float f = tt / ti ;
+    return f ;
+}
+/**
+      Function sdist(xp,yp,zp,cx,cy,cz,xv,yv,zv)
+c----------------------------------------------------------------------
+c        Distance from line to point
+c       xp, yp, zp -- point is at the line
+c       xv, yv, zv -- point is out of line
+********************************************************************
+*     Last update     V.L.Morgunov     08-Apr-2002                 *
+********************************************************************
+      real xp,yp,zp,cx,cy,cz,xv,yv,zv,t1,t2,t3,tt,sdist
+
+      t1 = cy*(zp-zv)-cz*(yp-yv)
+      t2 = cz*(xp-xv)-cx*(zp-zv)
+      t3 = cx*(yp-yv)-cy*(xp-xv)
+      tt = sqrt(cx**2+cy**2+cz**2)
+      sdist = sqrt(t1**2+t2**2+t3**2)/tt
+
+      return
+      end
+*/
+
+//=============================================================================
+
+float ClusterShapes::vecProduct(float * x1, float * x2) {
+
+    float x1abs(0.);
+    float x2abs(0.);
+    float prod(0.);
+
+    for (int i(0); i < 3; ++i) {
+	x1abs += x1[i]*x1[i];
+	x2abs += x2[i]*x2[i];
+	prod  += x1[i]*x2[i];
+    }
+
+
+    x1abs = sqrt(x1abs);
+    x2abs = sqrt(x2abs);
+
+    if (x1abs > 0.0 && x2abs > 0.0) {
+	prod = prod/(x1abs*x2abs);
+    }
+    else {
+	prod = 0.;
+    }
+
+    return prod;
+
+}
+
+//=============================================================================
+
+float ClusterShapes::vecProject(float * x, float * axis) {
+    float axisabs(0.);
+    float prod(0.);
+    for (int i(0); i < 3; ++i) {
+	axisabs += axis[i]*axis[i];
+	prod  += x[i]*axis[i];
+    }
+    axisabs = sqrt(axisabs);
+    if (axisabs > 0.0 ) {
+	prod = prod/axisabs;
+    }
+    else {
+	prod = 0.;
+    }
+    return prod;
+}
+
+//=============================================================================
+
+double ClusterShapes::DistanceHelix(double x, double y, double z, double X0, double Y0,
+				   double R0, double bz, double phi0, double * distRPZ) {
+
+  double phi  = atan2(y-Y0,x-X0);
+  double R    = sqrt( (y-Y0)*(y-Y0) + (x-X0)*(x-X0) );
+  double dXY2 = (R-R0)*(R-R0);
+  double _const_2pi = 2.0*M_PI;
+  double xN = (bz*z + phi0 - phi)/_const_2pi;
+
+  int n1 = 0;
+  int n2 = 0;
+  int nSpirals = 0;
+
+  if (xN > 0) {
+    n1 = (int)xN;
+    n2 = n1 + 1;
+  }
+  else {
+    n1 = (int)xN - 1;
+    n2 = n1 + 1;
+  }
+
+  if (fabs(n1-xN) < fabs(n2-xN)) {
+    nSpirals = n1;
+  }
+  else {
+    nSpirals = n2;
+  }
+
+  double dZ = (phi + _const_2pi*nSpirals - phi0)/bz - z;
+  double dZ2 = dZ*dZ;
+
+  distRPZ[0] = sqrt(dXY2);
+  distRPZ[1] = sqrt(dZ2);
+
+  return sqrt(dXY2 + dZ2);
+
+}
+
+//=============================================================================
+
+int ClusterShapes::transformToEigensystem(float* xStart, int& index_xStart, float X0,
+					  float Rm) {
+
+  if (_ifNotInertia == 1) findInertia();
+
+  float MainAxis[3];
+  float MainCentre[3];
+
+
+  MainAxis[0] = _VecAnalogInertia[0];
+  MainAxis[1] = _VecAnalogInertia[1];
+  MainAxis[2] = _VecAnalogInertia[2];
+  MainCentre[0] = _analogGravity[0];
+  MainCentre[1] = _analogGravity[1];
+  MainCentre[2] = _analogGravity[2];
+
+  int ifirst = 0;
+  float xx[3];
+  float prodmin = 0.0;
+  int index = 0;
+
+  for (int i(0); i < _nHits; ++i) {
+    xx[0] = _xHit[i] - MainCentre[0];
+    xx[1] = _yHit[i] - MainCentre[1];
+    xx[2] = _zHit[i] - MainCentre[2];
+    float prod = vecProject(xx,MainAxis);
+    if (ifirst == 0 || prod < prodmin) {
+      ifirst = 1;
+      prodmin = prod;
+      index = i;
+    }
+  }
+  xStart[0] = MainCentre[0] + prodmin*MainAxis[0];
+  xStart[1] = MainCentre[1] + prodmin*MainAxis[1];
+  xStart[2] = MainCentre[2] + prodmin*MainAxis[2];
+  index_xStart = index;
+
+  for (int i(0); i < _nHits; ++i) {
+    xx[0] = _xHit[i] - xStart[0];
+    xx[1] = _yHit[i] - xStart[1];
+    xx[2] = _zHit[i] - xStart[2];
+    float xx2(0.);
+    for (int j(0); j < 3; ++j) xx2 += xx[j]*xx[j];
+
+    _xl[i] = 0.001 + vecProject(xx,MainAxis);
+    _xt[i] = sqrt(std::max(0.0,xx2 + 0.1 - _xl[i]*_xl[i]));
+    //    std::cout << i << " " << _xl[i] << " " << _xt[i] << " " << _aHit[i] << " "
+    //              << std::endl;
+  }
+
+  for (int i = 0; i < _nHits; ++i) {
+    _t[i] = _xl[i]/X0;
+    _s[i] = _xt[i]/Rm;
+  }
+
+  _ifNotEigensystem = 0;
+
+  return 0; // no error messages at the moment
+
+}
+
+//=============================================================================
+
+float ClusterShapes::calculateChi2Fit3DProfileSimple(float a, float b, float c,
+						     float d) {
+
+  // ClusterShapes::transformToEigensystem needs to be executed before
+
+  float chi2 = 0.0;
+  float Ampl = 0.0;
+
+  for (int i(0); i < _nHits; ++i) {
+    // old definition of Ampl and chi2
+    //Ampl = a*(float)pow(_xl[i],b)*exp(-c*_xl[i]-d*_xt[i]); 
+    //chi2 += ((Ampl - _aHit[i])*(Ampl - _aHit[i]))/(_aHit[i]*_aHit[i]);
+
+    Ampl = a*(float)pow(_xl[i],b)*exp(-c*_xl[i]-d*_xt[i]);
+    chi2 += (log(_aHit[i]) - log(Ampl))*(log(_aHit[i]) - log(Ampl));
+  }
+
+  chi2 = chi2/std::max((float)1.0,(float)(_nHits - 4));
+
+  return chi2;
+
+}
+
+//=============================================================================
+
+float ClusterShapes::calculateChi2Fit3DProfileAdvanced(float E0, float a, float b,
+						       float d, float t0) {
+
+  // ClusterShapes::transformToEigensystem needs to be executed before
+
+  float chi2  = 0.0;
+  float Ampl  = 0.0;
+  float shift = 0.0;
+
+  for (int i(0); i < _nHits; ++i) {
+
+    shift = _t[i]-t0;
+
+    if (shift <= 0) Ampl = 0.0;
+    else {
+      Ampl = E0 * b * invG(a) * pow(b*(shift),a-1) 
+	* exp(-b*(shift)) * exp(-d*_s[i]);
+    }
+
+    // debug 
+    /*
+    std::cout << "OUT : " << Ampl << "  " << E0  << "  " << a  << "  " << b  << "  " 
+	      << d  << "  " << t0  << "  " << _t[i] << "  " << invG(a)  << "  "
+	      << pow(b*(_t[i]-t0),a-1)  << "  " << exp(-b*(_t[i]-t0))  << "  " 
+	      << exp(-d*_s[i]) 
+	      << std::endl;
+    */
+
+    chi2 += ((Ampl - _aHit[i])*(Ampl - _aHit[i]))/(_aHit[i]*_aHit[i]);
+    //chi2 += (log(_aHit[i]) - log(Ampl))*(log(_aHit[i]) - log(Ampl));
+    //chi2 += log((Ampl - _aHit[i])*(Ampl - _aHit[i]))/log(_aHit[i]*_aHit[i]);
+
+  }
+  chi2 = chi2/std::max((float)1.0,(float)(_nHits - 4));
+
+  return chi2;
+
+}
+
+//=============================================================================
+
+int ClusterShapes::fit3DProfileSimple(float& chi2, float& a, float& b, float& c,
+				      float& d) {
+
+  // Fit function: _aHit[i] = a * pow(_xl[i],b) * exp(-c*_xl[i]) * exp(-d*_xt[i])
+
+  float Slnxl(0.);
+  float Sxl(0.);
+  float Sxt(0.);
+  float Sln2xl(0.);
+  float Sxllnxl(0.);
+  float Sxtlnxl(0.);
+  float Sxlxl(0.);
+  float Sxlxt(0.);
+  float Sxtxt(0.);
+  float SlnA(0.);
+  float SlnAlnxl(0.);
+  float SlnAxl(0.);
+  float SlnAxt(0.);
+
+  // for a quadratic matrix
+  for (int i = 0; i < _nHits; i++) {
+    Slnxl += log(_xl[i]);
+    Sxl += _xl[i];
+    Sxt += _xt[i];
+    Sln2xl += log(_xl[i])*log(_xl[i]);
+    Sxllnxl += _xl[i]*log(_xl[i]);
+    Sxtlnxl += _xt[i]*log(_xl[i]);
+    Sxlxl += _xl[i]*_xl[i];
+    Sxlxt += _xl[i]*_xt[i];
+    Sxtxt += _xt[i]*_xt[i];
+    SlnA += log(_aHit[i]);
+    SlnAlnxl += log(_aHit[i])*log(_xl[i]);
+    SlnAxl += log(_aHit[i])*_xl[i];
+    SlnAxt += log(_aHit[i])*_xt[i]; 
+  }
+  // create system of linear equations, written as Ae = z
+
+  gsl_matrix* A = gsl_matrix_alloc(4,4);
+  gsl_vector* z = gsl_vector_alloc(4);
+  gsl_vector* e = gsl_vector_alloc(4);
+  
+  // initialise matrix and vectors
+  
+  gsl_matrix_set(A,0,0,_nHits);
+  gsl_matrix_set(A,0,1,Slnxl);
+  gsl_matrix_set(A,0,2,-Sxl);
+  gsl_matrix_set(A,0,3,-Sxt);
+  
+  gsl_matrix_set(A,1,0,Slnxl);
+  gsl_matrix_set(A,1,1,Sln2xl);
+  gsl_matrix_set(A,1,2,-Sxllnxl);
+  gsl_matrix_set(A,1,3,-Sxtlnxl);
+  
+  gsl_matrix_set(A,2,0,-Sxl);
+  gsl_matrix_set(A,2,1,-Sxllnxl);
+  gsl_matrix_set(A,2,2,Sxlxl);
+  gsl_matrix_set(A,2,3,Sxlxt);
+
+  gsl_matrix_set(A,3,0,-Sxt);
+  gsl_matrix_set(A,3,1,-Sxtlnxl);
+  gsl_matrix_set(A,3,2,Sxlxt);
+  gsl_matrix_set(A,3,3,Sxtxt);
+
+  gsl_vector_set(z,0,SlnA);
+  gsl_vector_set(z,1,SlnAlnxl);
+  gsl_vector_set(z,2,-SlnAxl);
+  gsl_vector_set(z,3,-SlnAxt);
+
+  gsl_linalg_HH_solve(A,z,e);
+
+  a = exp(gsl_vector_get(e,0));
+  b = gsl_vector_get(e,1);
+  c = gsl_vector_get(e,2);
+  d = gsl_vector_get(e,3);
+
+  chi2 = calculateChi2Fit3DProfileSimple(a,b,c,d);
+  
+  gsl_matrix_free(A);
+  gsl_vector_free(z);
+  gsl_vector_free(e);
+
+  int result = 0;  // no error handling at the moment
+  return result;
+
+}
+
+//=============================================================================
+
+int ClusterShapes::fit3DProfileAdvanced(float& chi2, double* par_init, double* par,
+					int npar, float* t, float* s, float* E, 
+					float E0) {
+
+  // local variables
+  int status = 0;
+  int iter = 0;
+  int max_iter = 1000;
+
+
+  // converging criteria
+  const double abs_error = 0.0;
+  const double rel_error = 1e-1;
+
+
+
+  gsl_multifit_function_fdf fitfunct;
+
+  const gsl_multifit_fdfsolver_type* T = gsl_multifit_fdfsolver_lmsder;
+
+  gsl_multifit_fdfsolver* Solver = gsl_multifit_fdfsolver_alloc(T,_nHits,npar);
+
+  gsl_matrix* covar = gsl_matrix_alloc(npar,npar);   // covariance matrix
+
+  data DataSet;
+  DataSet.n = _nHits;
+  DataSet.x = &t[0];
+  DataSet.y = &s[0];
+  DataSet.z = &E[0];  // _aHit[0]; // ???? normalise per volume ????
+
+
+  fitfunct.f = &ShapeFitFunct;
+  fitfunct.df = &dShapeFitFunct;
+  fitfunct.fdf = &fdfShapeFitFunct;
+  fitfunct.n = _nHits;
+  fitfunct.p = npar;
+  fitfunct.params = &DataSet;
+
+  gsl_vector_view pinit = gsl_vector_view_array(par_init,npar);
+  gsl_multifit_fdfsolver_set(Solver,&fitfunct,&pinit.vector);
+
+  gsl_set_error_handler_off();
+
+  // perform fit
+  do {
+    iter++;
+    std::cout << "Multidimensional Fit Iteration started ... ... ";
+    status = gsl_multifit_fdfsolver_iterate(Solver);
+    std::cout << "--- DONE ---" << std::endl;
+
+    if (status) break;
+    status = gsl_multifit_test_delta (Solver->dx,Solver->x,abs_error,rel_error);
+
+    // debug
+    /*
+    //    E0  = (float)gsl_vector_get(Solver->x,0);
+    par[0] = (float)gsl_vector_get(Solver->x,0);
+    par[1] = (float)gsl_vector_get(Solver->x,1);
+    par[2] = (float)gsl_vector_get(Solver->x,2);
+    par[3] = (float)gsl_vector_get(Solver->x,3);
+
+    std::cout << "Status of multidimensional fit : " << status << "  "
+	      << "Iterations : " << iter << std::endl;
+    std::cout << "E0 : " <<  "FIXED" << "\t" << "A : " << par[0] << "\t" << "B : " 
+	      <<  par[1] << "\t" << "D : " << par[2] << "\t" << "t0 : "  << par[3] 
+	      << std::endl << std::endl;
+    */
+
+  } while ( status==GSL_CONTINUE && iter < max_iter);
+
+  gsl_multifit_covar (Solver->J,rel_error,covar);
+
+  //  E0  = (float)gsl_vector_get(Solver->x,0);
+  par[0] = (float)gsl_vector_get(Solver->x,0); // A
+  par[1] = (float)gsl_vector_get(Solver->x,1); // B
+  par[2] = (float)gsl_vector_get(Solver->x,2); // D
+  par[3] = (float)gsl_vector_get(Solver->x,3); // t0
+
+  gsl_multifit_fdfsolver_free(Solver);
+  gsl_matrix_free(covar);
+  
+  chi2 = calculateChi2Fit3DProfileAdvanced(E0,par[0],par[1],par[2],par[3]);
+  if (status) chi2 = -1.0;
+
+  int result = 0;  // no error handling at the moment
+  return result;
+
+}
+
+//=============================================================================
+
diff --git a/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/ClusterShapes.h b/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/ClusterShapes.h
new file mode 100644
index 00000000..2b97b685
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/ClusterShapes.h
@@ -0,0 +1,356 @@
+#ifndef ClusterShapes_h
+#define ClusterShapes_h
+
+
+#include <stdio.h>
+#include <iostream>
+#include <iomanip>
+#include <string>
+#include <sstream>
+#include <cstdlib>
+#include "HelixClass.h"
+#include <math.h>
+
+
+/**
+ *    Utility class to derive properties of clusters, such as centre of gravity,
+ *    axes of inertia, fits of the cluster shape and so on. All the details are
+ *    explained in the documentation of the methods. Several classes of the GSL 
+ *    (GNU Scientific Library) are needed in this class.
+ *
+ *    @authors V. Morgunov (ITEP/DESY), A. Raspereza (DESY), O. Wendt (DESY)
+ *    @version $Id: ClusterShapes.h,v 1.14 2007-04-27 13:56:53 owendt Exp $
+ *
+ */
+class ClusterShapes {
+
+ public:
+
+  /**
+   *    Constructor
+   *    @param nhits : number of hits in the cluster
+   *    @param a     : amplitudes of elements ('cells') of the cluster. Stored in 
+   *                   an array, with one entry for each element ('cell'). Each entry 
+   *                   is depending on coordinates x,y,z (Cartesian), which are stored 
+   *                   in the arrays x,y,z.
+   *    @param x,y,z : array of coordinates corresponding to the array of amplitudes a.
+   *
+   *
+   */
+  ClusterShapes(int nhits, float* a, float* x, float* y, float* z);
+
+
+  /**
+   *    Destructor
+   */
+  ~ClusterShapes();
+
+  /**
+   *    Defining errors for Helix Fit
+   */
+   void setErrors(float *ex, float* ey, float *ez);
+
+   /**
+    *   Defining hit types for Helix Fit :
+    *   type 1 - cyllindrical detector
+    *   type 2 - Z disk detector
+    */
+   void setHitTypes(int *ityp);
+
+
+  /**
+   * returns the number of elements of the cluster
+   */
+   int getNumberOfHits();
+
+  /**
+   * returns the accumulated amplitude for the whole cluster (just the sum of the 
+   * energy in all the entries of the cluster)
+   */
+  float getTotalAmplitude();
+
+  /**
+   * returns an array, which represents a vector from the origin of the
+   * coordiante system, i.\ e.\ IP, to the centre of gravity of the cluster. The centre 
+   * of gravity is calculated with the energy of the entries of the cluster.
+   */
+  float* getCentreOfGravity();
+
+  /** US spelling of getCentreOfGravity */
+  inline float* getCenterOfGravity() { return getCentreOfGravity() ; }
+  
+  /**
+   * array of the inertias of mass (i.\ e.\ energy) corresponding to the three main axes 
+   * of inertia. The array is sorted in ascending order.
+   */
+  float* getEigenValInertia();
+
+  /**
+   * array of the three main axes of inertia (9 entries) starting
+   * with the axis corresponding to the smallest inertia of mass 
+   * (main principal axis). All axes are normalised to a length 
+   * of 1.
+   */
+  float* getEigenVecInertia();
+
+  /**
+   * 'mean' width of the cluster perpendicular to the main 
+   * principal axis, defined as: 
+   * width := sqrt( 1/TotalAmplitude * Sum(a[i]*d[i]*d[i]) ),
+   * where d[i] is the distance of the i-th point to the main
+   * principal axis.
+   */
+  float getWidth();
+
+  /**
+   * returns the coordinates of the cluster transformed into 
+   * the CoG-System.
+   * @param xlong  : pointer to an array, where the calculated longitudinal coordiantes
+   *                 are stored in.
+   * @param xtrans : pointer to an array, where the calculated transversal coordiantes
+   *                 are stored in.
+   */
+  int getEigenSytemCoordinates(float* xlong, float* xtrans);
+
+  /**
+   * returns the coordinates and the amplitudes of the cluster
+   * transformed into the CoG-System.
+   * @param xlong  : pointer to an array, where the calculated longitudinal coordiantes
+   *                 are stored in.
+   * @param xtrans : pointer to an array, where the calculated transversal coordiantes
+   *                 are stored in.
+   * @param a      : pointer to an array, where the amplitudes corresponding to the 
+   *                 longitudinal and transversal coordiantes are stored in.
+   */
+  int getEigenSytemCoordinates(float* xlong, float* xtrans, float* a);
+
+  /**
+   * performs a least square fit on the shape of an electro-
+   * magnetic-shower, which is defined as:
+   * A[i] = a * (xl[i]-xl0)^b * exp(-c*(xl[i]-xl0)) * exp(-d*xt[i]),
+   * where A[i] is the array of amplitudes, xl[i] is the 
+   * coordinate of the actual point along the main principal 
+   * axis and xt[i] the coordinate perpendicular to it. The return value 
+   * of the method itself is not used at the moment (always returns 0).
+   * @param a,b,c,d,xl0  : references to the parameters, which are fitted.
+   * @param chi2         : reference to the chi2 of the fit
+   * @param xStart       : pointer to the 'initial hit' of the cluster. It is defined 
+   *                       as the point with the largest distance to the CoG measured 
+   *                       in the direction towards the IP.
+   * @param index_xStart : index of the point in the cluster corresponding to xStart
+   * @param X0           : radiation length of the detector material. For a composite 
+   *                       detector this is meant to be the 'mean' radiation length.
+   * @param Rm           : Moliere radius of the the detector material. For a composite 
+   *                       detector this is meant to be the 'mean' Moliere radius.
+   */
+  int fit3DProfile(float& chi2, float& a, float& b, float& c, float& d, float& xl0, 
+		   float * xStart, int& index_xStart, float X0, float Rm);
+
+  /**
+   * returns the chi2 of the fit in the method Fit3DProfile (if simple
+   * parametrisation is used)for a given set of parameters a,b,c,d
+   * @param a,b,c,d,xl0  : fitted parameters, which have been calculated before
+   * @param X0           : radiation length of the detector material. For a composite 
+   *                       detector this is meant to be the 'mean' radiation length.
+   * @param Rm           : Moliere radius of the the detector material. For a composite 
+   *                       detector this is meant to be the 'mean' Moliere radius.
+   */
+  float getChi2Fit3DProfileSimple(float a, float b, float c, float d, float X0, 
+				  float Rm);
+
+  /**
+   * returns the chi2 of the fit in the method Fit3DProfile (if advanced
+   * parametrisation is used) for a given set of parameters E0,a,b,d,t0
+   * @param E0,a,b,d,t0 : fitted parameters, which have been calculated before
+   * @param X0          : radiation length of the detector material. For a composite 
+   *                      detector this is meant to be the 'mean' radiation length.
+   * @param Rm          : Moliere radius of the the detector material. For a composite 
+   *                      detector this is meant to be the 'mean' Moliere radius.
+   */
+  float getChi2Fit3DProfileAdvanced(float E0, float a, float b, float d, float t0, 
+				    float X0, float Rm);
+
+  /**
+   * performs a least square fit on a helix path in space, which
+   * which is defined as (Cartesian coordiantes):
+   *
+   * 1. parametrisation:
+   * x[i] = x0 + R*cos(b*z[i] + phi0)
+   * y[i] = y0 + R*sin(b*z[i] + phi0)
+   * z[i] = z[i],
+   * where x0,y0,R,b and phi0 are the parameters to be fitted and
+   * x[i],y[i],z[i] are the (Cartesian) coordiantes of the space
+   * points.
+   * 
+   * 2. parametrisation:   
+   * x[i] = x0 + R*cos(phi)
+   * y[i] = y0 + R*sin(phi)
+   * z[i] = z0 + b*phi
+   * and phi = atan2( y[i]-y0 , x[i]-x0 ),
+   * where x0,y0,z0,R and b are the parameters to be fitted and
+   * x[i],y[i],z[i] are the (Cartesian) coordiantes of the space
+   * points.
+   * 
+   * The method returns 1 if an error occured and 0 if not.
+   *
+   * The following output/input parameters are returned/needed:
+   *
+   * OUTPUTS:
+   * @param parameter     : array of parameters to be fitted.
+   *                        For parametrisation 1: parameter[5] = {x0,y0,R,b,phi0}
+   *                        For parametrisation 2: parameter[5] = {x0,y0,z0,R,b}
+   * @param dparameter    : error on the parameters, that means: 
+   *                        dparameter[i] = sqrt( CovarMatrix[i][i] )
+   * @param chi2          : chi2 of the fit
+   * @param distmax       : maximal distance between the points x[i],y[i]
+   *                        z[i] and the fitted function
+   *
+   * INPUTS:
+   * @param parametrisation : 1 for first and 2 for second parametrisation (see above)
+   * @param max_iter        : maximal number of iterations, before fit cancels
+   * @param status_out      : if set to 1, only the initial parameters of
+   *                          the fit are calculated and are stored in
+   *                          parameter. The entries of dparameter are
+   *                          set to 0.0
+   */
+  int FitHelix(int max_iter, int status_out, int parametrisation,
+	       double* parameter, double* dparameter, double& chi2, double& distmax, int direction=1);
+
+
+  int FitHelix(int max_iter, int status_out, int parametrisation,
+	       float* parameter, float* dparameter, float& chi2, float& distmax, int direction=1);
+
+  /**
+   * distance to the centre of gravity measured from IP
+   * (absolut value of the vector to the centre of gravity)
+   */
+  inline float radius() { return _radius; }
+
+  /**
+   * largest spatial axis length of the ellipsoid derived
+   * by the inertia tensor (by their eigenvalues and eigen-
+   * vectors)
+   */
+  inline float getElipsoid_r1() { return _r1; }
+
+  /**
+   * medium spatial axis length of the ellipsoid derived
+   * by the inertia tensor (by their eigenvalues and eigen-
+   * vectors)
+   */
+  inline float getElipsoid_r2() { return _r2; }
+
+  /**
+   * smallest spatial axis length of the ellipsoid derived
+   * by the inertia tensor (by their eigenvalues and eigen-   
+   * vectors)
+   */
+  inline float getElipsoid_r3() { return _r3; }
+
+  /**
+   * volume of the ellipsoid
+   */
+  inline float getElipsoid_vol() { return _vol; }
+
+  /**
+   * average radius of the ellipsoid (qubic root of volume)
+   */
+  inline float getElipsoid_r_ave() { return _r_ave; }
+
+  /**
+   * density of the ellipsoid defined by: totAmpl/vol
+   */
+  inline float getElipsoid_density() { return _density; }
+
+  /**
+   * eccentricity of the ellipsoid defined by: 
+   * Width/r1
+   */
+  inline float getElipsoid_eccentricity() { return _eccentricity; }
+
+  /**
+   * distance from centre of gravity to the point most far 
+   * away from IP projected on the main principal axis
+   */
+  inline float getElipsoid_r_forw() { return _r1_forw; }
+
+  /**
+   * distance from centre of gravity to the point nearest 
+   * to IP projected on the main principal axis    
+   */
+  inline float getElipsoid_r_back() { return _r1_back; }
+
+
+
+
+
+ private:
+
+  int _nHits;
+
+  float* _aHit;
+  float* _xHit;
+  float* _yHit;
+  float* _zHit;
+  float* _exHit;
+  float* _eyHit;
+  float* _ezHit;
+  int* _types;
+  float* _xl;
+  float* _xt;
+  float* _t;
+  float* _s;
+
+  int   _ifNotGravity;
+  float _totAmpl;
+  float _radius;
+  float _xgr;
+  float _ygr;
+  float _zgr;
+  float _analogGravity[3];
+
+  int   _ifNotWidth;
+  float _analogWidth;
+
+  int   _ifNotInertia;
+  float _ValAnalogInertia[3];
+  float _VecAnalogInertia[9];
+
+  int _ifNotEigensystem;
+
+  int   _ifNotElipsoid;
+  float _r1           ;  // Cluster spatial axis length -- the largest
+  float _r2           ;  // Cluster spatial axis length -- less
+  float _r3           ;  // Cluster spatial axis length -- less
+  float _vol          ;  // Cluster ellipsoid volume
+  float _r_ave        ;  // Cluster average radius  (qubic root)
+  float _density      ;  // Cluster density
+  float _eccentricity ;  // Cluster Eccentricity
+  float _r1_forw      ;
+  float _r1_back      ;
+
+  void  findElipsoid();
+  void  findGravity();
+  void  findInertia();
+  void  findWidth();
+  float findDistance(int i);
+  float vecProduct(float * x1, float * x2);
+  float vecProject(float * x, float * axis);
+  double DistanceHelix(double x, double y, double z, double X0, double Y0, double R0, double bz,
+		      double phi0, double * distRPhiZ);
+  int transformToEigensystem(float* xStart, int& index_xStart, float X0, float Xm);
+  float calculateChi2Fit3DProfileSimple(float a, float b, float c, float d);
+  float calculateChi2Fit3DProfileAdvanced(float E0, float a, float b, float d, float t0);
+  int fit3DProfileSimple(float& chi2, float& a, float& b, float& c, float& d);
+  int fit3DProfileAdvanced(float& chi2, double* par_init, double* par, int npar,
+			   float* t, float* s, float* E, float E0);
+
+  // private methods for non-linear, multidim. fitting (helix)
+  // static int functParametrisation1(const gsl_vector* par, void* data, gsl_vector* f);
+  // static int dfunctParametrisation1(const gsl_vector* par, void* d, gsl_matrix* J);
+  // static int fdfParametrisation1(const gsl_vector* par, void* d, gsl_vector* f, gsl_matrix* J);
+
+
+};
+
+#endif
diff --git a/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/HelixClass.cc b/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/HelixClass.cc
new file mode 100644
index 00000000..6582e17e
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/HelixClass.cc
@@ -0,0 +1,768 @@
+#include "HelixClass.h"
+#include <math.h>
+#include <stdlib.h>
+#include <iostream>
+#include "ced_cli.h"
+
+HelixClass::HelixClass() {
+    _const_2pi = 2.0*M_PI;
+    _const_pi2 = 0.5*M_PI;
+    _FCT = 2.99792458E-4;
+}
+
+HelixClass::~HelixClass() {}
+
+void HelixClass::Initialize_VP(float * pos, float * mom, float q, float B) {
+    _referencePoint[0] = pos[0];
+    _referencePoint[1] = pos[1];
+    _referencePoint[2] = pos[2];
+    _momentum[0] = mom[0];
+    _momentum[1] = mom[1];
+    _momentum[2] = mom[2];
+    _charge = q;
+    _bField = B;
+    _pxy = sqrt(mom[0]*mom[0]+mom[1]*mom[1]);
+    _radius = _pxy / (_FCT*B);
+    _omega = q/_radius;
+    _tanLambda = mom[2]/_pxy;
+    _phiMomRefPoint = atan2(mom[1],mom[0]);
+    _xCentre = pos[0] + _radius*cos(_phiMomRefPoint-_const_pi2*q);
+    _yCentre = pos[1] + _radius*sin(_phiMomRefPoint-_const_pi2*q);
+    _phiRefPoint = atan2(pos[1]-_yCentre,pos[0]-_xCentre);
+    _phiAtPCA = atan2(-_yCentre,-_xCentre);
+    _phi0 = -_const_pi2*q + _phiAtPCA;
+    while (_phi0<0) _phi0+=_const_2pi;
+    while (_phi0>=_const_2pi) _phi0-=_const_2pi;
+    _xAtPCA = _xCentre + _radius*cos(_phiAtPCA);
+    _yAtPCA = _yCentre + _radius*sin(_phiAtPCA);
+    //    _d0 = -_xAtPCA*sin(_phi0) + _yAtPCA*cos(_phi0);
+    double pxy = double(_pxy);
+    double radius = pxy/double(_FCT*B);
+    double xCentre = double(pos[0]) + radius*double(cos(_phiMomRefPoint-_const_pi2*q));
+    double yCentre = double(pos[1]) + radius*double(sin(_phiMomRefPoint-_const_pi2*q));
+    
+    double d0;
+
+    if (q>0) {
+      d0 = double(q)*radius - double(sqrt(xCentre*xCentre+yCentre*yCentre));
+    }
+    else {
+      d0 = double(q)*radius + double(sqrt(xCentre*xCentre+yCentre*yCentre));
+    }
+
+    _d0 = float(d0);
+
+//     if (fabs(_d0)>0.001 ) {
+//       std::cout << "New helix : " << std::endl;
+//       std::cout << " Position : " << pos[0] 
+// 		<< " " << pos[1]
+// 		<< " " << pos[2] << std::endl;
+//       std::cout << " Radius = " << _radius << std::endl;
+//       std::cout << " RC = " << sqrt(_xCentre*_xCentre+_yCentre*_yCentre) << std::endl;  
+//       std::cout << " D0 = " << _d0 << std::endl;
+//     }
+
+    _pxAtPCA = _pxy*cos(_phi0);
+    _pyAtPCA = _pxy*sin(_phi0);
+    float deltaPhi = _phiRefPoint - _phiAtPCA;    
+    float xCircles = -pos[2]*q/(_radius*_tanLambda) - deltaPhi;
+    xCircles = xCircles/_const_2pi;
+    int nCircles;
+    int n1,n2;
+
+    if (xCircles >= 0.) {
+	n1 = int(xCircles);
+	n2 = n1 + 1;
+    }
+    else {
+	n1 = int(xCircles) - 1;
+	n2 = n1 + 1;
+    }
+    
+    if (fabs(n1-xCircles) < fabs(n2-xCircles)) {
+	nCircles = n1;
+    }
+    else {
+	nCircles = n2;
+    }
+    _z0 = pos[2] + _radius*_tanLambda*q*(deltaPhi + _const_2pi*nCircles);
+
+}
+
+void HelixClass::Initialize_Canonical(float phi0, float d0, float z0, 
+				      float omega, float tanLambda, float B) {
+    _omega = omega;
+    _d0 = d0;
+    _phi0 = phi0;
+    _z0 = z0;
+    _tanLambda = tanLambda;
+    _charge = omega/fabs(omega);
+    _radius = 1./fabs(omega);
+    _xAtPCA = -_d0*sin(_phi0);
+    _yAtPCA = _d0*cos(_phi0);    
+    _referencePoint[0] = _xAtPCA;
+    _referencePoint[1] = _yAtPCA;
+    _referencePoint[2] = _z0;
+    _pxy = _FCT*B*_radius;
+    _momentum[0] = _pxy*cos(_phi0);
+    _momentum[1] = _pxy*sin(_phi0);
+    _momentum[2] = _tanLambda * _pxy;    
+    _pxAtPCA = _momentum[0];
+    _pyAtPCA = _momentum[1];
+    _phiMomRefPoint = atan2(_momentum[1],_momentum[0]);
+    _xCentre = _referencePoint[0] + 
+      _radius*cos(_phi0-_const_pi2*_charge);
+    _yCentre = _referencePoint[1] + 
+      _radius*sin(_phi0-_const_pi2*_charge);
+    _phiAtPCA = atan2(-_yCentre,-_xCentre);
+    _phiRefPoint =  _phiAtPCA ;
+    _bField = B;
+}
+
+
+void HelixClass::Initialize_BZ(float xCentre, float yCentre, float radius, 
+			       float bZ, float phi0, float B, float signPz,
+			       float zBegin) {
+
+  _radius = radius;
+  _pxy = _FCT*B*_radius;
+  _charge = -(bZ*signPz)/fabs(bZ*signPz);
+  _momentum[2] = -_charge*_pxy/(bZ*_radius);
+  _xCentre = xCentre;
+  _yCentre = yCentre;
+  _omega = _charge/radius;
+  _phiAtPCA = atan2(-_yCentre,-_xCentre);
+  _phi0 = -_const_pi2*_charge + _phiAtPCA;
+  while (_phi0<0) _phi0+=_const_2pi;
+  while (_phi0>=_const_2pi) _phi0-=_const_2pi;
+  _xAtPCA = _xCentre + _radius*cos(_phiAtPCA);
+  _yAtPCA = _yCentre + _radius*sin(_phiAtPCA);
+  _d0 = -_xAtPCA*sin(_phi0) + _yAtPCA*cos(_phi0);
+  _pxAtPCA = _pxy*cos(_phi0);
+  _pyAtPCA = _pxy*sin(_phi0);
+  _referencePoint[2] = zBegin;
+  _referencePoint[0] = xCentre + radius*cos(bZ*zBegin+phi0);
+  _referencePoint[1] = yCentre + radius*sin(bZ*zBegin+phi0);
+  _phiRefPoint = atan2(_referencePoint[1]-_yCentre,_referencePoint[0]-_xCentre);
+  _phiMomRefPoint =  -_const_pi2*_charge + _phiRefPoint;
+  _tanLambda = _momentum[2]/_pxy;
+  _momentum[0] = _pxy*cos(_phiMomRefPoint);
+  _momentum[1] = _pxy*sin(_phiMomRefPoint);
+  
+  float deltaPhi = _phiRefPoint - _phiAtPCA;    
+  float xCircles = bZ*_referencePoint[2] - deltaPhi;
+  xCircles = xCircles/_const_2pi;
+  int nCircles;
+  int n1,n2;
+
+  if (xCircles >= 0.) {
+    n1 = int(xCircles);
+    n2 = n1 + 1;
+  }
+  else {
+    n1 = int(xCircles) - 1;
+    n2 = n1 + 1;
+  }
+  
+  if (fabs(n1-xCircles) < fabs(n2-xCircles)) {
+    nCircles = n1;
+  }
+  else {
+    nCircles = n2;
+  }  
+  _z0 = _referencePoint[2] - (deltaPhi + _const_2pi*nCircles)/bZ;  
+  _bField = B;
+
+}
+
+const float * HelixClass::getMomentum() {
+    return _momentum;
+}
+const float * HelixClass::getReferencePoint() {
+    return _referencePoint;
+}
+float HelixClass::getPhi0() {
+  if (_phi0<0.0)
+    _phi0 += 2*M_PI;
+  return _phi0;
+}
+float HelixClass::getD0() {
+    return _d0;
+}
+float HelixClass::getZ0() {
+    return _z0;
+}
+float HelixClass::getOmega() {
+    return _omega;
+}
+float HelixClass::getTanLambda() {
+    return _tanLambda;
+}
+float HelixClass::getPXY() {
+    return _pxy;
+}
+float HelixClass::getXC() {
+  return _xCentre;
+}
+
+float HelixClass::getYC() {
+  return _yCentre;
+}
+
+float HelixClass::getRadius() {
+  return _radius;
+}
+
+float HelixClass::getBz() {
+  return _bZ;
+}
+
+float HelixClass::getPhiZ() {
+  return _phiZ;
+}
+
+float HelixClass::getCharge() {
+    return _charge;
+}
+
+float HelixClass::getPointInXY(float x0, float y0, float ax, float ay, 
+			      float * ref , float * point) {
+
+  float time;
+
+  float AA = sqrt(ax*ax+ay*ay);
+
+
+  if (AA <= 0) {
+    time = -1.0e+20; 
+    return time;
+  }
+
+
+  float BB = ax*(x0-_xCentre) + ay*(y0-_yCentre);
+  BB = BB / AA;
+
+  float CC = (x0-_xCentre)*(x0-_xCentre) 
+    + (y0-_yCentre)*(y0-_yCentre) - _radius*_radius;
+
+  CC = CC / AA;
+
+  float DET = BB*BB - CC;
+  float tt1 = 0.;
+  float tt2 = 0.;
+  float xx1,xx2,yy1,yy2; 
+
+
+  if (DET < 0 ) {
+    time = 1.0e+10;
+    point[0]=0.0;
+    point[1]=0.0;
+    point[2]=0.0;
+    return time;
+  }
+  
+  
+  tt1 = - BB + sqrt(DET);
+  tt2 = - BB - sqrt(DET);
+
+  xx1 = x0 + tt1*ax;
+  yy1 = y0 + tt1*ay;
+  xx2 = x0 + tt2*ax;
+  yy2 = y0 + tt2*ay;
+  
+  float phi1 = atan2(yy1-_yCentre,xx1-_xCentre);
+  float phi2 = atan2(yy2-_yCentre,xx2-_xCentre);
+  float phi0 = atan2(ref[1]-_yCentre,ref[0]-_xCentre);
+
+  float dphi1 = phi1 - phi0;
+  float dphi2 = phi2 - phi0;
+
+  if (dphi1 < 0 && _charge < 0) {
+    dphi1 = dphi1 + _const_2pi;
+  }
+  else if (dphi1 > 0 && _charge > 0) { 
+    dphi1 = dphi1 - _const_2pi;
+  }
+
+  if (dphi2 < 0 && _charge < 0) {
+    dphi2 = dphi2 + _const_2pi;
+  }
+  else if (dphi2 > 0 && _charge > 0) { 
+    dphi2 = dphi2 - _const_2pi;
+  }
+
+  // Times
+  tt1 = -_charge*dphi1*_radius/_pxy;
+  tt2 = -_charge*dphi2*_radius/_pxy;
+
+  if (tt1 < 0. )
+    std::cout << "WARNING " << tt1 << std::endl;
+  if (tt2 < 0. )
+    std::cout << "WARNING " << tt2 << std::endl;
+  
+
+  if (tt1 < tt2) {
+    point[0] = xx1;
+    point[1] = yy1;
+    time = tt1;
+  }
+  else {
+    point[0] = xx2;
+    point[1] = yy2;
+    time = tt2;
+  }
+
+  point[2] = ref[2]+time*_momentum[2];
+
+  
+
+  return time;
+
+}
+
+
+float HelixClass::getPointOnCircle(float Radius, float * ref, float * point) {
+
+  float distCenterToIP = sqrt(_xCentre*_xCentre + _yCentre*_yCentre);
+
+  point[0] = 0.0;
+  point[1] = 0.0;
+  point[2] = 0.0;
+
+  if ((distCenterToIP+_radius)<Radius) {
+    float xx = -1.0e+20;
+    return xx;
+  }
+
+  if ((_radius+Radius)<distCenterToIP) {
+    float xx = -1.0e+20;
+    return xx;
+  }
+
+  float phiCentre = atan2(_yCentre,_xCentre);
+  float phiStar   = Radius*Radius + distCenterToIP*distCenterToIP 
+                                    - _radius*_radius;
+
+  phiStar = 0.5*phiStar/fmax(1.0e-20,Radius*distCenterToIP);
+  
+  if (phiStar > 1.0) 
+    phiStar = 0.9999999;
+  
+  if (phiStar < -1.0)
+    phiStar = -0.9999999;
+  
+  phiStar = acos(phiStar);
+
+  float tt1,tt2,time;
+
+  float xx1 = Radius*cos(phiCentre+phiStar);
+  float yy1 = Radius*sin(phiCentre+phiStar);
+
+  float xx2 = Radius*cos(phiCentre-phiStar);
+  float yy2 = Radius*sin(phiCentre-phiStar);
+
+
+  float phi1 = atan2(yy1-_yCentre,xx1-_xCentre);
+  float phi2 = atan2(yy2-_yCentre,xx2-_xCentre);
+  float phi0 = atan2(ref[1]-_yCentre,ref[0]-_xCentre);
+
+  float dphi1 = phi1 - phi0;
+  float dphi2 = phi2 - phi0;
+
+  if (dphi1 < 0 && _charge < 0) {
+    dphi1 = dphi1 + _const_2pi;
+  }
+  else if (dphi1 > 0 && _charge > 0) { 
+    dphi1 = dphi1 - _const_2pi;
+  }
+
+  if (dphi2 < 0 && _charge < 0) {
+    dphi2 = dphi2 + _const_2pi;
+  }
+  else if (dphi2 > 0 && _charge > 0) { 
+    dphi2 = dphi2 - _const_2pi;
+  }
+
+  // Times
+  tt1 = -_charge*dphi1*_radius/_pxy;
+  tt2 = -_charge*dphi2*_radius/_pxy;
+
+  if (tt1 < 0. )
+    std::cout << "WARNING " << tt1 << std::endl;
+  if (tt2 < 0. )
+    std::cout << "WARNING " << tt2 << std::endl;
+  
+
+  float time2;
+  if (tt1 < tt2) {
+    point[0] = xx1;
+    point[1] = yy1;
+    point[3] = xx2;
+    point[4] = yy2;
+    time = tt1;
+    time2 = tt2;
+  }
+  else {
+    point[0] = xx2;
+    point[1] = yy2;
+    point[3] = xx1;
+    point[4] = yy1;
+    time = tt2;
+    time2 = tt1;
+  }
+
+  point[2] = ref[2]+time*_momentum[2];
+  point[5] = ref[2]+time2*_momentum[2];
+  
+
+  return time;
+
+}
+
+
+float HelixClass::getPointInZ(float zLine, float * ref, float * point) {
+
+  float time = zLine - ref[2];
+
+  if (_momentum[2] == 0.) {
+    time = -1.0e+20;
+    point[0] = 0.;
+    point[1] = 0.;
+    point[2] = 0.;
+    return time;
+  }
+
+  time = time/_momentum[2];
+
+  float phi0 = atan2(ref[1] - _yCentre , ref[0] - _xCentre);
+  float phi = phi0 - _charge*_pxy*time/_radius;
+  float xx = _xCentre + _radius*cos(phi);
+  float yy = _yCentre + _radius*sin(phi);
+
+  point[0] = xx;
+  point[1] = yy;
+  point[2] = zLine;
+
+  return time;
+
+
+}
+
+float HelixClass::getDistanceToPoint(float * xPoint, float * Distance) {
+
+  float zOnHelix;
+  float phi = atan2(xPoint[1]-_yCentre,xPoint[0]-_xCentre);
+  float phi0 = atan2(_referencePoint[1]-_yCentre,_referencePoint[0]-_xCentre);
+  //calculate distance to XYprojected centre of Helix, comparing this with distance to radius around centre gives DistXY
+  float DistXY = (_xCentre-xPoint[0])*(_xCentre-xPoint[0]) + (_yCentre-xPoint[1])*(_yCentre-xPoint[1]);
+  DistXY = sqrt(DistXY);
+  DistXY = fabs(DistXY - _radius);
+  
+  int nCircles = 0;
+
+  if (fabs(_tanLambda*_radius)>1.0e-20) {
+    float xCircles = phi0 - phi -_charge*(xPoint[2]-_referencePoint[2])/(_tanLambda*_radius);
+    xCircles = xCircles/_const_2pi;
+    int n1,n2;
+
+    if (xCircles >= 0.) {
+	n1 = int(xCircles);
+	n2 = n1 + 1;
+    }
+    else {
+	n1 = int(xCircles) - 1;
+	n2 = n1 + 1;
+    }
+    
+    if (fabs(n1-xCircles) < fabs(n2-xCircles)) {
+	nCircles = n1;
+    }
+    else {
+	nCircles = n2;
+    }
+
+  }
+  
+  float DPhi = _const_2pi*((float)nCircles) + phi - phi0;
+
+  zOnHelix = _referencePoint[2] - _charge*_radius*_tanLambda*DPhi;
+
+  float DistZ = fabs(zOnHelix - xPoint[2]);
+  float Time;
+
+  if (fabs(_momentum[2]) > 1.0e-20) {
+    Time = (zOnHelix - _referencePoint[2])/_momentum[2];
+  }
+  else {
+    Time = _charge*_radius*DPhi/_pxy;
+  }
+
+  Distance[0] = DistXY;
+  Distance[1] = DistZ;
+  Distance[2] = sqrt(DistXY*DistXY+DistZ*DistZ);
+
+  return Time;
+
+
+}
+
+//When we are not interested in the exact distance, we can check if we are
+//already far enough away in XY, before we start calculating in Z as the
+//distance will only increase
+float HelixClass::getDistanceToPoint(const std::vector<float>& xPoint, float distCut) {
+  //calculate distance to XYprojected centre of Helix, comparing this with distance to radius around centre gives DistXY
+  float tempx = xPoint[0]-_xCentre;
+  float tempy = xPoint[1]-_yCentre;
+  float tempsq = sqrt(tempx*tempx + tempy*tempy);
+  float tempdf = tempsq - _radius;
+  float DistXY = fabs( tempdf );
+  //If this is bigger than distCut, we dont have to know how much bigger this is
+  if( DistXY > distCut) {
+    return DistXY;
+  }
+
+  int nCircles = 0;
+  float phi = atan2(tempy,tempx);
+  float phi0 = atan2(_referencePoint[1]-_yCentre,_referencePoint[0]-_xCentre);
+  float phidiff = phi0-phi;
+  float  tempz = xPoint[2] - _referencePoint[2];//Yes referencePoint
+  float tanradius = _tanLambda*_radius;
+  if (fabs(tanradius)>1.0e-20) {
+    float xCircles = (phidiff -_charge*tempz/tanradius)/_const_2pi;
+    int n1,n2;
+    if (xCircles >= 0.) {
+	n1 = int(xCircles);
+	n2 = n1 + 1;
+    }
+    else {
+	n1 = int(xCircles) - 1;
+	n2 = n1 + 1;
+    }
+    if (fabs(n1-xCircles) < fabs(n2-xCircles)) {
+	nCircles = n1;
+    }
+    else {
+	nCircles = n2;
+    }
+  }
+  float DistZ = - tempz - _charge*tanradius*(_const_2pi*((float)nCircles) - phidiff);
+  return sqrt(DistXY*DistXY+DistZ*DistZ);
+}//getDistanceToPoint(vector,float)
+
+float HelixClass::getDistanceToPoint(const float* xPoint, float distCut) {
+  std::vector<float> xPosition(xPoint, xPoint + 3 );//We are expecting three coordinates, must be +3, last element is excluded!
+  return getDistanceToPoint(xPosition, distCut);
+}//getDistanceToPoint(float*,float)
+
+
+
+void HelixClass::setHelixEdges(float * xStart, float * xEnd) {
+  for (int i=0; i<3; ++i) {
+    _xStart[i] = xStart[i];
+    _xEnd[i] = xEnd[i];
+  }
+
+}
+
+float HelixClass::getDistanceToHelix(HelixClass * helix, float * pos, float * mom) {
+
+  float x01 = getXC();
+  float y01 = getYC();
+  
+  float x02 = helix->getXC();
+  float y02 = helix->getYC();
+  
+  float rad1 = getRadius();
+  float rad2 = helix->getRadius();
+
+  float distance = sqrt((x01-x02)*(x01-x02)+(y01-y02)*(y01-y02));
+
+  bool singlePoint = true;
+
+  float phi1 = 0;
+  float phi2 = 0;
+
+  if (rad1+rad2<distance) {
+    phi1 = atan2(y02-y01,x02-x01);
+    phi2 = atan2(y01-y02,x01-x02);
+  }
+  else if (distance+rad2<rad1) {
+    phi1 = atan2(y02-y01,x02-x01);
+    phi2 = phi1;
+  }
+  else if (distance+rad1<rad2) {
+    phi1 = atan2(y01-y02,x01-x02);
+    phi2 = phi1;
+  }
+  else {
+    singlePoint = false;
+    float cosAlpha = 0.5*(distance*distance+rad2*rad2-rad1*rad1)/(distance*rad2);
+    float alpha = acos(cosAlpha);
+    float phi0 = atan2(y01-y02,x01-x02);
+    phi1 = phi0 + alpha;
+    phi2 = phi0 - alpha;
+  }
+  
+
+  float ref1[3];
+  float ref2[3];
+  for (int i=0;i<3;++i) {
+    ref1[i]=_referencePoint[i];
+    ref2[i]=helix->getReferencePoint()[i];
+  }
+  
+  float pos1[3];
+  float pos2[3];
+  float mom1[3];
+  float mom2[3];
+
+
+  if (singlePoint ) {
+
+    float xSect1 = x01 + rad1*cos(phi1);
+    float ySect1 = y01 + rad1*sin(phi1);
+    float xSect2 = x02 + rad2*cos(phi2);
+    float ySect2 = y02 + rad2*sin(phi2);
+    float R1 = sqrt(xSect1*xSect1+ySect1*ySect1);
+    float R2 = sqrt(xSect2*xSect2+ySect2*ySect2);
+
+    getPointOnCircle(R1,ref1,pos1);
+    helix->getPointOnCircle(R2,ref2,pos2);
+    
+  }
+  else {    
+
+    float xSect1 = x02 + rad2*cos(phi1);
+    float ySect1 = y02 + rad2*sin(phi1);
+    float xSect2 = x02 + rad2*cos(phi2);
+    float ySect2 = y02 + rad2*sin(phi2);
+
+//     std::cout << "(xSect1,ySect1)=(" << xSect1 << "," << ySect1 << ")" << std::endl;
+//     std::cout << "(xSect2,ySect2)=(" << xSect2 << "," << ySect2 << ")" << std::endl;
+
+    float temp21[3];
+    float temp22[3];
+
+    float phiI2  = atan2(ref2[1]-y02,ref2[0]-x02); 
+    float phiF21 = atan2(ySect1-y02,xSect1-x02);
+    float phiF22 = atan2(ySect2-y02,xSect2-x02);
+    float deltaPhi21 = phiF21 - phiI2;
+    float deltaPhi22 = phiF22 - phiI2;
+    float charge2 = helix->getCharge();
+    float pxy2 = helix->getPXY();
+    float pz2   = helix->getMomentum()[2];
+    if (deltaPhi21 < 0 && charge2 < 0) {
+      deltaPhi21 += _const_2pi;
+    }
+    else if (deltaPhi21 > 0 && charge2 > 0) { 
+      deltaPhi21 -= _const_2pi;
+    }
+
+    if (deltaPhi22 < 0 && charge2 < 0) {
+      deltaPhi22 += _const_2pi;
+    }
+    else if (deltaPhi22 > 0 && charge2 > 0) { 
+      deltaPhi22 -= _const_2pi;
+    }
+
+    float time21 = -charge2*deltaPhi21*rad2/pxy2;
+    float time22 = -charge2*deltaPhi22*rad2/pxy2;
+
+    float Z21 = ref2[2] + time21*pz2;
+    float Z22 = ref2[2] + time22*pz2;
+
+    temp21[0] = xSect1; temp21[1] = ySect1; temp21[2] = Z21;
+    temp22[0] = xSect2; temp22[1] = ySect2; temp22[2] = Z22;
+    
+
+//     std::cout << "temp21 = " << temp21[0] << " " << temp21[1] << " " << temp21[2] << std::endl;
+//     std::cout << "temp22 = " << temp22[0] << " " << temp22[1] << " " << temp22[2] << std::endl;
+
+
+    float temp11[3];
+    float temp12[3];
+
+    float phiI1  = atan2(ref1[1]-y01,ref1[0]-x01); 
+    float phiF11 = atan2(ySect1-y01,xSect1-x01);
+    float phiF12 = atan2(ySect2-y01,xSect2-x01);
+    float deltaPhi11 = phiF11 - phiI1;
+    float deltaPhi12 = phiF12 - phiI1;
+    float charge1 = _charge;
+    float pxy1 = _pxy;
+    float pz1   = _momentum[2];
+    if (deltaPhi11 < 0 && charge1 < 0) {
+      deltaPhi11 += _const_2pi;
+    }
+    else if (deltaPhi11 > 0 && charge1 > 0) { 
+      deltaPhi11 -= _const_2pi;
+    }
+
+    if (deltaPhi12 < 0 && charge1 < 0) {
+      deltaPhi12 += _const_2pi;
+    }
+    else if (deltaPhi12 > 0 && charge1 > 0) { 
+      deltaPhi12 -= _const_2pi;
+    }
+
+    float time11 = -charge1*deltaPhi11*rad1/pxy1;
+    float time12 = -charge1*deltaPhi12*rad1/pxy1;
+
+    float Z11 = ref1[2] + time11*pz1;
+    float Z12 = ref1[2] + time12*pz1;
+
+    temp11[0] = xSect1; temp11[1] = ySect1; temp11[2] = Z11;
+    temp12[0] = xSect2; temp12[1] = ySect2; temp12[2] = Z12;
+    
+//     std::cout << "temp11 = " << temp11[0] << " " << temp11[1] << " " << temp11[2] << std::endl;
+//     std::cout << "temp12 = " << temp12[0] << " " << temp12[1] << " " << temp12[2] << std::endl;
+
+    float Dist1 = 0;
+    float Dist2 = 0;
+
+    for (int j=0;j<3;++j) {
+      Dist1 += (temp11[j]-temp21[j])*(temp11[j]-temp21[j]);
+      Dist2 += (temp12[j]-temp22[j])*(temp12[j]-temp22[j]);
+    }
+
+    if (Dist1<Dist2) {
+      for (int l=0;l<3;++l) {
+	pos1[l] = temp11[l];
+	pos2[l] = temp21[l];
+      }
+    }
+    else {
+       for (int l=0;l<3;++l) {
+	pos1[l] = temp12[l];
+	pos2[l] = temp22[l];
+      }
+    }
+
+  }
+
+  getExtrapolatedMomentum(pos1,mom1);
+  helix->getExtrapolatedMomentum(pos2,mom2);
+
+  float helixDistance = 0;
+
+  for (int i=0;i<3;++i) {
+    helixDistance += (pos1[i] - pos2[i])*(pos1[i] - pos2[i]);
+    pos[i] = 0.5*(pos1[i]+pos2[i]);
+    mom[i] = mom1[i]+mom2[i];
+  }
+  helixDistance = sqrt(helixDistance);
+
+  return helixDistance;
+
+}
+
+void HelixClass::getExtrapolatedMomentum(float * pos, float * momentum) {
+
+  float phi = atan2(pos[1]-_yCentre,pos[0]-_xCentre);
+  if (phi <0.) phi += _const_2pi;
+  phi = phi - _phiAtPCA + _phi0;
+  momentum[0] = _pxy*cos(phi);
+  momentum[1] = _pxy*sin(phi);
+  momentum[2] = _momentum[2];
+
+
+}
diff --git a/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/HelixClass.h b/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/HelixClass.h
new file mode 100644
index 00000000..794f8158
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/HelixClass.h
@@ -0,0 +1,302 @@
+#ifndef HELIXAR_H
+#define HELIXAR_H 1
+#include <vector>
+/**
+ *    Utility class to manipulate with different parameterisations <br>
+ *    of helix. Helix can be initialized in a three different <br>
+ *    ways using the following public methods : <br>
+ *    1) Initialize_VP(float * pos, float * mom, float q, float B) : <br>
+ *       initialization of helix is done using <br>
+ *       - position of the reference point : pos[3]; <br>
+ *       - momentum vector at the reference point : mom[3];<br>
+ *       - particle charge : q;<br>
+ *       - magnetic field (in Tesla) : B;<br>
+ *    2) Initialize_BZ(float xCentre, float yCentre, float radius, <br>
+ *				   float bZ, float phi0, float B, float signPz,<br>
+ *				   float zBegin):<br>
+ *       initialization of helix is done according to the following<br>
+ *       parameterization<br>
+ *       x = xCentre + radius*cos(bZ*z + phi0)<br>
+ *       y = yCentre + radius*sin(bZ*z + phi0)<br>
+ *       where (x,y,z) - position of point on the helix<br>
+ *       - (xCentre,yCentre) is the centre of circumference in R-Phi plane<br>
+ *       - radius is the radius of circumference<br>
+ *       - bZ is the helix slope parameter<br>
+ *       - phi0 is the initial phase of circumference<br>
+ *       - B is the magnetic field (in Tesla)<br>
+ *       - signPz is the sign of the z component of momentum vector<br>
+ *       - zBegin is the z coordinate of the reference point;<br>
+ *    3) void Initialize_Canonical(float phi0, float d0, float z0, float omega,<br> 
+ *			      float tanlambda, float B) :<br>
+ *    canonical (LEP-wise) parameterisation with the following parameters<br>
+ *       - phi0 - Phi angle of momentum vector at the point of<br>
+ *         closest approach to IP in R-Phi plane;
+ *       - d0 - signed distance of closest approach to IP in R-Phi plane;<br>
+ *       - z0 - z coordinate of the point of closest approach in R-Phi plane;<br>
+ *       - omega - signed curvature;<br>
+ *       - tanlambda - tangent of dip angle;<br>
+ *       - B - magnetic field (in Tesla);<br>
+ *    A set of public methods (getters) provide access to <br>
+ *    various parameters associated with helix. Helix Class contains<br>
+ *    also several utility methods, allowing for calculation of helix<br>
+ *    intersection points with planes parallel and perpendicular to <br>
+ *    z (beam) axis and determination of the distance of closest approach<br>
+ *    from arbitrary 3D point to the helix. <br>
+ *    @author A. Raspereza (DESY)<br>
+ *    @version $Id: HelixClass.h,v 1.16 2008-06-05 13:47:18 rasp Exp $<br>
+ *
+ */
+
+#include "LineClass.h"
+class HelixClass;
+
+class HelixClass {
+ public:
+
+/**
+ *  Constructor. Initializations of constants which are used
+ *  to calculate various parameters associated with helix.
+ */ 
+    HelixClass();
+/**
+ *  Destructor. 
+ */
+    ~HelixClass();
+/**
+ *   Initialization of helix using <br>
+ *     - position of the reference point : pos[3]; <br>
+ *     - momentum vector at the reference point : mom[3];<br>
+ *     - particle charge : q;<br>
+ *     - magnetic field (in Tesla) : B<br>
+ */  
+    void Initialize_VP(float * pos, float * mom, float q, float B);
+
+/**
+ *   Initialization of helix according to the following<br>
+ *   parameterization<br>
+ *   x = xCentre + radius*cos(bZ*z + phi0)<br>
+ *   y = yCentre + radius*sin(bZ*z + phi0)<br>
+ *     where (x,y,z) - position of point on the helix<br>
+ *     - (xCentre,yCentre) is the centre of circumference in R-Phi plane<br>
+ *     - radius is the radius of circumference<br>
+ *     - bZ is the helix slope parameter<br>
+ *     - phi0 is the initial phase of circumference<br>
+ *     - B is the magnetic field (in Tesla)<br>
+ *     - signPz is the sign of the z component of momentum vector<br>
+ *     - zBegin is the z coordinate of the reference point<br>
+ */  
+    void Initialize_BZ(float xCentre, float yCentre, float radius, 
+				   float bZ, float phi0, float B, float signPz,
+				   float zBegin);
+/**
+ *  Canonical (LEP-wise) parameterisation with the following parameters<br>
+ *     - phi0 - Phi angle of momentum vector at the point of<br>
+ *       closest approach to IP in R-Phi plane;
+ *     - d0 - signed distance of closest approach in R-Phi plane;<br>
+ *     - z0 - z coordinate of the point of closest approach to IP 
+ *       in R-Phi plane;<br>
+ *     - omega - signed curvature;<br>
+ *     - tanlambda - tangent of dip angle;<br>
+ *     - B - magnetic field (in Tesla)<br>
+ */  
+    void Initialize_Canonical(float phi0, float d0, float z0, float omega, 
+			      float tanlambda, float B);
+    /**
+     *  Returns momentum of particle at the point of closest approach <br>
+     *  to IP <br>
+     */
+    const float * getMomentum();
+
+    /**
+     *  Returns reference point of track <br>     
+     */
+    const float * getReferencePoint();
+
+    /**
+     *  Returns Phi angle of the momentum vector <br>
+     *  at the point of closest approach to IP <br>     
+     */
+    float getPhi0();
+
+    /**
+     *  Returns signed distance of closest approach <br>
+     *  to IP in the R-Phi plane <br>     
+     */
+    float getD0();
+
+    /**
+     *  Returns z coordinate of the point of closest 
+     *  approach to IP in the R-Phi plane <br>     
+     */
+    float getZ0();
+
+    /**
+     *  Returns signed curvature of the track <br>     
+     */
+    float getOmega();
+
+    /**
+     *  Returns tangent of dip angle of the track <br>     
+     */
+    float getTanLambda();
+
+    /**
+     *  Returns transverse momentum of the track <br>     
+     */
+    float getPXY();
+
+
+    /**
+     *  Returns x coordinate of circumference
+     */
+    float getXC();
+
+    /**
+     *  Returns y coordinate of circumference
+     */
+    float getYC();
+
+
+     /**
+     *  Returns radius of circumference
+     */
+    float getRadius();   
+
+
+    /**
+     *  Returns helix intersection point with the plane <br>
+     *  parallel to z axis. Plane is defined by two coordinates <br>
+     *  of the point belonging to the plane (x0,y0) and normal <br>
+     *  vector (ax,ay).  ref[3] is the reference point of the helix. <br>
+     *  point[3] - returned vector holding the coordinates of <br>
+     *  intersection point <br>     
+     */
+    float getPointInXY(float x0, float y0, float ax, float ay, 
+			      float * ref , float * point);
+
+    /**
+     *  Returns helix intersection point with the plane <br>
+     *  perpendicular to z axis. Plane is defined by z coordinate <br>
+     *  of the plane. ref[3] is the reference point of the helix. <br>
+     *  point[3] - returned vector holding the coordinates of <br>
+     *  intersection point <br>     
+     */
+    float getPointInZ(float zLine, float * ref, float * point);
+
+    /**
+     * Return distance of the closest approach of the helix to <br>
+     * arbitrary 3D point in space. xPoint[3] - coordinates of <br>
+     * space point. Distance[3] - vector of distances of helix to <br> 
+     * a given point in various projections : <br>
+     * Distance[0] - distance in R-Phi plane <br>
+     * Distance[1] - distance along Z axis <br>
+     * Distance[2] - 3D distance <br> 
+     */
+    float getDistanceToPoint(float * xPoint, float * Distance);
+
+    /**
+     * Return distance of the closest approach of the helix to <br>
+     * arbitrary 3D point in space. xPoint[3] - coordinates of <br>
+     * space point. distCut - limit on the distance between helix <br> 
+     * and the point to reduce calculation time <br>
+     * If R-Phi is found to be greater than distCut, rPhi distance is returned <br>
+     * If the R-Phi distance is not too big, than the exact 3D distance is returned <br>
+     * This function can be used, if the exact distance is not always needed <br>
+     */
+    float getDistanceToPoint(const float* xPoint, float distCut);
+    float getDistanceToPoint(const std::vector<float>& xPoint, float distCut);
+
+    /**
+     * This method calculates coordinates of both intersection <br>
+     * of the helix with a cylinder. <br>
+     * Rotational symmetry with respect to z axis is assumed,  <br>
+     * meaning that cylinder axis corresponds to the z axis <br>
+     * of reference frame. <br>
+     * Inputs : <br> 
+     * Radius - radius of cylinder. <br>
+     * ref[3] - point of closest approach to the origin of the helix. <br>
+     * Output : <br>
+     * point[6] - coordinates of intersection point. <br>
+     * Method returns also generic time, defined as the <br>
+     * ratio of helix length from reference point to the intersection <br>
+     * point to the particle momentum <br>
+     */
+    float getPointOnCircle(float Radius, float * ref, float * point);
+
+    /** Returns distance between two helixes <br>
+     * Output : <br> 
+     * pos[3] - position of the point of closest approach <br>
+     * mom[3] - momentum of V0 <br>
+     */
+    float getDistanceToHelix(HelixClass * helix, float * pos, float * mom);
+
+    /**
+     * Set Edges of helix 
+     */
+    void setHelixEdges(float * xStart, float * xEnd);
+
+    /**
+     * Returns starting point of helix
+     */
+    float * getStartingPoint() {return _xStart;}
+
+    /**
+     * Returns endpoint of helix
+     */
+    float * getEndPoint() {return _xEnd;}
+    
+    /**
+     * Returns BZ for the second parameterization
+     */
+    float getBz();
+
+    /**
+     * Returns Phi for the second parameterization
+     */
+    float getPhiZ();
+
+    /**
+     * Returns extrapolated momentum
+     */
+    void getExtrapolatedMomentum(float * pos, float * momentum);
+
+    /**
+     * Returns charge 
+     */
+    float getCharge();
+
+ private:    
+    float _momentum[3]; // momentum @ ref point 
+    float _referencePoint[3]; // coordinates @ ref point
+    float _phi0; // phi0 in canonical parameterization 
+    float _d0;   // d0 in canonical parameterisation
+    float _z0;   // z0 in canonical parameterisation
+    float _omega; // signed curvuture in canonical parameterisation
+    float _tanLambda; // TanLambda 
+    float _pxy; // Transverse momentum
+    float _charge; // Particle Charge
+    float _bField; // Magnetic field (assumed to point to Z>0)
+    float _radius; // radius of circle in XY plane
+    float _xCentre; // X of circle centre
+    float _yCentre; // Y of circle centre
+    float _phiRefPoint; // Phi w.r.t. (X0,Y0) of circle @ ref point
+    float _phiAtPCA; // Phi w.r.t. (X0,Y0) of circle @ PCA 
+    float _xAtPCA; // X @ PCA
+    float _yAtPCA; // Y @ PCA
+    float _pxAtPCA; // PX @ PCA
+    float _pyAtPCA; // PY @ PCA
+    float _phiMomRefPoint; // Phi of Momentum vector @ ref point
+    float _const_pi; // PI
+    float _const_2pi; // 2*PI
+    float _const_pi2; // PI/2    
+    float _FCT; // 2.99792458E-4
+    float _xStart[3]; // Starting point of track segment
+    float _xEnd[3]; // Ending point of track segment
+
+    float _bZ;
+    float _phiZ;
+
+};
+
+
+#endif
diff --git a/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/LineClass.cc b/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/LineClass.cc
new file mode 100644
index 00000000..e23db1fa
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/LineClass.cc
@@ -0,0 +1,73 @@
+#include "LineClass.h"
+#include <math.h> 
+
+/*
+ * Constructor
+ */
+
+LineClass::LineClass(float x0,
+		     float y0,
+		     float z0,
+		     float ax,
+		     float ay,
+		     float az) {
+  _x0[0] = x0;
+  _x0[1] = y0;
+  _x0[2] = z0;
+  _ax[0] = ax;
+  _ax[1] = ay;
+  _ax[2] = az;
+}
+
+LineClass::LineClass(float *x0,
+		     float *ax) {
+
+  for (int i=0; i<3; ++i) {
+    _x0[i] = x0[i];
+    _ax[i] = ax[i];
+  }
+}
+
+float * LineClass::getReferencePoint() {
+  return _x0;
+}
+
+float * LineClass::getDirectionalVector() {
+  return _ax;
+}
+
+void LineClass::setReferencePoint(float *x0) {
+  for (int i=0; i<3; ++i)
+    _x0[i] = x0[i];
+}
+ 
+void LineClass::setDirectionalVector(float *ax) {
+  for (int i=0; i<3; ++i)
+    _ax[i] = ax[i];
+ 
+}
+
+float LineClass::getDistanceToPoint(float * xpoint, float * pos) {
+  
+  float dif[3];
+  float prod = 0;
+  float den = 0;
+  for (int i=0; i<3; ++i) {
+    dif[i] = xpoint[i] - _x0[i];
+    prod += _ax[i]*dif[i];
+    den += _ax[i]*_ax[i];
+  }
+  float time = prod/fmax(1e-10,den);
+
+  float dist = 0.0;
+  for (int i=0; i<3; ++i) {
+    pos[i] = _x0[i] + _ax[i]*time;
+    dist += (xpoint[i]-pos[i])*(xpoint[i]-pos[i]);
+  }
+  dist = sqrt(dist);
+
+  return dist;
+
+
+
+}
diff --git a/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/LineClass.h b/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/LineClass.h
new file mode 100644
index 00000000..98970a26
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MarlinUtil/01-08/source/LineClass.h
@@ -0,0 +1,32 @@
+#ifndef LINECLASS_H
+#define LINECLASS_H  
+class LineClass {
+
+  public:
+  LineClass(float x0,
+	    float y0,
+	    float z0,
+	    float ax,
+	    float ay,
+	    float az);
+
+  LineClass(float *x0,
+	    float *ax);
+  
+  ~LineClass();
+
+  float * getReferencePoint();
+  void setReferencePoint(float *x0);
+  float * getDirectionalVector();
+  void setDirectionalVector(float *ax);
+  float getDistanceToPoint(float * xpoint, float * pos);
+
+ private:
+
+  float _x0[3];
+  float _ax[3];
+
+
+};
+
+#endif
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/CMakeLists.txt b/Reconstruction/PFA/Pandora/MatrixPandora/CMakeLists.txt
new file mode 100644
index 00000000..99429ca5
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/CMakeLists.txt
@@ -0,0 +1,57 @@
+gaudi_subdir(MatrixPandora v0r0)
+
+find_package(DD4hep COMPONENTS DDG4 REQUIRED)
+find_package(CLHEP REQUIRED)
+find_package(GSL REQUIRED )##don't use this,  use CEPC LCIO version one , due to the ClusterShape.cc file which is from LCIO
+message("GSL: ${GSL_LIBRARIES} ")
+set (gsl_include "/cvmfs/cepc.ihep.ac.cn/software/cepcsoft/x86_64-sl6-gcc49/external/GSL/1.14/install/include")
+set (gsl_lib1 "/cvmfs/cepc.ihep.ac.cn/software/cepcsoft/x86_64-sl6-gcc49/external/GSL/1.14/install/lib/libgsl.so")
+set (gsl_lib2 "/cvmfs/cepc.ihep.ac.cn/software/cepcsoft/x86_64-sl6-gcc49/external/GSL/1.14/install/lib/libgslcblas.so")
+find_package(LCIO REQUIRED ) 
+find_package(GEAR REQUIRED)
+#message("ENV GEAR: $ENV{GEAR}")
+find_package(EDM4HEP REQUIRED ) 
+#message("EDM4HEP_INCLUDE_DIRS: ${EDM4HEP_INCLUDE_DIR}")
+#message("EDM4HEP_LIB: ${EDM4HEP_LIBRARIES}")
+include_directories(${EDM4HEP_INCLUDE_DIR})
+link_libraries("/cvmfs/cepcsw.ihep.ac.cn/prototype/releases/externals/97.0.0/EDM4hep/lib64/libedm4hep.so")
+find_package(PandoraSDK REQUIRED ) 
+#message("PandoraSDK_INCLUDE_DIRS: ${PandoraSDK_INCLUDE_DIRS}")
+#message("PandoraSDK_LIB:          ${PandoraSDK_LIBRARIES}")
+find_package(LCContent REQUIRED ) 
+#message("LCContent_INCLUDE_DIRS: ${LCContent_INCLUDE_DIRS}")
+#message("LCContent_LIB:          ${LCContent_LIBRARIES}")
+include_directories(${PandoraSDK_INCLUDE_DIRS})
+link_libraries(${PandoraSDK_LIBRARIES})
+include_directories(${LCContent_INCLUDE_DIRS})
+link_libraries(${LCContent_LIBRARIES})
+
+
+list(APPEND CMAKE_MODULE_PATH "$ENV{ROOTSYS}/etc/cmake/")
+find_package(ROOT 5.26.00 REQUIRED COMPONENTS Eve Geom RGL EG)
+
+include_directories("../CED/CED/")
+include_directories("../MarlinUtil/01-08/source/")
+
+gaudi_depends_on_subdirs(
+    Service/EventSeeder
+    Service/GearSvc
+    Detector/DetInterface
+)
+
+set(dir_srcs
+    src/PandoraMatrixAlg.cpp
+    src/MCParticleCreator.cpp
+    src/GeometryCreator.cpp
+    src/CaloHitCreator.cpp
+    src/TrackCreator.cpp
+    src/PfoCreator.cpp
+    ../CED/CED/*.cc
+    ../MarlinUtil/01-08/source/*.cc
+)
+set(dir_include include)
+# Modules
+gaudi_add_module(MatrixPandora ${dir_srcs}
+    INCLUDE_DIRS ${gsl_include} ${dir_include} GaudiKernel FWCore CLHEP  ${LCIO_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS} gear DD4hep  
+    LINK_LIBRARIES ${gsl_lib1} ${gsl_lib2} GaudiKernel FWCore CLHEP ROOT ${LCIO_LIBRARIES} $ENV{GEAR}/lib/libgear.so $ENV{GEAR}/lib/libgearxml.so DD4hep ${DD4hep_COMPONENT_LIBRARIES} DDRec
+)
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/include/CaloHitCreator.h b/Reconstruction/PFA/Pandora/MatrixPandora/include/CaloHitCreator.h
new file mode 100644
index 00000000..c7714205
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/include/CaloHitCreator.h
@@ -0,0 +1,298 @@
+/**
+ *  @file   MarlinPandora/include/CaloHitCreator.h
+ * 
+ *  @brief  Header file for the calo hit creator class.
+ * 
+ *  $Log: $
+ */
+
+#ifndef CALO_HIT_CREATOR_H
+#define CALO_HIT_CREATOR_H 1
+
+#include "GaudiKernel/ISvcLocator.h"
+#include "edm4hep/CalorimeterHit.h"
+
+#include "gear/LayerLayout.h"
+
+#include "Api/PandoraApi.h"
+
+#include "DetInterface/IGeoSvc.h"
+#include "DD4hep/DD4hepUnits.h"
+#include "DD4hep/Detector.h"
+#include <DDRec/DetectorData.h>
+#include <DDRec/CellIDPositionConverter.h>
+#include "DD4hep/BitFieldCoder.h"
+
+
+#include <string>
+
+typedef std::vector<edm4hep::CalorimeterHit *> CalorimeterHitVector;
+
+namespace gear { class GearMgr; }
+
+class CollectionMaps;
+/**
+ *  @brief  CaloHitCreator class
+ */
+class CaloHitCreator
+{
+public:
+    typedef std::vector<std::string> StringVector;
+
+    /**
+     *  @brief  Settings class
+     */
+    class Settings
+    {
+    public:
+        /**
+         *  @brief  Default constructor
+         */
+        Settings();
+
+        StringVector    m_eCalCaloHitCollections;               ///< The ecal calorimeter hit collections
+        StringVector    m_hCalCaloHitCollections;               ///< The hcal calorimeter hit collections
+        StringVector    m_lCalCaloHitCollections;               ///< The lcal calorimeter hit collections
+        StringVector    m_lHCalCaloHitCollections;              ///< The lhcal calorimeter hit collections
+        StringVector    m_muonCaloHitCollections;               ///< The muon calorimeter hit collections
+
+        float           m_absorberRadLengthECal;                ///< The absorber radiation length in the ECal
+        float           m_absorberIntLengthECal;                ///< The absorber interaction length in the ECal
+        float           m_absorberRadLengthHCal;                ///< The absorber radiation length in the HCal
+        float           m_absorberIntLengthHCal;                ///< The absorber interaction length in the HCal
+        float           m_absorberRadLengthOther;               ///< The absorber radiation length in other detector regions
+        float           m_absorberIntLengthOther;               ///< The absorber interaction length in other detector regions
+
+        float           m_eCalToMip;                            ///< The calibration from deposited ECal energy to mip
+        float           m_hCalToMip;                            ///< The calibration from deposited HCal energy to mip
+        float           m_muonToMip;                            ///< The calibration from deposited Muon energy to mip
+        float           m_eCalMipThreshold;                     ///< Threshold for creating calo hits in the ECal, units mip
+        float           m_hCalMipThreshold;                     ///< Threshold for creating calo hits in the HCal, units mip
+        float           m_muonMipThreshold;                     ///< Threshold for creating calo hits in the HCal, units mip
+
+        float           m_eCalToEMGeV;                          ///< The calibration from deposited ECal energy to EM energy
+        float           m_eCalToHadGeVBarrel;                   ///< The calibration from deposited ECal barrel energy to hadronic energy
+        float           m_eCalToHadGeVEndCap;                   ///< The calibration from deposited ECal endcap energy to hadronic energy
+        float           m_hCalToEMGeV;                          ///< The calibration from deposited HCal energy to EM energy
+        float           m_hCalToHadGeV;                         ///< The calibration from deposited HCal energy to hadronic energy
+        int             m_muonDigitalHits;                      ///< Muon hits are treated as digital (energy from hit count)
+        float           m_muonHitEnergy;                        ///< The energy for a digital muon calorimeter hit, units GeV
+
+        float           m_maxHCalHitHadronicEnergy;             ///< The maximum hadronic energy allowed for a single hcal hit
+        int             m_nOuterSamplingLayers;                 ///< Number of layers from edge for hit to be flagged as an outer layer hit
+        float           m_layersFromEdgeMaxRearDistance;        ///< Maximum number of layers from candidate outer layer hit to rear of detector
+
+        int             m_hCalEndCapInnerSymmetryOrder;         ///< HCal end cap inner symmetry order (missing from ILD00 gear file)
+        float           m_hCalEndCapInnerPhiCoordinate;         ///< HCal end cap inner phi coordinate (missing from ILD00 gear file)
+
+        // For Strip Splitting method and hybrid ECAL.
+        int             m_stripSplittingOn;                     ///< To use SSA, this should be true (default is false)
+        int             m_useEcalScLayers;                      ///< To use scintillator layers ~ hybrid ECAL, this should be true (default is false)
+        float           m_eCalSiToMip;                          ///< The calibration from deposited Si-layer energy to mip
+        float           m_eCalScToMip;                          ///< The calibration from deposited Sc-layer energy to mip
+        float           m_eCalSiMipThreshold;                   ///< Threshold for creating calo hits in the Si-layers of ECAL, units mip
+        float           m_eCalScMipThreshold;                   ///< Threshold for creating calo hits in the Sc-layers of ECAL, units mip
+        float           m_eCalSiToEMGeV;                        ///< The calibration from deposited Si-layer energy to EM energy
+        float           m_eCalScToEMGeV;                        ///< The calibration from deposited Sc-layer energy to EM energy
+        float           m_eCalSiToHadGeVBarrel;                 ///< The calibration from deposited Si-layer energy on the enecaps to hadronic energy
+        float           m_eCalScToHadGeVBarrel;                 ///< The calibration from deposited Sc-layer energy on the endcaps to hadronic energy
+        float           m_eCalSiToHadGeVEndCap;                 ///< The calibration from deposited Si-layer energy on the enecaps to hadronic energy
+        float           m_eCalScToHadGeVEndCap;                 ///< The calibration from deposited Sc-layer energy on the endcaps to hadronic energy
+    };
+
+    /**
+     *  @brief  Constructor
+     * 
+     *  @param  settings the creator settings
+     *  @param  pPandora address of the relevant pandora instance
+     */
+     CaloHitCreator(const Settings &settings, const pandora::Pandora *const pPandora, ISvcLocator* svcloc, bool encoder_style);
+
+    /**
+     *  @brief  Destructor
+     */
+     ~CaloHitCreator();
+
+    /**
+     *  @brief  Create calo hits
+     * 
+     *  @param  pLCEvent the lcio event
+     */    
+    //pandora::StatusCode CreateCaloHits(const LCEvent *const pLCEvent);
+    pandora::StatusCode CreateCaloHits(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Get the calorimeter hit vector
+     * 
+     *  @return The calorimeter hit vector
+     */
+    const CalorimeterHitVector &GetCalorimeterHitVector() const;
+
+    /**
+     *  @brief  Reset the calo hit creator
+     */
+    void Reset();
+
+private:
+    /**
+     *  @brief  Create ecal calo hits
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    //pandora::StatusCode CreateECalCaloHits(const EVENT::LCEvent *const pLCEvent);
+    pandora::StatusCode CreateECalCaloHits(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Create hcal calo hits
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    pandora::StatusCode CreateHCalCaloHits(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Create muon calo hits
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    pandora::StatusCode CreateMuonCaloHits(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Create lcal calo hits
+     * 
+     *  @param  pLCEvent the lcio event
+     */    
+    pandora::StatusCode CreateLCalCaloHits(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Create lhcal calo hits
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    pandora::StatusCode CreateLHCalCaloHits(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Get common calo hit properties: position, parent address, input energy and time
+     * 
+     *  @param  pCaloHit the lcio calorimeter hit
+     *  @param  caloHitParameters the calo hit parameters to populate
+     */
+    void GetCommonCaloHitProperties(const edm4hep::CalorimeterHit *const pCaloHit, PandoraApi::CaloHit::Parameters &caloHitParameters) const;
+
+    /**
+     *  @brief  Get end cap specific calo hit properties: cell size, absorber radiation and interaction lengths, normal vector
+     * 
+     *  @param  pCaloHit the lcio calorimeter hit
+     *  @param  layerLayout the gear end cap layer layout
+     *  @param  caloHitParameters the calo hit parameters to populate
+     *  @param  absorberCorrection to receive the absorber thickness correction for the mip equivalent energy
+     */
+    void GetEndCapCaloHitProperties(const edm4hep::CalorimeterHit *const pCaloHit, const gear::LayerLayout &layerLayout,
+        PandoraApi::CaloHit::Parameters &caloHitParameters, float &absorberCorrection) const;
+
+    /**
+     *  @brief  Get barrel specific calo hit properties: cell size, absorber radiation and interaction lengths, normal vector
+     * 
+     *  @param  pCaloHit the lcio calorimeter hit
+     *  @param  layerLayout the gear barrel layer layout
+     *  @param  barrelSymmetryOrder the barrel order of symmetry
+     *  @param  barrelPhi0 the barrel orientation
+     *  @param  staveNumber the stave number
+     *  @param  caloHitParameters the calo hit parameters to populate
+     *  @param  absorberCorrection to receive the absorber thickness correction for the mip equivalent energy
+     */
+    void GetBarrelCaloHitProperties(const edm4hep::CalorimeterHit *const pCaloHit, const gear::LayerLayout &layerLayout,
+        unsigned int barrelSymmetryOrder, float barrelPhi0, unsigned int staveNumber, PandoraApi::CaloHit::Parameters &caloHitParameters,
+        float &absorberCorrection) const;
+
+    void GetBarrelCaloHitProperties(const edm4hep::CalorimeterHit *const pCaloHit, const std::vector<dd4hep::rec::LayeredCalorimeterData::Layer> &layerLayout,
+        unsigned int barrelSymmetryOrder, float barrelPhi0, unsigned int staveNumber, PandoraApi::CaloHit::Parameters &caloHitParameters,
+        float &absorberCorrection) const;
+    /**
+     *  @brief  Get number of active layers from position of a calo hit to the edge of the detector
+     * 
+     *  @param  pCaloHit the lcio calorimeter hit
+     */
+    int GetNLayersFromEdge(const edm4hep::CalorimeterHit *const pCaloHit) const;
+
+    /**
+     *  @brief  Get the maximum radius of a calo hit in a polygonal detector structure
+     * 
+     *  @param  pCaloHit the lcio calorimeter hit
+     *  @param  symmetryOrder the symmetry order
+     *  @param  phi0 the angular orientation
+     * 
+     *  @return the maximum radius
+     */
+    float GetMaximumRadius(const edm4hep::CalorimeterHit *const pCaloHit, const unsigned int symmetryOrder, const float phi0) const;
+
+    void GetCoding(const edm4hep::CalorimeterHit* pCaloHit, long& sys, long& x, long& y, long& z) const ;
+    int GetBarrelLayer(const edm4hep::CalorimeterHit *const pCaloHit, const std::vector<dd4hep::rec::LayeredCalorimeterData::Layer> &layerLayout) const;
+    /**
+     *  @brief  Get the layer coding string from the provided cell id encoding string
+     * 
+     *  @param  encodingString the cell id encoding string
+     * 
+     *  @return the layer coding string
+     */
+    std::string GetLayerCoding(const std::string &encodingString) const;
+
+    /**
+     *  @brief  Get the stave coding string from the provided cell id encoding string
+     * 
+     *  @param  encodingString the cell id encoding string
+     * 
+     *  @return the stave coding string
+     */
+    std::string GetStaveCoding(const std::string &encodingString) const;
+
+    const Settings                      m_settings;                         ///< The calo hit creator settings
+
+    const pandora::Pandora             *m_pPandora;                         ///< Address of the pandora object to create calo hits
+
+    float                               m_eCalBarrelOuterZ;                 ///< ECal barrel outer z coordinate
+    float                               m_hCalBarrelOuterZ;                 ///< HCal barrel outer z coordinate
+    float                               m_muonBarrelOuterZ;                 ///< Muon barrel outer z coordinate
+    float                               m_coilOuterR;                       ///< Coil outer r coordinate
+
+    float                               m_eCalBarrelInnerPhi0;              ///< ECal barrel inner phi0 coordinate
+    unsigned int                        m_eCalBarrelInnerSymmetry;          ///< ECal barrel inner symmetry order
+    float                               m_hCalBarrelInnerPhi0;              ///< HCal barrel inner phi0 coordinate
+    unsigned int                        m_hCalBarrelInnerSymmetry;          ///< HCal barrel inner symmetry order
+    float                               m_muonBarrelInnerPhi0;              ///< Muon barrel inner phi0 coordinate
+    unsigned int                        m_muonBarrelInnerSymmetry;          ///< Muon barrel inner symmetry order
+
+    float                               m_hCalEndCapOuterR;                 ///< HCal endcap outer r coordinate
+    float                               m_hCalEndCapOuterZ;                 ///< HCal endcap outer z coordinate
+    float                               m_hCalBarrelOuterR;                 ///< HCal barrel outer r coordinate
+    float                               m_hCalBarrelOuterPhi0;              ///< HCal barrel outer phi0 coordinate
+    unsigned int                        m_hCalBarrelOuterSymmetry;          ///< HCal barrel outer symmetry order
+
+    float                               m_hCalBarrelLayerThickness;         ///< HCal barrel layer thickness
+    float                               m_hCalEndCapLayerThickness;         ///< HCal endcap layer thickness
+
+    CalorimeterHitVector                m_calorimeterHitVector;             ///< The calorimeter hit vector
+    std::string                         m_encoder_str;
+    std::string                         m_encoder_str_MUON ; 
+    std::string                         m_encoder_str_LCal ; 
+    std::string                         m_encoder_str_LHCal; 
+    gear::GearMgr* _GEAR;
+    dd4hep::Detector* m_dd4hep;
+    dd4hep::rec::CellIDPositionConverter* m_cellIDConverter;
+    std::string                         m_compact ;
+};
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+inline const CalorimeterHitVector &CaloHitCreator::GetCalorimeterHitVector() const
+{
+    return m_calorimeterHitVector;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+inline void CaloHitCreator::Reset()
+{
+    m_calorimeterHitVector.clear();
+}
+
+#endif // #ifndef CALO_HIT_CREATOR_H
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/include/GeometryCreator.h b/Reconstruction/PFA/Pandora/MatrixPandora/include/GeometryCreator.h
new file mode 100644
index 00000000..0a0c1ae2
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/include/GeometryCreator.h
@@ -0,0 +1,166 @@
+/**
+ *  @file   MarlinPandora/include/GeometryCreator.h
+ * 
+ *  @brief  Header file for the geometry creator class.
+ * 
+ *  $Log: $
+ */
+
+#ifndef GEOMETRY_CREATOR_H
+#define GEOMETRY_CREATOR_H 1
+
+#include "Api/PandoraApi.h"
+
+#include "GaudiKernel/ISvcLocator.h"
+
+#include "DetInterface/IGeoSvc.h"
+#include "DD4hep/DD4hepUnits.h"
+#include "DD4hep/Detector.h"
+
+namespace gear { class CalorimeterParameters; class GearMgr; }
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+/**
+ *  @brief  GeometryCreator class
+ */
+class GeometryCreator
+{
+public:
+    /**
+     *  @brief  Settings class
+     */
+    class Settings
+    {
+    public:
+        /**
+         *  @brief  Default constructor
+         */
+        Settings();
+
+        float           m_absorberRadLengthECal;                ///< The absorber radiation length in the ECal
+        float           m_absorberIntLengthECal;                ///< The absorber interaction length in the ECal
+        float           m_absorberRadLengthHCal;                ///< The absorber radiation length in the HCal
+        float           m_absorberIntLengthHCal;                ///< The absorber interaction length in the HCal
+        float           m_absorberRadLengthOther;               ///< The absorber radiation length in other detector regions
+        float           m_absorberIntLengthOther;               ///< The absorber interaction length in other detector regions
+
+        int             m_eCalEndCapInnerSymmetryOrder;         ///< ECal end cap inner symmetry order (missing from ILD gear files)
+        float           m_eCalEndCapInnerPhiCoordinate;         ///< ECal end cap inner phi coordinate (missing from ILD gear files)
+        int             m_eCalEndCapOuterSymmetryOrder;         ///< ECal end cap outer symmetry order (missing from ILD gear files)
+        float           m_eCalEndCapOuterPhiCoordinate;         ///< ECal end cap outer phi coordinate (missing from ILD gear files)
+
+        int             m_hCalEndCapInnerSymmetryOrder;         ///< HCal end cap inner symmetry order (missing from ILD gear files)
+        float           m_hCalEndCapInnerPhiCoordinate;         ///< HCal end cap inner phi coordinate (missing from ILD gear files)
+        int             m_hCalEndCapOuterSymmetryOrder;         ///< HCal end cap outer symmetry order (missing from ILD gear files)
+        float           m_hCalEndCapOuterPhiCoordinate;         ///< HCal end cap outer phi coordinate (missing from ILD gear files)
+
+        int             m_hCalRingInnerSymmetryOrder;           ///< HCal ring inner symmetry order (missing from ILD gear files)
+        float           m_hCalRingInnerPhiCoordinate;           ///< HCal ring inner phi coordinate (missing from ILD gear files)
+        int             m_hCalRingOuterSymmetryOrder;           ///< HCal ring outer symmetry order (missing from ILD gear files)
+        float           m_hCalRingOuterPhiCoordinate;           ///< HCal ring outer phi coordinate (missing from ILD gear files)
+    };
+
+    /**
+     *  @brief  Constructor
+     * 
+     *  @param  settings the creator settings
+     *  @param  pPandora address of the relevant pandora instance
+     */
+     GeometryCreator(const Settings &settings, const pandora::Pandora *const pPandora);
+
+    /**
+     *  @brief  Destructor
+     */
+     ~GeometryCreator();
+
+    /**
+     *  @brief  Create geometry
+     */
+    //pandora::StatusCode CreateGeometry() const;
+    pandora::StatusCode CreateGeometry(ISvcLocator* svcloc);
+
+private:
+    typedef std::map<pandora::SubDetectorType, PandoraApi::Geometry::SubDetector::Parameters> SubDetectorTypeMap;
+    typedef std::map<std::string, PandoraApi::Geometry::SubDetector::Parameters> SubDetectorNameMap;
+
+    /**
+     *  @brief  Set mandatory sub detector parameters
+     * 
+     *  @param  subDetectorTypeMap the sub detector type map
+     */
+    void SetMandatorySubDetectorParameters(SubDetectorTypeMap &subDetectorTypeMap) const;
+
+    /**
+     *  @brief  Set additional sub detector parameters
+     * 
+     *  @param  subDetectorNameMap the sub detector name map (for smaller sub detectors, identified uniquely only by name)
+     */
+    void SetAdditionalSubDetectorParameters(SubDetectorNameMap &subDetectorNameMap) const;
+
+    /**
+     *  @brief  Set sub detector parameters to their gear default values
+     * 
+     *  @param  inputParameters input parameters, from gear
+     *  @param  subDetectorName the sub detector name
+     *  @param  subDetectorType the sub detector type
+     *  @param  parameters the sub detector parameters
+     */
+    void SetDefaultSubDetectorParameters(const gear::CalorimeterParameters &inputParameters, const std::string &subDetectorName,
+        const pandora::SubDetectorType subDetectorType, PandoraApi::Geometry::SubDetector::Parameters &parameters) const;
+
+    void SetDefaultSubDetectorParameters(const dd4hep::Detector* theDetector, const std::string &subDetectorName,
+        const pandora::SubDetectorType subDetectorType, PandoraApi::Geometry::SubDetector::Parameters &parameters) const;
+
+    /**
+     *  @brief  Set positions of gaps in ILD detector and add information missing from GEAR parameters file
+     * 
+     *  @param  subDetectorTypeMap the sub detector type map
+     *  @param  subDetectorNameMap the sub detector name map (for smaller sub detectors, identified uniquely only by name)
+     */
+    pandora::StatusCode SetILDSpecificGeometry(SubDetectorTypeMap &subDetectorTypeMap, SubDetectorNameMap &subDetectorNameMap) const;
+
+    /**
+     *  @brief  Add information missing from GEAR parameters file for ILD SDHCAL detector
+     * 
+     *  @param  subDetectorTypeMap the sub detector type map
+     */
+    pandora::StatusCode SetILD_SDHCALSpecificGeometry(SubDetectorTypeMap &subDetectorTypeMap) const;
+
+    /**
+     *  @brief  Specify positions of hcal barrel box gaps - ILD specific
+     */
+    pandora::StatusCode CreateHCalBarrelBoxGaps() const;
+
+    /**
+     *  @brief  Specify positions of hcal end cap box gaps - ILD specific
+     */
+    pandora::StatusCode CreateHCalEndCapBoxGaps() const;
+
+    /**
+     *  @brief  Specify positions of hcal barrel concentric polygon gaps - ILD specific
+     */
+    pandora::StatusCode CreateHCalBarrelConcentricGaps() const;
+
+    /**
+     *  @brief  Create box gaps at regular positions on polygonal prism, oriented along main z axis - ILD specific
+     * 
+     *  @param  symmetryOrder the pandora geometry parameters
+     *  @param  phi0 the phi coordinate
+     *  @param  innerRadius the inner r coordinate
+     *  @param  outerRadius the outer r coordinate
+     *  @param  minZ the minimum z coordinate
+     *  @param  maxZ the maximum z coordinate
+     *  @param  gapWidth the gap width
+     *  @param  vertexOffset position offset for vertex that doesn't point back to origin of xy plane
+     */
+    pandora::StatusCode CreateRegularBoxGaps(unsigned int symmetryOrder, float phi0, float innerRadius, float outerRadius, float minZ,
+        float maxZ, float gapWidth, pandora::CartesianVector vertexOffset = pandora::CartesianVector(0, 0, 0)) const;
+
+    const Settings          m_settings;                     ///< The geometry creator settings
+    const pandora::Pandora *m_pPandora;                     ///< Address of the pandora object to create the geometry
+    gear::GearMgr* _GEAR;
+    dd4hep::Detector* m_dd4hep;
+};
+
+#endif // #ifndef GEOMETRY_CREATOR_H
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/include/MCParticleCreator.h b/Reconstruction/PFA/Pandora/MatrixPandora/include/MCParticleCreator.h
new file mode 100644
index 00000000..4ad9f275
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/include/MCParticleCreator.h
@@ -0,0 +1,98 @@
+/**
+ *  @file   MarlinPandora/include/MCParticleCreator.h
+ * 
+ *  @brief  Header file for the mc particle creator class.
+ * 
+ *  $Log: $
+ */
+
+#ifndef MC_PARTICLE_CREATOR_H
+#define MC_PARTICLE_CREATOR_H 1
+
+#include "edm4hep/MCParticle.h"
+#include "Api/PandoraApi.h"
+
+#include "CaloHitCreator.h"
+#include "TrackCreator.h"
+/**
+ *  @brief  MCParticleCreator class
+ */
+
+class CollectionMaps;
+
+class MCParticleCreator
+{
+public:
+    typedef std::vector<std::string> StringVector;
+
+    /**
+     *  @brief  Settings class
+     */
+    class Settings
+    {
+    public:
+        /**
+         *  @brief  Default constructor
+         */
+        Settings();
+
+        StringVector    m_mcParticleCollections;                ///< The mc particle collections
+        StringVector    m_CaloHitRelationCollections;         ///< The SimCaloHit to CaloHit particle relations
+        StringVector    m_TrackRelationCollections;           ///< The SimTrackerHit to TrackerHit particle relations
+        float           m_bField;                             ///< m_bField
+    };
+
+    /**
+     *  @brief  Constructor
+     * 
+     *  @param  settings the creator settings
+     *  @param  pPandora address of the relevant pandora instance
+     */
+     MCParticleCreator(const Settings &settings, const pandora::Pandora *const pPandora);
+
+    /**
+     *  @brief  Destructor
+     */
+     ~MCParticleCreator();
+
+    /**
+     *  @brief  Create MCParticles
+     * 
+     *  @param  pLCEvent the lcio event
+     */    
+    //pandora::StatusCode CreateMCParticles(const EVENT::LCEvent *const pLCEvent) const;
+    //pandora::StatusCode CreateMCParticles(const std::map<std::string, const podio::CollectionBase*>& collectionMap ) const;
+    pandora::StatusCode CreateMCParticles(const CollectionMaps& collectionMaps ) const;
+
+    /**
+     *  @brief  Create Track to mc particle relationships
+     *
+     *  @param  pLCEvent the lcio event
+     *  @param  trackVector the vector containing all tracks successfully passed to pandora
+     */
+ //   pandora::StatusCode CreateTrackToMCParticleRelationships(const EVENT::LCEvent *const pLCEvent, const TrackVector &trackVector) const;
+     pandora::StatusCode CreateTrackToMCParticleRelationships(const CollectionMaps& collectionMaps, const TrackVector &trackVector) const;
+
+     void Reset();
+    /**
+     *  @brief  Create calo hit to mc particle relationships
+     *
+     *  @param  pLCEvent the lcio event
+     *  @param  calorimeterHitVector the vector containing all calorimeter hits successfully passed to pandora
+     */
+//    pandora::StatusCode CreateCaloHitToMCParticleRelationships(const EVENT::LCEvent *const pLCEvent, const CalorimeterHitVector &calorimeterHitVector) const;
+      pandora::StatusCode CreateCaloHitToMCParticleRelationships(const CollectionMaps& collectionMaps, const CalorimeterHitVector &calorimeterHitVector) const;
+
+private:
+    const Settings          m_settings;                         ///< The mc particle creator settings
+    const pandora::Pandora *m_pPandora;                         ///< Address of the pandora object to create the mc particles
+    const float             m_bField;                           ///< The bfield
+    std::map<unsigned int, const edm4hep::MCParticle*>*  m_id_pMC_map;
+};
+
+inline void MCParticleCreator::Reset()
+{
+    m_id_pMC_map->clear();
+}
+
+#endif // #ifndef MC_PARTICLE_CREATOR_H
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/include/PandoraMatrixAlg.h b/Reconstruction/PFA/Pandora/MatrixPandora/include/PandoraMatrixAlg.h
new file mode 100644
index 00000000..96a6938b
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/include/PandoraMatrixAlg.h
@@ -0,0 +1,324 @@
+#ifndef PandoraMatrixAlg_H
+#define PandoraMatrixAlg_H
+
+#include "FWCore/DataHandle.h"
+#include "GaudiAlg/GaudiAlgorithm.h"
+#include <gsl/gsl_rng.h>
+#include "edm4hep/ClusterCollection.h"
+#include "edm4hep/ReconstructedParticleCollection.h"
+#include "edm4hep/EventHeaderCollection.h"
+#include "edm4hep/SimTrackerHitCollection.h"
+#include "edm4hep/TrackerHitCollection.h"
+#include "edm4hep/CalorimeterHitCollection.h"
+#include "edm4hep/VertexCollection.h"
+#include "edm4hep/TrackCollection.h"
+#include "edm4hep/MCParticle.h" 
+#include "edm4hep/MCParticleCollection.h"
+#include "edm4hep/MCRecoCaloAssociation.h"
+#include "edm4hep/MCRecoTrackerAssociation.h"
+#include "edm4hep/MCRecoTrackerAssociationCollection.h"
+#include "edm4hep/MCRecoCaloAssociationCollection.h"
+#include "edm4hep/MCRecoParticleAssociation.h"
+#include "edm4hep/MCRecoParticleAssociationCollection.h"
+
+#include "Api/PandoraApi.h"
+
+#ifdef MONITORING
+#include "TApplication.h"
+#endif
+
+#include <iostream>
+#include <random>
+#include <string>
+#include <unistd.h>
+
+
+#include "CaloHitCreator.h"
+#include "GeometryCreator.h"
+#include "MCParticleCreator.h"
+#include "PfoCreator.h"
+#include "TrackCreator.h"
+
+#include "TROOT.h"
+#include "TTree.h"
+#include "TFile.h"
+
+
+/* PandoraMatrixAlg ========== <br>
+ * 
+ */
+namespace pandora {class Pandora;}
+
+class IEventSeeder;
+
+class CollectionMaps
+{
+public:
+    /**
+     *  @brief  Default constructor
+     */
+    CollectionMaps();
+    void clear();
+    std::map<std::string, const edm4hep::MCParticleCollection*> CollectionMap_MC;
+    std::map<std::string, const edm4hep::CalorimeterHitCollection*> CollectionMap_CaloHit;
+    std::map<std::string, const edm4hep::VertexCollection*> CollectionMap_Vertex;
+    std::map<std::string, const edm4hep::TrackCollection*> CollectionMap_Track;
+
+    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;
+};
+
+
+
+class PandoraMatrixAlg : public GaudiAlgorithm
+{
+  //friend class AlgFactory<PandoraMatrixAlg>;//gives error in 97 version
+ 
+public:
+ 
+  PandoraMatrixAlg(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 FinaliseSteeringParameters();
+  void FinaliseSteeringParameters(ISvcLocator* svcloc);
+  pandora::StatusCode RegisterUserComponents() const;
+  void Reset();
+  typedef std::vector<float> FloatVector;
+  typedef std::vector<std::string> StringVector;
+
+
+  class Settings
+  {
+  public:
+      /**
+       *  @brief  Default constructor
+       */
+      Settings();
+
+      std::string     m_pandoraSettingsXmlFile;           ///< The pandora settings xml file
+
+      float           m_innerBField;                      ///< The bfield in the main tracker, ecal and hcal, units Tesla
+      float           m_muonBarrelBField;                 ///< The bfield in the muon barrel, units Tesla
+      float           m_muonEndCapBField;                 ///< The bfield in the muon endcap, units Tesla
+
+      FloatVector     m_inputEnergyCorrectionPoints;      ///< The input energy points for non-linearity energy correction
+      FloatVector     m_outputEnergyCorrectionPoints;     ///< The output energy points for non-linearity energy correction
+  };
+
+
+    /**
+     *  @brief  Get address of the pandora instance
+     * 
+     *  @return address of the pandora instance
+     */
+    const pandora::Pandora *GetPandora() const;
+    StatusCode updateMap();
+    StatusCode updateMap(CollectionMaps & tmp_map);
+    StatusCode Ana();
+    StatusCode CreateMCRecoParticleAssociation();
+    //StatusCode Create_MC(); 
+protected:
+ 
+  typedef std::vector<float> FloatVec;
+
+  int _nEvt ;
+
+  IEventSeeder * _SEEDER;
+ 
+
+  Gaudi::Property< std::string >              m_PandoraSettingsXmlFile { this, "PandoraSettingsDefault_xml", "/junofs/users/wxfang/MyGit/MarlinPandora/scripts/PandoraSettingsDefault_wx.xml" };
+  Gaudi::Property<int>                        m_NEventsToSkip                   { this, "NEventsToSkip", 0 };
+
+  Gaudi::Property< std::vector<std::string> > m_TrackCollections{ this, "TrackCollections", {"MarlinTrkTracks"} };
+  Gaudi::Property< std::vector<std::string> > m_ECalCaloHitCollections{ this, "ECalCaloHitCollections", {"ECALBarrel","ECALEndcap","ECALOther"} };
+  Gaudi::Property< std::vector<std::string> > m_HCalCaloHitCollections{ this, "HCalCaloHitCollections", {"HCALBarrel","HCALEndcap","HCALOther"} };
+  Gaudi::Property< std::vector<std::string> > m_LCalCaloHitCollections{ this, "LCalCaloHitCollections", {"LCAL"} };
+  Gaudi::Property< std::vector<std::string> > m_LHCalCaloHitCollections{ this, "LHCalCaloHitCollections", {"LHCAL"} };
+  Gaudi::Property< std::vector<std::string> > m_MuonCaloHitCollections{ this, "MuonCaloHitCollections", {"MUON"} };
+  Gaudi::Property< std::vector<std::string> > m_MCParticleCollections{ this, "MCParticleCollections", {"MCParticle"} };
+  Gaudi::Property< std::vector<std::string> > m_RelCaloHitCollections{ this, "RelCaloHitCollections", {"RelationCaloHit","RelationMuonHit"} };
+  Gaudi::Property< std::vector<std::string> > m_RelTrackCollections{ this, "RelTrackCollections", {"MarlinTrkTracksMCTruthLink"} };
+  Gaudi::Property< std::vector<std::string> > m_KinkVertexCollections{ this, "KinkVertexCollections", {"KinkVertices"} };
+  Gaudi::Property< std::vector<std::string> > m_ProngVertexCollections{ this, "ProngVertexCollections", {"ProngVertices"} };
+  Gaudi::Property< std::vector<std::string> > m_SplitVertexCollections{ this, "SplitVertexCollections", {"SplitVertices"} };
+  Gaudi::Property< std::vector<std::string> > m_V0VertexCollections{ this, "V0VertexCollections", {"V0Vertices"} };
+  Gaudi::Property< std::string >              m_ClusterCollectionName { this, "ClusterCollectionName", "PandoraClusters" };
+  Gaudi::Property< std::string >              m_PFOCollectionName { this, "PFOCollectionName", "PandoraPFOs" };
+  Gaudi::Property<float>                      m_ECalToMipCalibration{ this, "ECalToMipCalibration", 160.0 };
+  Gaudi::Property<float>                      m_HCalToMipCalibration{ this, "HCalToMipCalibration", 34.8 };
+  Gaudi::Property<float>                      m_ECalMipThreshold{ this, "ECalMipThreshold", 0.5 };
+  Gaudi::Property<float>                      m_HCalMipThreshold{ this, "HCalMipThreshold", 0.3 };
+  Gaudi::Property<float>                      m_ECalToEMGeVCalibration{ this, "ECalToEMGeVCalibration", 1.007 };
+  Gaudi::Property<float>                      m_HCalToEMGeVCalibration{ this, "HCalToEMGeVCalibration", 1.007 };
+  Gaudi::Property<float>                      m_ECalToHadGeVCalibrationBarrel{ this, "ECalToHadGeVCalibrationBarrel", 1.12 };
+  Gaudi::Property<float>                      m_ECalToHadGeVCalibrationEndCap{ this, "ECalToHadGeVCalibrationEndCap", 1.12 };
+  Gaudi::Property<float>                      m_HCalToHadGeVCalibration{ this, "HCalToHadGeVCalibration", 1.07 };
+  Gaudi::Property<float>                      m_MuonToMipCalibration{ this, "MuonToMipCalibration", 10.0 };
+  Gaudi::Property<int>                        m_DigitalMuonHits{ this, "DigitalMuonHits", 0 };
+  Gaudi::Property<float>                      m_MaxHCalHitHadronicEnergy{ this, "MaxHCalHitHadronicEnergy", 1.0 };
+  Gaudi::Property<int>                        m_UseOldTrackStateCalculation{ this, "UseOldTrackStateCalculation", 0 };
+
+
+  Gaudi::Property<float>                      m_AbsorberRadLengthECal{ this, "AbsorberRadLengthECal", 0.2854 };
+  Gaudi::Property<float>                      m_AbsorberIntLengthECal{ this, "AbsorberIntLengthECal", 0.0101 };
+  Gaudi::Property<float>                      m_AbsorberRadLengthHCal{ this, "AbsorberRadLengthHCal", 0.0569 };
+  Gaudi::Property<float>                      m_AbsorberIntLengthHCal{ this, "AbsorberIntLengthHCal", 0.006  };
+  Gaudi::Property<float>                      m_AbsorberRadLengthOther{ this, "AbsorberRadLengthOther", 0.0569  };
+  Gaudi::Property<float>                      m_AbsorberIntLengthOther{ this, "AbsorberIntLengthOther", 0.006  };
+  Gaudi::Property< std::string >              m_StartVertexCollectionName { this, "StartVertexCollectionName", "PandoraPFANewStartVertices" };
+  Gaudi::Property< std::string >              m_StartVertexAlgorithmName { this, "StartVertexAlgorithmName", "PandoraPFANew" };
+  Gaudi::Property<float>                      m_EMStochasticTerm{ this, "EMStochasticTerm", 0.17  };
+  Gaudi::Property<float>                      m_HadStochasticTerm{ this, "HadStochasticTerm", 0.6  };
+  Gaudi::Property<float>                      m_EMConstantTerm{ this, "EMConstantTerm", 0.01  };
+  Gaudi::Property<float>                      m_HadConstantTerm{ this, "HadConstantTerm", 0.03  };
+  Gaudi::Property<float>                      m_MuonHitEnergy{ this, "MuonHitEnergy", 0.5 };
+  Gaudi::Property<int>                        m_NOuterSamplingLayers{ this, "NOuterSamplingLayers", 3 };
+  Gaudi::Property<float>                      m_LayersFromEdgeMaxRearDistance{ this, "LayersFromEdgeMaxRearDistance", 250.f };
+  Gaudi::Property<float>                      m_MuonBarrelBField{ this, "MuonBarrelBField", -1.5f };
+  Gaudi::Property<float>                      m_MuonEndCapBField{ this, "MuonEndCapBField", 0.01f };
+  Gaudi::Property<int>                        m_ShouldFormTrackRelationships{ this, "ShouldFormTrackRelationships", 1 };
+  Gaudi::Property<int>                        m_MinTrackHits{ this, "MinTrackHits", 5 };
+  Gaudi::Property<int>                        m_MinFtdTrackHits{ this, "MinFtdTrackHits", 0 };
+  Gaudi::Property<int>                        m_MaxTrackHits{ this, "MaxTrackHits", 5000 };
+  Gaudi::Property<float>                      m_D0TrackCut{ this, "D0TrackCut", 50. };
+  Gaudi::Property<float>                      m_Z0TrackCut{ this, "Z0TrackCut", 50. };
+  Gaudi::Property<int>                        m_UseNonVertexTracks{ this, "UseNonVertexTracks", 1 };
+  Gaudi::Property<int>                        m_UseUnmatchedNonVertexTracks{ this, "UseUnmatchedNonVertexTracks", 0 };
+  Gaudi::Property<int>                        m_UseUnmatchedVertexTracks{ this, "UseUnmatchedVertexTracks", 1 };
+  Gaudi::Property<float>                      m_UnmatchedVertexTrackMaxEnergy{ this, "UnmatchedVertexTrackMaxEnergy", 5. };
+  Gaudi::Property<float>                      m_D0UnmatchedVertexTrackCut{ this, "D0UnmatchedVertexTrackCut", 5. };
+  Gaudi::Property<float>                      m_Z0UnmatchedVertexTrackCut{ this, "Z0UnmatchedVertexTrackCut", 5. };
+  Gaudi::Property<float>                      m_ZCutForNonVertexTracks{ this, "ZCutForNonVertexTracks", 250. };
+  Gaudi::Property<int>                        m_ReachesECalNTpcHits{ this, "ReachesECalNTpcHits", 11 };
+  Gaudi::Property<int>                        m_ReachesECalNFtdHits{ this, "ReachesECalNFtdHits", 4 };
+  Gaudi::Property<float>                      m_ReachesECalTpcOuterDistance{ this, "ReachesECalTpcOuterDistance", -100. };
+  Gaudi::Property<int>                        m_ReachesECalMinFtdLayer{ this, "ReachesECalMinFtdLayer", 9 };
+  Gaudi::Property<float>                      m_ReachesECalTpcZMaxDistance{ this, "ReachesECalTpcZMaxDistance", -50. };
+  Gaudi::Property<float>                      m_ReachesECalFtdZMaxDistance{ this, "ReachesECalFtdZMaxDistance", -1. };
+  Gaudi::Property<float>                      m_CurvatureToMomentumFactor{ this, "CurvatureToMomentumFactor", 0.3 / 2000. };
+  Gaudi::Property<float>                      m_MinTrackECalDistanceFromIp{ this, "MinTrackECalDistanceFromIp", 100. };
+
+  Gaudi::Property<float>                      m_MaxTrackSigmaPOverP             { this, "MaxTrackSigmaPOverP", 0.15 };
+  Gaudi::Property<float>                      m_MinMomentumForTrackHitChecks    { this, "MinMomentumForTrackHitChecks", 1. };
+  Gaudi::Property<float>                      m_TpcMembraneMaxZ                 { this, "TpcMembraneMaxZ", 10. };
+  Gaudi::Property<float>                      m_MinTpcHitFractionOfExpected     { this, "MinTpcHitFractionOfExpected", 0.20 };
+  Gaudi::Property<int>                        m_MinFtdHitsForTpcHitFraction     { this, "MinFtdHitsForTpcHitFraction", 2 };
+  Gaudi::Property<float>                      m_MaxTpcInnerRDistance            { this, "MaxTpcInnerRDistance", 50.0 };
+  Gaudi::Property<int>                        m_ECalEndCapInnerSymmetryOrder    { this, "ECalEndCapInnerSymmetryOrder", 4 };
+  Gaudi::Property<float>                      m_ECalEndCapInnerPhiCoordinate    { this, "ECalEndCapInnerPhiCoordinate", 0. };
+  Gaudi::Property<int>                        m_ECalEndCapOuterSymmetryOrder    { this, "ECalEndCapOuterSymmetryOrder", 8 };
+  Gaudi::Property<float>                      m_ECalEndCapOuterPhiCoordinate    { this, "ECalEndCapOuterPhiCoordinate", 0. };
+  Gaudi::Property<int>                        m_HCalEndCapInnerSymmetryOrder    { this, "HCalEndCapInnerSymmetryOrder", 4 };
+  Gaudi::Property<float>                      m_HCalEndCapInnerPhiCoordinate    { this, "HCalEndCapInnerPhiCoordinate", 0. };
+  Gaudi::Property<int>                        m_HCalEndCapOuterSymmetryOrder    { this, "HCalEndCapOuterSymmetryOrder", 16 };
+  Gaudi::Property<float>                      m_HCalEndCapOuterPhiCoordinate    { this, "HCalEndCapOuterPhiCoordinate", 0. };
+  Gaudi::Property<int>                        m_HCalRingInnerSymmetryOrder      { this, "HCalRingInnerSymmetryOrder",   8  };
+  Gaudi::Property<float>                      m_HCalRingInnerPhiCoordinate      { this, "HCalRingInnerPhiCoordinate",   0. };
+  Gaudi::Property<int>                        m_HCalRingOuterSymmetryOrder      { this, "HCalRingOuterSymmetryOrder",   16 };
+  Gaudi::Property<float>                      m_HCalRingOuterPhiCoordinate      { this, "HCalRingOuterPhiCoordinate",   0. };
+  Gaudi::Property<bool>                       m_StripSplittingOn                { this, "StripSplittingOn", false };
+  Gaudi::Property<bool>                       m_UseEcalScLayers                 { this, "UseEcalScLayers", false };
+  Gaudi::Property<float>                      m_ECalSiToMipCalibration          { this, "ECalSiToMipCalibration", 1. };
+  Gaudi::Property<float>                      m_ECalScToMipCalibration          { this, "ECalScToMipCalibration", 1. };
+  Gaudi::Property<float>                      m_ECalSiMipThreshold              { this, "ECalSiMipThreshold", 0. };
+  Gaudi::Property<float>                      m_ECalScMipThreshold              { this, "ECalScMipThreshold", 0. };
+  Gaudi::Property<float>                      m_ECalSiToEMGeVCalibration        { this, "ECalSiToEMGeVCalibration", 1. };
+  Gaudi::Property<float>                      m_ECalScToEMGeVCalibration        { this, "ECalScToEMGeVCalibration", 1. };
+  Gaudi::Property<float>                      m_ECalSiToHadGeVCalibrationEndCap { this, "ECalSiToHadGeVCalibrationEndCap", 1. };
+  Gaudi::Property<float>                      m_ECalScToHadGeVCalibrationEndCap { this, "ECalScToHadGeVCalibrationEndCap", 1. };
+  Gaudi::Property<float>                      m_ECalSiToHadGeVCalibrationBarrel { this, "ECalSiToHadGeVCalibrationBarrel", 1. };
+  Gaudi::Property<float>                      m_ECalScToHadGeVCalibrationBarrel { this, "ECalScToHadGeVCalibrationBarrel", 1. };
+
+  Gaudi::Property<FloatVector>                m_InputEnergyCorrectionPoints { this, "InputEnergyCorrectionPoints", {} };
+  Gaudi::Property<FloatVector>                m_OutputEnergyCorrectionPoints { this, "OutputEnergyCorrectionPoints", {} };
+
+
+  static pandora::Pandora        *m_pPandora;
+  GeometryCreator                *m_pGeometryCreator;             ///< The geometry creator
+  CaloHitCreator                 *m_pCaloHitCreator;              ///< The calo hit creator
+  TrackCreator                   *m_pTrackCreator;                ///< The track creator
+  MCParticleCreator              *m_pMCParticleCreator;           ///< The mc particle creator
+  PfoCreator                     *m_pPfoCreator;                  ///< The pfo creator
+ 
+  Settings                        m_settings;                     ///< The settings for the pandora pfa new algo
+  CollectionMaps                  *m_CollectionMaps;               ///< The settings for the pandora pfa new algo
+  GeometryCreator::Settings       m_geometryCreatorSettings;      ///< The geometry creator settings
+  TrackCreator::Settings          m_trackCreatorSettings;         ///< The track creator settings
+  CaloHitCreator::Settings        m_caloHitCreatorSettings;       ///< The calo hit creator settings
+  MCParticleCreator::Settings     m_mcParticleCreatorSettings;    ///< The mc particle creator settings
+  PfoCreator::Settings            m_pfoCreatorSettings;           ///< The pfo creator settings
+
+  std::string                     m_detectorName;                 ///< The detector name
+  unsigned int                    m_nRun;                         ///< The run number
+  unsigned int                    m_nEvent;                       ///< The event number
+  //### For Ana #################
+  TFile* m_fout;
+  TTree* m_tree;
+  std::vector<int  > m_pReco_PID;    
+  std::vector<float> m_pReco_mass;
+  std::vector<float> m_pReco_energy;
+  std::vector<float> m_pReco_px;
+  std::vector<float> m_pReco_py;
+  std::vector<float> m_pReco_pz;
+  std::vector<float> m_pReco_charge;
+
+  std::vector<int>   m_mc_p_size;
+  std::vector<int>   m_mc_pid   ;
+  std::vector<float> m_mc_mass  ;
+  std::vector<float> m_mc_px    ;
+  std::vector<float> m_mc_py    ;
+  std::vector<float> m_mc_pz    ;
+  std::vector<float> m_mc_charge;
+  int m_hasConversion;
+  std::vector<float> m_hits_x ;
+  std::vector<float> m_hits_y ;
+  std::vector<float> m_hits_z ;
+  std::vector<float> m_hits_E ;
+
+  Gaudi::Property< std::string >              m_AnaOutput{ this, "AnaOutput", "/junofs/users/wxfang/MyGit/CEPCSW/Reconstruction/PFA/Pandora/GaudiPandora/Ana.root" };
+  //######################
+  
+  DataHandle<edm4hep::MCParticleCollection>     m_mcParCol_r  {"MCParticle", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_ECALBarrel_r{"ECALBarrel", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_ECALEndcap_r{"ECALEndcap", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_ECALOther_r {"ECALOther", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_HCALBarrel_r{"HCALBarrel", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_HCALEndcap_r{"HCALEndcap", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_HCALOther_r {"HCALOther", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_MUON_r      {"MUON", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_LCAL_r      {"LCAL", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_LHCAL_r     {"LHCAL", Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::CalorimeterHitCollection> m_BCAL_r      {"BCAL", Gaudi::DataHandle::Reader, this};
+
+  DataHandle<edm4hep::VertexCollection> m_KinkVertices_r    {"KinkVertices",Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::VertexCollection> m_ProngVertices_r   {"ProngVertices",Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::VertexCollection> m_SplitVertices_r   {"SplitVertices",Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::VertexCollection> m_V0Vertices_r      {"V0Vertices",Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::TrackCollection>  m_MarlinTrkTracks_r {"MarlinTrkTracks",Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::MCRecoCaloAssociationCollection>  m_MCRecoCaloAssociation_r {"MCRecoCaloAssociation",Gaudi::DataHandle::Reader, this};
+  DataHandle<edm4hep::MCRecoTrackerAssociationCollection>  m_MCRecoTrackerAssociation_r {"MCRecoTrackerAssociation",Gaudi::DataHandle::Reader, this};
+
+  DataHandle<edm4hep::ClusterCollection>                m_ClusterCollection_w {"PandoraClusters",Gaudi::DataHandle::Writer, this};
+  DataHandle<edm4hep::ReconstructedParticleCollection>  m_ReconstructedParticleCollection_w {"PandoraPFOs"    ,Gaudi::DataHandle::Writer, this};
+  DataHandle<edm4hep::VertexCollection>                 m_VertexCollection_w {"PandoraPFANewStartVertices",Gaudi::DataHandle::Writer, this};
+  DataHandle<edm4hep::MCRecoParticleAssociationCollection>  m_MCRecoParticleAssociation_w {"pfoMCRecoParticleAssociation",Gaudi::DataHandle::Writer, this};
+
+};
+
+#endif
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/include/PfoCreator.h b/Reconstruction/PFA/Pandora/MatrixPandora/include/PfoCreator.h
new file mode 100644
index 00000000..83c6e149
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/include/PfoCreator.h
@@ -0,0 +1,219 @@
+/**
+ *  @file   MarlinPandora/include/PfoCreator.h
+ * 
+ *  @brief  Header file for the pfo creator class.
+ * 
+ *  $Log: $
+ */
+
+#ifndef PFO_CREATOR_H
+#define PFO_CREATOR_H 1
+
+#include "FWCore/DataHandle.h"
+#include "edm4hep/Vector3f.h"
+#include "edm4hep/ClusterCollection.h"
+#include "edm4hep/Cluster.h"
+#include "edm4hep/ReconstructedParticleCollection.h"
+#include "edm4hep/ReconstructedParticle.h"
+#include "edm4hep/VertexCollection.h"
+#include "edm4hep/Vertex.h"
+#include "edm4hep/TrackCollection.h"
+#include "edm4hep/Track.h"
+#include "edm4hep/CalorimeterHitCollection.h"
+#include "edm4hep/CalorimeterHit.h"
+
+#include "ClusterShapes.h"
+#include "Api/PandoraApi.h"
+
+class CollectionMaps;
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+/**
+ *  @brief  PfoCreator class
+ */
+class PfoCreator
+{
+public:
+    /**
+     *  @brief  Settings class
+     */
+    class Settings
+    {
+    public:
+        /**
+         *  @brief  Default constructor
+         */
+        Settings();
+
+        std::string     m_clusterCollectionName;                ///< The name of the cluster output collection
+        std::string     m_pfoCollectionName;                    ///< The name of the pfo output collection
+        std::string     m_startVertexCollectionName;            ///< The name of the start vertex output collection
+        std::string     m_startVertexAlgName;                   ///< The name of the algorithm to fill the start vertex output collection
+        float           m_emStochasticTerm;                     ///< The stochastic term for EM shower energy resolution
+        float           m_hadStochasticTerm;                    ///< The stochastic term for Hadronic shower energy resolution
+        float           m_emConstantTerm;                       ///< The constant term for EM shower energy resolution
+        float           m_hadConstantTerm;                      ///< The constant term for Hadronic shower energy resolution
+    };
+
+    /**
+     *  @brief  Constructor
+     * 
+     *  @param  settings the creator settings
+     *  @param  pPandora address of the relevant pandora instance
+     */
+     PfoCreator(const Settings &settings, const pandora::Pandora *const pPandora);
+
+    /**
+     *  @brief  Destructor
+     */
+     ~PfoCreator();
+
+    /**
+     *  @brief  Create particle flow objects
+     * 
+     *  @param  pLCEvent the lcio event
+     */    
+    //pandora::StatusCode CreateParticleFlowObjects(EVENT::LCEvent *pLCEvent);
+    //pandora::StatusCode CreateParticleFlowObjects(const CollectionMaps& collectionMaps);
+    pandora::StatusCode CreateParticleFlowObjects(CollectionMaps& collectionMaps, DataHandle<edm4hep::ClusterCollection>& _pClusterCollection, DataHandle<edm4hep::ReconstructedParticleCollection>& _pReconstructedParticleCollection, DataHandle<edm4hep::VertexCollection>& _pStartVertexCollection);
+
+    CollectionMaps* m_collectionMaps;
+
+private:
+    /**
+     *  @brief  index for the subdetector
+     */
+    enum Index
+    {
+        ECAL_INDEX = 0,
+        HCAL_INDEX = 1,
+        YOKE_INDEX = 2,
+        LCAL_INDEX = 3,
+        LHCAL_INDEX = 4,
+        BCAL_INDEX = 5
+    };
+
+    /**
+     *  @brief  initialise sub detector name strings
+     * 
+     *  @param  subDetectorNames to receive the list of sub detector names
+     */
+    void InitialiseSubDetectorNames(pandora::StringVector &subDetectorNames) const;
+
+    /**
+     *  @brief  Set sub detector energies for a cluster
+     * 
+     *  @param  subDetectorNames the list of sub detector names
+     *  @param  pLcioCluster the address of the lcio cluster to be set sub detector energies
+     *  @param  pandoraCaloHitList the pandora calorimeter hit list
+     *  @param  hitE the vector to receive the energy of hits
+     *  @param  hitX the vector to receive the x position of hits
+     *  @param  hitY the vector to receive the y position of hits
+     *  @param  hitZ the vector to receive the z position of hits
+     */
+    void SetClusterSubDetectorEnergies(const pandora::StringVector &subDetectorNames, edm4hep::Cluster *const pLcioCluster,
+        const pandora::CaloHitList &pandoraCaloHitList, pandora::FloatVector &hitE, pandora::FloatVector &hitX, pandora::FloatVector &hitY,
+        pandora::FloatVector &hitZ) const;
+
+    /**
+     *  @brief  Set cluster energies and errors
+     * 
+     *  @param  pPandoraPfo the address of the pandora pfo
+     *  @param  pPandoraCluster the address of the pandora cluster
+     *  @param  pLcioCluster the address of the lcio cluster to be set energies and erros
+     *  @param  clusterCorrectEnergy a number to receive the cluster correct energy
+     */
+    void SetClusterEnergyAndError(const pandora::ParticleFlowObject *const pPandoraPfo, const pandora::Cluster *const pPandoraCluster, 
+        edm4hep::Cluster *const pLcioCluster, float &clusterCorrectEnergy) const;
+
+    /**
+     *  @brief  Set cluster position, errors and other shape info, by calculating culster shape first
+     * 
+     *  @param  nHitsInCluster number of hits in cluster
+     *  @param  hitE the vector of the energy of hits
+     *  @param  hitX the vector of the x position of hits
+     *  @param  hitY the vector of the y position of hits
+     *  @param  hitZ the vector of the z position of hits
+     *  @param  pLcioCluster the lcio cluster to be set positions and errors
+     *  @param  clusterPosition a CartesianVector to receive the cluster position
+     */
+    void SetClusterPositionAndError(const unsigned int nHitsInCluster, pandora::FloatVector &hitE, pandora::FloatVector &hitX, 
+        pandora::FloatVector &hitY, pandora::FloatVector &hitZ, edm4hep::Cluster *const pLcioCluster, pandora::CartesianVector &clusterPositionVec) const;
+
+    /**
+     *  @brief  Calculate reference point for pfo with tracks
+     * 
+     *  @param  pPandoraPfo the address of the pandora pfo
+     *  @param  referencePoint a CartesianVector to receive the reference point
+     */
+    pandora::StatusCode CalculateTrackBasedReferencePoint(const pandora::ParticleFlowObject *const pPandoraPfo, pandora::CartesianVector &referencePoint) const;
+
+    /**
+     *  @brief  Set reference point of the reconstructed particle
+     * 
+     *  @param  referencePoint a CartesianVector of the reference point
+     *  @param  pReconstructedParticle the address of the reconstructed particle to be reference point
+     */
+    void SetRecoParticleReferencePoint(const pandora::CartesianVector &referencePoint, edm4hep::ReconstructedParticle *const pReconstructedParticle) const;
+
+    /**
+     *  @brief  Add tracks to reconstructed particle
+     * 
+     *  @param  pPandoraPfo the address of the pandora pfo
+     *  @param  pReconstructedParticle the address of the reconstructed particle to be added tracks
+     */
+    void AddTracksToRecoParticle(const pandora::ParticleFlowObject *const pPandoraPfo, edm4hep::ReconstructedParticle *const pReconstructedParticle) const;
+
+    /**
+     *  @brief  Set properties of reconstructed particle from pandora pfo
+     * 
+     *  @param  pPandoraPfo the address of the pandora pfo 
+     *  @param  pReconstructedParticle the address of the reconstructed particle to be set properties
+     */
+    void SetRecoParticlePropertiesFromPFO(const pandora::ParticleFlowObject *const pPandoraPfo, edm4hep::ReconstructedParticle *const pReconstructedParticle) const;
+
+    /**
+     *  @brief  Whether parent and daughter tracks are associated with the same pfo
+     *
+     *  @param  pPandoraTrack the address of the pandora track
+     *  @param  allTrackList list of all tracks associated with reconstructed particle
+     * 
+     *  @return boolean
+     */
+    bool IsValidParentTrack(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const;
+
+    /**
+     *  @brief  Whether sibling tracks are associated with the same pfo
+     *
+     *  @param  pPandoraTrack the address of the pandora track
+     *  @param  allTrackList list of all tracks associated with reconstructed particle
+     * 
+     *  @return boolean
+     */
+    bool HasValidSiblingTrack(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const;
+
+    /**
+     *  @brief  Whether the track is the closest (of those associated with the same pfo) to the interaction point
+     *
+     *  @param  pPandoraTrack the address of the pandora track
+     *  @param  allTrackList list of all tracks associated to reconstructed particle
+     * 
+     *  @return boolean
+     */ 
+    bool IsClosestTrackToIP(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const;
+
+    /**
+     *  @brief  Whether at least one track sibling track is associated to the reconstructed particle 
+     *
+     *  @param  pPandoraTrack the address of the pandora track
+     *  @param  allTrackList list of all tracks associated to reconstructed particle
+     * 
+     *  @return boolean
+     */
+    bool AreAnyOtherSiblingsInList(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const;
+
+    const Settings              m_settings;                         ///< The pfo creator settings
+    const pandora::Pandora      *m_pPandora;                        ///< Address of the pandora object from which to extract the pfos
+};
+
+#endif // #ifndef PFO_CREATOR_H
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/include/TrackCreator.h b/Reconstruction/PFA/Pandora/MatrixPandora/include/TrackCreator.h
new file mode 100644
index 00000000..1e1588c2
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/include/TrackCreator.h
@@ -0,0 +1,373 @@
+/**
+ *  @file   MarlinPandora/include/TrackCreator.h
+ * 
+ *  @brief  Header file for the track creator class.
+ * 
+ *  $Log: $
+ */
+
+#ifndef TRACK_CREATOR_H
+#define TRACK_CREATOR_H 1
+
+
+
+
+#include "GaudiKernel/ISvcLocator.h"
+
+#include "edm4hep/Track.h"
+#include "edm4hep/TrackConst.h"
+#include "edm4hep/TrackState.h"
+#include "edm4hep/ReconstructedParticleConst.h"
+
+#include "Api/PandoraApi.h"
+#include "Objects/Helix.h"
+
+namespace gear { class GearMgr; }
+
+class CollectionMaps;
+
+typedef std::vector<const edm4hep::Track *> TrackVector;
+//typedef std::set<const edm4hep::Track *> TrackList;
+typedef std::set<unsigned int> TrackList;
+//typedef std::map<edm4hep::Track *, int> TrackToPidMap;
+typedef std::map<edm4hep::ConstTrack, int> TrackToPidMap;
+/*
+inline LCCollectionVec *newTrkCol(const std::string &name, LCEvent *evt , bool isSubset)
+{
+    LCCollectionVec* col = new LCCollectionVec( LCIO::TRACK ) ;
+
+    LCFlagImpl hitFlag(0) ;
+    hitFlag.setBit( LCIO::TRBIT_HITS ) ;
+    col->setFlag( hitFlag.getFlag()  ) ;
+    evt->addCollection( col , name ) ;
+    col->setSubset( isSubset ) ;
+
+    return col ;
+}
+*/
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+/**
+ *  @brief  TrackCreator class
+ */
+class TrackCreator
+{
+public:
+    typedef std::vector<double> DoubleVector;
+    typedef std::vector<std::string> StringVector;
+
+    /**
+     *  @brief  Settings class
+     */
+    class Settings
+    {
+    public:
+        /**
+         *  @brief  Default constructor
+         */
+        Settings();
+
+        StringVector    m_trackCollections;                     ///< The reconstructed track collections
+        StringVector    m_kinkVertexCollections;                ///< The kink vertex collections
+        StringVector    m_prongVertexCollections;               ///< The prong vertex collections
+        StringVector    m_splitVertexCollections;               ///< The split vertex collections
+        StringVector    m_v0VertexCollections;                  ///< The v0 vertex collections
+
+        StringVector    m_prongSplitVertexCollections;          ///< Concatenated list of prong and split vertex collections
+        int             m_shouldFormTrackRelationships;         ///< Whether to form pandora track relationships using v0 and kink info
+
+        int             m_minTrackHits;                         ///< Track quality cut: the minimum number of track hits
+        int             m_minFtdTrackHits;                      ///< Track quality cut: the minimum number of FTD track hits for FTD only tracks
+        int             m_maxTrackHits;                         ///< Track quality cut: the maximum number of track hits
+
+        float           m_d0TrackCut;                           ///< Track d0 cut used to determine whether track can be used to form pfo
+        float           m_z0TrackCut;                           ///< Track z0 cut used to determine whether track can be used to form pfo
+
+        int             m_usingNonVertexTracks;                 ///< Whether can form pfos from tracks that don't start at vertex
+        int             m_usingUnmatchedNonVertexTracks;        ///< Whether can form pfos from unmatched tracks that don't start at vertex
+        int             m_usingUnmatchedVertexTracks;           ///< Whether can form pfos from unmatched tracks that start at vertex
+        float           m_unmatchedVertexTrackMaxEnergy;        ///< Maximum energy for unmatched vertex track
+
+        float           m_d0UnmatchedVertexTrackCut;            ///< d0 cut used to determine whether unmatched vertex track can form pfo
+        float           m_z0UnmatchedVertexTrackCut;            ///< z0 cut used to determine whether unmatched vertex track can form pfo
+        float           m_zCutForNonVertexTracks;               ///< Non vtx track z cut to determine whether track can be used to form pfo
+
+        int             m_reachesECalNTpcHits;                  ///< Minimum number of tpc hits to consider track as reaching ecal
+        int             m_reachesECalNFtdHits;                  ///< Minimum number of ftd hits to consider track as reaching ecal
+        float           m_reachesECalTpcOuterDistance;          ///< Max distance from track to tpc r max to id whether track reaches ecal
+        int             m_reachesECalMinFtdLayer;               ///< Min layer in Ftd for tracks to be considered to have reached decal
+        float           m_reachesECalTpcZMaxDistance;           ///< Max distance from track to tpc z max to id whether track reaches ecal
+        float           m_reachesECalFtdZMaxDistance;           ///< Max distance from track hit to ftd z position to identify ftd hits
+        float           m_curvatureToMomentumFactor;            ///< Constant relating track curvature in b field to momentum
+
+        float           m_minTrackECalDistanceFromIp;           ///< Sanity check on separation between ip and track projected ecal position
+        float           m_maxTrackSigmaPOverP;                  ///< Track fraction momentum error cut
+        float           m_minMomentumForTrackHitChecks;         ///< Min track momentum required to perform final quality checks on number of hits
+
+        float           m_tpcMembraneMaxZ;                      ///< Tpc membrane max z coordinate
+        float           m_maxTpcInnerRDistance;                 ///< Track cut on distance from tpc inner r to id whether track can form pfo
+        float           m_minTpcHitFractionOfExpected;          ///< Minimum fraction of TPC hits compared to expected
+        int             m_minFtdHitsForTpcHitFraction;          ///< Minimum number of FTD hits to ignore TPC hit fraction
+    };
+
+    /**
+     *  @brief  Constructor
+     * 
+     *  @param  settings the creator settings
+     *  @param  pPandora address of the relevant pandora instance
+     */
+     //TrackCreator(const Settings &settings, const pandora::Pandora *const pPandora);
+     TrackCreator(const Settings &settings, const pandora::Pandora *const pPandora, ISvcLocator* svcloc);
+
+    /**
+     *  @brief  Destructor
+     */
+     ~TrackCreator();
+
+    /**
+     *  @brief  Create associations between tracks, V0s, kinks, etc
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    //pandora::StatusCode CreateTrackAssociations(const EVENT::LCEvent *const pLCEvent);
+    pandora::StatusCode CreateTrackAssociations(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Create tracks, insert user code here
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    //pandora::StatusCode CreateTracks(EVENT::LCEvent *pLCEvent);
+    pandora::StatusCode CreateTracks(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Get the track vector
+     * 
+     *  @return The track vector
+     */
+    const TrackVector &GetTrackVector() const;
+
+    /**
+     *  @brief  Reset the track creator
+     */
+    void Reset();
+
+private:
+    /**
+     *  @brief  Extract kink information from specified lcio collections
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    //pandora::StatusCode ExtractKinks(const EVENT::LCEvent *const pLCEvent);
+    pandora::StatusCode ExtractKinks(const CollectionMaps& collectionMaps);
+
+    /**
+     *  @brief  Extract prong and split information from specified lcio collections
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+    //pandora::StatusCode ExtractProngsAndSplits(const EVENT::LCEvent *const pLCEvent);
+
+    /**
+     *  @brief  Extract v0 information from specified lcio collections
+     * 
+     *  @param  pLCEvent the lcio event
+     */
+   //pandora::StatusCode ExtractV0s(const EVENT::LCEvent *const pLCEvent);
+
+    /**
+     *  @brief  Whether the track vertex conflicts with previously provided relationship information
+     * 
+     *  @param  trackVec the vector of tracks associated with the vertex
+     */
+    //bool IsConflictingRelationship(const EVENT::TrackVec &trackVec) const;
+    //bool IsConflictingRelationship(edm4hep::ConstTrack &trackVec) const;
+    bool IsConflictingRelationship(const edm4hep::ConstReconstructedParticle &Particle) const;
+
+    /**
+     *  @brief  Whether a track is a v0 track
+     * 
+     *  @param  pTrack the lcio track
+     * 
+     *  @return boolean
+     */
+    //bool IsV0(const EVENT::Track *const pTrack) const;
+    bool IsV0(unsigned int pTrack_id) const;
+
+    /**
+     *  @brief  Whether a track is a parent track
+     * 
+     *  @param  pTrack the lcio track
+     * 
+     *  @return boolean
+     */
+    //bool IsParent(const EVENT::Track *const pTrack) const;
+    bool IsParent(unsigned int pTrack_id) const;
+
+    /**
+     *  @brief  Whether a track is a daughter track
+     * 
+     *  @param  pTrack the lcio track
+     * 
+     *  @return boolean
+     */
+    //bool IsDaughter(const EVENT::Track *const pTrack) const;
+    bool IsDaughter(unsigned int pTrack_id) const;
+
+    /**
+     *  @brief  Copy track states stored in lcio tracks to pandora track parameters
+     * 
+     *  @param  pTrack the lcio track
+     *  @param  trackParameters the track parameters
+     */
+    //void GetTrackStates(const EVENT::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const;
+    void GetTrackStates(const edm4hep::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const;
+
+    /**
+     *  @brief  Copy track state from lcio track state instance to pandora input track state
+     * 
+     *  @param  pTrackState the lcio track state instance
+     *  @param  inputTrackState the pandora input track state
+     */
+    //void CopyTrackState(const TrackState *const pTrackState, pandora::InputTrackState &inputTrackState) const;
+    void CopyTrackState(const edm4hep::TrackState & pTrackState, pandora::InputTrackState &inputTrackState) const;
+
+    /**
+     *  @brief  Obtain track time when it reaches ECAL
+     * 
+     *  @param  pTrack the lcio track
+     */
+    //float CalculateTrackTimeAtCalorimeter(const EVENT::Track *const pTrack) const;
+    float CalculateTrackTimeAtCalorimeter(const edm4hep::Track *const pTrack) const;
+
+    /**
+     *  @brief  Decide whether track reaches the ecal surface
+     * 
+     *  @param  pTrack the lcio track
+     *  @param  trackParameters the track parameters
+     */
+    //void TrackReachesECAL(const EVENT::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const;
+    void TrackReachesECAL(const edm4hep::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const;
+    //void TrackReachesECAL(const edm4hep::Track& pTrack, PandoraApi::Track::Parameters &trackParameters) const;
+
+    /**
+     *  @brief  Determine whether a track can be used to form a pfo under the following conditions:
+     *          1) if the track proves to be associated with a cluster, OR
+     *          2) if the track proves to have no cluster associations
+     * 
+     *  @param  pTrack the lcio track
+     *  @param  trackParameters the track parameters
+     */
+    //void DefineTrackPfoUsage(const EVENT::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const;
+    void DefineTrackPfoUsage(const edm4hep::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const;
+
+    /**
+     *  @brief  Whether track passes the quality cuts required in order to be used to form a pfo
+     * 
+     *  @param  pTrack the lcio track
+     *  @param  trackParameters the track parameters
+     * 
+     *  @return boolean
+     */
+    //bool PassesQualityCuts(const EVENT::Track *const pTrack, const PandoraApi::Track::Parameters &trackParameters) const;
+    bool PassesQualityCuts(const edm4hep::Track *const pTrack, const PandoraApi::Track::Parameters &trackParameters) const;
+
+    /**
+     *  @brief  Get number of hits in TPC of a track
+     * 
+     *  @param  pTrack the lcio track
+     * 
+     *  @return number of hits in TPC of a track
+     */
+    //int GetNTpcHits(const EVENT::Track *const pTrack) const;
+    int GetNTpcHits(const edm4hep::Track *const pTrack) const;
+
+    /**
+     *  @brief  Get number of hits in FTD of a track
+     * 
+     *  @param  pTrack the lcio track
+     * 
+     *  @return number of hits in FTDof a track
+     */
+    //int GetNFtdHits(const EVENT::Track *const pTrack) const;
+    int GetNFtdHits(const edm4hep::Track *const pTrack) const;
+
+    const Settings          m_settings;                     ///< The track creator settings
+    const pandora::Pandora *m_pPandora;                     ///< Address of the pandora object to create tracks and track relationships
+
+    float             m_bField;                       ///< The bfield
+
+    float             m_tpcInnerR;                    ///< The tpc inner radius
+    float             m_tpcOuterR;                    ///< The tpc outer radius
+    unsigned int      m_tpcMaxRow;                    ///< The tpc maximum row number
+    float             m_tpcZmax;                      ///< The tpc maximum z coordinate
+    float                   m_cosTpc;                       ///< Cos(theta) value at end of tpc
+
+    DoubleVector            m_ftdInnerRadii;                ///< List of ftd inner radii
+    DoubleVector            m_ftdOuterRadii;                ///< List of ftd outer radii
+    DoubleVector            m_ftdZPositions;                ///< List of ftd z positions
+    unsigned int            m_nFtdLayers;                   ///< Number of ftd layers
+    float                   m_tanLambdaFtd;                 ///< Tan lambda for first ftd layer
+
+    int               m_eCalBarrelInnerSymmetry;      ///< ECal barrel inner symmetry order
+    float             m_eCalBarrelInnerPhi0;          ///< ECal barrel inner phi 0
+    float             m_eCalBarrelInnerR;             ///< ECal barrel inner radius
+    float             m_eCalEndCapInnerZ;             ///< ECal endcap inner z
+
+    float                   m_minEtdZPosition;              ///< Min etd z position
+    float                   m_minSetRadius;                 ///< Min set radius
+
+    TrackVector             m_trackVector;                  ///< The track vector
+    TrackList               m_v0TrackList;                  ///< The list of v0 tracks
+    TrackList               m_parentTrackList;              ///< The list of parent tracks
+    TrackList               m_daughterTrackList;            ///< The list of daughter tracks
+    TrackToPidMap           m_trackToPidMap;                ///< The map from track addresses to particle ids, where set by kinks/V0s
+    gear::GearMgr* _GEAR;
+};
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+inline const TrackVector &TrackCreator::GetTrackVector() const
+{
+    return m_trackVector;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+inline void TrackCreator::Reset()
+{
+    m_trackVector.clear();
+    m_v0TrackList.clear();
+    m_parentTrackList.clear();
+    m_daughterTrackList.clear();
+    m_trackToPidMap.clear();
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+//inline bool TrackCreator::IsV0(const Track *const pTrack) const
+inline bool TrackCreator::IsV0(unsigned int pTrack_id) const // should check here, if id is correct one to do this
+{
+    //return (m_v0TrackList.end() != m_v0TrackList.find(pTrack));
+    return (m_v0TrackList.end() != m_v0TrackList.find(pTrack_id));
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+//inline bool TrackCreator::IsParent(const Track *const pTrack) const
+inline bool TrackCreator::IsParent(unsigned int pTrack_id) const
+{
+    //return (m_parentTrackList.end() != m_parentTrackList.find(pTrack));
+    return (m_parentTrackList.end() != m_parentTrackList.find(pTrack_id));
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+//inline bool TrackCreator::IsDaughter(const Track *const pTrack) const
+inline bool TrackCreator::IsDaughter(unsigned int pTrack_id) const
+{
+    //return (m_daughterTrackList.end() != m_daughterTrackList.find(pTrack));
+    return (m_daughterTrackList.end() != m_daughterTrackList.find(pTrack_id));
+}
+
+#endif // #ifndef TRACK_CREATOR_H
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/include/Utility.h b/Reconstruction/PFA/Pandora/MatrixPandora/include/Utility.h
new file mode 100644
index 00000000..0bd75b5d
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/include/Utility.h
@@ -0,0 +1,6 @@
+#ifndef MYUTILITY
+#define MYUTILITY 1
+
+#include <sstream>
+std::string Convert (float number);
+#endif
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/include/cellIDDecoder.h b/Reconstruction/PFA/Pandora/MatrixPandora/include/cellIDDecoder.h
new file mode 100644
index 00000000..f1515996
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/include/cellIDDecoder.h
@@ -0,0 +1,127 @@
+#ifndef cellIDDecoder_h
+#define cellIDDecoder_h 1
+
+//#include <vector>
+//#include "EVENT/LCObject.h"
+#include "EVENT/LCCollection.h"
+//#include "EVENT/SimTrackerHit.h"
+#include "UTIL/BitField64.h"
+#include "lcio.h"
+#include <string>
+
+// fixes problem in gcc 4.0.3
+#include "EVENT/LCParameters.h"
+
+//#include <sstream>
+//#include <typeinfo>
+
+//##################### changed for EMD4HEP ########
+//need check !!
+
+namespace ID_UTIL{
+
+
+  /** Convenient class for decoding cellIDs from collection parameter LCIO::CellIDEncoding.
+   *  See UTIL::BitField64 for a description of the encoding string. 
+   * 
+   *  @see BitField64
+   *  @version $Id: CellIDDecoder.h,v 1.9.16.1 2011-03-04 14:09:07 engels Exp $
+   */
+  template <class T> 
+  class CellIDDecoder {
+    
+  public:  
+
+    CellIDDecoder() = default ;
+    CellIDDecoder(const CellIDDecoder& ) = delete ;
+    CellIDDecoder& operator=(const CellIDDecoder& ) = delete ;
+    
+
+    /** Constructor takes encoding string as argument.
+     */
+  CellIDDecoder( const std::string& encoder_str ) : _oldHit(0) {
+      
+      if( encoder_str.length() == 0 ){
+      	throw( lcio::Exception( "CellIDDecoder : string of length zero provided as encoder string" ) ) ;
+      }
+      _b = new UTIL::BitField64( encoder_str ) ; 
+      
+    }
+    
+    /** Constructor reads encoding string from collection parameter LCIO::CellIDEncoding.
+     */
+    CellIDDecoder( const EVENT::LCCollection* col ) : _oldHit(0) {
+      
+      std::string initString("") ; 
+
+      if( col !=0 ) 
+	initString = col->getParameters().getStringVal(  lcio::LCIO::CellIDEncoding ) ;
+      
+      if( initString.size() == 0 ) {
+	
+	initString = _defaultEncoding ;
+
+	std::cout << "    ----------------------------------------- " << std::endl  
+		  << "       WARNING: CellIDDecoder - no CellIDEncoding parameter in collection ! " 
+		  << std::endl 
+		  << "         -> using default : \"" << initString << "\"" 
+		  << std::endl 
+		  << "    ------------------------------------------ "  
+		  << std::endl ;
+      }
+      
+      _b = new UTIL::BitField64(  initString ) ; 
+    }
+    
+    ~CellIDDecoder(){ 
+      
+      delete _b ;
+    } 
+    
+    
+    /** Provides access to the bit fields, e.g. <br>
+     *   int layer =  myCellIDEncoding( hit )[ "layer" ] ;
+     * 
+     */
+    inline const UTIL::BitField64 & operator()( const T* hit ){  
+      
+      if( hit != _oldHit && hit ) {
+	auto id = hit->getCellID();
+        unsigned int id0 = id&0xFFFFFFFF;
+        unsigned int id1 = id>>32;
+	//lcio::long64 val = lcio::long64( hit->getCellID0() & 0xffffffff ) | ( lcio::long64( hit->getCellID1() ) << 32      ) ;
+	lcio::long64 val = lcio::long64( id0 & 0xffffffff ) | ( lcio::long64( id1 ) << 32      ) ;
+	
+	_b->setValue( val ) ;
+
+	_oldHit = hit ;
+      }
+      
+      return  *_b ;
+    }
+    
+
+    /** This can be used to set the default encoding that is used if no
+     *  CellIDEncoding parameter is set in the collection, e.g. in older lcio files.
+     */ 
+    static void setDefaultEncoding(const std::string& defaultEncoding ) {
+
+      _defaultEncoding = std::string( defaultEncoding ) ;
+    }
+    
+  protected:
+    UTIL::BitField64* _b{} ;
+    const T* _oldHit{NULL} ;
+    
+    static std::string _defaultEncoding;
+  } ; 
+  
+  template <class T>
+  std::string CellIDDecoder<T>::_defaultEncoding
+  = std::string("byte0:8,byte1:8,byte2:8,byte3:8,byte4:8,byte5:8,byte6:8,byte7:8") ;
+
+  
+} // namespace
+#endif
+
+
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/src/CaloHitCreator.cpp b/Reconstruction/PFA/Pandora/MatrixPandora/src/CaloHitCreator.cpp
new file mode 100644
index 00000000..3196cde2
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/src/CaloHitCreator.cpp
@@ -0,0 +1,959 @@
+/**
+ *  @file   MarlinPandora/src/CaloHitCreator.cc
+ * 
+ *  @brief  Implementation of the calo hit creator class.
+ * 
+ *  $Log: $
+ */
+
+
+#include "gear/GearParameters.h"
+#include "gear/CalorimeterParameters.h"
+#include "gear/GearDistanceProperties.h"
+#include "gear/GearPointProperties.h"
+#include "gear/GEAR.h"
+#include "gear/TPCParameters.h"
+#include "gear/PadRowLayout2D.h"
+#include "gear/LayerLayout.h"
+
+#include <DD4hep/Detector.h>
+#include <DDRec/DetectorData.h>
+#include <DDRec/CellIDPositionConverter.h>
+#include <DD4hep/Objects.h>
+
+#include "UTIL/CellIDDecoder.h"
+#include "cellIDDecoder.h"
+#include "GaudiKernel/IService.h"
+#include "GearSvc/IGearSvc.h"
+
+#include "PandoraMatrixAlg.h"
+#include "CaloHitCreator.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+
+CaloHitCreator::CaloHitCreator(const Settings &settings, const pandora::Pandora *const pPandora, ISvcLocator* svcloc, bool encoder_style) :
+    m_settings(settings),
+    m_pPandora(pPandora)
+{
+    m_encoder_str = ""; 
+    m_encoder_str_MUON = ""; 
+    m_encoder_str_LCal = ""; 
+    m_encoder_str_LHCal = ""; 
+    if(encoder_style==0) 
+    {
+        m_encoder_str     = "M:3,S-1:3,I:9,J:9,K-1:6";// LCIO style
+        m_encoder_str_MUON="S-1:4,M:3,K-1:6,I:16,GRZone:3,J:32:16";
+        m_encoder_str_LCal="I:10,J:10,K:10,S-1:2";
+        m_encoder_str_LHCal=m_encoder_str;
+    }
+    IGearSvc*  iSvc = 0;
+    StatusCode sc = svcloc->service("GearSvc", iSvc, false);
+    if ( !sc ) 
+    {
+        throw "Failed to find GearSvc ...";
+    }
+    _GEAR = iSvc->getGearMgr();
+
+
+    IGeoSvc*  Svc = 0;
+    StatusCode sc1 = svcloc->service("GeoSvc", Svc, false);
+    if ( !sc1 )
+    {
+        throw "Failed to find GeoSvc ...";
+    }
+    m_dd4hep = Svc->lcdd();
+    const dd4hep::DetElement &detElement = m_dd4hep->detector("CaloDetector");
+    dd4hep::rec::LayeredCalorimeterData* Data = detElement.extension<dd4hep::rec::LayeredCalorimeterData>() ;
+    if(!Data) throw "Failed to get LayeredCalorimeterData ...";
+    m_cellIDConverter = new dd4hep::rec::CellIDPositionConverter(*m_dd4hep);
+    /*
+    m_compact = "/junofs/users/wxfang/MyGit/lcg97/UsingK4FWCore/0514/CEPCSW/Detector/DetEcalMatrix/compact/det.xml";
+    dd4hep::Detector& description = dd4hep::Detector::getInstance();
+    description.fromCompact( m_compact );
+    m_cellIDConverter = new dd4hep::rec::CellIDPositionConverter(description);
+    */
+
+    m_eCalBarrelOuterZ        = Data->extent[3];
+    m_eCalBarrelInnerPhi0     = Data->inner_phi0;
+    m_eCalBarrelInnerSymmetry = Data->inner_symmetry;
+    m_hCalBarrelOuterZ        = (_GEAR->getHcalBarrelParameters().getExtent()[3]);
+    m_muonBarrelOuterZ        = (_GEAR->getYokeBarrelParameters().getExtent()[3]);
+    m_coilOuterR              = (_GEAR->getGearParameters("CoilParameters").getDoubleVal("Coil_cryostat_outer_radius"));
+    m_hCalBarrelInnerPhi0     = (_GEAR->getHcalBarrelParameters().getPhi0());
+    m_hCalBarrelInnerSymmetry = (_GEAR->getHcalBarrelParameters().getSymmetryOrder());
+    m_muonBarrelInnerPhi0     = (_GEAR->getYokeBarrelParameters().getPhi0());
+    m_muonBarrelInnerSymmetry = (_GEAR->getYokeBarrelParameters().getSymmetryOrder());
+    m_hCalEndCapOuterR        = (_GEAR->getHcalEndcapParameters().getExtent()[1]);
+    m_hCalEndCapOuterZ        = (_GEAR->getHcalEndcapParameters().getExtent()[3]);
+    m_hCalBarrelOuterR        = (_GEAR->getHcalBarrelParameters().getExtent()[1]);
+    m_hCalBarrelOuterPhi0     =((std::find(_GEAR->getHcalBarrelParameters().getIntKeys().begin(),
+        _GEAR->getHcalBarrelParameters().getIntKeys().end(),
+        "Hcal_outer_polygon_phi0") != _GEAR->getHcalBarrelParameters().getIntKeys().end() ?
+        _GEAR->getHcalBarrelParameters().getIntVal("Hcal_outer_polygon_phi0")
+        : 0));
+    m_hCalBarrelOuterSymmetry = ((std::find(_GEAR->getHcalBarrelParameters().getIntKeys().begin(),
+        _GEAR->getHcalBarrelParameters().getIntKeys().end(),
+        "Hcal_outer_polygon_order") != _GEAR->getHcalBarrelParameters().getIntKeys().end() ?
+        _GEAR->getHcalBarrelParameters().getIntVal("Hcal_outer_polygon_order")
+        : 0));
+
+
+    const gear::LayerLayout &hCalEndCapLayerLayout(_GEAR->getHcalEndcapParameters().getLayerLayout());
+    const gear::LayerLayout &hCalBarrelLayerLayout(_GEAR->getHcalBarrelParameters().getLayerLayout()); 
+    m_hCalEndCapLayerThickness = hCalEndCapLayerLayout.getThickness(hCalEndCapLayerLayout.getNLayers() - 1);
+    m_hCalBarrelLayerThickness = hCalBarrelLayerLayout.getThickness(hCalBarrelLayerLayout.getNLayers() - 1);
+    if ((m_hCalEndCapLayerThickness < std::numeric_limits<float>::epsilon()) || (m_hCalBarrelLayerThickness < std::numeric_limits<float>::epsilon()))
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+
+
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+CaloHitCreator::~CaloHitCreator()
+{
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode CaloHitCreator::CreateCaloHits(const CollectionMaps& collectionMaps)
+{
+    
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateECalCaloHits (collectionMaps));
+    // not used for ECAL Matrix
+    /*
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateHCalCaloHits (collectionMaps));
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateMuonCaloHits (collectionMaps));
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateLCalCaloHits (collectionMaps));
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateLHCalCaloHits(collectionMaps));
+    */
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode CaloHitCreator::CreateECalCaloHits(const CollectionMaps& collectionMaps)
+{
+    std::cout<<"start CreateECalCaloHits:"<<std::endl;
+    for (StringVector::const_iterator iter = m_settings.m_eCalCaloHitCollections.begin(), iterEnd = m_settings.m_eCalCaloHitCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_CaloHit.find(*iter) == collectionMaps.collectionMap_CaloHit.end()) { std::cout<<"not find "<<(*iter)<<std::endl; continue;}
+        try
+        {
+            const std::vector<edm4hep::CalorimeterHit>& pCaloHitCollection = (collectionMaps.collectionMap_CaloHit.find(*iter))->second;
+            const int nElements(pCaloHitCollection.size());
+
+            std::cout<<(*iter)<<"has nElements="<<nElements<<std::endl;
+            if (0 == nElements)
+                continue;
+
+            const gear::LayerLayout &endcapLayerLayout(_GEAR->getEcalEndcapParameters().getLayerLayout());
+            const dd4hep::DetElement &detElement = m_dd4hep->detector("CaloDetector");
+            dd4hep::rec::LayeredCalorimeterData* Data = detElement.extension<dd4hep::rec::LayeredCalorimeterData>() ;
+            const std::vector<dd4hep::rec::LayeredCalorimeterData::Layer>& barrelLayerLayout = Data->layers;
+            ID_UTIL::CellIDDecoder<const edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str);
+            const std::string layerCodingString(m_encoder_str);
+            const std::string layerCoding(this->GetLayerCoding(layerCodingString));
+            const std::string staveCoding(this->GetStaveCoding(layerCodingString));
+
+            for (int i = 0; i < nElements; ++i)
+            {
+                try
+                {
+                    const edm4hep::CalorimeterHit& pCaloHit0 = pCaloHitCollection.at(i);
+                    const edm4hep::CalorimeterHit* pCaloHit = &(pCaloHit0);
+
+                    if (NULL == pCaloHit)
+                        throw ("CreateECalCaloHits pCaloHit Collection type mismatch");
+
+                    float eCalToMip(m_settings.m_eCalToMip), eCalMipThreshold(m_settings.m_eCalMipThreshold), eCalToEMGeV(m_settings.m_eCalToEMGeV),
+                        eCalToHadGeVBarrel(m_settings.m_eCalToHadGeVBarrel), eCalToHadGeVEndCap(m_settings.m_eCalToHadGeVEndCap);
+
+                    // Hybrid ECAL including pure ScECAL.
+                    if (m_settings.m_useEcalScLayers)
+                    {
+                        std::string collectionName(*iter);
+                        std::transform(collectionName.begin(), collectionName.end(), collectionName.begin(), ::tolower);
+
+                        if (collectionName.find("ecal", 0) == std::string::npos)
+                            std::cout << "WARNING: mismatching hybrid Ecal collection name. " << collectionName << std::endl;
+
+                        if (collectionName.find("si", 0) != std::string::npos)
+                        {
+                             eCalToMip = m_settings.m_eCalSiToMip;
+                             eCalMipThreshold = m_settings.m_eCalSiMipThreshold;
+                             eCalToEMGeV = m_settings.m_eCalSiToEMGeV;
+                             eCalToHadGeVBarrel = m_settings.m_eCalSiToHadGeVBarrel;
+                             eCalToHadGeVEndCap = m_settings.m_eCalSiToHadGeVEndCap;
+                        }
+                        else if (collectionName.find("sc", 0) != std::string::npos)
+                        {
+                             eCalToMip = m_settings.m_eCalScToMip;
+                             eCalMipThreshold = m_settings.m_eCalScMipThreshold;
+                             eCalToEMGeV = m_settings.m_eCalScToEMGeV;
+                             eCalToHadGeVBarrel = m_settings.m_eCalScToHadGeVBarrel;
+                             eCalToHadGeVEndCap = m_settings.m_eCalScToHadGeVEndCap;
+                        }
+                    }
+
+                    PandoraApi::CaloHit::Parameters caloHitParameters;
+                    caloHitParameters.m_hitType = pandora::ECAL;
+                    caloHitParameters.m_isDigital = false;//FIXME, maybe this means the MIP thershold cut haven't be applied yet ?
+                    /* 
+                    dd4hep::Position position = m_cellIDConverter->position(pCaloHit->getCellID());
+                    long id_sys,id_x,id_y,id_z ;
+                    GetCoding(pCaloHit, id_sys, id_x, id_y, id_z);
+                    std::cout << "ECAL id =" << pCaloHit->getCellID()<<",sys="<<id_sys<<",x="<<id_x<<",y="<<id_y<<",z="<<id_z << std::endl;
+                    std::cout << "ECAL id =" << pCaloHit->getCellID()<<",x="<<position.x()<<",y="<<position.y()<<",z="<<position.z() << std::endl;
+                    */
+                    caloHitParameters.m_isInOuterSamplingLayer = false;
+                    this->GetCommonCaloHitProperties(pCaloHit, caloHitParameters);
+
+                    float absorberCorrection(1.);
+
+                    if (std::fabs(pCaloHit->getPosition()[2]) < m_eCalBarrelOuterZ)
+                    {
+                        caloHitParameters.m_layer = GetBarrelLayer(pCaloHit, barrelLayerLayout);
+                        this->GetBarrelCaloHitProperties(pCaloHit, barrelLayerLayout, m_eCalBarrelInnerSymmetry, m_eCalBarrelInnerPhi0, 6, caloHitParameters, absorberCorrection);//6
+                        caloHitParameters.m_hadronicEnergy = eCalToHadGeVBarrel * pCaloHit->getEnergy();
+                        
+                    }
+                    else
+                    { // will not be used for ECAL Matrix
+                        caloHitParameters.m_layer = cellIdDecoder(pCaloHit)[layerCoding.c_str()] + 1;//FIXME, should use + 1? because the decoded layer is start from 0.
+                        this->GetEndCapCaloHitProperties(pCaloHit, endcapLayerLayout, caloHitParameters, absorberCorrection);
+                        caloHitParameters.m_hadronicEnergy = eCalToHadGeVEndCap * pCaloHit->getEnergy();
+                    }
+
+                    caloHitParameters.m_mipEquivalentEnergy = pCaloHit->getEnergy() * eCalToMip * absorberCorrection;
+
+                    if (caloHitParameters.m_mipEquivalentEnergy.Get() < eCalMipThreshold)
+                        continue;
+
+                    caloHitParameters.m_electromagneticEnergy = eCalToEMGeV * pCaloHit->getEnergy();
+
+                    // ATTN If using strip splitting, must correct cell sizes for use in PFA to minimum of strip width and strip length
+                    if (m_settings.m_stripSplittingOn)
+                    {
+                        const float splitCellSize(std::min(caloHitParameters.m_cellSize0.Get(), caloHitParameters.m_cellSize1.Get()));
+                        caloHitParameters.m_cellSize0 = splitCellSize;
+                        caloHitParameters.m_cellSize1 = splitCellSize;
+                    }
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::CaloHit::Create(*m_pPandora, caloHitParameters));
+                    m_calorimeterHitVector.push_back(const_cast<edm4hep::CalorimeterHit*>(pCaloHit));
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout<<"ERROR Failed to extract ecal calo hit: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout<<"WARNING Failed to extract ecal calo hit: " <<  std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout<< "Failed to extract ecal calo hit collection: " << *iter << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode CaloHitCreator::CreateHCalCaloHits(const CollectionMaps& collectionMaps)
+{
+    for (StringVector::const_iterator iter = m_settings.m_hCalCaloHitCollections.begin(), iterEnd = m_settings.m_hCalCaloHitCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_CaloHit.find(*iter) == collectionMaps.collectionMap_CaloHit.end()) { std::cout<<"not find "<<(*iter)<<std::endl; continue;}
+        try
+        {
+            const std::vector<edm4hep::CalorimeterHit>& pCaloHitCollection = (collectionMaps.collectionMap_CaloHit.find(*iter))->second;
+            const int nElements(pCaloHitCollection.size());
+
+            std::cout<<(*iter)<<"has nElements="<<nElements<<std::endl;
+            if (0 == nElements)
+                continue;
+
+            const gear::LayerLayout &endcapLayerLayout(_GEAR->getHcalEndcapParameters().getLayerLayout());
+            const gear::LayerLayout &barrelLayerLayout(_GEAR->getHcalBarrelParameters().getLayerLayout());
+
+            ID_UTIL::CellIDDecoder<const edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str);
+            const std::string layerCodingString(m_encoder_str);
+            const std::string layerCoding(this->GetLayerCoding(layerCodingString));
+            const std::string staveCoding(this->GetStaveCoding(layerCodingString));
+
+            for (int i = 0; i < nElements; ++i)
+            {
+                try
+                {
+                    const edm4hep::CalorimeterHit& pCaloHit0 = pCaloHitCollection.at(i);
+                    const edm4hep::CalorimeterHit* pCaloHit = &(pCaloHit0);
+
+                    if (NULL == pCaloHit)
+                        throw ("CreateHCalCaloHits Collection type mismatch");
+
+                    PandoraApi::CaloHit::Parameters caloHitParameters;
+                    caloHitParameters.m_hitType = pandora::HCAL;
+                    caloHitParameters.m_isDigital = false;
+                    caloHitParameters.m_layer = cellIdDecoder(pCaloHit)[layerCoding.c_str()];
+                    caloHitParameters.m_isInOuterSamplingLayer = (this->GetNLayersFromEdge(pCaloHit) <= m_settings.m_nOuterSamplingLayers);
+                    this->GetCommonCaloHitProperties(pCaloHit, caloHitParameters);
+
+                    float absorberCorrection(1.);
+
+                    if (std::fabs(pCaloHit->getPosition()[2]) < m_hCalBarrelOuterZ)
+                    {
+                        this->GetBarrelCaloHitProperties(pCaloHit, barrelLayerLayout, m_hCalBarrelInnerSymmetry, m_hCalBarrelInnerPhi0,
+                            m_hCalBarrelInnerSymmetry - int(cellIdDecoder(pCaloHit)[ staveCoding] / 2), caloHitParameters, absorberCorrection);
+                    }
+                    else
+                    {
+                        this->GetEndCapCaloHitProperties(pCaloHit, endcapLayerLayout, caloHitParameters, absorberCorrection);
+                    }
+
+                    caloHitParameters.m_mipEquivalentEnergy = pCaloHit->getEnergy() * m_settings.m_hCalToMip * absorberCorrection;
+
+                    if (caloHitParameters.m_mipEquivalentEnergy.Get() < m_settings.m_hCalMipThreshold)
+                        continue;
+
+                    caloHitParameters.m_hadronicEnergy = std::min(m_settings.m_hCalToHadGeV * pCaloHit->getEnergy(), m_settings.m_maxHCalHitHadronicEnergy);
+                    caloHitParameters.m_electromagneticEnergy = m_settings.m_hCalToEMGeV * pCaloHit->getEnergy();
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::CaloHit::Create(*m_pPandora, caloHitParameters));
+                    m_calorimeterHitVector.push_back(const_cast<edm4hep::CalorimeterHit*>(pCaloHit));
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout << "Error, Failed to extract hcal calo hit: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout<<"WARNING Failed to extract hcal calo hit: " << std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout << "Failed to extract hcal calo hit collection: " << *iter << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode CaloHitCreator::CreateMuonCaloHits(const CollectionMaps& collectionMaps)
+{
+    for (StringVector::const_iterator iter = m_settings.m_muonCaloHitCollections.begin(), iterEnd = m_settings.m_muonCaloHitCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_CaloHit.find(*iter) == collectionMaps.collectionMap_CaloHit.end()) { std::cout<<"not find "<<(*iter)<<std::endl; continue;}
+        try
+        {
+            const std::vector<edm4hep::CalorimeterHit>& pCaloHitCollection = (collectionMaps.collectionMap_CaloHit.find(*iter))->second;
+            const int nElements(pCaloHitCollection.size());
+
+            std::cout<<(*iter)<<"has nElements="<<nElements<<std::endl;
+            if (0 == nElements)
+                continue;
+
+            const gear::LayerLayout &endcapLayerLayout(_GEAR->getYokeEndcapParameters().getLayerLayout());
+            const gear::LayerLayout &barrelLayerLayout(_GEAR->getYokeBarrelParameters().getLayerLayout()); 
+            const gear::LayerLayout &plugLayerLayout(_GEAR->getYokePlugParameters().getLayerLayout());
+
+            ID_UTIL::CellIDDecoder<const edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str_MUON);
+            const std::string layerCodingString(m_encoder_str_MUON);
+            const std::string layerCoding(this->GetLayerCoding(layerCodingString));
+            const std::string staveCoding(this->GetStaveCoding(layerCodingString));
+
+            for (int i = 0; i < nElements; ++i)
+            {
+                try
+                {
+                    const edm4hep::CalorimeterHit& pCaloHit0 = pCaloHitCollection.at(i);
+                    const edm4hep::CalorimeterHit* pCaloHit = &(pCaloHit0);
+
+                    if (NULL == pCaloHit)
+                        throw ("Muon Collection type mismatch");
+
+                    PandoraApi::CaloHit::Parameters caloHitParameters;
+                    caloHitParameters.m_hitType = pandora::MUON;
+                    caloHitParameters.m_layer = cellIdDecoder(pCaloHit)[layerCoding.c_str()] + 1;//FIXME, should use +1? it starts from 0.
+                    caloHitParameters.m_isInOuterSamplingLayer = true;
+                    this->GetCommonCaloHitProperties(pCaloHit, caloHitParameters);
+
+                    const float radius(std::sqrt(pCaloHit->getPosition()[0] * pCaloHit->getPosition()[0] +
+                        pCaloHit->getPosition()[1] * pCaloHit->getPosition()[1]));
+
+                    const bool isWithinCoil(radius < m_coilOuterR);
+                    const bool isInBarrelRegion(std::fabs(pCaloHit->getPosition()[2]) < m_muonBarrelOuterZ);
+
+                    float absorberCorrection(1.);
+
+                    if (isInBarrelRegion && isWithinCoil)
+                    {
+                        this->GetEndCapCaloHitProperties(pCaloHit, plugLayerLayout, caloHitParameters, absorberCorrection);
+                    }
+                    else if (isInBarrelRegion)
+                    {
+                        this->GetBarrelCaloHitProperties(pCaloHit, barrelLayerLayout, m_muonBarrelInnerSymmetry, m_muonBarrelInnerPhi0,
+                            cellIdDecoder(pCaloHit)[ staveCoding ], caloHitParameters, absorberCorrection);
+                    }
+                    else
+                    {
+                        this->GetEndCapCaloHitProperties(pCaloHit, endcapLayerLayout, caloHitParameters, absorberCorrection);
+                    }
+
+                    if (m_settings.m_muonDigitalHits > 0)
+                    {
+                        caloHitParameters.m_isDigital = true;
+                        caloHitParameters.m_inputEnergy = m_settings.m_muonHitEnergy;
+                        caloHitParameters.m_hadronicEnergy = m_settings.m_muonHitEnergy;
+                        caloHitParameters.m_electromagneticEnergy = m_settings.m_muonHitEnergy;
+                        caloHitParameters.m_mipEquivalentEnergy = 1.f;
+                    }
+                    else
+                    {
+                        caloHitParameters.m_isDigital = false;
+                        caloHitParameters.m_inputEnergy = pCaloHit->getEnergy();
+                        caloHitParameters.m_hadronicEnergy = pCaloHit->getEnergy();
+                        caloHitParameters.m_electromagneticEnergy = pCaloHit->getEnergy();
+                        caloHitParameters.m_mipEquivalentEnergy = pCaloHit->getEnergy() * m_settings.m_muonToMip;
+                    }
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::CaloHit::Create(*m_pPandora, caloHitParameters));
+                    m_calorimeterHitVector.push_back(const_cast<edm4hep::CalorimeterHit*>(pCaloHit));
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout << "Failed to extract muon hit: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout << "Failed to extract muon hit: "  << std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout << "Failed to extract muon hit collection: " << *iter  << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode CaloHitCreator::CreateLCalCaloHits(const CollectionMaps& collectionMaps)
+{
+    for (StringVector::const_iterator iter = m_settings.m_lCalCaloHitCollections.begin(), iterEnd = m_settings.m_lCalCaloHitCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_CaloHit.find(*iter) == collectionMaps.collectionMap_CaloHit.end()) { std::cout<<"not find "<<(*iter)<<std::endl; continue;}
+        try
+        {
+            const std::vector<edm4hep::CalorimeterHit>& pCaloHitCollection = (collectionMaps.collectionMap_CaloHit.find(*iter))->second;
+            const int nElements(pCaloHitCollection.size());
+
+            std::cout<<(*iter)<<"has nElements="<<nElements<<std::endl;
+            if (0 == nElements)
+                continue;
+
+            const gear::LayerLayout &endcapLayerLayout(_GEAR->getLcalParameters().getLayerLayout()); 
+
+            ID_UTIL::CellIDDecoder<const edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str_LCal);
+            const std::string layerCodingString(m_encoder_str_LCal);
+            const std::string layerCoding(this->GetLayerCoding(layerCodingString));
+
+            for (int i = 0; i < nElements; ++i)
+            {
+                try
+                {
+                    const edm4hep::CalorimeterHit& pCaloHit0 = pCaloHitCollection.at(i);
+                    const edm4hep::CalorimeterHit* pCaloHit = &(pCaloHit0);
+
+                    if (NULL == pCaloHit)
+                        throw ("LCal Collection type mismatch");
+
+                    PandoraApi::CaloHit::Parameters caloHitParameters;
+                    caloHitParameters.m_hitType = pandora::ECAL;
+                    caloHitParameters.m_isDigital = false;
+                    caloHitParameters.m_layer = cellIdDecoder(pCaloHit)[layerCoding.c_str()];
+                    caloHitParameters.m_isInOuterSamplingLayer = false;
+                    this->GetCommonCaloHitProperties(pCaloHit, caloHitParameters);
+
+                    float absorberCorrection(1.);
+                    this->GetEndCapCaloHitProperties(pCaloHit, endcapLayerLayout, caloHitParameters, absorberCorrection);
+
+                    caloHitParameters.m_mipEquivalentEnergy = pCaloHit->getEnergy() * m_settings.m_eCalToMip * absorberCorrection;
+
+                    if (caloHitParameters.m_mipEquivalentEnergy.Get() < m_settings.m_eCalMipThreshold)
+                        continue;
+
+                    caloHitParameters.m_electromagneticEnergy = m_settings.m_eCalToEMGeV * pCaloHit->getEnergy();
+                    caloHitParameters.m_hadronicEnergy = m_settings.m_eCalToHadGeVEndCap * pCaloHit->getEnergy();
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::CaloHit::Create(*m_pPandora, caloHitParameters));
+                    m_calorimeterHitVector.push_back(const_cast<edm4hep::CalorimeterHit*>(pCaloHit));
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout << "Failed to extract lcal calo hit: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout << "Failed to extract lcal calo hit: " << std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout << "Failed to extract lcal calo hit collection: " << *iter << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode CaloHitCreator::CreateLHCalCaloHits(const CollectionMaps& collectionMaps)
+{
+    for (StringVector::const_iterator iter = m_settings.m_lHCalCaloHitCollections.begin(), iterEnd = m_settings.m_lHCalCaloHitCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_CaloHit.find(*iter) == collectionMaps.collectionMap_CaloHit.end()) { std::cout<<"not find "<<(*iter)<<std::endl; continue;}
+        try
+        {
+            const std::vector<edm4hep::CalorimeterHit>& pCaloHitCollection = (collectionMaps.collectionMap_CaloHit.find(*iter))->second;
+            const int nElements(pCaloHitCollection.size());
+
+            std::cout<<(*iter)<<"has nElements="<<nElements<<std::endl;
+            if (0 == nElements)
+                continue;
+
+            const gear::LayerLayout &endcapLayerLayout(_GEAR->getLHcalParameters().getLayerLayout());
+
+            ID_UTIL::CellIDDecoder<const edm4hep::CalorimeterHit> cellIdDecoder(m_encoder_str_LHCal);
+            const std::string layerCodingString(m_encoder_str_LHCal);
+            const std::string layerCoding(this->GetLayerCoding(layerCodingString));
+
+            for (int i = 0; i < nElements; ++i)
+            {
+                try
+                {
+                    const edm4hep::CalorimeterHit& pCaloHit0 = pCaloHitCollection.at(i);
+                    const edm4hep::CalorimeterHit* pCaloHit = &(pCaloHit0);
+
+                    if (NULL == pCaloHit)
+                        throw ("LHCal Collection type mismatch");
+
+                    PandoraApi::CaloHit::Parameters caloHitParameters;
+                    caloHitParameters.m_hitType = pandora::HCAL;
+                    caloHitParameters.m_isDigital = false;
+                    caloHitParameters.m_layer = cellIdDecoder(pCaloHit)[layerCoding.c_str()];
+                    caloHitParameters.m_isInOuterSamplingLayer = (this->GetNLayersFromEdge(pCaloHit) <= m_settings.m_nOuterSamplingLayers);
+                    this->GetCommonCaloHitProperties(pCaloHit, caloHitParameters);
+
+                    float absorberCorrection(1.);
+                    this->GetEndCapCaloHitProperties(pCaloHit, endcapLayerLayout, caloHitParameters, absorberCorrection);
+
+                    caloHitParameters.m_mipEquivalentEnergy = pCaloHit->getEnergy() * m_settings.m_hCalToMip * absorberCorrection;
+
+                    if (caloHitParameters.m_mipEquivalentEnergy.Get() < m_settings.m_hCalMipThreshold)
+                        continue;
+
+                    caloHitParameters.m_hadronicEnergy = std::min(m_settings.m_hCalToHadGeV * pCaloHit->getEnergy(), m_settings.m_maxHCalHitHadronicEnergy);
+                    caloHitParameters.m_electromagneticEnergy = m_settings.m_hCalToEMGeV * pCaloHit->getEnergy();
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::CaloHit::Create(*m_pPandora, caloHitParameters));
+                    m_calorimeterHitVector.push_back(const_cast<edm4hep::CalorimeterHit*>(pCaloHit));
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout << "Failed to extract lhcal calo hit: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout << "Failed to extract lhcal calo hit: " << std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout << "Failed to extract lhcal calo hit collection: " << *iter << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void CaloHitCreator::GetCommonCaloHitProperties(const edm4hep::CalorimeterHit *const pCaloHit, PandoraApi::CaloHit::Parameters &caloHitParameters) const
+{
+    const float pCaloHitPosition[3]={pCaloHit->getPosition()[0], pCaloHit->getPosition()[1], pCaloHit->getPosition()[2]};
+    const pandora::CartesianVector positionVector(pCaloHitPosition[0], pCaloHitPosition[1], pCaloHitPosition[2]);
+
+    caloHitParameters.m_cellGeometry = pandora::RECTANGULAR;
+    caloHitParameters.m_positionVector = positionVector;
+    caloHitParameters.m_expectedDirection = positionVector.GetUnitVector();
+    caloHitParameters.m_pParentAddress = pCaloHit;
+    caloHitParameters.m_inputEnergy = pCaloHit->getEnergy();
+    caloHitParameters.m_time = pCaloHit->getTime();
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void CaloHitCreator::GetEndCapCaloHitProperties(const edm4hep::CalorimeterHit *const pCaloHit, const gear::LayerLayout &layerLayout,
+    PandoraApi::CaloHit::Parameters &caloHitParameters, float &absorberCorrection) const
+{
+    caloHitParameters.m_hitRegion = pandora::ENDCAP;
+
+    const int physicalLayer(std::min(static_cast<int>(caloHitParameters.m_layer.Get()), layerLayout.getNLayers() - 1));
+    caloHitParameters.m_cellSize0 = layerLayout.getCellSize0(physicalLayer);
+    caloHitParameters.m_cellSize1 = layerLayout.getCellSize1(physicalLayer);
+    caloHitParameters.m_cellThickness = layerLayout.getThickness(physicalLayer);
+
+    const float radiationLength((pandora::ECAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberRadLengthECal :
+        (pandora::HCAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberRadLengthHCal : m_settings.m_absorberRadLengthOther);
+    const float interactionLength((pandora::ECAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberIntLengthECal :
+        (pandora::HCAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberIntLengthHCal : m_settings.m_absorberIntLengthOther);
+
+    const float layerAbsorberThickness(layerLayout.getAbsorberThickness(physicalLayer));
+    caloHitParameters.m_nCellRadiationLengths = radiationLength * layerAbsorberThickness;
+    caloHitParameters.m_nCellInteractionLengths = interactionLength * layerAbsorberThickness;
+
+    if (caloHitParameters.m_nCellRadiationLengths.Get() < std::numeric_limits<float>::epsilon() || caloHitParameters.m_nCellInteractionLengths.Get() < std::numeric_limits<float>::epsilon())
+    {
+        std::cout<<"WARNING CaloHitCreator::GetEndCapCaloHitProperties Calo hit has 0 radiation length or interaction length: \
+            not creating a Pandora calo hit." << std::endl;
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+    }
+
+    absorberCorrection = 1.;
+    for (unsigned int i = 0, iMax = layerLayout.getNLayers(); i < iMax; ++i)
+    {
+        const float absorberThickness(layerLayout.getAbsorberThickness(i));
+
+        if (absorberThickness < std::numeric_limits<float>::epsilon())
+            continue;
+
+        if (layerAbsorberThickness > std::numeric_limits<float>::epsilon())
+            absorberCorrection = absorberThickness / layerAbsorberThickness;
+
+        break;
+    }
+
+    caloHitParameters.m_cellNormalVector = (pCaloHit->getPosition()[2] > 0) ? pandora::CartesianVector(0, 0, 1) :
+        pandora::CartesianVector(0, 0, -1);
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void CaloHitCreator::GetBarrelCaloHitProperties(const edm4hep::CalorimeterHit *const pCaloHit, const gear::LayerLayout &layerLayout,
+    unsigned int barrelSymmetryOrder, float barrelPhi0, unsigned int staveNumber, PandoraApi::CaloHit::Parameters &caloHitParameters,
+    float &absorberCorrection) const
+{
+    caloHitParameters.m_hitRegion = pandora::BARREL;
+
+    const int physicalLayer(std::min(static_cast<int>(caloHitParameters.m_layer.Get()), layerLayout.getNLayers() - 1));
+    caloHitParameters.m_cellSize0 = layerLayout.getCellSize0(physicalLayer);
+    caloHitParameters.m_cellSize1 = layerLayout.getCellSize1(physicalLayer);
+    caloHitParameters.m_cellThickness = layerLayout.getThickness(physicalLayer);
+
+    const float radiationLength((pandora::ECAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberRadLengthECal :
+        (pandora::HCAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberRadLengthHCal : m_settings.m_absorberRadLengthOther);
+    const float interactionLength((pandora::ECAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberIntLengthECal :
+        (pandora::HCAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberIntLengthHCal : m_settings.m_absorberIntLengthOther);
+
+    const float layerAbsorberThickness(layerLayout.getAbsorberThickness(physicalLayer));
+    caloHitParameters.m_nCellRadiationLengths = radiationLength * layerAbsorberThickness;
+    caloHitParameters.m_nCellInteractionLengths = interactionLength * layerAbsorberThickness;
+
+    //std::cout<<"m_layer="<<caloHitParameters.m_layer.Get()<<",layerLayout.getNLayers() - 1="<<layerLayout.getNLayers() - 1<<",layerAbsorberThickness="<<layerAbsorberThickness<<std::endl;
+    if (caloHitParameters.m_nCellRadiationLengths.Get() < std::numeric_limits<float>::epsilon() || caloHitParameters.m_nCellInteractionLengths.Get() < std::numeric_limits<float>::epsilon())
+    {
+        std::cout<<"interactionLength="<<interactionLength<<",layerAbsorberThickness="<<layerAbsorberThickness<<",radiationLength="<<radiationLength<<",physicalLayer="<<physicalLayer<<",l1="<<caloHitParameters.m_layer.Get()<<",l2="<<layerLayout.getNLayers()-1<<std::endl;
+        std::cout<<"WARNIN CaloHitCreator::GetBarrelCaloHitProperties Calo hit has 0 radiation length or interaction length: \
+            not creating a Pandora calo hit." << std::endl;
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+    }
+
+    absorberCorrection = 1.;
+    for (unsigned int i = 0, iMax = layerLayout.getNLayers(); i < iMax; ++i)
+    {
+        const float absorberThickness(layerLayout.getAbsorberThickness(i));
+
+        if (absorberThickness < std::numeric_limits<float>::epsilon())
+            continue;
+
+        if (layerAbsorberThickness > std::numeric_limits<float>::epsilon())
+            absorberCorrection = absorberThickness / layerAbsorberThickness;
+
+        break;
+    }
+
+    if (barrelSymmetryOrder > 2)
+    {
+        const float phi = barrelPhi0 + (2. * M_PI * static_cast<float>(staveNumber) / static_cast<float>(barrelSymmetryOrder));
+        caloHitParameters.m_cellNormalVector = pandora::CartesianVector(-std::sin(phi), std::cos(phi), 0);
+    }
+    else
+    {
+        const float pCaloHitPosition[3]={pCaloHit->getPosition()[0], pCaloHit->getPosition()[1], pCaloHit->getPosition()[2]};
+
+        if (pCaloHitPosition[1] != 0)
+        {
+            const float phi = barrelPhi0 + std::atan(pCaloHitPosition[0] / pCaloHitPosition[1]);
+            caloHitParameters.m_cellNormalVector = pandora::CartesianVector(std::sin(phi), std::cos(phi), 0);
+        }
+        else
+        {
+            caloHitParameters.m_cellNormalVector = (pCaloHitPosition[0] > 0) ? pandora::CartesianVector(1, 0, 0) :
+                pandora::CartesianVector(-1, 0, 0);
+        }
+    }
+}
+
+void CaloHitCreator::GetBarrelCaloHitProperties(const edm4hep::CalorimeterHit *const pCaloHit, const std::vector<dd4hep::rec::LayeredCalorimeterData::Layer> &layerLayout,
+    unsigned int barrelSymmetryOrder, float barrelPhi0, unsigned int staveNumber, PandoraApi::CaloHit::Parameters &caloHitParameters,
+    float &absorberCorrection) const
+{
+    caloHitParameters.m_hitRegion = pandora::BARREL;
+
+    const int physicalLayer = std::max(0, int(caloHitParameters.m_layer.Get()-1) );
+    caloHitParameters.m_cellSize0 = layerLayout.at(physicalLayer).cellSize0;
+    caloHitParameters.m_cellSize1 = layerLayout.at(physicalLayer).cellSize1;
+    caloHitParameters.m_cellThickness = layerLayout.at(physicalLayer).sensitive_thickness;
+
+    const float radiationLength((pandora::ECAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberRadLengthECal :
+        (pandora::HCAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberRadLengthHCal : m_settings.m_absorberRadLengthOther);
+    const float interactionLength((pandora::ECAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberIntLengthECal :
+        (pandora::HCAL == caloHitParameters.m_hitType.Get()) ? m_settings.m_absorberIntLengthHCal : m_settings.m_absorberIntLengthOther);
+
+    const float layerAbsorberThickness(layerLayout.at(physicalLayer).absorberThickness);
+    caloHitParameters.m_nCellRadiationLengths   = radiationLength   * layerAbsorberThickness;
+    caloHitParameters.m_nCellInteractionLengths = interactionLength * layerAbsorberThickness;
+
+    
+    if (caloHitParameters.m_nCellRadiationLengths.Get() < std::numeric_limits<float>::epsilon() || caloHitParameters.m_nCellInteractionLengths.Get() < std::numeric_limits<float>::epsilon())
+    {
+        std::cout<<"interactionLength="<<interactionLength<<",layerAbsorberThickness="<<layerAbsorberThickness<<",radiationLength="<<radiationLength<<",physicalLayer="<<physicalLayer<<",l1="<<caloHitParameters.m_layer.Get()<<",l2="<<layerLayout.size()-1<<std::endl;
+        std::cout<<"WARNIN CaloHitCreator::GetBarrelCaloHitProperties Calo hit has 0 radiation length or interaction length: \
+            not creating a Pandora calo hit." << std::endl;
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+    }
+    
+    absorberCorrection = 1.;
+    for (unsigned int i = 0, iMax = layerLayout.size(); i < iMax; ++i)
+    {
+        const float absorberThickness(layerLayout.at(i).absorberThickness);
+
+        if (absorberThickness < std::numeric_limits<float>::epsilon())
+            continue;
+
+        if (layerAbsorberThickness > std::numeric_limits<float>::epsilon())
+            absorberCorrection = absorberThickness / layerAbsorberThickness;
+
+        break;
+    }
+    if (barrelSymmetryOrder > 2)
+    {
+        float phi = barrelPhi0 + (2. * M_PI * float(staveNumber) / float(barrelSymmetryOrder));
+        caloHitParameters.m_cellNormalVector = pandora::CartesianVector(-std::sin(phi), std::cos(phi), 0);
+        //std::cout<<"barrelPhi0="<<barrelPhi0<<",phi="<<phi<<",GetX()="<<caloHitParameters.m_cellNormalVector.Get().GetX()<<",GetY()="<<caloHitParameters.m_cellNormalVector.Get().GetY()<<",GetZ()="<<caloHitParameters.m_cellNormalVector.Get().GetZ()<<std::endl;
+    }
+    else
+    {
+        const float pCaloHitPosition[3]={pCaloHit->getPosition()[0], pCaloHit->getPosition()[1], pCaloHit->getPosition()[2]};
+
+        if (pCaloHitPosition[1] != 0)
+        {
+            const float phi = barrelPhi0 + std::atan(pCaloHitPosition[0] / pCaloHitPosition[1]);
+            caloHitParameters.m_cellNormalVector = pandora::CartesianVector(std::sin(phi), std::cos(phi), 0);
+        }
+        else
+        {
+            caloHitParameters.m_cellNormalVector = (pCaloHitPosition[0] > 0) ? pandora::CartesianVector(1, 0, 0) :
+                pandora::CartesianVector(-1, 0, 0);
+        }
+    }
+}
+//------------------------------------------------------------------------------------------------------------------------------------------
+int CaloHitCreator::GetBarrelLayer(const edm4hep::CalorimeterHit *const pCaloHit, const std::vector<dd4hep::rec::LayeredCalorimeterData::Layer> &layerLayout) const
+{
+    int layer = -1 ;   
+    for (unsigned int i = 0, iMax = layerLayout.size(); i < iMax; ++i)
+    {
+        const float distance(layerLayout.at(i).distance);
+        const float sensitive_thickness(layerLayout.at(i).sensitive_thickness);
+        if( (distance - 0.5*sensitive_thickness) <= pCaloHit->getPosition().x && pCaloHit->getPosition().x <= (distance + 0.5*sensitive_thickness)) {layer = i+1 ; break;}
+    }
+    if(layer==-1)
+    {
+        int lmax = layerLayout.size()-1;
+        std::cout<<"Error BarrelLayer, set to default 1, Hit.x="<<pCaloHit->getPosition().x<<", min_x="<<layerLayout.at(0).distance-0.5*layerLayout.at(0).sensitive_thickness<<", max_x="<<layerLayout.at(lmax).distance+0.5*layerLayout.at(lmax).sensitive_thickness<<std::endl;
+        layer = 1;
+    }
+    return layer;
+}
+
+int CaloHitCreator::GetNLayersFromEdge(const edm4hep::CalorimeterHit *const pCaloHit) const
+{
+    // Calo hit coordinate calculations
+    const float barrelMaximumRadius(this->GetMaximumRadius(pCaloHit, m_hCalBarrelOuterSymmetry, m_hCalBarrelOuterPhi0));
+    const float endCapMaximumRadius(this->GetMaximumRadius(pCaloHit, m_settings.m_hCalEndCapInnerSymmetryOrder, m_settings.m_hCalEndCapInnerPhiCoordinate));
+    const float caloHitAbsZ(std::fabs(pCaloHit->getPosition()[2]));
+
+    // Distance from radial outer
+    float radialDistanceToEdge(std::numeric_limits<float>::max());
+
+    if (caloHitAbsZ < m_eCalBarrelOuterZ)
+    {
+        radialDistanceToEdge = (m_hCalBarrelOuterR - barrelMaximumRadius) / m_hCalBarrelLayerThickness;
+    }
+    else
+    {
+        radialDistanceToEdge = (m_hCalEndCapOuterR - endCapMaximumRadius) / m_hCalEndCapLayerThickness;
+    }
+
+    // Distance from rear of endcap outer
+    float rearDistanceToEdge(std::numeric_limits<float>::max());
+
+    if (caloHitAbsZ >= m_eCalBarrelOuterZ)
+    {
+        rearDistanceToEdge = (m_hCalEndCapOuterZ - caloHitAbsZ) / m_hCalEndCapLayerThickness;
+    }
+    else
+    {
+        const float rearDistance((m_eCalBarrelOuterZ - caloHitAbsZ) / m_hCalBarrelLayerThickness);
+
+        if (rearDistance < m_settings.m_layersFromEdgeMaxRearDistance)
+        {
+            const float overlapDistance((m_hCalEndCapOuterR - endCapMaximumRadius) / m_hCalEndCapLayerThickness);
+            rearDistanceToEdge = std::max(rearDistance, overlapDistance);
+        }
+    }
+
+    return static_cast<int>(std::min(radialDistanceToEdge, rearDistanceToEdge));
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+float CaloHitCreator::GetMaximumRadius(const edm4hep::CalorimeterHit *const pCaloHit, const unsigned int symmetryOrder, const float phi0) const
+{
+    
+    const float pCaloHitPosition[3]={pCaloHit->getPosition()[0], pCaloHit->getPosition()[1], pCaloHit->getPosition()[2]};
+    if (symmetryOrder <= 2)
+        return std::sqrt((pCaloHitPosition[0] * pCaloHitPosition[0]) + (pCaloHitPosition[1] * pCaloHitPosition[1]));
+
+    float maximumRadius(0.f);
+    const float twoPi(2.f * M_PI);
+
+    for (unsigned int i = 0; i < symmetryOrder; ++i)
+    {
+        const float phi = phi0 + i * twoPi / static_cast<float>(symmetryOrder);
+        float radius = pCaloHitPosition[0] * std::cos(phi) + pCaloHitPosition[1] * std::sin(phi);
+
+        if (radius > maximumRadius)
+            maximumRadius = radius;
+    }
+
+    return maximumRadius;
+}
+
+void CaloHitCreator::GetCoding(const edm4hep::CalorimeterHit* pCaloHit, long& sys, long& x, long& y, long& z) const
+{
+    //sys = (pCaloHit->getCellID() << (64-8)) >> (64-8) ;
+    //x   = (pCaloHit->getCellID() << (64-(8+16))) >> (64-(8+16)) ;
+    //y   = (pCaloHit->getCellID() << (64-(8+16+16))) >> (64-(8+16+16)) ;
+    //z   = (pCaloHit->getCellID() << (64-(8+16+16+16))) >> (64-(8+16+16+16)) ;
+    unsigned long long id = pCaloHit->getCellID();
+    sys =  id               &  0xFF ;
+    x   = (id >> 8)         &  0xFFFF ;
+    y   = (id >> (8+16))    &  0xFFFF ;
+    z   = (id >> (8+16+16)) &  0xFFFF ;
+}
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+std::string CaloHitCreator::GetLayerCoding(const std::string &encodingString) const
+{
+    if (encodingString.find("layer") != std::string::npos)
+        return std::string("layer");
+
+    if (encodingString.find("K-1") != std::string::npos)
+        return std::string("K-1");
+
+    if (encodingString.find("K") != std::string::npos)
+        return std::string("K");
+
+    return std::string("unknown_layer_encoding");
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+std::string CaloHitCreator::GetStaveCoding(const std::string &encodingString) const
+{
+    if (encodingString.find("stave") != std::string::npos)
+        return std::string("stave");
+
+    if (encodingString.find("S-1") != std::string::npos)
+        return std::string("S-1");
+
+    return std::string("unknown_stave_encoding") ;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+CaloHitCreator::Settings::Settings() :
+    m_absorberRadLengthECal(1.f),
+    m_absorberIntLengthECal(1.f),
+    m_absorberRadLengthHCal(1.f),
+    m_absorberIntLengthHCal(1.f),
+    m_absorberRadLengthOther(1.f),
+    m_absorberIntLengthOther(1.f),
+    m_eCalToMip(1.f),
+    m_hCalToMip(1.f),
+    m_muonToMip(1.f),
+    m_eCalMipThreshold(0.f),
+    m_hCalMipThreshold(0.f),
+    m_muonMipThreshold(0.f),
+    m_eCalToEMGeV(1.f),
+    m_eCalToHadGeVBarrel(1.f),
+    m_eCalToHadGeVEndCap(1.f),
+    m_hCalToEMGeV(1.f),
+    m_hCalToHadGeV(1.f),
+    m_muonDigitalHits(1),
+    m_muonHitEnergy(0.5f),
+    m_maxHCalHitHadronicEnergy(10000.f),
+    m_nOuterSamplingLayers(3),
+    m_layersFromEdgeMaxRearDistance(250.f),
+    m_hCalEndCapInnerSymmetryOrder(4),
+    m_hCalEndCapInnerPhiCoordinate(0.f),
+    m_stripSplittingOn(0),
+    m_useEcalScLayers(0),
+    m_eCalSiToMip(1.f),
+    m_eCalScToMip(1.f),
+    m_eCalSiMipThreshold(0.f),
+    m_eCalScMipThreshold(0.f),
+    m_eCalSiToEMGeV(1.f),
+    m_eCalScToEMGeV(1.f),
+    m_eCalSiToHadGeVBarrel(1.f),
+    m_eCalScToHadGeVBarrel(1.f),
+    m_eCalSiToHadGeVEndCap(1.f),
+    m_eCalScToHadGeVEndCap(1.f)
+{
+}
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/src/GeometryCreator.cpp b/Reconstruction/PFA/Pandora/MatrixPandora/src/GeometryCreator.cpp
new file mode 100644
index 00000000..3ab6cc80
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/src/GeometryCreator.cpp
@@ -0,0 +1,488 @@
+/**
+ *  @file   MarlinPandora/src/GeometryCreator.cc
+ * 
+ *  @brief  Implementation of the geometry creator class.
+ * 
+ *  $Log: $
+ */
+#include "GaudiKernel/IService.h"
+
+#include "GearSvc/IGearSvc.h"
+#include "gear/BField.h"
+#include "gear/GEAR.h"
+#include "gear/GearParameters.h"
+#include "gear/CalorimeterParameters.h"
+#include "gear/TPCParameters.h"
+#include "gear/PadRowLayout2D.h"
+#include "gear/LayerLayout.h"
+
+#include "GeometryCreator.h"
+
+#include <DD4hep/Detector.h>
+#include <DDRec/DetectorData.h>
+
+GeometryCreator::GeometryCreator(const Settings &settings, const pandora::Pandora *const pPandora) :
+    m_settings(settings),
+    m_pPandora(pPandora)
+{
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+GeometryCreator::~GeometryCreator()
+{
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+//pandora::StatusCode GeometryCreator::CreateGeometry() const
+//pandora::StatusCode GeometryCreator::CreateGeometry(ISvcLocator* svcloc) const
+pandora::StatusCode GeometryCreator::CreateGeometry(ISvcLocator* svcloc) 
+{
+    IGearSvc*  iSvc = 0;
+    StatusCode sc = svcloc->service("GearSvc", iSvc, false);
+    if ( !sc ) 
+    {
+        throw "Failed to find GearSvc ...";
+    }
+    _GEAR = iSvc->getGearMgr();
+
+
+    IGeoSvc*  Svc = 0;
+    StatusCode sc1 = svcloc->service("GeoSvc", Svc, false);
+    if ( !sc1 )
+    {
+        throw "Failed to find GeoSvc ...";
+    }
+    m_dd4hep = Svc->lcdd();
+    
+
+    //auto _gear = service<IGearSvc>("GearSvc");
+    //if ( !_gear ) 
+    //{
+    //    throw "Failed to find GearSvc ...";
+    //}
+    //_GEAR = _gear->getGearMgr();
+    
+
+    try
+    {
+        SubDetectorTypeMap subDetectorTypeMap;
+        this->SetMandatorySubDetectorParameters(subDetectorTypeMap);
+
+        SubDetectorNameMap subDetectorNameMap;
+        this->SetAdditionalSubDetectorParameters(subDetectorNameMap);
+        
+        if (std::string::npos != _GEAR->getDetectorName().find("ILD"))
+            this->SetILDSpecificGeometry(subDetectorTypeMap, subDetectorNameMap);
+        
+        for (SubDetectorTypeMap::const_iterator iter = subDetectorTypeMap.begin(), iterEnd = subDetectorTypeMap.end(); iter != iterEnd; ++iter)
+            PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::Geometry::SubDetector::Create(*m_pPandora, iter->second));
+
+        for (SubDetectorNameMap::const_iterator iter = subDetectorNameMap.begin(), iterEnd = subDetectorNameMap.end(); iter != iterEnd; ++iter)
+            PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::Geometry::SubDetector::Create(*m_pPandora, iter->second));
+    }
+    catch (gear::Exception &exception)
+    {
+        std::cout << "Failure in marlin pandora geometry creator, gear exception: " << exception.what() << std::endl;
+        throw exception;
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void GeometryCreator::SetMandatorySubDetectorParameters(SubDetectorTypeMap &subDetectorTypeMap) const
+{
+    std::cout << "Begin SetMandatorySubDetectorParameters:" << std::endl;
+    PandoraApi::Geometry::SubDetector::Parameters eCalBarrelParameters, eCalEndCapParameters, hCalBarrelParameters, hCalEndCapParameters,
+        muonBarrelParameters, muonEndCapParameters;
+
+    this->SetDefaultSubDetectorParameters(m_dd4hep, "CaloDetector", pandora::ECAL_BARREL, eCalBarrelParameters);
+    //this->SetDefaultSubDetectorParameters(_GEAR->getEcalBarrelParameters(), "ECalBarrel", pandora::ECAL_BARREL, eCalBarrelParameters);
+    this->SetDefaultSubDetectorParameters(_GEAR->getEcalEndcapParameters(), "ECalEndCap", pandora::ECAL_ENDCAP, eCalEndCapParameters);
+    this->SetDefaultSubDetectorParameters(_GEAR->getHcalBarrelParameters(), "HCalBarrel", pandora::HCAL_BARREL, hCalBarrelParameters);
+    this->SetDefaultSubDetectorParameters(_GEAR->getHcalEndcapParameters(), "HCalEndCap", pandora::HCAL_ENDCAP, hCalEndCapParameters);
+    this->SetDefaultSubDetectorParameters(_GEAR->getYokeBarrelParameters(), "MuonBarrel", pandora::MUON_BARREL, muonBarrelParameters);
+    this->SetDefaultSubDetectorParameters(_GEAR->getYokeEndcapParameters(), "MuonEndCap", pandora::MUON_ENDCAP, muonEndCapParameters);
+
+    subDetectorTypeMap[pandora::ECAL_BARREL] = eCalBarrelParameters;
+    subDetectorTypeMap[pandora::ECAL_ENDCAP] = eCalEndCapParameters;
+    subDetectorTypeMap[pandora::HCAL_BARREL] = hCalBarrelParameters;
+    subDetectorTypeMap[pandora::HCAL_ENDCAP] = hCalEndCapParameters;
+    subDetectorTypeMap[pandora::MUON_BARREL] = muonBarrelParameters;
+    subDetectorTypeMap[pandora::MUON_ENDCAP] = muonEndCapParameters;
+
+    PandoraApi::Geometry::SubDetector::Parameters trackerParameters;
+    const gear::TPCParameters &tpcParameters(_GEAR->getTPCParameters());
+    trackerParameters.m_subDetectorName = "Tracker";
+    trackerParameters.m_subDetectorType = pandora::INNER_TRACKER;
+    trackerParameters.m_innerRCoordinate = tpcParameters.getPadLayout().getPlaneExtent()[0];
+    trackerParameters.m_innerZCoordinate = 0.f;
+    trackerParameters.m_innerPhiCoordinate = 0.f;
+    trackerParameters.m_innerSymmetryOrder = 0;
+    trackerParameters.m_outerRCoordinate = tpcParameters.getPadLayout().getPlaneExtent()[1];
+    trackerParameters.m_outerZCoordinate = tpcParameters.getMaxDriftLength();
+    trackerParameters.m_outerPhiCoordinate = 0.f;
+    trackerParameters.m_outerSymmetryOrder = 0;
+    trackerParameters.m_isMirroredInZ = true;
+    trackerParameters.m_nLayers = 0;
+    subDetectorTypeMap[pandora::INNER_TRACKER] = trackerParameters;
+
+    PandoraApi::Geometry::SubDetector::Parameters coilParameters;
+    const gear::GearParameters &gearParameters(_GEAR->getGearParameters("CoilParameters"));
+    coilParameters.m_subDetectorName = "Coil";
+    coilParameters.m_subDetectorType = pandora::COIL;
+    coilParameters.m_innerRCoordinate = gearParameters.getDoubleVal("Coil_cryostat_inner_radius");
+    coilParameters.m_innerZCoordinate = 0.f;
+    coilParameters.m_innerPhiCoordinate = 0.f;
+    coilParameters.m_innerSymmetryOrder = 0;
+    coilParameters.m_outerRCoordinate = gearParameters.getDoubleVal("Coil_cryostat_outer_radius");
+    coilParameters.m_outerZCoordinate = gearParameters.getDoubleVal("Coil_cryostat_half_z");
+    coilParameters.m_outerPhiCoordinate = 0.f;
+    coilParameters.m_outerSymmetryOrder = 0;
+    coilParameters.m_isMirroredInZ = true;
+    coilParameters.m_nLayers = 0;
+    subDetectorTypeMap[pandora::COIL] = coilParameters;
+    std::cout << "End SetMandatorySubDetectorParameters" << std::endl;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void GeometryCreator::SetAdditionalSubDetectorParameters(SubDetectorNameMap &subDetectorNameMap) const
+{
+    std::cout << "Begin SetAdditionalSubDetectorParameters:" << std::endl;
+    try
+    {
+        PandoraApi::Geometry::SubDetector::Parameters parameters;
+        this->SetDefaultSubDetectorParameters(_GEAR->getEcalPlugParameters(), "ECalPlug", pandora::SUB_DETECTOR_OTHER, parameters);
+        subDetectorNameMap[parameters.m_subDetectorName.Get()] = parameters;
+    }
+    catch (gear::Exception &exception)
+    {
+        std::cout << " warning pandora geometry creator: " << exception.what() << std::endl;
+    }
+
+    try
+    {
+        PandoraApi::Geometry::SubDetector::Parameters parameters;
+        this->SetDefaultSubDetectorParameters(_GEAR->getHcalRingParameters(), "HCalRing", pandora::SUB_DETECTOR_OTHER, parameters);
+        subDetectorNameMap[parameters.m_subDetectorName.Get()] = parameters;
+    }
+    catch (gear::Exception &exception)
+    {
+         std::cout<< "warning pandora geometry creator: " << exception.what() << std::endl;
+    }
+
+    try
+    {
+        PandoraApi::Geometry::SubDetector::Parameters parameters;
+        this->SetDefaultSubDetectorParameters(_GEAR->getLcalParameters(), "LCal", pandora::SUB_DETECTOR_OTHER, parameters);
+        subDetectorNameMap[parameters.m_subDetectorName.Get()] = parameters;
+    }
+    catch (gear::Exception &exception)
+    {
+         std::cout << "warning pandora geometry creator: " << exception.what() << std::endl;
+    }
+
+    try
+    {
+        PandoraApi::Geometry::SubDetector::Parameters parameters;
+        this->SetDefaultSubDetectorParameters(_GEAR->getLHcalParameters(), "LHCal", pandora::SUB_DETECTOR_OTHER, parameters);
+        subDetectorNameMap[parameters.m_subDetectorName.Get()] = parameters;
+    }
+    catch (gear::Exception &exception)
+    {
+         std::cout << "warning pandora geometry creator: " << exception.what() << std::endl;
+    }
+    std::cout << "End SetAdditionalSubDetectorParameters" << std::endl;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void GeometryCreator::SetDefaultSubDetectorParameters(const gear::CalorimeterParameters &inputParameters, const std::string &subDetectorName,
+    const pandora::SubDetectorType subDetectorType, PandoraApi::Geometry::SubDetector::Parameters &parameters) const
+{
+    const gear::LayerLayout &layerLayout = inputParameters.getLayerLayout();
+
+    parameters.m_subDetectorName = subDetectorName;
+    parameters.m_subDetectorType = subDetectorType;
+    parameters.m_innerRCoordinate = inputParameters.getExtent()[0];
+    parameters.m_innerZCoordinate = inputParameters.getExtent()[2];
+    parameters.m_innerPhiCoordinate = inputParameters.getPhi0();
+    parameters.m_innerSymmetryOrder = inputParameters.getSymmetryOrder();
+    parameters.m_outerRCoordinate = inputParameters.getExtent()[1];
+    parameters.m_outerZCoordinate = inputParameters.getExtent()[3];
+    parameters.m_outerPhiCoordinate = inputParameters.getPhi0();
+    parameters.m_outerSymmetryOrder = inputParameters.getSymmetryOrder();
+    parameters.m_isMirroredInZ = true;
+    parameters.m_nLayers = layerLayout.getNLayers();
+    std::cout << "inputParameters.getExtent()[0]="<<inputParameters.getExtent()[0]<<",inputParameters.getExtent()[1]="<<inputParameters.getExtent()[1]<<",inputParameters.getExtent()[2]="<<inputParameters.getExtent()[2]<<",inputParameters.getExtent()[3]="<<inputParameters.getExtent()[3]<< std::endl;
+
+    // ATTN Not always going to be correct for any optional subdetectors, but impact of this is negligible for ILD
+    const float radiationLength(((pandora::ECAL_BARREL == subDetectorType) || (pandora::ECAL_ENDCAP == subDetectorType)) ? m_settings.m_absorberRadLengthECal :
+        ((pandora::HCAL_BARREL == subDetectorType) || (pandora::HCAL_ENDCAP == subDetectorType)) ? m_settings.m_absorberRadLengthHCal : m_settings.m_absorberRadLengthOther);
+    const float interactionLength(((pandora::ECAL_BARREL == subDetectorType) || (pandora::ECAL_ENDCAP == subDetectorType)) ? m_settings.m_absorberIntLengthECal :
+        ((pandora::HCAL_BARREL == subDetectorType) || (pandora::HCAL_ENDCAP == subDetectorType)) ? m_settings.m_absorberIntLengthHCal : m_settings.m_absorberIntLengthOther);
+
+    for (int i = 0; i < layerLayout.getNLayers(); ++i)
+    {
+        PandoraApi::Geometry::LayerParameters layerParameters;
+        layerParameters.m_closestDistanceToIp = layerLayout.getDistance(i) + (0.5 * (layerLayout.getThickness(i) + layerLayout.getAbsorberThickness(i)));
+        layerParameters.m_nRadiationLengths = radiationLength * layerLayout.getAbsorberThickness(i);
+        layerParameters.m_nInteractionLengths = interactionLength * layerLayout.getAbsorberThickness(i);
+        parameters.m_layerParametersVector.push_back(layerParameters);
+    }
+}
+
+void GeometryCreator::SetDefaultSubDetectorParameters(const dd4hep::Detector* theDetector , const std::string &subDetectorName,
+    const pandora::SubDetectorType subDetectorType, PandoraApi::Geometry::SubDetector::Parameters &parameters) const
+{
+    //const std::vector< dd4hep::DetElement > &detElements = theDetector.detectors("EcalMatrix", true);
+    const dd4hep::DetElement &detElement = theDetector->detector(subDetectorName);
+    dd4hep::rec::LayeredCalorimeterData* Data = detElement.extension<dd4hep::rec::LayeredCalorimeterData>() ;
+ 
+
+    parameters.m_subDetectorName    = subDetectorName;
+    parameters.m_subDetectorType    = subDetectorType;
+    parameters.m_innerRCoordinate   = Data->extent[0];
+    parameters.m_innerZCoordinate   = Data->extent[2];
+    parameters.m_innerPhiCoordinate = Data->inner_phi0;
+    parameters.m_innerSymmetryOrder = Data->inner_symmetry;
+    parameters.m_outerRCoordinate   = Data->extent[1];
+    parameters.m_outerZCoordinate   = Data->extent[3];
+    parameters.m_outerPhiCoordinate = Data->outer_phi0;
+    parameters.m_outerSymmetryOrder = Data->outer_symmetry;
+    parameters.m_nLayers            = Data->layers.size();
+    parameters.m_isMirroredInZ      = true;
+    const std::vector<dd4hep::rec::LayeredCalorimeterData::Layer>& layers= Data->layers;
+    std::cout << "Extent()[0]="<<Data->extent[0]<<",Extent()[1]="<<Data->extent[1]<<",Extent()[2]="<<Data->extent[2]<<",Extent()[3]="<<Data->extent[3]<< std::endl;
+
+    // ATTN Not always going to be correct for any optional subdetectors, but impact of this is negligible for ILD
+    const float radiationLength(((pandora::ECAL_BARREL == subDetectorType) || (pandora::ECAL_ENDCAP == subDetectorType)) ? m_settings.m_absorberRadLengthECal :
+        ((pandora::HCAL_BARREL == subDetectorType) || (pandora::HCAL_ENDCAP == subDetectorType)) ? m_settings.m_absorberRadLengthHCal : m_settings.m_absorberRadLengthOther);
+    const float interactionLength(((pandora::ECAL_BARREL == subDetectorType) || (pandora::ECAL_ENDCAP == subDetectorType)) ? m_settings.m_absorberIntLengthECal :
+        ((pandora::HCAL_BARREL == subDetectorType) || (pandora::HCAL_ENDCAP == subDetectorType)) ? m_settings.m_absorberIntLengthHCal : m_settings.m_absorberIntLengthOther);
+   
+    //for (int i = 0; i < layerLayout.getNLayers(); ++i)
+    for (auto const& layer : layers)
+    {
+        PandoraApi::Geometry::LayerParameters layerParameters;
+        //layerParameters.m_closestDistanceToIp = layer.distance + 0.5 * layer.sensitive_thickness;//Thickness of the sensitive element (e.g. scintillator)
+        layerParameters.m_closestDistanceToIp = layer.distance ;
+        layerParameters.m_nRadiationLengths   = radiationLength   * layer.absorberThickness;
+        layerParameters.m_nInteractionLengths = interactionLength * layer.absorberThickness;
+        std::cout<<"m_closestDistanceToIp="<<layerParameters.m_closestDistanceToIp.Get()<<",m_nRadiationLengths="<<layerParameters.m_nRadiationLengths.Get()<<",m_nInteractionLengths="<<layerParameters.m_nInteractionLengths.Get()<<std::endl;
+        parameters.m_layerParametersVector.push_back(layerParameters);
+    }
+}
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode GeometryCreator::SetILDSpecificGeometry(SubDetectorTypeMap &subDetectorTypeMap, SubDetectorNameMap &subDetectorNameMap) const
+{
+    // Set positions of gaps in ILD detector and add information missing from GEAR parameters file
+    try
+    {
+        const gear::CalorimeterParameters &hCalBarrelParameters = _GEAR->getHcalBarrelParameters();
+        subDetectorTypeMap[pandora::HCAL_BARREL].m_outerPhiCoordinate = hCalBarrelParameters.getIntVal("Hcal_outer_polygon_phi0");
+        subDetectorTypeMap[pandora::HCAL_BARREL].m_outerSymmetryOrder = hCalBarrelParameters.getIntVal("Hcal_outer_polygon_order");
+    }
+    catch (gear::Exception &)
+    {
+        // aLaVideauGeometry
+        return this->SetILD_SDHCALSpecificGeometry(subDetectorTypeMap);
+    }
+
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_innerSymmetryOrder = m_settings.m_eCalEndCapInnerSymmetryOrder;
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_innerPhiCoordinate = m_settings.m_eCalEndCapInnerPhiCoordinate;
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_outerSymmetryOrder = m_settings.m_eCalEndCapOuterSymmetryOrder;
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_outerPhiCoordinate = m_settings.m_eCalEndCapOuterPhiCoordinate;
+
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_innerSymmetryOrder = m_settings.m_hCalEndCapInnerSymmetryOrder;
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_innerPhiCoordinate = m_settings.m_hCalEndCapInnerPhiCoordinate;
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_outerSymmetryOrder = m_settings.m_hCalEndCapOuterSymmetryOrder;
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_outerPhiCoordinate = m_settings.m_hCalEndCapOuterPhiCoordinate;
+
+    subDetectorNameMap["HCalRing"].m_innerSymmetryOrder = m_settings.m_hCalRingInnerSymmetryOrder;
+    subDetectorNameMap["HCalRing"].m_innerPhiCoordinate = m_settings.m_hCalRingInnerPhiCoordinate;
+    subDetectorNameMap["HCalRing"].m_outerSymmetryOrder = m_settings.m_hCalRingOuterSymmetryOrder;
+    subDetectorNameMap["HCalRing"].m_outerPhiCoordinate = m_settings.m_hCalRingOuterPhiCoordinate;
+
+    // Gaps in detector active material
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateHCalBarrelBoxGaps());
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateHCalEndCapBoxGaps());
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateHCalBarrelConcentricGaps());
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode GeometryCreator::SetILD_SDHCALSpecificGeometry(SubDetectorTypeMap &subDetectorTypeMap) const
+{
+    // Non-default values (and those missing from GEAR parameters file)...
+    // The following 2 parameters have no sense for Videau Geometry, set them to 0
+    subDetectorTypeMap[pandora::HCAL_BARREL].m_outerPhiCoordinate = 0;
+    subDetectorTypeMap[pandora::HCAL_BARREL].m_outerSymmetryOrder = 0;
+
+    // Endcap is identical to standard ILD geometry, only HCAL barrel is different
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_innerSymmetryOrder = m_settings.m_eCalEndCapInnerSymmetryOrder;
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_innerPhiCoordinate = m_settings.m_eCalEndCapInnerPhiCoordinate;
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_outerSymmetryOrder = m_settings.m_eCalEndCapOuterSymmetryOrder;
+    subDetectorTypeMap[pandora::ECAL_ENDCAP].m_outerPhiCoordinate = m_settings.m_eCalEndCapOuterPhiCoordinate;
+
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_innerSymmetryOrder = m_settings.m_hCalEndCapInnerSymmetryOrder;
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_innerPhiCoordinate = m_settings.m_hCalEndCapInnerPhiCoordinate;
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_outerSymmetryOrder = m_settings.m_hCalEndCapOuterSymmetryOrder;
+    subDetectorTypeMap[pandora::HCAL_ENDCAP].m_outerPhiCoordinate = m_settings.m_hCalEndCapOuterPhiCoordinate;
+
+    // TODO implement gaps between modules
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode GeometryCreator::CreateHCalBarrelBoxGaps() const
+{
+    const std::string detectorName(_GEAR->getDetectorName());
+
+    const gear::CalorimeterParameters &hCalBarrelParameters = _GEAR->getHcalBarrelParameters();
+    const unsigned int innerSymmetryOrder(hCalBarrelParameters.getSymmetryOrder());
+    const unsigned int outerSymmetryOrder(hCalBarrelParameters.getIntVal("Hcal_outer_polygon_order"));
+
+    if ((0 == innerSymmetryOrder) || (2 != outerSymmetryOrder / innerSymmetryOrder))
+    {
+        std::cout << " Detector " << detectorName << " doesn't conform to expected ILD-specific geometry" << std::endl;
+        return pandora::STATUS_CODE_INVALID_PARAMETER;
+    }
+
+    const float innerRadius(hCalBarrelParameters.getExtent()[0]);
+    const float outerRadius(hCalBarrelParameters.getExtent()[1]);
+    const float outerZ(hCalBarrelParameters.getExtent()[3]);
+    const float phi0(hCalBarrelParameters.getPhi0());
+
+    const float staveGap(hCalBarrelParameters.getDoubleVal("Hcal_stave_gaps"));
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateRegularBoxGaps(innerSymmetryOrder, phi0, innerRadius, outerRadius,
+        -outerZ, outerZ, staveGap));
+
+    const float outerPseudoPhi0(M_PI / static_cast<float>(innerSymmetryOrder));
+    const float cosOuterPseudoPhi0(std::cos(outerPseudoPhi0));
+
+    if ((0 == outerPseudoPhi0) || (0.f == cosOuterPseudoPhi0))
+    {
+        std::cout << " Detector " << detectorName << " doesn't conform to expected ILD-specific geometry" << std::endl;
+        return pandora::STATUS_CODE_INVALID_PARAMETER;
+    }
+
+    const float middleStaveGap(hCalBarrelParameters.getDoubleVal("Hcal_middle_stave_gaps"));
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateRegularBoxGaps(innerSymmetryOrder, outerPseudoPhi0,
+        innerRadius / cosOuterPseudoPhi0, outerRadius, -outerZ, outerZ, middleStaveGap));
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode GeometryCreator::CreateHCalEndCapBoxGaps() const
+{
+    const gear::CalorimeterParameters &hCalEndCapParameters = _GEAR->getHcalEndcapParameters();
+
+    const float staveGap(hCalEndCapParameters.getDoubleVal("Hcal_stave_gaps"));
+    const float innerRadius(hCalEndCapParameters.getExtent()[0]);
+    const float outerRadius(hCalEndCapParameters.getExtent()[1]);
+    const float innerZ(hCalEndCapParameters.getExtent()[2]);
+    const float outerZ(hCalEndCapParameters.getExtent()[3]);
+
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateRegularBoxGaps(m_settings.m_hCalEndCapInnerSymmetryOrder,
+        m_settings.m_hCalEndCapInnerPhiCoordinate, innerRadius, outerRadius, innerZ, outerZ, staveGap,
+        pandora::CartesianVector(-innerRadius, 0, 0)));
+
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CreateRegularBoxGaps(m_settings.m_hCalEndCapInnerSymmetryOrder,
+        m_settings.m_hCalEndCapInnerPhiCoordinate, innerRadius, outerRadius, -outerZ, -innerZ, staveGap,
+        pandora::CartesianVector(innerRadius, 0, 0)));
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode GeometryCreator::CreateHCalBarrelConcentricGaps() const
+{
+    const gear::CalorimeterParameters &hCalBarrelParameters = _GEAR->getHcalBarrelParameters();
+    const float gapWidth(hCalBarrelParameters.getDoubleVal("Hcal_stave_gaps"));
+
+    PandoraApi::Geometry::ConcentricGap::Parameters gapParameters;
+
+    gapParameters.m_minZCoordinate = -0.5f * gapWidth;
+    gapParameters.m_maxZCoordinate =  0.5f * gapWidth;
+    gapParameters.m_innerRCoordinate = hCalBarrelParameters.getExtent()[0];
+    gapParameters.m_innerPhiCoordinate = hCalBarrelParameters.getPhi0();
+    gapParameters.m_innerSymmetryOrder = hCalBarrelParameters.getSymmetryOrder();
+    gapParameters.m_outerRCoordinate = hCalBarrelParameters.getExtent()[1];
+    gapParameters.m_outerPhiCoordinate = hCalBarrelParameters.getIntVal("Hcal_outer_polygon_phi0");
+    gapParameters.m_outerSymmetryOrder = hCalBarrelParameters.getIntVal("Hcal_outer_polygon_order");
+
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::Geometry::ConcentricGap::Create(*m_pPandora, gapParameters));
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode GeometryCreator::CreateRegularBoxGaps(unsigned int symmetryOrder, float phi0, float innerRadius, float outerRadius,
+    float minZ, float maxZ, float gapWidth, pandora::CartesianVector vertexOffset) const
+{
+    const pandora::CartesianVector basicGapVertex(pandora::CartesianVector(-0.5f * gapWidth, innerRadius, minZ) + vertexOffset);
+    const pandora::CartesianVector basicSide1(gapWidth, 0, 0);
+    const pandora::CartesianVector basicSide2(0, outerRadius - innerRadius, 0);
+    const pandora::CartesianVector basicSide3(0, 0, maxZ - minZ);
+
+    for (unsigned int i = 0; i < symmetryOrder; ++i)
+    {
+        const float phi = phi0 + (2. * M_PI * static_cast<float>(i) / static_cast<float>(symmetryOrder));
+        const float sinPhi(std::sin(phi));
+        const float cosPhi(std::cos(phi));
+
+        PandoraApi::Geometry::BoxGap::Parameters gapParameters;
+
+        gapParameters.m_vertex = pandora::CartesianVector(cosPhi * basicGapVertex.GetX() + sinPhi * basicGapVertex.GetY(),
+            -sinPhi * basicGapVertex.GetX() + cosPhi * basicGapVertex.GetY(), basicGapVertex.GetZ());
+        gapParameters.m_side1 = pandora::CartesianVector(cosPhi * basicSide1.GetX() + sinPhi * basicSide1.GetY(),
+            -sinPhi * basicSide1.GetX() + cosPhi * basicSide1.GetY(), basicSide1.GetZ());
+        gapParameters.m_side2 = pandora::CartesianVector(cosPhi * basicSide2.GetX() + sinPhi * basicSide2.GetY(),
+            -sinPhi * basicSide2.GetX() + cosPhi * basicSide2.GetY(), basicSide2.GetZ());
+        gapParameters.m_side3 = pandora::CartesianVector(cosPhi * basicSide3.GetX() + sinPhi * basicSide3.GetY(),
+            -sinPhi * basicSide3.GetX() + cosPhi * basicSide3.GetY(), basicSide3.GetZ());
+
+        PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::Geometry::BoxGap::Create(*m_pPandora, gapParameters));
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+GeometryCreator::Settings::Settings() :
+    m_absorberRadLengthECal(1.f),
+    m_absorberIntLengthECal(1.f),
+    m_absorberRadLengthHCal(1.f),
+    m_absorberIntLengthHCal(1.f),
+    m_absorberRadLengthOther(1.f),
+    m_absorberIntLengthOther(1.f),
+    m_eCalEndCapInnerSymmetryOrder(4),
+    m_eCalEndCapInnerPhiCoordinate(0.f),
+    m_eCalEndCapOuterSymmetryOrder(8),
+    m_eCalEndCapOuterPhiCoordinate(0.f),
+    m_hCalEndCapInnerSymmetryOrder(4),
+    m_hCalEndCapInnerPhiCoordinate(0.f),
+    m_hCalEndCapOuterSymmetryOrder(16),
+    m_hCalEndCapOuterPhiCoordinate(0.f),
+    m_hCalRingInnerSymmetryOrder(8),
+    m_hCalRingInnerPhiCoordinate(0.f),
+    m_hCalRingOuterSymmetryOrder(16),
+    m_hCalRingOuterPhiCoordinate(0.f)
+{
+}
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/src/MCParticleCreator.cpp b/Reconstruction/PFA/Pandora/MatrixPandora/src/MCParticleCreator.cpp
new file mode 100644
index 00000000..21a2a741
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/src/MCParticleCreator.cpp
@@ -0,0 +1,372 @@
+/**
+ *  @file   MarlinPandora/src/MCParticleCreator.cc
+ * 
+ *  @brief  Implementation of the mc particle creator class.
+ * 
+ *  $Log: $
+ */
+
+
+#include "edm4hep/MCParticleConst.h"
+#include "edm4hep/MCParticle.h" 
+#include "edm4hep/MCRecoCaloAssociation.h" 
+#include "edm4hep/SimCalorimeterHitConst.h" 
+#include "edm4hep/CaloHitContributionConst.h" 
+#include "edm4hep/Track.h" 
+#include "edm4hep/MCRecoTrackerAssociation.h" 
+#include "edm4hep/SimTrackerHitConst.h" 
+
+
+
+
+#include "PandoraMatrixAlg.h"
+#include "MCParticleCreator.h"
+
+#include <cmath>
+#include <limits>
+#include <assert.h>
+
+MCParticleCreator::MCParticleCreator(const Settings &settings, const pandora::Pandora *const pPandora) :
+    m_settings(settings),
+    m_pPandora(pPandora),
+    m_bField(settings.m_bField)
+{
+m_id_pMC_map = new std::map<unsigned int, const edm4hep::MCParticle*>;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+MCParticleCreator::~MCParticleCreator()
+{
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode MCParticleCreator::CreateMCParticles(const CollectionMaps& collectionMaps ) const
+{
+    for (StringVector::const_iterator iter = m_settings.m_mcParticleCollections.begin(), iterEnd = m_settings.m_mcParticleCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_MC.find(*iter) == collectionMaps.collectionMap_MC.end()) continue;
+        try
+        {
+            const std::vector<edm4hep::MCParticle>& pMCParticleCollection = (collectionMaps.collectionMap_MC.find(*iter))->second;
+            std::cout<<"Do CreateMCParticles, collection:"<<(*iter)<<", size="<<pMCParticleCollection.size()<<std::endl;
+            for (int im = 0; im < pMCParticleCollection.size(); im++)
+            {
+                try
+                {
+                    const edm4hep::MCParticle& pMcParticle = pMCParticleCollection.at(im);
+                    PandoraApi::MCParticle::Parameters mcParticleParameters;
+                    mcParticleParameters.m_energy =   sqrt(pMcParticle.getMomentum()[0] * pMcParticle.getMomentum()[0] + pMcParticle.getMomentum()[1] * pMcParticle.getMomentum()[1] + pMcParticle.getMomentum()[2] * pMcParticle.getMomentum()[2] + pMcParticle.getMass() * pMcParticle.getMass());
+                    mcParticleParameters.m_particleId = pMcParticle.getPDG();
+                    mcParticleParameters.m_mcParticleType = pandora::MC_3D;
+                    mcParticleParameters.m_pParentAddress = &pMcParticle;
+                    unsigned int p_id = pMcParticle.id();
+                    const edm4hep::MCParticle* p_mc = &pMcParticle;
+                    (*m_id_pMC_map) [p_id]   = p_mc;
+                    mcParticleParameters.m_momentum = pandora::CartesianVector(pMcParticle.getMomentum()[0], pMcParticle.getMomentum()[1],
+                        pMcParticle.getMomentum()[2]);
+                    mcParticleParameters.m_vertex = pandora::CartesianVector(pMcParticle.getVertex()[0], pMcParticle.getVertex()[1],
+                        pMcParticle.getVertex()[2]);
+                    mcParticleParameters.m_endpoint = pandora::CartesianVector(pMcParticle.getEndpoint()[0], pMcParticle.getEndpoint()[1],
+                        pMcParticle.getEndpoint()[2]);
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::MCParticle::Create(*m_pPandora, mcParticleParameters));
+
+                    // Create parent-daughter relationships
+                    for(std::vector<edm4hep::ConstMCParticle>::const_iterator itDaughter = pMcParticle.daughters_begin(),
+                        itDaughterEnd = pMcParticle.daughters_end(); itDaughter != itDaughterEnd; ++itDaughter)
+                    {   
+                        for (int ida = 0; ida < pMCParticleCollection.size(); ida++)
+                        {
+                           if(pMCParticleCollection.at(ida).id()==(*itDaughter).id())
+                           {
+                              
+                                const edm4hep::MCParticle& dMcParticle = pMCParticleCollection.at(ida);
+                                if(&pMcParticle == &dMcParticle){std::cout<< "error, mother and daughter are the same mc particle, don't save SetMCParentDaughterRelationship"<<std::endl;}
+                                else PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetMCParentDaughterRelationship(*m_pPandora, &pMcParticle, &dMcParticle));
+                                break;
+                           }
+                        }
+                    }
+                    
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout << "Failed to extract MCParticle: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout << "Failed to extract MCParticle: " <<  std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout << "Failed to extract MCParticles collection: " << *iter << ", " <<  std::endl;
+        }
+    }
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+/*
+pandora::StatusCode MCParticleCreator::CreateTrackToMCParticleRelationships(const EVENT::LCEvent *const pLCEvent, const TrackVector &trackVector) const
+{
+    for (StringVector::const_iterator iter = m_settings.m_lcTrackRelationCollections.begin(), iterEnd = m_settings.m_lcTrackRelationCollections.end();
+         iter != iterEnd; ++iter)
+    {
+        try
+        {
+            const EVENT::LCCollection *pMCRelationCollection = pLCEvent->getCollection(*iter);
+            UTIL::LCRelationNavigator navigate(pMCRelationCollection);
+
+            for (TrackVector::const_iterator trackIter = trackVector.begin(), trackIterEnd = trackVector.end();
+                trackIter != trackIterEnd; ++trackIter)
+            {
+                try
+                {
+                    EVENT::Track *pTrack = *trackIter;
+                    const EVENT::LCObjectVec &objectVec = navigate.getRelatedToObjects(*trackIter);
+
+                    // Get reconstructed momentum at dca
+                    const pandora::Helix helixFit(pTrack->getPhi(), pTrack->getD0(), pTrack->getZ0(), pTrack->getOmega(), pTrack->getTanLambda(), m_bField);
+                    const float recoMomentum(helixFit.GetMomentum().GetMagnitude());
+
+                    // Use momentum magnitude to identify best mc particle
+                    MCParticle *pBestMCParticle = NULL;
+                    float bestDeltaMomentum(std::numeric_limits<float>::max());
+
+                    for (EVENT::LCObjectVec::const_iterator itRel = objectVec.begin(), itRelEnd = objectVec.end(); itRel != itRelEnd; ++itRel)
+                    {
+                        EVENT::MCParticle *pMCParticle = NULL;
+                        pMCParticle = dynamic_cast<MCParticle *>(*itRel);
+
+                        if (NULL == pMCParticle)
+                            continue;
+
+                        const float trueMomentum(pandora::CartesianVector(pMCParticle->getMomentum()[0], pMCParticle->getMomentum()[1],
+                            pMCParticle->getMomentum()[2]).GetMagnitude());
+
+                        const float deltaMomentum(std::fabs(recoMomentum - trueMomentum));
+
+                        if (deltaMomentum < bestDeltaMomentum)
+                        {
+                            pBestMCParticle = pMCParticle;
+                            bestDeltaMomentum = deltaMomentum;
+                        }
+                    }
+
+                    if (NULL == pBestMCParticle)
+                        continue;
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackToMCParticleRelationship(*m_pPandora, pTrack,
+                        pBestMCParticle));
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    streamlog_out(ERROR) << "Failed to extract track to mc particle relationship: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (EVENT::Exception &exception)
+                {
+                    streamlog_out(WARNING) << "Failed to extract track to mc particle relationship: " << exception.what() << std::endl;
+                }
+            }
+        }
+        catch (EVENT::Exception &exception)
+        {
+            streamlog_out(DEBUG5) << "Failed to extract track to mc particle relationships collection: " << *iter << ", " << exception.what() << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+*/
+//------------------------------------------------------------------------------------------------------------------------------------------
+/*
+pandora::StatusCode MCParticleCreator::CreateCaloHitToMCParticleRelationships(const EVENT::LCEvent *const pLCEvent, const CalorimeterHitVector &calorimeterHitVector) const
+{
+    typedef std::map<MCParticle *, float> MCParticleToEnergyWeightMap;
+    MCParticleToEnergyWeightMap mcParticleToEnergyWeightMap;
+
+    for (StringVector::const_iterator iter = m_settings.m_lcCaloHitRelationCollections.begin(), iterEnd = m_settings.m_lcCaloHitRelationCollections.end();
+         iter != iterEnd; ++iter)
+    {
+        try
+        {
+            const EVENT::LCCollection *pMCRelationCollection = pLCEvent->getCollection(*iter);
+            UTIL::LCRelationNavigator navigate(pMCRelationCollection);
+
+            for (CalorimeterHitVector::const_iterator caloHitIter = calorimeterHitVector.begin(),
+                caloHitIterEnd = calorimeterHitVector.end(); caloHitIter != caloHitIterEnd; ++caloHitIter)
+            {
+                try
+                {
+                    mcParticleToEnergyWeightMap.clear();
+                    const EVENT::LCObjectVec &objectVec = navigate.getRelatedToObjects(*caloHitIter);
+
+                    for (EVENT::LCObjectVec::const_iterator itRel = objectVec.begin(), itRelEnd = objectVec.end(); itRel != itRelEnd; ++itRel)
+                    {
+                        EVENT::SimCalorimeterHit *pSimHit = dynamic_cast<SimCalorimeterHit *>(*itRel);
+
+                        if (NULL == pSimHit)
+                            continue;
+
+                        for (int iCont = 0, iEnd = pSimHit->getNMCContributions(); iCont < iEnd; ++iCont)
+                        {
+                            mcParticleToEnergyWeightMap[pSimHit->getParticleCont(iCont)] += pSimHit->getEnergyCont(iCont);
+                        }
+                    }
+
+                    for (MCParticleToEnergyWeightMap::const_iterator mcParticleIter = mcParticleToEnergyWeightMap.begin(),
+                        mcParticleIterEnd = mcParticleToEnergyWeightMap.end(); mcParticleIter != mcParticleIterEnd; ++mcParticleIter)
+                    {
+                        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetCaloHitToMCParticleRelationship(*m_pPandora,
+                            *caloHitIter, mcParticleIter->first, mcParticleIter->second));
+                    }
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    streamlog_out(ERROR) << "Failed to extract calo hit to mc particle relationship: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (EVENT::Exception &exception)
+                {
+                    streamlog_out(WARNING) << "Failed to extract calo hit to mc particle relationship: " << exception.what() << std::endl;
+                }
+            }
+        }
+        catch (EVENT::Exception &exception)
+        {
+            streamlog_out(DEBUG5) << "Failed to extract calo hit to mc particle relationships collection: " << *iter << ", " << exception.what() << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+*/
+
+//-------------- using sim calo hit and digi calo hit, no weight here---------------------------------------------------------------------------------------------------------------
+pandora::StatusCode MCParticleCreator::CreateCaloHitToMCParticleRelationships(const CollectionMaps& collectionMaps, const CalorimeterHitVector &calorimeterHitVector) const
+{
+    std::cout<<"Do CreateCaloHitToMCParticleRelationships"<<std::endl;
+    typedef std::map<const edm4hep::MCParticle *, float> MCParticleToEnergyWeightMap;
+    MCParticleToEnergyWeightMap mcParticleToEnergyWeightMap;
+
+    for (StringVector::const_iterator iter = m_settings.m_CaloHitRelationCollections.begin(), iterEnd = m_settings.m_CaloHitRelationCollections.end();
+         iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_CaloRel.find(*iter) == collectionMaps.collectionMap_CaloRel.end()) continue;
+        try
+        {
+            const std::vector<edm4hep::MCRecoCaloAssociation>& pMCRecoCaloAssociationCollection = (collectionMaps.collectionMap_CaloRel.find(*iter))->second;
+
+            for (unsigned i_calo=0; i_calo < calorimeterHitVector.size(); i_calo++)
+            {
+                try
+                {
+                    mcParticleToEnergyWeightMap.clear();
+                    for(unsigned ic=0; ic < pMCRecoCaloAssociationCollection.size(); ic++)
+                    {
+                        if( pMCRecoCaloAssociationCollection.at(ic).getRec().id() != (*(calorimeterHitVector.at(i_calo))).id() ) continue;
+                        
+                        const edm4hep::ConstSimCalorimeterHit pSimHit = pMCRecoCaloAssociationCollection.at(ic).getSim();
+                        for (int iCont = 0, iEnd = pSimHit.contributions_size(); iCont < iEnd; ++iCont)
+                        {
+                            edm4hep::ConstCaloHitContribution conb = pSimHit.getContributions(iCont);
+                            const edm4hep::ConstMCParticle ipa = conb.getParticle();
+                            float  ien = conb.getEnergy();
+                            if( m_id_pMC_map->find(ipa.id()) == m_id_pMC_map->end() ) continue;
+                            const edm4hep::MCParticle * p_tmp = (*m_id_pMC_map)[ipa.id()]; 
+                            mcParticleToEnergyWeightMap[p_tmp] += ien;
+                        }
+                        
+                    }
+
+                    for (MCParticleToEnergyWeightMap::const_iterator mcParticleIter = mcParticleToEnergyWeightMap.begin(),
+                        mcParticleIterEnd = mcParticleToEnergyWeightMap.end(); mcParticleIter != mcParticleIterEnd; ++mcParticleIter)
+                    {
+                        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetCaloHitToMCParticleRelationship(*m_pPandora,
+                            calorimeterHitVector.at(i_calo), mcParticleIter->first, mcParticleIter->second));
+                    }
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout<<"ERROR Failed to extract calo hit to mc particle relationship: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout<<"WARNING Failed to extract calo hit to mc particle relationship " << std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout<<"DEBUG5 Failed to extract calo hit to mc particle relationships collection: " << *iter << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+
+//-------------- using sim tracker hit and tracker hit, no weight here---------------------------------------------------------------------------------------------------------------
+pandora::StatusCode MCParticleCreator::CreateTrackToMCParticleRelationships(const CollectionMaps& collectionMaps, const TrackVector &trackVector) const
+{
+    std::cout<<"Do CreateTrackToMCParticleRelationships"<<std::endl;
+    for (unsigned ik = 0; ik < trackVector.size(); ik++)
+    {
+        const edm4hep::Track *pTrack = trackVector.at(ik);
+        // Get reconstructed momentum at dca
+        const pandora::Helix helixFit(pTrack->getTrackStates(0).phi, pTrack->getTrackStates(0).D0, pTrack->getTrackStates(0).Z0, pTrack->getTrackStates(0).omega, pTrack->getTrackStates(0).tanLambda, m_bField);
+        const float recoMomentum(helixFit.GetMomentum().GetMagnitude());
+        // Use momentum magnitude to identify best mc particle
+        edm4hep::MCParticle *pBestMCParticle = NULL;
+        float bestDeltaMomentum(std::numeric_limits<float>::max());
+        try
+        {
+            for (StringVector::const_iterator iter = m_settings.m_TrackRelationCollections.begin(), iterEnd = m_settings.m_TrackRelationCollections.end(); iter != iterEnd; ++iter)
+            {
+                if(collectionMaps.collectionMap_TrkRel.find(*iter) == collectionMaps.collectionMap_TrkRel.end()) continue;
+                const std::vector<edm4hep::MCRecoTrackerAssociation>& pMCRecoTrackerAssociationCollection = (collectionMaps.collectionMap_TrkRel.find(*iter))->second;
+                for(unsigned ith=0 ; ith<pTrack->trackerHits_size(); ith++)
+                {
+                    for(unsigned ic=0; ic < pMCRecoTrackerAssociationCollection.size(); ic++)
+                    {
+                        if( pMCRecoTrackerAssociationCollection.at(ic).getRec().id() != pTrack->getTrackerHits(ith).id() ) continue;
+                        const edm4hep::ConstSimTrackerHit pSimHit = pMCRecoTrackerAssociationCollection.at(ic).getSim();
+                        const edm4hep::ConstMCParticle ipa = pSimHit.getMCParticle();
+                        if( m_id_pMC_map->find(ipa.id()) == m_id_pMC_map->end() ) continue;
+                        const float trueMomentum(pandora::CartesianVector(ipa.getMomentum()[0], ipa.getMomentum()[1], ipa.getMomentum()[2]).GetMagnitude());
+                        const float deltaMomentum(std::fabs(recoMomentum - trueMomentum));
+                        if (deltaMomentum < bestDeltaMomentum)
+                        {
+                            pBestMCParticle =const_cast<edm4hep::MCParticle*>((*m_id_pMC_map)[ipa.id()]);
+                            bestDeltaMomentum = deltaMomentum;
+                        }
+                    }
+                }
+            }
+            
+            if (NULL == pBestMCParticle) continue;
+            PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackToMCParticleRelationship(*m_pPandora, pTrack, pBestMCParticle));
+        }
+        catch (pandora::StatusCodeException &statusCodeException)
+        {
+            std::cout<<"ERROR Failed to extract track to mc particle relationship: " << statusCodeException.ToString() << std::endl;
+        }
+        catch (...)
+        {
+            std::cout<<"WARNING Failed to extract track to mc particle relationship: " << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+MCParticleCreator::Settings::Settings()
+{
+}
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/src/PandoraMatrixAlg.cpp b/Reconstruction/PFA/Pandora/MatrixPandora/src/PandoraMatrixAlg.cpp
new file mode 100644
index 00000000..07527dbb
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/src/PandoraMatrixAlg.cpp
@@ -0,0 +1,932 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+#include "GearSvc/IGearSvc.h"
+#include "PandoraMatrixAlg.h"
+#include "EventSeeder/IEventSeeder.h"
+#include "edm4hep/Vector3f.h"
+#include "edm4hep/Vector3d.h"
+#include "edm4hep/SimCalorimeterHit.h"
+#include "edm4hep/CaloHitContribution.h"
+#include "edm4hep/ClusterConst.h"
+#include "UTIL/ILDConf.h"
+#include <cmath>
+#include <algorithm>
+#include "gear/BField.h"
+#include <gear/GEAR.h>
+
+#include "LCContent.h"
+
+
+pandora::Pandora* PandoraMatrixAlg::m_pPandora=0;
+
+DECLARE_COMPONENT( PandoraMatrixAlg )
+
+template<typename T ,typename T1>
+StatusCode getCol(T & t, T1 & t1)
+{
+    try {
+        t1 = t.get();
+    }
+    catch ( GaudiException &e ) {
+        std::cout << "Collection " << t.fullKey() << " is unavailable in event "  << std::endl;
+    }
+    return StatusCode::SUCCESS;
+}
+
+
+
+PandoraMatrixAlg::PandoraMatrixAlg(const std::string& name, ISvcLocator* svcLoc)
+  : GaudiAlgorithm(name, svcLoc),
+    _nEvt(0)
+{
+ m_CollectionMaps = new CollectionMaps();
+  
+ declareProperty("ReadMCParticle"                      , m_mcParCol_r,                        "Handle of the MCParticle    input collection" );
+ declareProperty("ReadECALBarrel"                      , m_ECALBarrel_r,                      "Handle of the ECALBarrel    input collection" );
+ declareProperty("ReadECALEndcap"                      , m_ECALEndcap_r,                      "Handle of the ECALEndcap    input collection" );
+ declareProperty("ReadECALOther"                       , m_ECALOther_r,                       "Handle of the ECALOther     input collection" );
+ declareProperty("ReadHCALBarrel"                      , m_HCALBarrel_r,                      "Handle of the HCALBarrel    input collection" );
+ declareProperty("ReadHCALEndcap"                      , m_HCALEndcap_r,                      "Handle of the HCALEndcap    input collection" );
+ declareProperty("ReadHCALOther"                       , m_HCALOther_r,                       "Handle of the HCALOther     input collection" );
+ declareProperty("ReadMUON"                            , m_MUON_r,                            "Handle of the MUON          input collection" );
+ declareProperty("ReadLCAL"                            , m_LCAL_r,                            "Handle of the LCAL          input collection" );
+ declareProperty("ReadLHCAL"                           , m_LHCAL_r,                           "Handle of the LHCAL         input collection" );
+ declareProperty("ReadBCAL"                            , m_BCAL_r,                            "Handle of the BCAL          input collection" );
+ declareProperty("ReadKinkVertices"                    , m_KinkVertices_r,                    "Handle of the KinkVertices  input collection" );
+ declareProperty("ReadProngVertices"                   , m_ProngVertices_r,                   "Handle of the ProngVertices input collection" );
+ declareProperty("ReadSplitVertices"                   , m_SplitVertices_r,                   "Handle of the SplitVertices input collection" );
+ declareProperty("ReadV0Vertices"                      , m_V0Vertices_r,                      "Handle of the V0Vertices    input collection" );
+ declareProperty("ReadTracks"                          , m_MarlinTrkTracks_r,                 "Handle of the Tracks        input collection" );
+ declareProperty("MCRecoCaloAssociation"               , m_MCRecoCaloAssociation_r,           "Handle of the MCRecoCaloAssociation input collection" );
+ declareProperty("MCRecoTrackerAssociation"            , m_MCRecoTrackerAssociation_r,        "Handle of the MCRecoTrackerAssociation input collection" );
+ declareProperty("WriteClusterCollection"              , m_ClusterCollection_w,               "Handle of the ClusterCollection               output collection" );
+ declareProperty("WriteReconstructedParticleCollection", m_ReconstructedParticleCollection_w, "Handle of the ReconstructedParticleCollection output collection" );
+ declareProperty("WriteVertexCollection"               , m_VertexCollection_w,                "Handle of the VertexCollection                output collection" );
+
+}
+
+
+void PandoraMatrixAlg::FinaliseSteeringParameters(ISvcLocator* svcloc)
+{
+    // ATTN: This function seems to be necessary for operations that cannot easily be performed at construction of the processor,
+    // when the steering file is parsed e.g. the call to GEAR to get the inner bfield
+    
+    m_caloHitCreatorSettings.m_absorberRadLengthECal = m_geometryCreatorSettings.m_absorberRadLengthECal;
+    m_caloHitCreatorSettings.m_absorberIntLengthECal = m_geometryCreatorSettings.m_absorberIntLengthECal;
+    m_caloHitCreatorSettings.m_absorberRadLengthHCal = m_geometryCreatorSettings.m_absorberRadLengthHCal;
+    m_caloHitCreatorSettings.m_absorberIntLengthHCal = m_geometryCreatorSettings.m_absorberIntLengthHCal;
+    m_caloHitCreatorSettings.m_absorberRadLengthOther = m_geometryCreatorSettings.m_absorberRadLengthOther;
+    m_caloHitCreatorSettings.m_absorberIntLengthOther = m_geometryCreatorSettings.m_absorberIntLengthOther;
+
+    m_caloHitCreatorSettings.m_hCalEndCapInnerSymmetryOrder = m_geometryCreatorSettings.m_hCalEndCapInnerSymmetryOrder;
+    m_caloHitCreatorSettings.m_hCalEndCapInnerPhiCoordinate = m_geometryCreatorSettings.m_hCalEndCapInnerPhiCoordinate;
+    
+    m_trackCreatorSettings.m_prongSplitVertexCollections = m_trackCreatorSettings.m_prongVertexCollections;
+    m_trackCreatorSettings.m_prongSplitVertexCollections.insert(m_trackCreatorSettings.m_prongSplitVertexCollections.end(),
+        m_trackCreatorSettings.m_splitVertexCollections.begin(), m_trackCreatorSettings.m_splitVertexCollections.end());
+    
+    //m_settings.m_innerBField = marlin::Global::GEAR->getBField().at(gear::Vector3D(0., 0., 0.)).z();
+    IGearSvc*  iSvc = 0;
+    StatusCode sc = svcloc->service("GearSvc", iSvc, false);
+    if ( !sc ) 
+    {
+        throw "Failed to find GearSvc ...";
+    }
+    gear::GearMgr* _GEAR = iSvc->getGearMgr();
+    m_settings.m_innerBField = _GEAR->getBField().at(gear::Vector3D(0., 0., 0.)).z();
+    std::cout<<"m_innerBField="<<m_settings.m_innerBField<<std::endl;    
+    m_mcParticleCreatorSettings.m_bField = m_settings.m_innerBField;
+}
+
+
+
+
+StatusCode PandoraMatrixAlg::initialize()
+{
+
+  std::cout<<"hi init PandoraMatrixAlg"<<std::endl;
+
+  std::string s_output =m_AnaOutput; 
+  m_fout = new TFile(s_output.c_str(),"RECREATE"); 
+  m_tree = new TTree("evt","tree");
+  m_tree->Branch("m_pReco_PID"   , &m_pReco_PID);
+  m_tree->Branch("m_pReco_mass"  , &m_pReco_mass);
+  m_tree->Branch("m_pReco_energy", &m_pReco_energy);
+  m_tree->Branch("m_pReco_px"    , &m_pReco_px);
+  m_tree->Branch("m_pReco_py"    , &m_pReco_py);
+  m_tree->Branch("m_pReco_pz"    , &m_pReco_pz);
+  m_tree->Branch("m_pReco_charge", &m_pReco_charge);
+
+  m_tree->Branch("m_mc_p_size", &m_mc_p_size);
+  m_tree->Branch("m_mc_pid"   , &m_mc_pid   );
+  m_tree->Branch("m_mc_mass"  , &m_mc_mass  );
+  m_tree->Branch("m_mc_px"    , &m_mc_px    );
+  m_tree->Branch("m_mc_py"    , &m_mc_py    );
+  m_tree->Branch("m_mc_pz"    , &m_mc_pz    );
+  m_tree->Branch("m_mc_charge", &m_mc_charge);
+  m_tree->Branch("m_hasConversion", &m_hasConversion);
+
+
+  m_tree->Branch("m_hits_x", &m_hits_x);
+  m_tree->Branch("m_hits_y", &m_hits_y);
+  m_tree->Branch("m_hits_z", &m_hits_z);
+  m_tree->Branch("m_hits_E", &m_hits_E);
+
+
+  // XML file
+  m_settings.m_pandoraSettingsXmlFile =  m_PandoraSettingsXmlFile ; 
+  // Hadronic energy non-linearity correction
+  m_settings.m_inputEnergyCorrectionPoints = m_InputEnergyCorrectionPoints;
+  m_settings.m_outputEnergyCorrectionPoints = m_OutputEnergyCorrectionPoints;
+  // B-field parameters
+  m_settings.m_muonBarrelBField = m_MuonBarrelBField; 
+  m_settings.m_muonEndCapBField = m_MuonEndCapBField;
+  
+  m_trackCreatorSettings.m_trackCollections = m_TrackCollections ; 
+  m_trackCreatorSettings.m_kinkVertexCollections = m_KinkVertexCollections; 
+  m_trackCreatorSettings.m_prongVertexCollections = m_ProngVertexCollections;
+  m_trackCreatorSettings.m_splitVertexCollections = m_SplitVertexCollections;
+  m_trackCreatorSettings.m_v0VertexCollections = m_V0VertexCollections; 
+  
+  m_caloHitCreatorSettings.m_eCalCaloHitCollections = m_ECalCaloHitCollections;
+  m_caloHitCreatorSettings.m_hCalCaloHitCollections = m_HCalCaloHitCollections;
+  m_caloHitCreatorSettings.m_lCalCaloHitCollections = m_LCalCaloHitCollections;
+  m_caloHitCreatorSettings.m_lHCalCaloHitCollections = m_LHCalCaloHitCollections;
+  m_caloHitCreatorSettings.m_muonCaloHitCollections = m_MuonCaloHitCollections; 
+  m_mcParticleCreatorSettings.m_mcParticleCollections = m_MCParticleCollections;
+  m_mcParticleCreatorSettings.m_CaloHitRelationCollections = m_RelCaloHitCollections; 
+  m_mcParticleCreatorSettings.m_TrackRelationCollections = m_RelTrackCollections;
+  
+  
+  // Absorber properties
+  m_geometryCreatorSettings.m_absorberRadLengthECal = m_AbsorberRadLengthECal;
+  m_geometryCreatorSettings.m_absorberIntLengthECal = m_AbsorberIntLengthECal;
+  m_geometryCreatorSettings.m_absorberRadLengthHCal = m_AbsorberRadLengthHCal;
+  m_geometryCreatorSettings.m_absorberIntLengthHCal = m_AbsorberIntLengthHCal;
+  m_geometryCreatorSettings.m_absorberRadLengthOther = m_AbsorberRadLengthOther;
+  m_geometryCreatorSettings.m_absorberIntLengthOther = m_AbsorberIntLengthOther;
+  
+  // Name of PFO collection written by GaudiPandora
+  
+  m_pfoCreatorSettings.m_clusterCollectionName = m_ClusterCollectionName;// not used  
+  m_pfoCreatorSettings.m_pfoCollectionName = m_PFOCollectionName;//
+  m_pfoCreatorSettings.m_startVertexCollectionName = m_StartVertexCollectionName; //
+  m_pfoCreatorSettings.m_startVertexAlgName = m_StartVertexAlgorithmName;//
+   
+  m_pfoCreatorSettings.m_emStochasticTerm = m_EMStochasticTerm;
+  m_pfoCreatorSettings.m_hadStochasticTerm = m_HadStochasticTerm;
+  m_pfoCreatorSettings.m_emConstantTerm = m_EMConstantTerm;
+  m_pfoCreatorSettings.m_hadConstantTerm = m_HadConstantTerm;
+  
+  // Calibration constants
+  m_caloHitCreatorSettings.m_eCalToMip = m_ECalToMipCalibration; 
+  m_caloHitCreatorSettings.m_hCalToMip = m_HCalToMipCalibration;
+  m_caloHitCreatorSettings.m_eCalMipThreshold = m_ECalMipThreshold; 
+  m_caloHitCreatorSettings.m_muonToMip = m_MuonToMipCalibration;
+  m_caloHitCreatorSettings.m_hCalMipThreshold = m_HCalMipThreshold; 
+  m_caloHitCreatorSettings.m_eCalToEMGeV = m_ECalToEMGeVCalibration;
+  m_caloHitCreatorSettings.m_hCalToEMGeV = m_HCalToEMGeVCalibration;
+  m_caloHitCreatorSettings.m_eCalToHadGeVEndCap = m_ECalToHadGeVCalibrationEndCap;
+  m_caloHitCreatorSettings.m_eCalToHadGeVBarrel = m_ECalToHadGeVCalibrationBarrel; 
+  m_caloHitCreatorSettings.m_hCalToHadGeV = m_HCalToHadGeVCalibration;
+  m_caloHitCreatorSettings.m_muonDigitalHits = m_DigitalMuonHits; 
+  m_caloHitCreatorSettings.m_muonHitEnergy = m_MuonHitEnergy; 
+  m_caloHitCreatorSettings.m_maxHCalHitHadronicEnergy = m_MaxHCalHitHadronicEnergy; 
+  m_caloHitCreatorSettings.m_nOuterSamplingLayers = m_NOuterSamplingLayers; 
+  m_caloHitCreatorSettings.m_layersFromEdgeMaxRearDistance = m_LayersFromEdgeMaxRearDistance; 
+  
+  // Track relationship parameters
+  m_trackCreatorSettings.m_shouldFormTrackRelationships = m_ShouldFormTrackRelationships; 
+  // Initial track hit specifications
+  m_trackCreatorSettings.m_minTrackHits = m_MinTrackHits;
+  m_trackCreatorSettings.m_minFtdTrackHits = m_MinFtdTrackHits; 
+  m_trackCreatorSettings.m_maxTrackHits = m_MaxTrackHits; 
+  ////m_trackCreatorSettings.m_useOldTrackStateCalculation = m_UseOldTrackStateCalculation;
+  // Track PFO usage parameters
+  m_trackCreatorSettings.m_d0TrackCut = m_D0TrackCut; 
+  m_trackCreatorSettings.m_z0TrackCut = m_Z0TrackCut;
+  m_trackCreatorSettings.m_usingNonVertexTracks = m_UseNonVertexTracks;
+  m_trackCreatorSettings.m_usingUnmatchedNonVertexTracks = m_UseUnmatchedNonVertexTracks;
+  m_trackCreatorSettings.m_usingUnmatchedVertexTracks = m_UseUnmatchedVertexTracks;
+  m_trackCreatorSettings.m_unmatchedVertexTrackMaxEnergy = m_UnmatchedVertexTrackMaxEnergy; 
+  m_trackCreatorSettings.m_d0UnmatchedVertexTrackCut = m_D0UnmatchedVertexTrackCut; 
+  m_trackCreatorSettings.m_z0UnmatchedVertexTrackCut = m_Z0UnmatchedVertexTrackCut;
+  m_trackCreatorSettings.m_zCutForNonVertexTracks = m_ZCutForNonVertexTracks;
+  // Track "reaches ecal" parameters
+  m_trackCreatorSettings.m_reachesECalNTpcHits = m_ReachesECalNTpcHits;
+  m_trackCreatorSettings.m_reachesECalNFtdHits = m_ReachesECalNFtdHits;
+  m_trackCreatorSettings.m_reachesECalTpcOuterDistance = m_ReachesECalTpcOuterDistance;
+  m_trackCreatorSettings.m_reachesECalMinFtdLayer = m_ReachesECalMinFtdLayer;
+  m_trackCreatorSettings.m_reachesECalTpcZMaxDistance = m_ReachesECalTpcZMaxDistance;
+  m_trackCreatorSettings.m_reachesECalFtdZMaxDistance = m_ReachesECalFtdZMaxDistance;
+  m_trackCreatorSettings.m_curvatureToMomentumFactor = m_CurvatureToMomentumFactor;
+  m_trackCreatorSettings.m_minTrackECalDistanceFromIp = m_MinTrackECalDistanceFromIp;
+  // Final track quality parameters
+  m_trackCreatorSettings.m_maxTrackSigmaPOverP = m_MaxTrackSigmaPOverP;
+  m_trackCreatorSettings.m_minMomentumForTrackHitChecks = m_MinMomentumForTrackHitChecks;
+  m_trackCreatorSettings.m_tpcMembraneMaxZ = m_TpcMembraneMaxZ;
+  m_trackCreatorSettings.m_minTpcHitFractionOfExpected = m_MinTpcHitFractionOfExpected;
+  m_trackCreatorSettings.m_minFtdHitsForTpcHitFraction = m_MinFtdHitsForTpcHitFraction;
+  m_trackCreatorSettings.m_maxTpcInnerRDistance = m_MaxTpcInnerRDistance;
+  
+  
+  // Additional geometry parameters
+  m_geometryCreatorSettings.m_eCalEndCapInnerSymmetryOrder = m_ECalEndCapInnerSymmetryOrder;
+  m_geometryCreatorSettings.m_eCalEndCapInnerPhiCoordinate = m_ECalEndCapInnerPhiCoordinate;
+  m_geometryCreatorSettings.m_eCalEndCapOuterSymmetryOrder = m_ECalEndCapOuterSymmetryOrder;
+  m_geometryCreatorSettings.m_eCalEndCapOuterPhiCoordinate = m_ECalEndCapOuterPhiCoordinate;
+  m_geometryCreatorSettings.m_hCalEndCapInnerSymmetryOrder = m_HCalEndCapInnerSymmetryOrder;
+  m_geometryCreatorSettings.m_hCalEndCapInnerPhiCoordinate = m_HCalEndCapInnerPhiCoordinate;
+  m_geometryCreatorSettings.m_hCalEndCapOuterSymmetryOrder = m_HCalEndCapOuterSymmetryOrder;
+  m_geometryCreatorSettings.m_hCalEndCapOuterPhiCoordinate = m_HCalEndCapOuterPhiCoordinate;
+  m_geometryCreatorSettings.m_hCalRingInnerSymmetryOrder = m_HCalRingInnerSymmetryOrder;
+  m_geometryCreatorSettings.m_hCalRingInnerPhiCoordinate = m_HCalRingInnerPhiCoordinate;
+  m_geometryCreatorSettings.m_hCalRingOuterSymmetryOrder = m_HCalRingOuterSymmetryOrder; 
+  m_geometryCreatorSettings.m_hCalRingOuterPhiCoordinate = m_HCalRingOuterPhiCoordinate;
+  
+  // For Strip Splitting method and also for hybrid ECAL
+  m_caloHitCreatorSettings.m_stripSplittingOn = m_StripSplittingOn;
+  m_caloHitCreatorSettings.m_useEcalScLayers = m_UseEcalScLayers;
+  // Parameters for hybrid ECAL
+  // Energy to MIP for Si-layers and Sc-layers, respectively.
+  //Si
+  m_caloHitCreatorSettings.m_eCalSiToMip = m_ECalSiToMipCalibration;
+  //Sc
+  m_caloHitCreatorSettings.m_eCalScToMip = m_ECalScToMipCalibration;
+  // MipThreshold for Si-layers and Sc-layers, respectively.
+  // Si
+  m_caloHitCreatorSettings.m_eCalSiMipThreshold = m_ECalSiMipThreshold;
+  //Sc
+  m_caloHitCreatorSettings.m_eCalScMipThreshold = m_ECalScMipThreshold;
+  // EcalToEM for Si-layers and Sc-layers, respectively.
+  //Si
+  m_caloHitCreatorSettings.m_eCalSiToEMGeV = m_ECalSiToEMGeVCalibration;
+  //Sc
+  m_caloHitCreatorSettings.m_eCalScToEMGeV = m_ECalScToEMGeVCalibration;
+  // EcalToHad for Si-layers and Sc-layers of the endcaps, respectively.
+  //Si
+  m_caloHitCreatorSettings.m_eCalSiToHadGeVEndCap = m_ECalSiToHadGeVCalibrationEndCap;
+  //Sc
+  m_caloHitCreatorSettings.m_eCalScToHadGeVEndCap = m_ECalScToHadGeVCalibrationEndCap;
+  // EcalToHad for Si-layers and Sc-layers of the barrel, respectively.
+  //Si
+  m_caloHitCreatorSettings.m_eCalSiToHadGeVBarrel = m_ECalSiToHadGeVCalibrationBarrel;
+  //Sc
+  m_caloHitCreatorSettings.m_eCalScToHadGeVBarrel = m_ECalScToHadGeVCalibrationBarrel;
+
+  try
+  {
+      ISvcLocator* svcloc = serviceLocator();
+      this->FinaliseSteeringParameters(svcloc);
+      m_pPandora = new pandora::Pandora();
+      m_pMCParticleCreator = new MCParticleCreator(m_mcParticleCreatorSettings, m_pPandora);
+      m_pGeometryCreator = new GeometryCreator(m_geometryCreatorSettings, m_pPandora);
+      PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pGeometryCreator->CreateGeometry(svcloc));
+      m_pCaloHitCreator = new CaloHitCreator(m_caloHitCreatorSettings, m_pPandora, svcloc, 0);
+      m_pTrackCreator = new TrackCreator(m_trackCreatorSettings, m_pPandora, svcloc);
+      m_pPfoCreator = new PfoCreator(m_pfoCreatorSettings, m_pPandora);
+      PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->RegisterUserComponents());
+      PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::ReadSettings(*m_pPandora, m_settings.m_pandoraSettingsXmlFile));
+  }
+  catch (pandora::StatusCodeException &statusCodeException)
+  {
+      std::cout << "Failed to initialize gaudi pandora: " << statusCodeException.ToString() << std::endl;
+      throw statusCodeException;
+  }
+  catch (...)
+  {
+      std::cout << "Failed to initialize gaudi pandora: unrecognized exception" << std::endl;
+      throw;
+  }
+
+
+  return GaudiAlgorithm::initialize();
+}
+
+StatusCode PandoraMatrixAlg::execute()
+{
+    
+    try
+    {
+        std::cout<<"execute PandoraMatrixAlg"<<std::endl;
+        
+        updateMap();
+        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pMCParticleCreator->CreateMCParticles(*m_CollectionMaps));
+        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pCaloHitCreator->CreateCaloHits(*m_CollectionMaps));
+        //PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pMCParticleCreator->CreateCaloHitToMCParticleRelationships(*m_CollectionMaps, m_pCaloHitCreator->GetCalorimeterHitVector() ));
+        //PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pTrackCreator->CreateTrackAssociations(*m_CollectionMaps));
+        //PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pTrackCreator->CreateTracks(*m_CollectionMaps));
+        //PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pMCParticleCreator->CreateTrackToMCParticleRelationships(*m_CollectionMaps, m_pTrackCreator->GetTrackVector() ));
+        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::ProcessEvent(*m_pPandora));
+        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, m_pPfoCreator->CreateParticleFlowObjects(*m_CollectionMaps, m_ClusterCollection_w, m_ReconstructedParticleCollection_w, m_VertexCollection_w));
+        
+        StatusCode sc0 = CreateMCRecoParticleAssociation();
+        StatusCode sc = Ana();
+
+        PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::Reset(*m_pPandora));
+        this->Reset();
+    }
+    catch (pandora::StatusCodeException &statusCodeException)
+    {
+        std::cout << "Gaudi pandora failed to process event: " << statusCodeException.ToString() << std::endl;
+        throw statusCodeException;
+    }
+    catch (...)
+    {
+        std::cout << "Gaudi pandora failed to process event: unrecognized exception" << std::endl;
+        throw;
+    }
+  
+  info() << "PandoraMatrixAlg Processed " << _nEvt << " events " << endmsg;
+  _nEvt ++ ;
+
+  return StatusCode::SUCCESS;
+}
+
+StatusCode PandoraMatrixAlg::finalize()
+{
+  info() << "Finalized. Processed " << _nEvt << " events " <<",saved tree with entries="<<m_tree->GetEntries()<< endmsg;
+  m_fout->cd();
+  m_tree->Write();
+  m_fout->Close();
+  delete m_pPandora;
+  delete m_pGeometryCreator;
+  delete m_pCaloHitCreator;
+  delete m_pTrackCreator;
+  delete m_pMCParticleCreator;
+  delete m_pPfoCreator;
+  return GaudiAlgorithm::finalize();
+}
+
+
+
+
+pandora::StatusCode PandoraMatrixAlg::RegisterUserComponents() const
+{
+    
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, LCContent::RegisterAlgorithms(*m_pPandora));
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, LCContent::RegisterBasicPlugins(*m_pPandora));
+
+    
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, LCContent::RegisterBFieldPlugin(*m_pPandora,
+        m_settings.m_innerBField, m_settings.m_muonBarrelBField, m_settings.m_muonEndCapBField));
+
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, LCContent::RegisterNonLinearityEnergyCorrection(*m_pPandora,
+        "NonLinearity", pandora::HADRONIC, m_settings.m_inputEnergyCorrectionPoints, m_settings.m_outputEnergyCorrectionPoints));
+    
+    
+    /*
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::RegisterAlgorithmFactory(*m_pPandora,
+        "ExternalClustering", new ExternalClusteringAlgorithm::Factory));
+    */
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+
+void PandoraMatrixAlg::Reset()
+{
+    m_pCaloHitCreator->Reset();
+    m_pTrackCreator->Reset();
+    m_pMCParticleCreator->Reset();
+
+    std::vector<int>()  .swap(m_pReco_PID   );
+    std::vector<float>().swap(m_pReco_mass);
+    std::vector<float>().swap(m_pReco_energy);
+    std::vector<float>().swap(m_pReco_px);
+    std::vector<float>().swap(m_pReco_py);
+    std::vector<float>().swap(m_pReco_pz);
+    std::vector<float>().swap(m_pReco_charge);
+
+    std::vector<int>()  .swap(m_mc_p_size);
+    std::vector<int>()  .swap(m_mc_pid   );
+    std::vector<float>().swap(m_mc_mass  );
+    std::vector<float>().swap(m_mc_px    );
+    std::vector<float>().swap(m_mc_py    );
+    std::vector<float>().swap(m_mc_pz    );
+    std::vector<float>().swap(m_mc_charge);
+
+    std::vector<float>()  .swap(m_hits_x);
+    std::vector<float>()  .swap(m_hits_y);
+    std::vector<float>()  .swap(m_hits_z);
+    std::vector<float>()  .swap(m_hits_E);
+
+    m_hasConversion = 0;
+
+    m_CollectionMaps->clear();
+}
+
+const pandora::Pandora *PandoraMatrixAlg::GetPandora() const
+{
+    if (NULL == m_pPandora)
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_NOT_INITIALIZED);
+
+    return m_pPandora;
+}
+PandoraMatrixAlg::Settings::Settings() :
+    m_innerBField(3.5f),
+    m_muonBarrelBField(-1.5f),
+    m_muonEndCapBField(0.01f)
+{
+}
+CollectionMaps::CollectionMaps()
+{
+}
+void CollectionMaps::clear()
+{
+CollectionMap_MC.clear();
+CollectionMap_CaloHit.clear();
+CollectionMap_Vertex.clear();
+CollectionMap_Track.clear();
+collectionMap_MC.clear();
+collectionMap_CaloHit.clear();
+collectionMap_Vertex.clear();
+collectionMap_Track.clear();
+collectionMap_CaloRel.clear();
+collectionMap_TrkRel.clear();
+}
+
+StatusCode PandoraMatrixAlg::updateMap()
+{
+        const edm4hep::MCParticleCollection*     MCParticle = nullptr;
+        const edm4hep::CalorimeterHitCollection* ECALBarrel = nullptr;        
+        const edm4hep::CalorimeterHitCollection* ECALEndcap = nullptr; 
+        const edm4hep::CalorimeterHitCollection* ECALOther  = nullptr; 
+        const edm4hep::CalorimeterHitCollection* HCALBarrel = nullptr; 
+        const edm4hep::CalorimeterHitCollection* HCALEndcap = nullptr; 
+        const edm4hep::CalorimeterHitCollection* HCALOther  = nullptr; 
+        const edm4hep::CalorimeterHitCollection* MUON       = nullptr; 
+        const edm4hep::CalorimeterHitCollection* LCAL       = nullptr; 
+        const edm4hep::CalorimeterHitCollection* LHCAL      = nullptr; 
+        const edm4hep::CalorimeterHitCollection* BCAL       = nullptr; 
+        const edm4hep::VertexCollection* KinkVertices       = nullptr; 
+        const edm4hep::VertexCollection* ProngVertices      = nullptr; 
+        const edm4hep::VertexCollection* SplitVertices      = nullptr; 
+        const edm4hep::VertexCollection* V0Vertices         = nullptr; 
+        const edm4hep::TrackCollection*  MarlinTrkTracks    = nullptr; 
+        const edm4hep::MCRecoCaloAssociationCollection*  mcRecoCaloAssociation    = nullptr; 
+        const edm4hep::MCRecoTrackerAssociationCollection*  mcRecoTrackerAssociation    = nullptr; 
+        StatusCode sc = StatusCode::SUCCESS;
+        sc =  getCol(m_mcParCol_r  , MCParticle );
+        sc =  getCol(m_ECALBarrel_r, ECALBarrel );
+        sc =  getCol(m_ECALEndcap_r, ECALEndcap );
+        sc =  getCol(m_ECALOther_r , ECALOther  );
+        sc =  getCol(m_HCALBarrel_r, HCALBarrel );
+        sc =  getCol(m_HCALEndcap_r, HCALEndcap );
+        sc =  getCol(m_HCALOther_r , HCALOther  );
+        sc =  getCol(m_MUON_r      , MUON       );
+        sc =  getCol(m_LCAL_r      , LCAL       );
+        sc =  getCol(m_LHCAL_r     , LHCAL      );
+        sc =  getCol(m_BCAL_r      , BCAL       );        
+        sc =  getCol(m_KinkVertices_r  , KinkVertices );        
+        sc =  getCol(m_ProngVertices_r , ProngVertices);        
+        sc =  getCol(m_SplitVertices_r , SplitVertices);        
+        sc =  getCol(m_V0Vertices_r    , V0Vertices   );        
+        sc =  getCol(m_MarlinTrkTracks_r , MarlinTrkTracks   );        
+        sc =  getCol(m_MCRecoCaloAssociation_r , mcRecoCaloAssociation   );        
+        sc =  getCol(m_MCRecoTrackerAssociation_r , mcRecoTrackerAssociation);        
+
+        if (NULL != MCParticle   )  
+        {
+            std::vector<edm4hep::MCParticle> v_mc;
+            m_CollectionMaps->CollectionMap_MC ["MCParticle"] = MCParticle ;
+            m_CollectionMaps->collectionMap_MC ["MCParticle"] = v_mc;
+            for(unsigned int i=0 ; i< MCParticle->size(); i++) m_CollectionMaps->collectionMap_MC ["MCParticle"].push_back(MCParticle->at(i));
+        }
+        if (NULL != ECALBarrel   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["ECALBarrel"] = ECALBarrel ;
+            m_CollectionMaps->collectionMap_CaloHit["ECALBarrel"] = v_cal ;
+            for(unsigned int i=0 ; i< ECALBarrel->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["ECALBarrel"].push_back(ECALBarrel->at(i));
+        }
+        if (NULL != ECALEndcap   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["ECALEndcap"] = ECALEndcap ;
+            m_CollectionMaps->collectionMap_CaloHit["ECALEndcap"] = v_cal ;
+            for(unsigned int i=0 ; i< ECALEndcap->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["ECALEndcap"].push_back(ECALEndcap->at(i));
+        }
+        if (NULL != ECALOther   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["ECALOther"] = ECALOther ;
+            m_CollectionMaps->collectionMap_CaloHit["ECALOther"] = v_cal ;
+            for(unsigned int i=0 ; i< ECALOther->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["ECALOther"].push_back(ECALOther->at(i));
+        }
+        if (NULL != HCALBarrel   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["HCALBarrel"] = HCALBarrel ;
+            m_CollectionMaps->collectionMap_CaloHit["HCALBarrel"] = v_cal ;
+            for(unsigned int i=0 ; i< HCALBarrel->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["HCALBarrel"].push_back(HCALBarrel->at(i));
+        }
+        if (NULL != HCALEndcap   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["HCALEndcap"] = HCALEndcap ;
+            m_CollectionMaps->collectionMap_CaloHit["HCALEndcap"] = v_cal ;
+            for(unsigned int i=0 ; i< HCALEndcap->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["HCALEndcap"].push_back(HCALEndcap->at(i));
+        }
+        if (NULL != HCALOther   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["HCALOther"] = HCALOther ;
+            m_CollectionMaps->collectionMap_CaloHit["HCALOther"] = v_cal ;
+            for(unsigned int i=0 ; i< HCALOther->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["HCALOther"].push_back(HCALOther->at(i));
+        }
+        if (NULL != MUON   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["MUON"] = MUON ;
+            m_CollectionMaps->collectionMap_CaloHit["MUON"] = v_cal ;
+            for(unsigned int i=0 ; i< MUON->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["MUON"].push_back(MUON->at(i));
+        }
+        if (NULL != LCAL   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["LCAL"] = LCAL ;
+            m_CollectionMaps->collectionMap_CaloHit["LCAL"] = v_cal ;
+            for(unsigned int i=0 ; i< LCAL->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["LCAL"].push_back(LCAL->at(i));
+        }
+        if (NULL != LHCAL   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["LHCAL"] = LHCAL ;
+            m_CollectionMaps->collectionMap_CaloHit["LHCAL"] = v_cal ;
+            for(unsigned int i=0 ; i< LHCAL->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["LHCAL"].push_back(LHCAL->at(i));
+        }
+        if (NULL != BCAL   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            m_CollectionMaps->CollectionMap_CaloHit["BCAL"] = BCAL ;
+            m_CollectionMaps->collectionMap_CaloHit["BCAL"] = v_cal ;
+            for(unsigned int i=0 ; i< BCAL->size(); i++) m_CollectionMaps->collectionMap_CaloHit ["BCAL"].push_back(BCAL->at(i));
+        }
+        if (NULL != KinkVertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            m_CollectionMaps->CollectionMap_Vertex["KinkVertices"] = KinkVertices ;
+            m_CollectionMaps->collectionMap_Vertex["KinkVertices"] = v_cal ;
+            for(unsigned int i=0 ; i< KinkVertices->size(); i++) m_CollectionMaps->collectionMap_Vertex ["KinkVertices"].push_back(KinkVertices->at(i));
+        }
+        if (NULL != ProngVertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            m_CollectionMaps->CollectionMap_Vertex["ProngVertices"] = ProngVertices ;
+            m_CollectionMaps->collectionMap_Vertex["ProngVertices"] = v_cal ;
+            for(unsigned int i=0 ; i< ProngVertices->size(); i++) m_CollectionMaps->collectionMap_Vertex ["ProngVertices"].push_back(ProngVertices->at(i));
+        }
+        if (NULL != SplitVertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            m_CollectionMaps->CollectionMap_Vertex["SplitVertices"] = SplitVertices ;
+            m_CollectionMaps->collectionMap_Vertex["SplitVertices"] = v_cal ;
+            for(unsigned int i=0 ; i< SplitVertices->size(); i++) m_CollectionMaps->collectionMap_Vertex ["SplitVertices"].push_back(SplitVertices->at(i));
+        }
+        if (NULL != V0Vertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            m_CollectionMaps->CollectionMap_Vertex["V0Vertices"] = V0Vertices ;
+            m_CollectionMaps->collectionMap_Vertex["V0Vertices"] = v_cal ;
+            for(unsigned int i=0 ; i< V0Vertices->size(); i++) m_CollectionMaps->collectionMap_Vertex ["V0Vertices"].push_back(V0Vertices->at(i));
+        }
+        if (NULL != MarlinTrkTracks   )
+        {
+            std::vector<edm4hep::Track> v_cal;
+            m_CollectionMaps->CollectionMap_Track["MarlinTrkTracks"] = MarlinTrkTracks ;
+            m_CollectionMaps->collectionMap_Track["MarlinTrkTracks"] = v_cal ;
+            for(unsigned int i=0 ; i< MarlinTrkTracks->size(); i++) m_CollectionMaps->collectionMap_Track ["MarlinTrkTracks"].push_back(MarlinTrkTracks->at(i));
+        }
+        if (NULL != mcRecoCaloAssociation )
+        {
+            std::vector<edm4hep::MCRecoCaloAssociation> v_cal;
+            m_CollectionMaps->collectionMap_CaloRel["RecoCaloAssociation"] = v_cal ;
+            for(unsigned int i=0 ; i< mcRecoCaloAssociation->size(); i++) m_CollectionMaps->collectionMap_CaloRel ["RecoCaloAssociation"].push_back(mcRecoCaloAssociation->at(i));
+        }
+        
+        else
+        {
+            if (NULL != MCParticle   )
+            {
+                for(unsigned int i=0 ; i< MCParticle->size(); i++)
+                {
+                    if(MCParticle->at(i).parents_size()==0)
+                    {
+                        std::cout<<"create recoCaloAssociation by hand now"<<std::endl;
+                        for(std::map<std::string, std::vector<edm4hep::CalorimeterHit> >::iterator iter = m_CollectionMaps->collectionMap_CaloHit.begin(); iter != m_CollectionMaps->collectionMap_CaloHit.end(); iter++)
+                        {
+                            std::string prefix = "RecoCaloAssociation_";
+                            std::string key = prefix + iter->first;
+                            std::cout<<"create for "<<key<<std::endl;
+                            std::vector<edm4hep::MCRecoCaloAssociation> v_cal;
+                            m_CollectionMaps->collectionMap_CaloRel[key] = v_cal ;
+                            for(std::vector<edm4hep::CalorimeterHit>::iterator it=iter->second.begin(); it != iter->second.end(); it ++)
+                            {
+                                edm4hep::SimCalorimeterHit sim_hit( it->getCellID(), it->getEnergy(), it->getPosition() );
+                                edm4hep::CaloHitContribution conb ( MCParticle->at(i).getPDG(), it->getEnergy(), 0, it->getPosition() ); 
+                                conb.setParticle( MCParticle->at(i) );
+                                sim_hit.addContribution(conb);
+                                edm4hep::MCRecoCaloAssociation calo_association;
+                                calo_association.setRec(*it);
+                                calo_association.setSim(sim_hit);
+                                m_CollectionMaps->collectionMap_CaloRel[key].push_back(calo_association); 
+                            }
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        
+        if (NULL != mcRecoTrackerAssociation )
+        {
+            std::vector<edm4hep::MCRecoTrackerAssociation> v_cal;
+            m_CollectionMaps->collectionMap_TrkRel["RecoTrackerAssociation"] = v_cal ;
+            for(unsigned int i=0 ; i< mcRecoTrackerAssociation->size(); i++) m_CollectionMaps->collectionMap_TrkRel ["RecoTrackerAssociation"].push_back(mcRecoTrackerAssociation->at(i));
+        }
+    return StatusCode::SUCCESS;
+}
+
+
+StatusCode PandoraMatrixAlg::updateMap(CollectionMaps & tmp_map)
+{
+        const edm4hep::MCParticleCollection*     MCParticle = nullptr;
+        const edm4hep::CalorimeterHitCollection* ECALBarrel = nullptr;        
+        const edm4hep::CalorimeterHitCollection* ECALEndcap = nullptr; 
+        const edm4hep::CalorimeterHitCollection* ECALOther  = nullptr; 
+        const edm4hep::CalorimeterHitCollection* HCALBarrel = nullptr; 
+        const edm4hep::CalorimeterHitCollection* HCALEndcap = nullptr; 
+        const edm4hep::CalorimeterHitCollection* HCALOther  = nullptr; 
+        const edm4hep::CalorimeterHitCollection* MUON       = nullptr; 
+        const edm4hep::CalorimeterHitCollection* LCAL       = nullptr; 
+        const edm4hep::CalorimeterHitCollection* LHCAL      = nullptr; 
+        const edm4hep::CalorimeterHitCollection* BCAL       = nullptr; 
+        const edm4hep::VertexCollection* KinkVertices       = nullptr; 
+        const edm4hep::VertexCollection* ProngVertices      = nullptr; 
+        const edm4hep::VertexCollection* SplitVertices      = nullptr; 
+        const edm4hep::VertexCollection* V0Vertices         = nullptr; 
+        const edm4hep::TrackCollection*  MarlinTrkTracks    = nullptr; 
+        const edm4hep::MCRecoCaloAssociationCollection*  mcRecoCaloAssociation    = nullptr; 
+        const edm4hep::MCRecoTrackerAssociationCollection*  mcRecoTrackerAssociation    = nullptr; 
+        StatusCode sc = StatusCode::SUCCESS;
+        sc =  getCol(m_mcParCol_r  , MCParticle );
+        sc =  getCol(m_ECALBarrel_r, ECALBarrel );
+        sc =  getCol(m_ECALEndcap_r, ECALEndcap );
+        sc =  getCol(m_ECALOther_r , ECALOther  );
+        sc =  getCol(m_HCALBarrel_r, HCALBarrel );
+        sc =  getCol(m_HCALEndcap_r, HCALEndcap );
+        sc =  getCol(m_HCALOther_r , HCALOther  );
+        sc =  getCol(m_MUON_r      , MUON       );
+        sc =  getCol(m_LCAL_r      , LCAL       );
+        sc =  getCol(m_LHCAL_r     , LHCAL      );
+        sc =  getCol(m_BCAL_r      , BCAL       );        
+        sc =  getCol(m_KinkVertices_r  , KinkVertices );        
+        sc =  getCol(m_ProngVertices_r , ProngVertices);        
+        sc =  getCol(m_SplitVertices_r , SplitVertices);        
+        sc =  getCol(m_V0Vertices_r    , V0Vertices   );        
+        sc =  getCol(m_MarlinTrkTracks_r , MarlinTrkTracks   );        
+        sc =  getCol(m_MCRecoCaloAssociation_r , mcRecoCaloAssociation   );        
+        sc =  getCol(m_MCRecoTrackerAssociation_r , mcRecoTrackerAssociation  );        
+
+        if (NULL != MCParticle   )  
+        {
+            std::vector<edm4hep::MCParticle> v_mc;
+            tmp_map.CollectionMap_MC ["MCParticle"] = MCParticle ;
+            tmp_map.collectionMap_MC ["MCParticle"] = v_mc;
+            for(unsigned int i=0 ; i< MCParticle->size(); i++) tmp_map.collectionMap_MC ["MCParticle"].push_back(MCParticle->at(i));
+        }
+        if (NULL != ECALBarrel   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["ECALBarrel"] = ECALBarrel ;
+            tmp_map.collectionMap_CaloHit["ECALBarrel"] = v_cal ;
+            for(unsigned int i=0 ; i< ECALBarrel->size(); i++) tmp_map.collectionMap_CaloHit ["ECALBarrel"].push_back(ECALBarrel->at(i));
+        }
+        if (NULL != ECALEndcap   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["ECALEndcap"] = ECALEndcap ;
+            tmp_map.collectionMap_CaloHit["ECALEndcap"] = v_cal ;
+            for(unsigned int i=0 ; i< ECALEndcap->size(); i++) tmp_map.collectionMap_CaloHit ["ECALEndcap"].push_back(ECALEndcap->at(i));
+        }
+        if (NULL != ECALOther   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["ECALOther"] = ECALOther ;
+            tmp_map.collectionMap_CaloHit["ECALOther"] = v_cal ;
+            for(unsigned int i=0 ; i< ECALOther->size(); i++) tmp_map.collectionMap_CaloHit ["ECALOther"].push_back(ECALOther->at(i));
+        }
+        if (NULL != HCALBarrel   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["HCALBarrel"] = HCALBarrel ;
+            tmp_map.collectionMap_CaloHit["HCALBarrel"] = v_cal ;
+            for(unsigned int i=0 ; i< HCALBarrel->size(); i++) tmp_map.collectionMap_CaloHit ["HCALBarrel"].push_back(HCALBarrel->at(i));
+        }
+        if (NULL != HCALEndcap   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["HCALEndcap"] = HCALEndcap ;
+            tmp_map.collectionMap_CaloHit["HCALEndcap"] = v_cal ;
+            for(unsigned int i=0 ; i< HCALEndcap->size(); i++) tmp_map.collectionMap_CaloHit ["HCALEndcap"].push_back(HCALEndcap->at(i));
+        }
+        if (NULL != HCALOther   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["HCALOther"] = HCALOther ;
+            tmp_map.collectionMap_CaloHit["HCALOther"] = v_cal ;
+            for(unsigned int i=0 ; i< HCALOther->size(); i++) tmp_map.collectionMap_CaloHit ["HCALOther"].push_back(HCALOther->at(i));
+        }
+        if (NULL != MUON   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["MUON"] = MUON ;
+            tmp_map.collectionMap_CaloHit["MUON"] = v_cal ;
+            for(unsigned int i=0 ; i< MUON->size(); i++) tmp_map.collectionMap_CaloHit ["MUON"].push_back(MUON->at(i));
+        }
+        if (NULL != LCAL   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["LCAL"] = LCAL ;
+            tmp_map.collectionMap_CaloHit["LCAL"] = v_cal ;
+            for(unsigned int i=0 ; i< LCAL->size(); i++) tmp_map.collectionMap_CaloHit ["LCAL"].push_back(LCAL->at(i));
+        }
+        if (NULL != LHCAL   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["LHCAL"] = LHCAL ;
+            tmp_map.collectionMap_CaloHit["LHCAL"] = v_cal ;
+            for(unsigned int i=0 ; i< LHCAL->size(); i++) tmp_map.collectionMap_CaloHit ["LHCAL"].push_back(LHCAL->at(i));
+        }
+        if (NULL != BCAL   )
+        {
+            std::vector<edm4hep::CalorimeterHit> v_cal;
+            tmp_map.CollectionMap_CaloHit["BCAL"] = BCAL ;
+            tmp_map.collectionMap_CaloHit["BCAL"] = v_cal ;
+            for(unsigned int i=0 ; i< BCAL->size(); i++) tmp_map.collectionMap_CaloHit ["BCAL"].push_back(BCAL->at(i));
+        }
+        if (NULL != KinkVertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            tmp_map.CollectionMap_Vertex["KinkVertices"] = KinkVertices ;
+            tmp_map.collectionMap_Vertex["KinkVertices"] = v_cal ;
+            for(unsigned int i=0 ; i< KinkVertices->size(); i++) tmp_map.collectionMap_Vertex ["KinkVertices"].push_back(KinkVertices->at(i));
+        }
+        if (NULL != ProngVertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            tmp_map.CollectionMap_Vertex["ProngVertices"] = ProngVertices ;
+            tmp_map.collectionMap_Vertex["ProngVertices"] = v_cal ;
+            for(unsigned int i=0 ; i< ProngVertices->size(); i++) tmp_map.collectionMap_Vertex ["ProngVertices"].push_back(ProngVertices->at(i));
+        }
+        if (NULL != SplitVertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            tmp_map.CollectionMap_Vertex["SplitVertices"] = SplitVertices ;
+            tmp_map.collectionMap_Vertex["SplitVertices"] = v_cal ;
+            for(unsigned int i=0 ; i< SplitVertices->size(); i++) tmp_map.collectionMap_Vertex ["SplitVertices"].push_back(SplitVertices->at(i));
+        }
+        if (NULL != V0Vertices   )
+        {
+            std::vector<edm4hep::Vertex> v_cal;
+            tmp_map.CollectionMap_Vertex["V0Vertices"] = V0Vertices ;
+            tmp_map.collectionMap_Vertex["V0Vertices"] = v_cal ;
+            for(unsigned int i=0 ; i< V0Vertices->size(); i++) tmp_map.collectionMap_Vertex ["V0Vertices"].push_back(V0Vertices->at(i));
+        }
+        if (NULL != MarlinTrkTracks   )
+        {
+            std::vector<edm4hep::Track> v_cal;
+            tmp_map.CollectionMap_Track["MarlinTrkTracks"] = MarlinTrkTracks ;
+            tmp_map.collectionMap_Track["MarlinTrkTracks"] = v_cal ;
+            for(unsigned int i=0 ; i< MarlinTrkTracks->size(); i++) tmp_map.collectionMap_Track ["MarlinTrkTracks"].push_back(MarlinTrkTracks->at(i));
+        }
+        if (NULL != mcRecoCaloAssociation )
+        {
+            std::vector<edm4hep::MCRecoCaloAssociation> v_cal;
+            tmp_map.collectionMap_CaloRel["RecoCaloAssociation"] = v_cal ;
+            for(unsigned int i=0 ; i< mcRecoCaloAssociation->size(); i++) tmp_map.collectionMap_CaloRel ["RecoCaloAssociation"].push_back(mcRecoCaloAssociation->at(i));
+        }
+        if (NULL != mcRecoTrackerAssociation )
+        {
+            std::vector<edm4hep::MCRecoTrackerAssociation> v_cal;
+            tmp_map.collectionMap_TrkRel["RecoTrackerAssociation"] = v_cal ;
+            for(unsigned int i=0 ; i< mcRecoTrackerAssociation->size(); i++) tmp_map.collectionMap_TrkRel ["RecoTrackerAssociation"].push_back(mcRecoTrackerAssociation->at(i));
+        }
+    return StatusCode::SUCCESS;
+}
+
+
+StatusCode PandoraMatrixAlg::Ana()
+{
+    int n_current = m_tree->GetEntries()+1;
+    const edm4hep::ReconstructedParticleCollection* reco_col = m_ReconstructedParticleCollection_w.get();
+    const edm4hep::MCRecoParticleAssociationCollection* reco_associa_col = m_MCRecoParticleAssociation_w.get();
+    std::cout<<"reco_col size="<<reco_col->size()<<std::endl;
+    for(int i=0; i<reco_col->size();i++)
+    {
+        std::cout<<"reco="<<i<<std::endl;
+        const edm4hep::ReconstructedParticle pReco = reco_col->at(i);
+        const float px = pReco.getMomentum()[0];
+        const float py = pReco.getMomentum()[1];
+        const float pz = pReco.getMomentum()[2];
+        const float energy = pReco.getEnergy();
+        const float mass = pReco.getMass();
+        const float charge = pReco.getCharge();
+        const int type = pReco.getType();
+        std::cout<<"MYDBUG evt="<<n_current<<",rec i="<<i<<",particleId="<<type<<",mass="<<mass<<",charge="<<charge<<",energy="<<energy<<",px="<<px<<",py="<<py<<",pz="<<pz<<std::endl;
+        m_pReco_PID.push_back(type);
+        m_pReco_mass.push_back(mass);
+        m_pReco_charge.push_back(charge);
+        m_pReco_energy.push_back(energy);
+        m_pReco_px.push_back(px);
+        m_pReco_py.push_back(py);
+        m_pReco_pz.push_back(pz);
+        for(int j=0; j < reco_associa_col->size(); j++)
+        {
+            if(reco_associa_col->at(j).getRec().id() != pReco.id() ) continue;
+            std::cout<<"MC pid ="<<reco_associa_col->at(j).getSim().getPDG()<<", px="<<reco_associa_col->at(j).getSim().getMomentum()[0]<<", py="<<reco_associa_col->at(j).getSim().getMomentum()[1]<<",pz="<<reco_associa_col->at(j).getSim().getMomentum()[2]<<std::endl;
+        }
+    }
+    const edm4hep::MCParticleCollection*     MCParticle = nullptr;
+    StatusCode sc = StatusCode::SUCCESS;
+    sc =  getCol(m_mcParCol_r  , MCParticle );
+    if (NULL != MCParticle   )  
+    { 
+        for(unsigned int i=0 ; i< MCParticle->size(); i++)
+        {
+            m_mc_p_size.push_back(MCParticle->at(i).parents_size());
+            m_mc_pid   .push_back(MCParticle->at(i).getPDG());
+            m_mc_mass  .push_back(MCParticle->at(i).getMass());
+            m_mc_px    .push_back(MCParticle->at(i).getMomentum()[0]);
+            m_mc_py    .push_back(MCParticle->at(i).getMomentum()[1]);
+            m_mc_pz    .push_back(MCParticle->at(i).getMomentum()[2]);
+            m_mc_charge.push_back(MCParticle->at(i).getCharge());
+            //for(unsigned int j =0 ; j< MCParticle->at(i).daughters_size(); j++) da_pids.push_back( MCParticle->at(i).getDaughters(j).getPDG());
+            if(MCParticle->at(i).parents_size()==0) std::cout<<"MYDBUG evt="<<n_current<<", mc i="<<i<<",px="<<MCParticle->at(i).getMomentum()[0]<<",py="<<MCParticle->at(i).getMomentum()[1]<<",pz="<<MCParticle->at(i).getMomentum()[2]<<std::endl;
+            if (MCParticle->at(i).getPDG() != 22) continue;
+            int hasEm = 0;
+            int hasEp = 0;
+            for(unsigned int j =0 ; j< MCParticle->at(i).daughters_size(); j++)
+            {
+                if      (MCParticle->at(i).getDaughters(j).getPDG() ==  11 ) hasEm=1;
+                else if (MCParticle->at(i).getDaughters(j).getPDG() == -11 ) hasEp=1;
+            }
+            if(hasEm && hasEp) m_hasConversion=1;
+        }
+    }
+    //sanity check calo info
+    const edm4hep::CalorimeterHitCollection*     CaloCol = nullptr;
+    sc =  getCol(m_ECALBarrel_r, CaloCol);
+    if (NULL != CaloCol   )  
+    { 
+        for(unsigned int i=0 ; i< CaloCol->size(); i++)
+        {
+            m_hits_x.push_back(CaloCol->at(i).getPosition()[0]);
+            m_hits_y.push_back(CaloCol->at(i).getPosition()[1]);
+            m_hits_z.push_back(CaloCol->at(i).getPosition()[2]);
+            m_hits_E.push_back(CaloCol->at(i).getEnergy()     );
+        }
+    }
+
+    m_tree->Fill();
+    return StatusCode::SUCCESS;
+}
+
+// create simple MCRecoParticleAssociation using calorimeter hit only
+StatusCode PandoraMatrixAlg::CreateMCRecoParticleAssociation()
+{
+    edm4hep::MCRecoParticleAssociationCollection* pMCRecoParticleAssociationCollection  = m_MCRecoParticleAssociation_w.createAndPut();
+    const edm4hep::ReconstructedParticleCollection* reco_col = m_ReconstructedParticleCollection_w.get();
+    std::cout<<"CreateMCRecoParticleAssociation, reco_col size="<<reco_col->size()<<std::endl;
+    for(int i=0; i<reco_col->size();i++)
+    {
+        std::map<int, edm4hep::ConstMCParticle> mc_map;
+        const edm4hep::ReconstructedParticle pReco = reco_col->at(i);
+        for(int j=0; j < pReco.clusters_size(); j++)
+        {
+            edm4hep::ConstCluster cluster = pReco.getClusters(j);
+            for(int k=0; k < cluster.hits_size(); k++)
+            {
+                edm4hep::ConstCalorimeterHit hit = cluster.getHits(k);
+                for(std::map<std::string, std::vector<edm4hep::MCRecoCaloAssociation> >::iterator iter = m_CollectionMaps->collectionMap_CaloRel.begin(); iter != m_CollectionMaps->collectionMap_CaloRel.end(); iter++)
+                {
+                    for(std::vector<edm4hep::MCRecoCaloAssociation>::iterator it = iter->second.begin(); it != iter->second.end(); it ++)
+                    {
+                        if(it->getRec().id() != hit.id()) continue;
+                        for(std::vector<edm4hep::ConstCaloHitContribution>::const_iterator itc = it->getSim().contributions_begin(); itc != it->getSim().contributions_end(); itc++)
+                        {
+                            if(mc_map.find(itc->getParticle().id()) == mc_map.end()) mc_map[itc->getParticle().id()] = itc->getParticle() ;
+                        }
+                    }
+                }
+            }
+        }
+        for(std::map<int, edm4hep::ConstMCParticle>::iterator it = mc_map.begin(); it != mc_map.end(); it ++)
+        {      
+            edm4hep::MCRecoParticleAssociation association = pMCRecoParticleAssociationCollection->create();
+            association.setRec(pReco);
+            association.setSim(it->second);
+        }
+    }
+    return StatusCode::SUCCESS;
+}
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/src/PfoCreator.cpp b/Reconstruction/PFA/Pandora/MatrixPandora/src/PfoCreator.cpp
new file mode 100644
index 00000000..1c6069c5
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/src/PfoCreator.cpp
@@ -0,0 +1,429 @@
+/**
+ *  @file   MarlinPandora/src/PfoCreator.cc
+ * 
+ *  @brief  Implementation of the pfo creator class.
+ * 
+ *  $Log: $
+ */
+
+//#include "CalorimeterHitType.h"
+
+#include "Api/PandoraApi.h"
+
+#include "Objects/Cluster.h"
+#include "Objects/ParticleFlowObject.h"
+#include "Objects/Track.h"
+
+#include "Pandora/PdgTable.h"
+#include "PfoCreator.h"
+#include "PandoraMatrixAlg.h"
+
+#include <algorithm>
+#include <cmath>
+
+PfoCreator::PfoCreator(const Settings &settings, const pandora::Pandora *const pPandora) :
+    m_settings(settings),
+    m_pPandora(pPandora)
+{
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+PfoCreator::~PfoCreator()
+{
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode PfoCreator::CreateParticleFlowObjects(CollectionMaps& collectionMaps, DataHandle<edm4hep::ClusterCollection>& _pClusterCollection, DataHandle<edm4hep::ReconstructedParticleCollection>& _pReconstructedParticleCollection, DataHandle<edm4hep::VertexCollection>& _pStartVertexCollection)
+{
+    m_collectionMaps = &collectionMaps;
+    edm4hep::ClusterCollection* pClusterCollection                              = _pClusterCollection.createAndPut();
+    edm4hep::ReconstructedParticleCollection* pReconstructedParticleCollection  = _pReconstructedParticleCollection.createAndPut();
+    edm4hep::VertexCollection* pStartVertexCollection                           = _pStartVertexCollection.createAndPut();
+ 
+    const pandora::PfoList *pPandoraPfoList = NULL;
+    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::GetCurrentPfoList(*m_pPandora, pPandoraPfoList));
+
+
+    //IMPL::LCFlagImpl lcFlagImpl(pClusterCollection->getFlag());
+    //lcFlagImpl.setBit(LCIO::CLBIT_HITS);
+    //pClusterCollection->setFlag(lcFlagImpl.getFlag());
+
+    pandora::StringVector subDetectorNames;
+    this->InitialiseSubDetectorNames(subDetectorNames);
+    //pClusterCollection->parameters().setValues("ClusterSubdetectorNames", subDetectorNames);
+
+    // Create lcio "reconstructed particles" from the pandora "particle flow objects"
+    std::cout<<"pPandoraPfoList size="<<pPandoraPfoList->size()<<std::endl;
+    for (pandora::PfoList::const_iterator pIter = pPandoraPfoList->begin(), pIterEnd = pPandoraPfoList->end(); pIter != pIterEnd; ++pIter)
+    {
+        const pandora::ParticleFlowObject *const pPandoraPfo(*pIter);
+        //IMPL::ReconstructedParticleImpl *const pReconstructedParticle(new ReconstructedParticleImpl());
+        edm4hep::ReconstructedParticle pReconstructedParticle0 = pReconstructedParticleCollection->create();
+        edm4hep::ReconstructedParticle* pReconstructedParticle = &pReconstructedParticle0;
+
+        const bool hasTrack(!pPandoraPfo->GetTrackList().empty());
+        const pandora::ClusterList &clusterList(pPandoraPfo->GetClusterList());
+
+        //std::cout<<"ClusterList size="<<clusterList.size()<<std::endl;
+        float clustersTotalEnergy(0.f);
+        pandora::CartesianVector referencePoint(0.f, 0.f, 0.f), clustersWeightedPosition(0.f, 0.f, 0.f);
+        for (pandora::ClusterList::const_iterator cIter = clusterList.begin(), cIterEnd = clusterList.end(); cIter != cIterEnd; ++cIter)
+        {
+            const pandora::Cluster *const pPandoraCluster(*cIter);
+            pandora::CaloHitList pandoraCaloHitList;
+            pPandoraCluster->GetOrderedCaloHitList().FillCaloHitList(pandoraCaloHitList);
+            pandoraCaloHitList.insert(pandoraCaloHitList.end(), pPandoraCluster->GetIsolatedCaloHitList().begin(), pPandoraCluster->GetIsolatedCaloHitList().end());
+
+            pandora::FloatVector hitE, hitX, hitY, hitZ;
+            //IMPL::ClusterImpl *const p_Cluster(new ClusterImpl());
+            edm4hep::Cluster p_Cluster0 = pClusterCollection->create();
+            edm4hep::Cluster* p_Cluster = &p_Cluster0;
+            this->SetClusterSubDetectorEnergies(subDetectorNames, p_Cluster, pandoraCaloHitList, hitE, hitX, hitY, hitZ);
+
+            float clusterCorrectEnergy(0.f);
+            this->SetClusterEnergyAndError(pPandoraPfo, pPandoraCluster, p_Cluster, clusterCorrectEnergy);
+
+            pandora::CartesianVector clusterPosition(0.f, 0.f, 0.f);
+            const unsigned int nHitsInCluster(pandoraCaloHitList.size());
+            this->SetClusterPositionAndError(nHitsInCluster, hitE, hitX, hitY, hitZ, p_Cluster, clusterPosition);
+
+            if (!hasTrack)
+            {
+                clustersWeightedPosition += clusterPosition * clusterCorrectEnergy;
+                clustersTotalEnergy += clusterCorrectEnergy;
+            }
+
+            //pClusterCollection->addElement(p_Cluster);
+            edm4hep::ConstCluster p_ClusterCon = *p_Cluster;
+            pReconstructedParticle->addCluster(p_ClusterCon);
+        }
+
+        if (!hasTrack)
+        {
+            if (clustersTotalEnergy < std::numeric_limits<float>::epsilon())
+            {
+                std::cout<<"WARNING PfoCreator::CreateParticleFlowObjects: invalid cluster energy " << clustersTotalEnergy << std::endl;
+                throw pandora::StatusCodeException(pandora::STATUS_CODE_FAILURE);
+            }
+            else
+            {
+                referencePoint = clustersWeightedPosition * (1.f / clustersTotalEnergy);
+            }
+        }
+        else
+        {
+            PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->CalculateTrackBasedReferencePoint(pPandoraPfo, referencePoint));
+        }
+
+        this->SetRecoParticleReferencePoint(referencePoint, pReconstructedParticle);
+        this->AddTracksToRecoParticle(pPandoraPfo, pReconstructedParticle);
+        this->SetRecoParticlePropertiesFromPFO(pPandoraPfo, pReconstructedParticle);
+
+        edm4hep::Vertex pStartVertex0 = pStartVertexCollection->create();
+        edm4hep::Vertex* pStartVertex = &pStartVertex0;
+        //pStartVertex->setAlgorithmType(m_settings.m_startVertexAlgName.c_str());
+        pStartVertex->setAlgorithmType(0);
+        const float ref_value[3] = {referencePoint.GetX(),referencePoint.GetY(),referencePoint.GetZ()};
+        pStartVertex->setPosition(edm4hep::Vector3f(ref_value));
+        pStartVertex->setAssociatedParticle(*pReconstructedParticle);
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void PfoCreator::InitialiseSubDetectorNames(pandora::StringVector &subDetectorNames) const
+{
+    subDetectorNames.push_back("ecal");
+    subDetectorNames.push_back("hcal");
+    subDetectorNames.push_back("yoke");
+    subDetectorNames.push_back("lcal");
+    subDetectorNames.push_back("lhcal");
+    subDetectorNames.push_back("bcal");
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void PfoCreator::SetClusterSubDetectorEnergies(const pandora::StringVector &subDetectorNames, edm4hep::Cluster *const p_Cluster,
+    const pandora::CaloHitList &pandoraCaloHitList, pandora::FloatVector &hitE, pandora::FloatVector &hitX, pandora::FloatVector &hitY,
+    pandora::FloatVector &hitZ) const
+{
+    for (pandora::CaloHitList::const_iterator hIter = pandoraCaloHitList.begin(), hIterEnd = pandoraCaloHitList.end(); hIter != hIterEnd; ++hIter)
+    {
+        const pandora::CaloHit *const pPandoraCaloHit(*hIter);
+        edm4hep::CalorimeterHit *const pCalorimeterHit0 = (edm4hep::CalorimeterHit*)(pPandoraCaloHit->GetParentAddress());
+        const edm4hep::CalorimeterHit pCalorimeterHit = *pCalorimeterHit0;
+        
+        p_Cluster->addHit(pCalorimeterHit);
+
+        const float caloHitEnergy(pCalorimeterHit.getEnergy());
+        hitE.push_back(caloHitEnergy);
+        hitX.push_back(pCalorimeterHit.getPosition()[0]);
+        hitY.push_back(pCalorimeterHit.getPosition()[1]);
+        hitZ.push_back(pCalorimeterHit.getPosition()[2]);
+        /*
+        std::vector<float> &subDetectorEnergies = p_Cluster->subdetectorEnergies();
+        subDetectorEnergies.resize(subDetectorNames.size());
+
+        switch (CHT(pCalorimeterHit->getType()).caloID())
+        {
+            case CHT::ecal:  subDetectorEnergies[ECAL_INDEX ] += caloHitEnergy; break;
+            case CHT::hcal:  subDetectorEnergies[HCAL_INDEX ] += caloHitEnergy; break;
+            case CHT::yoke:  subDetectorEnergies[YOKE_INDEX ] += caloHitEnergy; break;
+            case CHT::lcal:  subDetectorEnergies[LCAL_INDEX ] += caloHitEnergy; break;
+            case CHT::lhcal: subDetectorEnergies[LHCAL_INDEX] += caloHitEnergy; break;
+            case CHT::bcal:  subDetectorEnergies[BCAL_INDEX ] += caloHitEnergy; break;
+            default: streamlog_out(WARNING) << "PfoCreator::SetClusterSubDetectorEnergies: no subdetector found for hit with type: " << pCalorimeterHit->getType() << std::endl;
+        }
+        */
+    }
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void PfoCreator::SetClusterEnergyAndError(const pandora::ParticleFlowObject *const pPandoraPfo, const pandora::Cluster *const pPandoraCluster, 
+    edm4hep::Cluster *const p_Cluster, float &clusterCorrectEnergy) const
+{
+    const bool isEmShower((pandora::PHOTON == pPandoraPfo->GetParticleId()) || (pandora::E_MINUS == std::abs(pPandoraPfo->GetParticleId())));
+    clusterCorrectEnergy = (isEmShower ? pPandoraCluster->GetCorrectedElectromagneticEnergy(*m_pPandora) : pPandoraCluster->GetCorrectedHadronicEnergy(*m_pPandora));
+
+    if (clusterCorrectEnergy < std::numeric_limits<float>::epsilon())
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_FAILURE);
+
+    const float stochasticTerm(isEmShower ? m_settings.m_emStochasticTerm : m_settings.m_hadStochasticTerm); 
+    const float constantTerm(isEmShower ? m_settings.m_emConstantTerm : m_settings.m_hadConstantTerm);
+    const float energyError(std::sqrt(stochasticTerm * stochasticTerm / clusterCorrectEnergy + constantTerm * constantTerm) * clusterCorrectEnergy);
+
+    p_Cluster->setEnergy(clusterCorrectEnergy);
+    p_Cluster->setEnergyError(energyError);
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void PfoCreator::SetClusterPositionAndError(const unsigned int nHitsInCluster, pandora::FloatVector &hitE, pandora::FloatVector &hitX, 
+    pandora::FloatVector &hitY, pandora::FloatVector &hitZ, edm4hep::Cluster *const p_Cluster, pandora::CartesianVector &clusterPositionVec) const
+{
+    ClusterShapes *const pClusterShapes(new ClusterShapes(nHitsInCluster, hitE.data(), hitX.data(), hitY.data(), hitZ.data()));
+
+    try
+    {
+        p_Cluster->setPhi(std::atan2(pClusterShapes->getEigenVecInertia()[1], pClusterShapes->getEigenVecInertia()[0]));
+        p_Cluster->setITheta(std::acos(pClusterShapes->getEigenVecInertia()[2]));
+        p_Cluster->setPosition(pClusterShapes->getCentreOfGravity());
+        //ATTN these two lines below would only compile with ilcsoft HEAD V2015-10-13 and above
+        //p_Cluster->setPositionError(pClusterShapes->getCenterOfGravityErrors());
+        //p_Cluster->setDirectionError(pClusterShapes->getEigenVecInertiaErrors());
+        clusterPositionVec.SetValues(pClusterShapes->getCentreOfGravity()[0], pClusterShapes->getCentreOfGravity()[1], pClusterShapes->getCentreOfGravity()[2]);
+    }
+    catch (...)
+    {
+        std::cout<<"WARNING PfoCreator::SetClusterPositionAndError: unidentified exception caught." << std::endl;
+    }
+
+    delete pClusterShapes;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode PfoCreator::CalculateTrackBasedReferencePoint(const pandora::ParticleFlowObject *const pPandoraPfo, pandora::CartesianVector &referencePoint) const
+{
+    const pandora::TrackList &trackList(pPandoraPfo->GetTrackList());
+
+    float totalTrackMomentumAtDca(0.f), totalTrackMomentumAtStart(0.f);
+    pandora::CartesianVector referencePointAtDCAWeighted(0.f, 0.f, 0.f), referencePointAtStartWeighted(0.f, 0.f, 0.f);
+
+    bool hasSiblings(false);
+    for (pandora::TrackList::const_iterator tIter = trackList.begin(), tIterEnd = trackList.end(); tIter != tIterEnd; ++tIter)
+    {
+        const pandora::Track *const pPandoraTrack(*tIter);
+
+        if (!this->IsValidParentTrack(pPandoraTrack, trackList))
+            continue;
+
+        if (this->HasValidSiblingTrack(pPandoraTrack, trackList))
+        {
+            // Presence of sibling tracks typically represents a conversion
+            const pandora::CartesianVector &trackStartPoint((pPandoraTrack->GetTrackStateAtStart()).GetPosition());
+            const float trackStartMomentum(((pPandoraTrack->GetTrackStateAtStart()).GetMomentum()).GetMagnitude());
+            referencePointAtStartWeighted += trackStartPoint * trackStartMomentum;
+            totalTrackMomentumAtStart += trackStartMomentum;
+            hasSiblings = true;
+        }
+        else
+        {
+            const edm4hep::Track *const pLcioTrack0 = (edm4hep::Track*)(pPandoraTrack->GetParentAddress());
+            const edm4hep::Track pLcioTrack = *pLcioTrack0;
+
+            const float z0(pPandoraTrack->GetZ0());
+            pandora::CartesianVector intersectionPoint(0.f, 0.f, 0.f);
+
+            //intersectionPoint.SetValues(pLcioTrack->getD0() * std::cos(pLcioTrack->getPhi()), pLcioTrack->getD0() * std::sin(pLcioTrack->getPhi()), z0);
+            if(pLcioTrack.trackStates_size()==0) throw "zero trackStates size find";
+            intersectionPoint.SetValues(pLcioTrack.getTrackStates(0).D0 * std::cos(pLcioTrack.getTrackStates(0).phi), pLcioTrack.getTrackStates(0).D0 * std::sin(pLcioTrack.getTrackStates(0).phi), z0);
+            const float trackMomentumAtDca((pPandoraTrack->GetMomentumAtDca()).GetMagnitude());
+            referencePointAtDCAWeighted += intersectionPoint * trackMomentumAtDca;
+            totalTrackMomentumAtDca += trackMomentumAtDca;
+        }
+    }
+
+    if (hasSiblings)
+    {
+        if (totalTrackMomentumAtStart < std::numeric_limits<float>::epsilon())
+        {
+            std::cout<<" WARNING PfoCreator::CalculateTrackBasedReferencePoint: invalid track momentum " << totalTrackMomentumAtStart << std::endl;
+            throw pandora::StatusCodeException(pandora::STATUS_CODE_FAILURE);
+        }
+        else
+        {
+            referencePoint = referencePointAtStartWeighted * (1.f / totalTrackMomentumAtStart);
+        }
+    }
+    else
+    {
+        if (totalTrackMomentumAtDca < std::numeric_limits<float>::epsilon())
+        {
+            std::cout<<"WARNING PfoCreator::CalculateTrackBasedReferencePoint: invalid track momentum " << totalTrackMomentumAtDca << std::endl;
+            throw pandora::StatusCodeException(pandora::STATUS_CODE_FAILURE);
+        }
+        else
+        {
+            referencePoint = referencePointAtDCAWeighted * (1.f / totalTrackMomentumAtDca);
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+bool PfoCreator::IsValidParentTrack(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const
+{
+    const pandora::TrackList &parentTrackList(pPandoraTrack->GetParentList());
+
+    for (pandora::TrackList::const_iterator iter = parentTrackList.begin(), iterEnd = parentTrackList.end(); iter != iterEnd; ++iter)
+    {
+        if (allTrackList.end() != std::find(allTrackList.begin(), allTrackList.end(), *iter))
+            continue;
+
+        // ATTN This track must have a parent not in the all track list; still use it if it is the closest to the ip
+        std::cout<<"WARNING PfoCreator::IsValidParentTrack: mismatch in track relationship information, use information as available " << std::endl;
+
+        if (this->IsClosestTrackToIP(pPandoraTrack, allTrackList))
+            return true;
+
+        return false;
+    }
+
+    // Ideal case: All parents are associated to same pfo
+    return true;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+bool PfoCreator::HasValidSiblingTrack(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const
+{
+    const pandora::TrackList &siblingTrackList(pPandoraTrack->GetSiblingList());
+
+    for (pandora::TrackList::const_iterator iter = siblingTrackList.begin(), iterEnd = siblingTrackList.end(); iter != iterEnd; ++iter)
+    {
+        if (allTrackList.end() != std::find(allTrackList.begin(), allTrackList.end(), *iter))
+            continue;
+
+        // ATTN This track must have a sibling not in the all track list; still use it if it has a second sibling that is in the list
+        std::cout<<"WARNING PfoCreator::HasValidSiblingTrack: mismatch in track relationship information, use information as available " << std::endl;
+
+        if (this->AreAnyOtherSiblingsInList(pPandoraTrack, allTrackList))
+            return true;
+
+        return false;
+    }
+
+    // Ideal case: All siblings associated to same pfo
+    return true;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+bool PfoCreator::IsClosestTrackToIP(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const 
+{
+    const pandora::Track *pClosestTrack(NULL);
+    float closestTrackDisplacement(std::numeric_limits<float>::max()); 
+
+    for (pandora::TrackList::const_iterator iter = allTrackList.begin(), iterEnd = allTrackList.end(); iter != iterEnd; ++iter)
+    {
+        const pandora::Track *const pTrack(*iter);
+        const float trialTrackDisplacement(pTrack->GetTrackStateAtStart().GetPosition().GetMagnitude());
+
+        if (trialTrackDisplacement < closestTrackDisplacement)
+        {
+            closestTrackDisplacement = trialTrackDisplacement;
+            pClosestTrack = pTrack;
+        }
+    }
+
+    return (pPandoraTrack == pClosestTrack);
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+bool PfoCreator::AreAnyOtherSiblingsInList(const pandora::Track *const pPandoraTrack, const pandora::TrackList &allTrackList) const
+{
+    const pandora::TrackList &siblingTrackList(pPandoraTrack->GetSiblingList());
+
+    for (pandora::TrackList::const_iterator iter = siblingTrackList.begin(), iterEnd = siblingTrackList.end(); iter != iterEnd; ++iter)
+    {
+        if (allTrackList.end() != std::find(allTrackList.begin(), allTrackList.end(), *iter))
+            return true;
+    }
+
+    return false;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void PfoCreator::SetRecoParticleReferencePoint(const pandora::CartesianVector &referencePoint, edm4hep::ReconstructedParticle *const pReconstructedParticle) const
+{
+    const float referencePointArray[3] = {referencePoint.GetX(), referencePoint.GetY(), referencePoint.GetZ()};
+    pReconstructedParticle->setReferencePoint(referencePointArray);
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void PfoCreator::AddTracksToRecoParticle(const pandora::ParticleFlowObject *const pPandoraPfo, edm4hep::ReconstructedParticle *const pReconstructedParticle) const
+{
+    const pandora::TrackList &trackList(pPandoraPfo->GetTrackList());
+
+    for (pandora::TrackList::const_iterator tIter = trackList.begin(), tIterEnd = trackList.end(); tIter != tIterEnd; ++tIter)
+    {
+        const pandora::Track *const pTrack(*tIter);
+        const edm4hep::Track *const pLcioTrack0 = (edm4hep::Track*)(pTrack->GetParentAddress());
+        const edm4hep::Track pLcioTrack = *pLcioTrack0;
+        pReconstructedParticle->addTrack(pLcioTrack);
+
+    }
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void PfoCreator::SetRecoParticlePropertiesFromPFO(const pandora::ParticleFlowObject *const pPandoraPfo, edm4hep::ReconstructedParticle *const pReconstructedParticle) const
+{
+    const float momentum[3] = {pPandoraPfo->GetMomentum().GetX(), pPandoraPfo->GetMomentum().GetY(), pPandoraPfo->GetMomentum().GetZ()};
+    pReconstructedParticle->setMomentum(momentum);
+    pReconstructedParticle->setEnergy(pPandoraPfo->GetEnergy());
+    pReconstructedParticle->setMass(pPandoraPfo->GetMass());
+    pReconstructedParticle->setCharge(pPandoraPfo->GetCharge());
+    pReconstructedParticle->setType(pPandoraPfo->GetParticleId());
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+PfoCreator::Settings::Settings():
+    m_emStochasticTerm(0.17f),
+    m_hadStochasticTerm(0.6f),
+    m_emConstantTerm(0.01f),
+    m_hadConstantTerm(0.03f)
+{
+}
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/src/TrackCreator.cpp b/Reconstruction/PFA/Pandora/MatrixPandora/src/TrackCreator.cpp
new file mode 100644
index 00000000..1a3c0f15
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/src/TrackCreator.cpp
@@ -0,0 +1,987 @@
+/**
+ *  @file   MarlinPandora/src/TrackCreator.cc
+ * 
+ *  @brief  Implementation of the track creator class.
+ * 
+ *  $Log: $
+ */
+
+
+#include "UTIL/ILDConf.h"
+
+#include "edm4hep/Vertex.h"
+#include "edm4hep/ReconstructedParticle.h"
+
+#include "gear/BField.h"
+#include "gear/CalorimeterParameters.h"
+#include "gear/PadRowLayout2D.h"
+#include "gear/TPCParameters.h"
+#include "gear/FTDParameters.h"
+#include "gear/FTDLayerLayout.h"
+
+#include "GaudiKernel/IService.h"
+#include "GearSvc/IGearSvc.h"
+#include "PandoraMatrixAlg.h"
+
+#include "TrackCreator.h"
+#include "Pandora/PdgTable.h"
+
+#include <algorithm>
+#include <cmath>
+#include <limits>
+
+TrackCreator::TrackCreator(const Settings &settings, const pandora::Pandora *const pPandora, ISvcLocator* svcloc) :
+    m_settings(settings),
+    m_pPandora(pPandora)
+{
+
+    IGearSvc*  iSvc = 0;
+    StatusCode sc = svcloc->service("GearSvc", iSvc, false);
+    if ( !sc ) 
+    {
+        throw "Failed to find GearSvc ...";
+    }
+    _GEAR = iSvc->getGearMgr();
+
+
+    m_bField                  = (_GEAR->getBField().at(gear::Vector3D(0., 0., 0.)).z());
+    m_tpcInnerR               = (_GEAR->getTPCParameters().getPadLayout().getPlaneExtent()[0]);
+    m_tpcOuterR               = (_GEAR->getTPCParameters().getPadLayout().getPlaneExtent()[1]);
+    m_tpcMaxRow               = (_GEAR->getTPCParameters().getPadLayout().getNRows());
+    m_tpcZmax                 = (_GEAR->getTPCParameters().getMaxDriftLength());
+    m_eCalBarrelInnerSymmetry = (_GEAR->getEcalBarrelParameters().getSymmetryOrder());
+    m_eCalBarrelInnerPhi0     = (_GEAR->getEcalBarrelParameters().getPhi0());
+    m_eCalBarrelInnerR        = (_GEAR->getEcalBarrelParameters().getExtent()[0]);
+    m_eCalEndCapInnerZ        = (_GEAR->getEcalEndcapParameters().getExtent()[2]);
+    // fg: FTD description in GEAR has changed ...
+    try
+    {
+        m_ftdInnerRadii = _GEAR->getGearParameters("FTD").getDoubleVals("FTDInnerRadius");
+        m_ftdOuterRadii = _GEAR->getGearParameters("FTD").getDoubleVals("FTDOuterRadius");
+        m_ftdZPositions = _GEAR->getGearParameters("FTD").getDoubleVals("FTDZCoordinate");
+        m_nFtdLayers = m_ftdZPositions.size();
+    }
+    catch (gear::UnknownParameterException &)
+    {
+        const gear::FTDLayerLayout &ftdLayerLayout(_GEAR->getFTDParameters().getFTDLayerLayout());
+        std::cout << " Filling FTD parameters from gear::FTDParameters - n layers: " << ftdLayerLayout.getNLayers() << std::endl;
+
+        for(unsigned int i = 0, N = ftdLayerLayout.getNLayers(); i < N; ++i)
+        {
+            // Create a disk to represent even number petals front side
+            m_ftdInnerRadii.push_back(ftdLayerLayout.getSensitiveRinner(i));
+            m_ftdOuterRadii.push_back(ftdLayerLayout.getMaxRadius(i));
+
+            // Take the mean z position of the staggered petals
+            const double zpos(ftdLayerLayout.getZposition(i));
+            m_ftdZPositions.push_back(zpos);
+            std::cout << "     layer " << i << " - mean z position = " << zpos << std::endl;
+        }
+
+        m_nFtdLayers = m_ftdZPositions.size() ;
+    }
+
+    // Check tpc parameters
+    if ((std::fabs(m_tpcZmax) < std::numeric_limits<float>::epsilon()) || (std::fabs(m_tpcInnerR) < std::numeric_limits<float>::epsilon())
+        || (std::fabs(m_tpcOuterR - m_tpcInnerR) < std::numeric_limits<float>::epsilon()))
+    {
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+    }
+
+    m_cosTpc = m_tpcZmax / std::sqrt(m_tpcZmax * m_tpcZmax + m_tpcInnerR * m_tpcInnerR);
+
+    // Check ftd parameters
+    if ((0 == m_nFtdLayers) || (m_nFtdLayers != m_ftdInnerRadii.size()) || (m_nFtdLayers != m_ftdOuterRadii.size()))
+    {
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+    }
+
+    for (unsigned int iFtdLayer = 0; iFtdLayer < m_nFtdLayers; ++iFtdLayer)
+    {
+        if ((std::fabs(m_ftdOuterRadii[iFtdLayer]) < std::numeric_limits<float>::epsilon()) ||
+            (std::fabs(m_ftdInnerRadii[iFtdLayer]) < std::numeric_limits<float>::epsilon()))
+        {
+            throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+        }
+    }
+
+    m_tanLambdaFtd = m_ftdZPositions[0] / m_ftdOuterRadii[0];
+
+    // Calculate etd and set parameters
+    // fg: make SET and ETD optional - as they might not be in the model ...
+    try
+    {
+        const DoubleVector &etdZPositions(_GEAR->getGearParameters("ETD").getDoubleVals("ETDLayerZ"));
+        const DoubleVector &setInnerRadii(_GEAR->getGearParameters("SET").getDoubleVals("SETLayerRadius"));
+
+        if (etdZPositions.empty() || setInnerRadii.empty())
+            throw pandora::StatusCodeException(pandora::STATUS_CODE_INVALID_PARAMETER);
+
+        m_minEtdZPosition = *(std::min_element(etdZPositions.begin(), etdZPositions.end()));
+        m_minSetRadius = *(std::min_element(setInnerRadii.begin(), setInnerRadii.end()));
+    }
+    catch(gear::UnknownParameterException &)
+    {
+        std::cout << "Warnning, ETDLayerZ or SETLayerRadius parameters missing from GEAR parameters!" << std::endl
+                               << "     -> both will be set to " << std::numeric_limits<float>::quiet_NaN() << std::endl;
+
+        //fg: Set them to NAN, so that they cannot be used to set   trackParameters.m_reachesCalorimeter = true;
+        m_minEtdZPosition = std::numeric_limits<float>::quiet_NaN();
+        m_minSetRadius = std::numeric_limits<float>::quiet_NaN();
+    }
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+TrackCreator::~TrackCreator()
+{
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode TrackCreator::CreateTrackAssociations(const CollectionMaps& collectionMaps)
+{
+//    Don't use it now, because the Vertex.getAssociatedParticle() doesn't work for LCIO to plcio transfer
+//    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->ExtractKinks(collectionMaps));
+//    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->ExtractProngsAndSplits(const CollectionMaps& collectionMaps));
+//    PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, this->ExtractV0s(const CollectionMaps& collectionMaps));
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+/*
+pandora::StatusCode TrackCreator::ExtractKinks(const CollectionMaps& collectionMaps)
+{
+    std::cout<<"start TrackCreator::ExtractKinks:"<<std::endl;
+    for (StringVector::const_iterator iter = m_settings.m_kinkVertexCollections.begin(), iterEnd = m_settings.m_kinkVertexCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.CollectionMap_Vertex.find(*iter) == collectionMaps.CollectionMap_Vertex.end()) { std::cout<<"not find "<<(*iter)<<std::endl; continue;}
+        try
+        {
+            const edm4hep::VertexCollection *pKinkCollection = (collectionMaps.CollectionMap_Vertex.find(*iter))->second;
+
+            for (int i = 0, iMax = pKinkCollection->size(); i < iMax; ++i)
+            {
+                try
+                {
+                    const edm4hep::Vertex  pVertex0 = pKinkCollection->at(i);
+                    const edm4hep::Vertex* pVertex  = &(pVertex0);
+
+                    if (NULL == pVertex) throw ("Collection type mismatch");
+
+                    std::cout<<"pVertex0 getChi2="<<pVertex0.getChi2()<<std::endl;
+                    std::cout<<"pVertex getChi2="<<pVertex->getChi2()<<std::endl;
+                    std::cout<<"Hi 0 "<<std::endl;
+                    const plcio::ConstReconstructedParticle pReconstructedParticle0 = pVertex0.getAssociatedParticle();
+                    std::cout<<"Hi 1"<<std::endl;
+                    //EVENT::ReconstructedParticle *pReconstructedParticle = pVertex->getAssociatedParticle();
+                    const plcio::ConstReconstructedParticle pReconstructedParticle = pVertex->getAssociatedParticle();
+                    std::cout<<"Hi 2:"<<&pReconstructedParticle<<std::endl;
+                    //const EVENT::TrackVec &trackVec(pReconstructedParticle->getTracks());
+                    //plcio::ConstTrack trackVec = pReconstructedParticle.getTracks();
+
+                    std::cout<<"pReconstructedParticle0 en="<<pReconstructedParticle0.getEnergy()<<std::endl;
+                    std::cout<<"pReconstructedParticle en="<<pReconstructedParticle.getEnergy()<<std::endl;
+                    //if (this->IsConflictingRelationship(trackVec))continue;
+                    if (this->IsConflictingRelationship(pReconstructedParticle))continue;
+
+                    //const int vertexPdgCode(pReconstructedParticle->getType());
+                    const int vertexPdgCode(pReconstructedParticle.getType());
+
+                    // Extract the kink vertex information
+                    //for (unsigned int iTrack = 0, nTracks = trackVec.size(); iTrack < nTracks; ++iTrack)
+                    //for (unsigned int iTrack = 0, nTracks = trackVec.tracks_size(); iTrack < nTracks; ++iTrack)
+                    for (unsigned int iTrack = 0, nTracks = pReconstructedParticle.tracks_size(); iTrack < nTracks; ++iTrack)
+                    {
+                        //EVENT::Track *pTrack = trackVec[iTrack];
+                        //plcio::ConstTrack pTrack = trackVec.getTracks(iTrack);
+                        plcio::ConstTrack pTrack = pReconstructedParticle.getTracks(iTrack);
+                        //(0 == iTrack) ? m_parentTrackList.insert(pTrack) : m_daughterTrackList.insert(pTrack);
+                        (0 == iTrack) ? m_parentTrackList.insert(pTrack.id()) : m_daughterTrackList.insert(pTrack.id());
+                       //std::cout << "KinkTrack " << iTrack << ", nHits " << pTrack.getTrackerHits().size() << std::endl;
+                       std::cout << "KinkTrack " << iTrack << ", nHits " << pTrack.trackerHits_size() << std::endl;
+
+                        int trackPdgCode = pandora::UNKNOWN_PARTICLE_TYPE;
+
+                        if (0 == iTrack)
+                        {
+                            trackPdgCode = vertexPdgCode;
+                        }
+                        else
+                        {
+                            switch (vertexPdgCode)
+                            {
+                            case pandora::PI_PLUS :
+                            case pandora::K_PLUS :
+                                trackPdgCode = pandora::MU_PLUS;
+                                break;
+                            case pandora::PI_MINUS :
+                            case pandora::K_MINUS :
+                                trackPdgCode = pandora::MU_MINUS;
+                                break;
+                            case pandora::HYPERON_MINUS_BAR :
+                            case pandora::SIGMA_PLUS :
+                                trackPdgCode = pandora::PI_PLUS;
+                                break;
+                            case pandora::SIGMA_MINUS :
+                            case pandora::HYPERON_MINUS :
+                                trackPdgCode = pandora::PI_PLUS;
+                                break;
+                            default :
+                                //(pTrack->getOmega() > 0) ? trackPdgCode = pandora::PI_PLUS : trackPdgCode = pandora::PI_MINUS;
+                                (pTrack.getTrackStates(0).omega > 0) ? trackPdgCode = pandora::PI_PLUS : trackPdgCode = pandora::PI_MINUS;
+                                break;
+                            }
+                        }
+
+                        m_trackToPidMap.insert(TrackToPidMap::value_type(pTrack, trackPdgCode));
+
+                        if (0 == m_settings.m_shouldFormTrackRelationships)
+                            continue;
+
+                        // Make track parent-daughter relationships
+                        if (0 == iTrack)
+                        {
+                            for (unsigned int jTrack = iTrack + 1; jTrack < nTracks; ++jTrack)
+                            {
+                                //PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackParentDaughterRelationship(*m_pPandora, pTrack, trackVec[jTrack]));
+                                //PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackParentDaughterRelationship(*m_pPandora, (int*)(pTrack.id()), (int*)(trackVec.getTracks(jTrack).id()) ) );
+                                PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackParentDaughterRelationship(*m_pPandora, (int*)(pTrack.id()), (int*)(pReconstructedParticle.getTracks(jTrack).id()) ) );
+                            }
+                        }
+
+                        // Make track sibling relationships
+                        else
+                        {
+                            for (unsigned int jTrack = iTrack + 1; jTrack < nTracks; ++jTrack)
+                            {
+                                //PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackSiblingRelationship(*m_pPandora, pTrack, trackVec[jTrack]));
+                                //PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackSiblingRelationship(*m_pPandora, (int*)(pTrack.id()), (int*)(trackVec.getTracks(jTrack).id()) ));
+                                PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackSiblingRelationship(*m_pPandora, (int*)(pTrack.id()), (int*)(pReconstructedParticle.getTracks(jTrack).id()) ));
+                            }
+                        }
+                    }
+                }
+                catch (...)
+                {
+                    std::cout << "Failed to extract kink vertex: " <<  std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout << "Failed to extract kink vertex collection: " << *iter <<  std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+*/
+//------------------------------------------------------------------------------------------------------------------------------------------
+/*
+pandora::StatusCode TrackCreator::ExtractProngsAndSplits(const EVENT::LCEvent *const pLCEvent)
+{
+    for (StringVector::const_iterator iter = m_settings.m_prongSplitVertexCollections.begin(), iterEnd = m_settings.m_prongSplitVertexCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        try
+        {
+            const EVENT::LCCollection *pProngOrSplitCollection = pLCEvent->getCollection(*iter);
+
+            for (int i = 0, iMax = pProngOrSplitCollection->getNumberOfElements(); i < iMax; ++i)
+            {
+                try
+                {
+                    EVENT::Vertex *pVertex = dynamic_cast<Vertex*>(pProngOrSplitCollection->getElementAt(i));
+
+                    if (NULL == pVertex)
+                        throw EVENT::Exception("Collection type mismatch");
+
+                    EVENT::ReconstructedParticle *pReconstructedParticle = pVertex->getAssociatedParticle();
+                    const EVENT::TrackVec &trackVec(pReconstructedParticle->getTracks());
+
+                    if (this->IsConflictingRelationship(trackVec))
+                        continue;
+
+                    // Extract the prong/split vertex information
+                    for (unsigned int iTrack = 0, nTracks = trackVec.size(); iTrack < nTracks; ++iTrack)
+                    {
+                        EVENT::Track *pTrack = trackVec[iTrack];
+                        (0 == iTrack) ? m_parentTrackList.insert(pTrack) : m_daughterTrackList.insert(pTrack);
+                        streamlog_out(DEBUG) << "Prong or Split Track " << iTrack << ", nHits " << pTrack->getTrackerHits().size() << std::endl;
+
+                        if (0 == m_settings.m_shouldFormTrackRelationships)
+                            continue;
+
+                        // Make track parent-daughter relationships
+                        if (0 == iTrack)
+                        {
+                            for (unsigned int jTrack = iTrack + 1; jTrack < nTracks; ++jTrack)
+                            {
+                                PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackParentDaughterRelationship(*m_pPandora,
+                                    pTrack, trackVec[jTrack]));
+                            }
+                        }
+
+                        // Make track sibling relationships
+                        else
+                        {
+                            for (unsigned int jTrack = iTrack + 1; jTrack < nTracks; ++jTrack)
+                            {
+                                PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackSiblingRelationship(*m_pPandora,
+                                    pTrack, trackVec[jTrack]));
+                            }
+                        }
+                    }
+                }
+                catch (EVENT::Exception &exception)
+                {
+                    streamlog_out(WARNING) << "Failed to extract prong/split vertex: " << exception.what() << std::endl;
+                }
+            }
+        }
+        catch (EVENT::Exception &exception)
+        {
+            streamlog_out(DEBUG5) << "Failed to extract prong/split vertex collection: " << *iter << ", " << exception.what() << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode TrackCreator::ExtractV0s(const EVENT::LCEvent *const pLCEvent)
+{
+    for (StringVector::const_iterator iter = m_settings.m_v0VertexCollections.begin(), iterEnd = m_settings.m_v0VertexCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        try
+        {
+            const EVENT::LCCollection *pV0Collection = pLCEvent->getCollection(*iter);
+
+            for (int i = 0, iMax = pV0Collection->getNumberOfElements(); i < iMax; ++i)
+            {
+                try
+                {
+                    EVENT::Vertex *pVertex = dynamic_cast<Vertex*>(pV0Collection->getElementAt(i));
+
+                    if (NULL == pVertex)
+                        throw EVENT::Exception("Collection type mismatch");
+
+                    EVENT::ReconstructedParticle *pReconstructedParticle = pVertex->getAssociatedParticle();
+                    const EVENT::TrackVec &trackVec(pReconstructedParticle->getTracks());
+
+                    if (this->IsConflictingRelationship(trackVec))
+                        continue;
+
+                    // Extract the v0 vertex information
+                    const int vertexPdgCode(pReconstructedParticle->getType());
+
+                    for (unsigned int iTrack = 0, nTracks = trackVec.size(); iTrack < nTracks; ++iTrack)
+                    {
+                        EVENT::Track *pTrack = trackVec[iTrack];
+                        m_v0TrackList.insert(pTrack);
+                        streamlog_out(DEBUG) << "V0Track " << iTrack << ", nHits " << pTrack->getTrackerHits().size() << std::endl;
+
+                        int trackPdgCode = pandora::UNKNOWN_PARTICLE_TYPE;
+
+                        switch (vertexPdgCode)
+                        {
+                        case pandora::PHOTON :
+                            (pTrack->getOmega() > 0) ? trackPdgCode = pandora::E_PLUS : trackPdgCode = pandora::E_MINUS;
+                            break;
+                        case pandora::LAMBDA :
+                            (pTrack->getOmega() > 0) ? trackPdgCode = pandora::PROTON : trackPdgCode = pandora::PI_MINUS;
+                            break;
+                        case pandora::LAMBDA_BAR :
+                            (pTrack->getOmega() > 0) ? trackPdgCode = pandora::PI_PLUS : trackPdgCode = pandora::PROTON_BAR;
+                            break;
+                        case pandora::K_SHORT :
+                            (pTrack->getOmega() > 0) ? trackPdgCode = pandora::PI_PLUS : trackPdgCode = pandora::PI_MINUS;
+                            break;
+                        default :
+                            (pTrack->getOmega() > 0) ? trackPdgCode = pandora::PI_PLUS : trackPdgCode = pandora::PI_MINUS;
+                            break;
+                        }
+
+                        m_trackToPidMap.insert(TrackToPidMap::value_type(pTrack, trackPdgCode));
+
+                        if (0 == m_settings.m_shouldFormTrackRelationships)
+                            continue;
+
+                        // Make track sibling relationships
+                        for (unsigned int jTrack = iTrack + 1; jTrack < nTracks; ++jTrack)
+                        {
+                            PANDORA_RETURN_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::SetTrackSiblingRelationship(*m_pPandora,
+                                pTrack, trackVec[jTrack]));
+                        }
+                    }
+                }
+                catch (EVENT::Exception &exception)
+                {
+                    streamlog_out(WARNING) << "Failed to extract v0 vertex: " << exception.what() << std::endl;
+                }
+            }
+        }
+        catch (EVENT::Exception &exception)
+        {
+            streamlog_out(DEBUG5) << "Failed to extract v0 vertex collection: " << *iter << ", " << exception.what() << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+*/
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+bool TrackCreator::IsConflictingRelationship(const edm4hep::ConstReconstructedParticle &Particle) const
+{
+    //for (unsigned int iTrack = 0, nTracks = trackVec.size(); iTrack < nTracks; ++iTrack)
+    //for (unsigned int iTrack = 0, nTracks = trackVec.tracks_size(); iTrack < nTracks; ++iTrack)
+    std::cout<<"Particle en="<<Particle.getEnergy()<<std::endl;
+    for (unsigned int iTrack = 0, nTracks = Particle.tracks_size(); iTrack < nTracks; ++iTrack)
+    {
+        //EVENT::Track *pTrack = trackVec[iTrack];
+        edm4hep::ConstTrack pTrack = Particle.getTracks(iTrack) ;
+        unsigned int pTrack_id = pTrack.id() ;
+
+        //if (this->IsDaughter(pTrack) || this->IsParent(pTrack) || this->IsV0(pTrack))
+        if (this->IsDaughter(pTrack_id) || this->IsParent(pTrack_id) || this->IsV0(pTrack_id))
+            return true;
+    }
+
+    return false;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+pandora::StatusCode TrackCreator::CreateTracks(const CollectionMaps& collectionMaps)
+{
+    std::cout<<"start TrackCreator::CreateTracks:"<<std::endl;
+    for (StringVector::const_iterator iter = m_settings.m_trackCollections.begin(), iterEnd = m_settings.m_trackCollections.end();
+        iter != iterEnd; ++iter)
+    {
+        if(collectionMaps.collectionMap_Track.find(*iter) == collectionMaps.collectionMap_Track.end()) { std::cout<<"not find "<<(*iter)<<std::endl; continue;}
+        try
+        {
+            const std::vector<edm4hep::Track>& pTrackCollection = (collectionMaps.collectionMap_Track.find(*iter))->second;
+            std::cout<<(*iter)<<" size="<<pTrackCollection.size()<<std::endl;
+
+            for (int i = 0, iMax = pTrackCollection.size(); i < iMax; ++i)
+            {
+                try
+                {
+                    const edm4hep::Track& pTrack0 = pTrackCollection.at(i);
+                    const edm4hep::Track* pTrack  = (const edm4hep::Track*)(&pTrack0);
+
+                    if (NULL == pTrack) throw ("Collection type mismatch");
+
+                    int minTrackHits = m_settings.m_minTrackHits;
+                    //const float tanLambda(std::fabs(pTrack->getTanLambda()));
+                    //std::cout<<"track states size="<<pTrack->trackStates_size()<<std::endl;
+                    const float tanLambda(std::fabs(pTrack->getTrackStates(0).tanLambda));
+
+                    if (tanLambda > m_tanLambdaFtd)
+                    {
+                        int expectedFtdHits(0);
+
+                        for (unsigned int iFtdLayer = 0; iFtdLayer < m_nFtdLayers; ++iFtdLayer)
+                        {
+                            if ((tanLambda > m_ftdZPositions[iFtdLayer] / m_ftdOuterRadii[iFtdLayer]) &&
+                                (tanLambda < m_ftdZPositions[iFtdLayer] / m_ftdInnerRadii[iFtdLayer]))
+                            {
+                                expectedFtdHits++;
+                            }
+                        }
+
+                        minTrackHits = std::max(m_settings.m_minFtdTrackHits, expectedFtdHits);
+                    }
+
+                    const int nTrackHits(static_cast<int>(pTrack->trackerHits_size()));
+
+                    if ((nTrackHits < minTrackHits) || (nTrackHits > m_settings.m_maxTrackHits))
+                        continue;
+
+                    // Proceed to create the pandora track
+                    PandoraApi::Track::Parameters trackParameters;
+                    //trackParameters.m_d0 = pTrack->getD0();
+                    trackParameters.m_d0 = pTrack->getTrackStates(0).D0;
+                    //trackParameters.m_z0 = pTrack->getZ0();
+                    trackParameters.m_z0 = pTrack->getTrackStates(0).Z0;
+                    trackParameters.m_pParentAddress = pTrack;
+                    // By default, assume tracks are charged pions
+                    const float signedCurvature(pTrack->getTrackStates(0).omega);
+                    trackParameters.m_particleId = (signedCurvature > 0) ? pandora::PI_PLUS : pandora::PI_MINUS;
+                    trackParameters.m_mass = pandora::PdgTable::GetParticleMass(pandora::PI_PLUS);
+
+                    // Use particle id information from V0 and Kink finders
+                    TrackToPidMap::const_iterator iter_t = m_trackToPidMap.find(*pTrack);
+
+                    if(iter_t != m_trackToPidMap.end())
+                    {
+                        trackParameters.m_particleId = (*iter_t).second;
+                        trackParameters.m_mass = pandora::PdgTable::GetParticleMass((*iter_t).second);
+                    }
+
+                    if (std::numeric_limits<float>::epsilon() < std::fabs(signedCurvature))
+                        trackParameters.m_charge = static_cast<int>(signedCurvature / std::fabs(signedCurvature));
+
+                    this->GetTrackStates(pTrack, trackParameters);
+                    this->TrackReachesECAL(pTrack, trackParameters);
+                    this->DefineTrackPfoUsage(pTrack, trackParameters);
+
+                    PANDORA_THROW_RESULT_IF(pandora::STATUS_CODE_SUCCESS, !=, PandoraApi::Track::Create(*m_pPandora, trackParameters));
+                    m_trackVector.push_back(pTrack);
+                }
+                catch (pandora::StatusCodeException &statusCodeException)
+                {
+                    std::cout<<"ERROR Failed to extract a track: " << statusCodeException.ToString() << std::endl;
+                }
+                catch (...)
+                {
+                    std::cout << "WARNNING Failed to extract a track "<< std::endl;
+                }
+            }
+        }
+        catch (...)
+        {
+            std::cout<<"WARNING Failed to extract track collection: " << *iter << std::endl;
+        }
+    }
+
+    return pandora::STATUS_CODE_SUCCESS;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void TrackCreator::GetTrackStates(const edm4hep::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const
+{
+    //const TrackState *pTrackState = pTrack->getTrackState(TrackState::AtIP);
+    edm4hep::TrackState pTrackState = pTrack->getTrackStates(1); // ref  /cvmfs/cepcsw.ihep.ac.cn/prototype/LCIO/include/EVENT/TrackState.h 
+
+    //if (!pTrackState)  throw pandora::StatusCodeException(pandora::STATUS_CODE_NOT_INITIALIZED);
+
+    //const double pt(m_bField * 2.99792e-4 / std::fabs(pTrackState->getOmega()));
+    const double pt(m_bField * 2.99792e-4 / std::fabs(pTrackState.omega));
+    //trackParameters.m_momentumAtDca = pandora::CartesianVector(std::cos(pTrackState->getPhi()), std::sin(pTrackState->getPhi()), pTrackState->getTanLambda()) * pt;
+    trackParameters.m_momentumAtDca = pandora::CartesianVector(std::cos(pTrackState.phi), std::sin(pTrackState.phi), pTrackState.tanLambda) * pt;
+    //this->CopyTrackState(pTrack->getTrackState(TrackState::AtFirstHit), trackParameters.m_trackStateAtStart);
+    this->CopyTrackState(pTrack->getTrackStates(2), trackParameters.m_trackStateAtStart);
+
+    //fg: curling TPC tracks have pointers to track segments stored -> need to get track states from last segment!
+    //const EVENT::Track *pEndTrack = (pTrack->getTracks().empty() ?  pTrack  :  pTrack->getTracks().back());
+    auto pEndTrack = (pTrack->tracks_size() ==0 ) ?  *pTrack  :  pTrack->getTracks(pTrack->tracks_size()-1);
+
+    //std::cout<<"GetTrackStates 1.6"<<", end track trackStates_size()="<<pEndTrack.trackStates_size()<<std::endl;
+    
+    //this->CopyTrackState(pEndTrack->getTrackState(TrackState::AtLastHit), trackParameters.m_trackStateAtEnd);
+    //this->CopyTrackState(pEndTrack->getTrackState(TrackState::AtCalorimeter), trackParameters.m_trackStateAtCalorimeter);
+    
+    this->CopyTrackState(pEndTrack.getTrackStates(3), trackParameters.m_trackStateAtEnd);
+    //this->CopyTrackState(pEndTrack.getTrackStates(4), trackParameters.m_trackStateAtCalorimeter);
+    //FIXME ? LCIO input only has 4 states, so 4 can't be used.
+    this->CopyTrackState(pEndTrack.getTrackStates(3), trackParameters.m_trackStateAtCalorimeter);
+    
+    
+    trackParameters.m_isProjectedToEndCap = ((std::fabs(trackParameters.m_trackStateAtCalorimeter.Get().GetPosition().GetZ()) < m_eCalEndCapInnerZ) ? false : true);
+
+    // Convert generic time (length from reference point to intersection, divided by momentum) into nanoseconds
+    const float minGenericTime(this->CalculateTrackTimeAtCalorimeter(pTrack));
+    const float particleMass(trackParameters.m_mass.Get());
+    const float particleEnergy(std::sqrt(particleMass * particleMass + trackParameters.m_momentumAtDca.Get().GetMagnitudeSquared()));
+    trackParameters.m_timeAtCalorimeter = minGenericTime * particleEnergy / 299.792f;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+float TrackCreator::CalculateTrackTimeAtCalorimeter(const edm4hep::Track *const pTrack) const
+{
+    //const pandora::Helix helix(pTrack->getPhi(), pTrack->getD0(), pTrack->getZ0(), pTrack->getOmega(), pTrack->getTanLambda(), m_bField);
+    const pandora::Helix helix(pTrack->getTrackStates(0).phi, pTrack->getTrackStates(0).D0, pTrack->getTrackStates(0).Z0, pTrack->getTrackStates(0).omega, pTrack->getTrackStates(0).tanLambda, m_bField);
+    const pandora::CartesianVector &referencePoint(helix.GetReferencePoint());
+
+    // First project to endcap
+    float minGenericTime(std::numeric_limits<float>::max());
+
+    pandora::CartesianVector bestECalProjection(0.f, 0.f, 0.f);
+    const int signPz((helix.GetMomentum().GetZ() > 0.f) ? 1 : -1);
+    (void) helix.GetPointInZ(static_cast<float>(signPz) * m_eCalEndCapInnerZ, referencePoint, bestECalProjection, minGenericTime);
+
+    // Then project to barrel surface(s)
+    pandora::CartesianVector barrelProjection(0.f, 0.f, 0.f);
+    if (m_eCalBarrelInnerSymmetry > 0)
+    {
+        // Polygon
+        float twopi_n = 2. * M_PI / (static_cast<float>(m_eCalBarrelInnerSymmetry));
+
+        for (int i = 0; i < m_eCalBarrelInnerSymmetry; ++i)
+        {
+            float genericTime(std::numeric_limits<float>::max());
+            const float phi(twopi_n * static_cast<float>(i) + m_eCalBarrelInnerPhi0);
+
+            const pandora::StatusCode statusCode(helix.GetPointInXY(m_eCalBarrelInnerR * std::cos(phi), m_eCalBarrelInnerR * std::sin(phi),
+                std::cos(phi + 0.5 * M_PI), std::sin(phi + 0.5 * M_PI), referencePoint, barrelProjection, genericTime));
+
+            if ((pandora::STATUS_CODE_SUCCESS == statusCode) && (genericTime < minGenericTime))
+            {
+                minGenericTime = genericTime;
+                bestECalProjection = barrelProjection;
+            }
+        }
+    }
+    else
+    {
+        // Cylinder
+        float genericTime(std::numeric_limits<float>::max());
+        const pandora::StatusCode statusCode(helix.GetPointOnCircle(m_eCalBarrelInnerR, referencePoint, barrelProjection, genericTime));
+
+        if ((pandora::STATUS_CODE_SUCCESS == statusCode) && (genericTime < minGenericTime))
+        {
+            minGenericTime = genericTime;
+            bestECalProjection = barrelProjection;
+        }
+    }
+
+    if (bestECalProjection.GetMagnitudeSquared() < std::numeric_limits<float>::epsilon())
+        throw pandora::StatusCodeException(pandora::STATUS_CODE_NOT_INITIALIZED);
+
+    return minGenericTime;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void TrackCreator::CopyTrackState(const edm4hep::TrackState & pTrackState, pandora::InputTrackState &inputTrackState) const
+{
+    //if (!pTrackState)  throw pandora::StatusCodeException(pandora::STATUS_CODE_NOT_INITIALIZED);
+    //const double pt(m_bField * 2.99792e-4 / std::fabs(pTrackState->getOmega()));
+    const double pt(m_bField * 2.99792e-4 / std::fabs(pTrackState.omega));
+
+    //const double px(pt * std::cos(pTrackState->getPhi()));
+    const double px(pt * std::cos(pTrackState.phi));
+    //const double py(pt * std::sin(pTrackState->getPhi()));
+    const double py(pt * std::sin(pTrackState.phi));
+    //const double pz(pt * pTrackState->getTanLambda());
+    const double pz(pt * pTrackState.tanLambda);
+
+    const double xs(pTrackState.referencePoint[0]);
+    const double ys(pTrackState.referencePoint[1]);
+    const double zs(pTrackState.referencePoint[2]);
+
+    inputTrackState = pandora::TrackState(xs, ys, zs, px, py, pz);
+}
+
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void TrackCreator::TrackReachesECAL(const edm4hep::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const
+{
+    
+    // Calculate hit position information
+    float hitZMin(std::numeric_limits<float>::max());
+    float hitZMax(-std::numeric_limits<float>::max());
+    float hitOuterR(-std::numeric_limits<float>::max());
+
+    int maxOccupiedFtdLayer=0;
+    
+    
+    const unsigned int nTrackHits(pTrack->trackerHits_size());
+    for (unsigned int i = 0; i < nTrackHits; ++i)
+    {
+        
+        const edm4hep::ConstTrackerHit Hit ( pTrack->getTrackerHits(i) );
+        const edm4hep::Vector3d pos = Hit.getPosition();
+        
+        float x = float(pos[0]);
+        float y = float(pos[1]);
+        float z = float(pos[2]);
+        float r = std::sqrt(x * x + y * y);
+
+        if (z > hitZMax) hitZMax = z;
+
+        if (z < hitZMin) hitZMin = z;
+
+        if (r > hitOuterR) hitOuterR = r;
+
+        if ((r > m_tpcInnerR) && (r < m_tpcOuterR) && (std::fabs(z) <= m_tpcZmax))  continue;
+
+        for (unsigned int j = 0; j < m_nFtdLayers; ++j)
+        {
+            if ((r > m_ftdInnerRadii[j]) && (r < m_ftdOuterRadii[j]) &&
+                (std::fabs(z) - m_settings.m_reachesECalFtdZMaxDistance < m_ftdZPositions[j]) &&
+                (std::fabs(z) + m_settings.m_reachesECalFtdZMaxDistance > m_ftdZPositions[j]))
+            {
+                //if (static_cast<int>(j) > maxOccupiedFtdLayer) maxOccupiedFtdLayer = static_cast<int>(j);
+                if ( j > maxOccupiedFtdLayer) maxOccupiedFtdLayer = j;
+                break;
+            }
+        }
+    }
+    const int nTpcHits(this->GetNTpcHits(pTrack));
+    const int nFtdHits(this->GetNFtdHits(pTrack));
+
+    // Look to see if there are hits in etd or set, implying track has reached edge of ecal
+    if ((hitOuterR > m_minSetRadius) || (hitZMax > m_minEtdZPosition))
+    {
+        trackParameters.m_reachesCalorimeter = true;
+        return;
+    }
+
+    // Require sufficient hits in tpc or ftd, then compare extremal hit positions with tracker dimensions
+    if ((nTpcHits >= m_settings.m_reachesECalNTpcHits) || (nFtdHits >= m_settings.m_reachesECalNFtdHits))
+    {
+        if ((hitOuterR - m_tpcOuterR > m_settings.m_reachesECalTpcOuterDistance) ||
+            (std::fabs(hitZMax) - m_tpcZmax > m_settings.m_reachesECalTpcZMaxDistance) ||
+            (std::fabs(hitZMin) - m_tpcZmax > m_settings.m_reachesECalTpcZMaxDistance) ||
+            (maxOccupiedFtdLayer >= m_settings.m_reachesECalMinFtdLayer))
+        {
+            trackParameters.m_reachesCalorimeter = true;
+            return;
+        }
+    }
+
+    // If track is lowpt, it may curl up and end inside tpc inner radius
+    const pandora::CartesianVector &momentumAtDca(trackParameters.m_momentumAtDca.Get());
+    const float cosAngleAtDca(std::fabs(momentumAtDca.GetZ()) / momentumAtDca.GetMagnitude());
+    const float pX(momentumAtDca.GetX()), pY(momentumAtDca.GetY());
+    const float pT(std::sqrt(pX * pX + pY * pY));
+
+    if ((cosAngleAtDca > m_cosTpc) || (pT < m_settings.m_curvatureToMomentumFactor * m_bField * m_tpcOuterR))
+    {
+        trackParameters.m_reachesCalorimeter = true;
+        return;
+    }
+    
+    trackParameters.m_reachesCalorimeter = false;
+    
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+void TrackCreator::DefineTrackPfoUsage(const edm4hep::Track *const pTrack, PandoraApi::Track::Parameters &trackParameters) const
+{
+    bool canFormPfo(false);
+    bool canFormClusterlessPfo(false);
+
+    //if (trackParameters.m_reachesCalorimeter.Get() && !this->IsParent(pTrack))
+    if (trackParameters.m_reachesCalorimeter.Get() && !this->IsParent(pTrack->id()))
+    {
+        //const float d0(std::fabs(pTrack->getD0())), z0(std::fabs(pTrack->getZ0()));
+        const float d0(std::fabs(pTrack->getTrackStates(0).D0)), z0(std::fabs(pTrack->getTrackStates(0).Z0));
+
+        //EVENT::TrackerHitVec trackerHitvec(pTrack->getTrackerHits());
+        float rInner(std::numeric_limits<float>::max()), zMin(std::numeric_limits<float>::max());
+
+        //for (EVENT::TrackerHitVec::const_iterator iter = trackerHitvec.begin(), iterEnd = trackerHitvec.end(); iter != iterEnd; ++iter)
+        for (std::vector<edm4hep::ConstTrackerHit>::const_iterator iter = pTrack->trackerHits_begin(), iterEnd = pTrack->trackerHits_end(); iter != iterEnd; ++iter)
+        {
+            //const double *pPosition((*iter)->getPosition());
+            const edm4hep::Vector3d pPosition = (*iter).getPosition(); 
+            const float x(pPosition[0]), y(pPosition[1]), absoluteZ(std::fabs(pPosition[2]));
+            const float r(std::sqrt(x * x + y * y));
+
+            if (r < rInner)
+                rInner = r;
+
+            if (absoluteZ < zMin)
+                zMin = absoluteZ;
+        }
+
+        if (this->PassesQualityCuts(pTrack, trackParameters))
+        {
+            const pandora::CartesianVector &momentumAtDca(trackParameters.m_momentumAtDca.Get());
+            const float pX(momentumAtDca.GetX()), pY(momentumAtDca.GetY()), pZ(momentumAtDca.GetZ());
+            const float pT(std::sqrt(pX * pX + pY * pY));
+
+            const float zCutForNonVertexTracks(m_tpcInnerR * std::fabs(pZ / pT) + m_settings.m_zCutForNonVertexTracks);
+            const bool passRzQualityCuts((zMin < zCutForNonVertexTracks) && (rInner < m_tpcInnerR + m_settings.m_maxTpcInnerRDistance));
+
+            const bool isV0(this->IsV0(pTrack->id()));
+            const bool isDaughter(this->IsDaughter(pTrack->id()));
+
+            // Decide whether track can be associated with a pandora cluster and used to form a charged PFO
+            if ((d0 < m_settings.m_d0TrackCut) && (z0 < m_settings.m_z0TrackCut) && (rInner < m_tpcInnerR + m_settings.m_maxTpcInnerRDistance))
+            {
+                canFormPfo = true;
+            }
+            else if (passRzQualityCuts && (0 != m_settings.m_usingNonVertexTracks))
+            {
+                canFormPfo = true;
+            }
+            else if (isV0 || isDaughter)
+            {
+                canFormPfo = true;
+            }
+
+            // Decide whether track can be used to form a charged PFO, even if track fails to be associated with a pandora cluster
+            const float particleMass(trackParameters.m_mass.Get());
+            const float trackEnergy(std::sqrt(momentumAtDca.GetMagnitudeSquared() + particleMass * particleMass));
+
+            if ((0 != m_settings.m_usingUnmatchedVertexTracks) && (trackEnergy < m_settings.m_unmatchedVertexTrackMaxEnergy))
+            {
+                if ((d0 < m_settings.m_d0UnmatchedVertexTrackCut) && (z0 < m_settings.m_z0UnmatchedVertexTrackCut) &&
+                    (rInner < m_tpcInnerR + m_settings.m_maxTpcInnerRDistance))
+                {
+                    canFormClusterlessPfo = true;
+                }
+                else if (passRzQualityCuts && (0 != m_settings.m_usingNonVertexTracks) && (0 != m_settings.m_usingUnmatchedNonVertexTracks))
+                {
+                    canFormClusterlessPfo = true;
+                }
+                else if (isV0 || isDaughter)
+                {
+                    canFormClusterlessPfo = true;
+                }
+            }
+        }
+        else if (this->IsDaughter(pTrack->id()) || this->IsV0(pTrack->id()))
+        {
+            std::cout<<"WARNING Recovering daughter or v0 track " << trackParameters.m_momentumAtDca.Get().GetMagnitude() << std::endl;
+            canFormPfo = true;
+        }
+    }
+
+    trackParameters.m_canFormPfo = canFormPfo;
+    trackParameters.m_canFormClusterlessPfo = canFormClusterlessPfo;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+bool TrackCreator::PassesQualityCuts(const edm4hep::Track *const pTrack, const PandoraApi::Track::Parameters &trackParameters) const
+{
+    // First simple sanity checks
+    if (trackParameters.m_trackStateAtCalorimeter.Get().GetPosition().GetMagnitude() < m_settings.m_minTrackECalDistanceFromIp)
+        return false;
+
+    //if (std::fabs(pTrack->getOmega()) < std::numeric_limits<float>::epsilon())
+    if (std::fabs(pTrack->getTrackStates(0).omega) < std::numeric_limits<float>::epsilon())
+    {
+        std::cout<<"ERROR Track has Omega = 0 " << std::endl;
+        return false;
+    }
+
+    // Check momentum uncertainty is reasonable to use track
+    const pandora::CartesianVector &momentumAtDca(trackParameters.m_momentumAtDca.Get());
+    //const float sigmaPOverP(std::sqrt(pTrack->getCovMatrix()[5]) / std::fabs(pTrack->getOmega()));
+    const float sigmaPOverP(std::sqrt(pTrack->getTrackStates(0).covMatrix[5]) / std::fabs(pTrack->getTrackStates(0).omega));
+
+    if (sigmaPOverP > m_settings.m_maxTrackSigmaPOverP)
+    {
+        std::cout<<"WARNING Dropping track : " << momentumAtDca.GetMagnitude() << "+-" << sigmaPOverP * (momentumAtDca.GetMagnitude())
+                               << " chi2 = " <<  pTrack->getChi2() << " " << pTrack->getNdf()
+                               << " from " << pTrack->trackerHits_size() << std::endl;
+        return false;
+    }
+
+    // Require reasonable number of TPC hits 
+    if (momentumAtDca.GetMagnitude() > m_settings.m_minMomentumForTrackHitChecks)
+    {
+        const float pX(fabs(momentumAtDca.GetX()));
+        const float pY(fabs(momentumAtDca.GetY()));
+        const float pZ(fabs(momentumAtDca.GetZ()));
+        const float pT(std::sqrt(pX * pX + pY * pY));
+        const float rInnermostHit(pTrack->getRadiusOfInnermostHit());
+
+        if ((std::numeric_limits<float>::epsilon() > std::fabs(pT)) || (std::numeric_limits<float>::epsilon() > std::fabs(pZ)) || (rInnermostHit == m_tpcOuterR))
+        {
+            std::cout<<"ERROR Invalid track parameter, pT " << pT << ", pZ " << pZ << ", rInnermostHit " << rInnermostHit << std::endl;
+            return false;
+        }
+
+        float nExpectedTpcHits(0.);
+
+        if (pZ < m_tpcZmax / m_tpcOuterR * pT)
+        {
+            const float innerExpectedHitRadius(std::max(m_tpcInnerR, rInnermostHit));
+            const float frac((m_tpcOuterR - innerExpectedHitRadius) / (m_tpcOuterR - m_tpcInnerR));
+            nExpectedTpcHits = m_tpcMaxRow * frac;
+        }
+
+        if ((pZ <= m_tpcZmax / m_tpcInnerR * pT) && (pZ >= m_tpcZmax / m_tpcOuterR * pT))
+        {
+            const float innerExpectedHitRadius(std::max(m_tpcInnerR, rInnermostHit));
+            const float frac((m_tpcZmax * pT / pZ - innerExpectedHitRadius) / (m_tpcOuterR - innerExpectedHitRadius));
+            nExpectedTpcHits = frac * m_tpcMaxRow;
+        }
+
+        // TODO Get TPC membrane information from GEAR when available
+        if (std::fabs(pZ) / momentumAtDca.GetMagnitude() < m_settings.m_tpcMembraneMaxZ / m_tpcInnerR)
+            nExpectedTpcHits = 0;
+
+        const int nTpcHits(this->GetNTpcHits(pTrack));
+        const int nFtdHits(this->GetNFtdHits(pTrack));
+
+        const int minTpcHits = static_cast<int>(nExpectedTpcHits * m_settings.m_minTpcHitFractionOfExpected);
+
+        if ((nTpcHits < minTpcHits) && (nFtdHits < m_settings.m_minFtdHitsForTpcHitFraction))
+        {
+            std::cout<<"WARNING Dropping track : " << momentumAtDca.GetMagnitude() << " Number of TPC hits = " << nTpcHits
+                                   << " < " << minTpcHits << " nftd = " << nFtdHits  << std::endl;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+int TrackCreator::GetNTpcHits(const edm4hep::Track *const pTrack) const
+{
+    // ATTN
+    //fg: hit numbers are now given in different order wrt LOI:  
+    // trk->subdetectorHitNumbers()[ 2 * ILDDetID::TPC - 1 ] =  hitsInFit ;  
+    // trk->subdetectorHitNumbers()[ 2 * ILDDetID::TPC - 2 ] =  hitCount ;  
+    // ---- use hitsInFit :
+    //return pTrack->getSubdetectorHitNumbers()[ 2 * lcio::ILDDetID::TPC - 1 ];
+    return pTrack->getSubDetectorHitNumbers(2 * lcio::ILDDetID::TPC - 1);// still use LCIO code now
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+int TrackCreator::GetNFtdHits(const edm4hep::Track *const pTrack) const
+{
+    // ATTN
+    //fg: hit numbers are now given in different order wrt LOI:  
+    // trk->subdetectorHitNumbers()[ 2 * ILDDetID::TPC - 1 ] =  hitsInFit ;  
+    // trk->subdetectorHitNumbers()[ 2 * ILDDetID::TPC - 2 ] =  hitCount ;  
+    // ---- use hitsInFit :
+    //return pTrack->getSubdetectorHitNumbers()[ 2 * lcio::ILDDetID::FTD - 1 ];
+    return pTrack->getSubDetectorHitNumbers( 2 * lcio::ILDDetID::FTD - 1 );
+}
+
+//------------------------------------------------------------------------------------------------------------------------------------------
+//------------------------------------------------------------------------------------------------------------------------------------------
+
+TrackCreator::Settings::Settings() :
+    m_shouldFormTrackRelationships(1),
+    m_minTrackHits(5),
+    m_minFtdTrackHits(0),
+    m_maxTrackHits(5000.f),
+    m_d0TrackCut(50.f),
+    m_z0TrackCut(50.f),
+    m_usingNonVertexTracks(1),
+    m_usingUnmatchedNonVertexTracks(0),
+    m_usingUnmatchedVertexTracks(1),
+    m_unmatchedVertexTrackMaxEnergy(5.f),
+    m_d0UnmatchedVertexTrackCut(5.f),
+    m_z0UnmatchedVertexTrackCut(5.f),
+    m_zCutForNonVertexTracks(250.f),
+    m_reachesECalNTpcHits(11),
+    m_reachesECalNFtdHits(4),
+    m_reachesECalTpcOuterDistance(-100.f),
+    m_reachesECalMinFtdLayer(9),
+    m_reachesECalTpcZMaxDistance(-50.f),
+    m_reachesECalFtdZMaxDistance(1.f),
+    m_curvatureToMomentumFactor(0.3f / 2000.f),
+    m_minTrackECalDistanceFromIp(100.f),
+    m_maxTrackSigmaPOverP(0.15f),
+    m_minMomentumForTrackHitChecks(1.f),
+    m_tpcMembraneMaxZ(10.f),
+    m_maxTpcInnerRDistance(50.f),
+    m_minTpcHitFractionOfExpected(0.2f),
+    m_minFtdHitsForTpcHitFraction(2)
+{
+}
diff --git a/Reconstruction/PFA/Pandora/MatrixPandora/src/Utility.cpp b/Reconstruction/PFA/Pandora/MatrixPandora/src/Utility.cpp
new file mode 100644
index 00000000..819fa15d
--- /dev/null
+++ b/Reconstruction/PFA/Pandora/MatrixPandora/src/Utility.cpp
@@ -0,0 +1,7 @@
+#include "Utility.h"
+
+std::string Convert (float number){
+    std::ostringstream buff;
+    buff<<number;
+    return buff.str();   
+}
-- 
GitLab