diff --git a/Tracking/Acts/ActsConfig/python/ActsAnalysisConfig.py b/Tracking/Acts/ActsConfig/python/ActsAnalysisConfig.py
index bf81afdfab92224d1b80d2af506e3a893dee37bc..90aa6e1b3430d962e63bb56c437fd71822bbb8f6 100644
--- a/Tracking/Acts/ActsConfig/python/ActsAnalysisConfig.py
+++ b/Tracking/Acts/ActsConfig/python/ActsAnalysisConfig.py
@@ -38,7 +38,67 @@ def ActsTrackAnalysisAlgCfg(flags,
 
     acc.merge(helper.result())
     return acc
-    
+
+def ActsTrackParticleAnalysisAlgCfg(flags,
+                                    name: str = 'ActsTrackParticleAnalysisAlg',
+                                    **kwargs) -> ComponentAccumulator:
+    acc = ComponentAccumulator()
+    kwargs.setdefault('MonitorTrackStateCounts',True)
+    kwargs.setdefault('TrackParticleLocation', 'ActsTrackParticles')
+
+    kwargs.setdefault('MonGroupName', kwargs['TrackParticleLocation'])
+
+    from AthenaMonitoring import AthMonitorCfgHelper
+    helper = AthMonitorCfgHelper(flags, 'TrackParticleAnalysisAlgCfg')
+    monitoringAlgorithm = helper.addAlgorithm(CompFactory.ActsTrk.TrackParticleAnalysisAlg, name, **kwargs)
+    monitoringGroup = helper.addGroup(monitoringAlgorithm, kwargs['MonGroupName'], '/'+name+'/')
+
+    monitoringGroup.defineHistogram('pt', title='TrackParticle pt;pt (MeV);Entries', type='TH1F', path=kwargs['MonGroupName'],
+                                    xbins=100, xmin=0, xmax=10e3)
+    monitoringGroup.defineHistogram('eta', title='TrackParticle eta;eta;Entries', type='TH1F', path=kwargs['MonGroupName'],
+                                    xbins=50, xmin=-4, xmax=4)
+
+    # hit counts
+    monitoringGroup.defineHistogram('pixelHits', title='Number of pixel hits;N;Entries', type='TH1I', path=kwargs['MonGroupName'],
+                                    xbins=50, xmin=-1, xmax=49)
+    monitoringGroup.defineHistogram('innermostHits', title='Number of innermost pixel hits;N;Entries', type='TH1I', path=kwargs['MonGroupName'],
+                                    xbins=6, xmin=-1, xmax=5)
+    monitoringGroup.defineHistogram('nextToInnermostHits', title='Number of next-to-innermost pixel hits;N;Entries', type='TH1I',
+                                    path=kwargs['MonGroupName'], xbins=6, xmin=-1, xmax=5)
+    monitoringGroup.defineHistogram('expectInnermostHit', title='Innermost pixel hit expected;N;Entries', type='TH1I', path=kwargs['MonGroupName'],
+                                    xbins=3, xmin=-1, xmax=2)
+    monitoringGroup.defineHistogram('expectNextToInnermostHit', title='Next-to-innermost pixel hit expected;N;Entries', type='TH1I',
+                                    path=kwargs['MonGroupName'],xbins=3, xmin=-1, xmax=2)
+
+    if kwargs['MonitorTrackStateCounts'] :
+        # have to add artifical dependency, because the ActsTracks are created by
+        # a special reader algorithm from the various component branches, but the
+        # element links pointing to the ActsTracks would not add this dependency
+        # by themselves.
+        if 'ExtraInputs' not in kwargs :
+            tracks_name = kwargs['TrackParticleLocation']
+            if tracks_name[-len('TrackParticles'):] == 'TrackParticles' :
+                kwargs.setdefault('ExtraInputs',{('ActsTrk::TrackContainer',tracks_name[:-len('TrackParticles')]+'Tracks')})
+
+        monitoringGroup.defineHistogram('States', title='Number of states on track;N;Entries', type='TH1I', path=kwargs['MonGroupName'],
+                                        xbins=50, xmin=0, xmax=50)
+        monitoringGroup.defineHistogram('Measurements', title='Number of measurements on track;N;Entries', type='TH1I', path=kwargs['MonGroupName'],
+                                        xbins=50, xmin=0, xmax=50)
+        monitoringGroup.defineHistogram('Parameters', title='Number of parameters on track;N;Entries', type='TH1I', path=kwargs['MonGroupName'],
+                                        xbins=50, xmin=0, xmax=50)
+        monitoringGroup.defineHistogram('Outliers', title='Number of outliers on track;N;Entries', type='TH1I', path=kwargs['MonGroupName'],
+                                        xbins=50, xmin=0, xmax=50)
+        monitoringGroup.defineHistogram('Holes', title='Number of holes on track;N;Entries', type='TH1I', path=kwargs['MonGroupName'],
+                                        xbins=50, xmin=0, xmax=50)
+        monitoringGroup.defineHistogram('SharedHits', title='Number of shared hits on track;N;Entries', type='TH1I', path=kwargs['MonGroupName'],
+                                        xbins=50, xmin=0, xmax=50)
+        monitoringGroup.defineHistogram('MaterialStates', title='Number of material states on track;N;Entries', type='TH1I', path=kwargs['MonGroupName'],
+                                        xbins=50, xmin=0, xmax=50)
+    acc.merge(helper.result())
+
+    acc.addEventAlgo( CompFactory.ActsTrk.TrackParticleAnalysisAlg(name, **kwargs) )
+    return acc
+
 def ActsHgtdClusterAnalysisAlgCfg(flags,
                                   name: str = "ActsHgtdClusterAnalysisAlg",
                                   **kwargs) -> ComponentAccumulator:
diff --git a/Tracking/Acts/ActsConfig/python/ActsPostIncludes.py b/Tracking/Acts/ActsConfig/python/ActsPostIncludes.py
index 7fd116b7637358f1410cbec7b3d3cdd2cb2963de..af252cab5230da46d8c3ac770dea5e5097c579c9 100644
--- a/Tracking/Acts/ActsConfig/python/ActsPostIncludes.py
+++ b/Tracking/Acts/ActsConfig/python/ActsPostIncludes.py
@@ -73,7 +73,15 @@ def PersistifyActsEDMCfg(flags) -> ComponentAccumulator:
                        f"xAOD::TrackSurfaceAuxContainer#{prefix}TrackStateSurfacesAux.",
                        f"xAOD::TrackSurfaceContainer#{prefix}TrackSurfaces",
                        f"xAOD::TrackSurfaceAuxContainer#{prefix}TrackSurfacesAux."]
-                
+
+    # add track particles created by the Acts TrackToTrackParticleCnvAlg to the AOD
+    trackCnvPrefixes = ["ActsCombined"]
+    tracksSuffix = "Tracks"
+    exclude="." if flags.Acts.EDM.PersistifyTracks else ".-actsTrack"
+    for prefix in trackCnvPrefixes:
+        toAOD += [f"xAOD::TrackParticleContainer#{prefix}{tracksSuffix}ParticlesAlt",
+                  f"xAOD::TrackParticleAuxContainer#{prefix}{tracksSuffix}ParticlesAltAux{exclude}"]
+
     # If there is nothing to persistify, returns an empty CA
     if len(toAOD) == 0:
         return acc
diff --git a/Tracking/Acts/ActsConfig/test/ActsPersistifyEDM.sh b/Tracking/Acts/ActsConfig/test/ActsPersistifyEDM.sh
index 33490ece148469781b8dc948f6b50f9bb151bf97..616565011ef05defcddcf5c881c06b6d2f659ffa 100755
--- a/Tracking/Acts/ActsConfig/test/ActsPersistifyEDM.sh
+++ b/Tracking/Acts/ActsConfig/test/ActsPersistifyEDM.sh
@@ -29,7 +29,9 @@ ActsReadEDM.py \
    readClusters=True \
    readSpacePoints=True \
    readTracks=True \
-   tracks="ActsTracks"
+   tracks="ActsTracks" \
+   readTrackParticles=True \
+   trackParticles="ActsCombinedTracksParticlesAlt"
 
 rc=$?
 if [ $rc != 0 ]; then
diff --git a/Tracking/Acts/ActsEvent/ActsEvent/ActsEventDict.h b/Tracking/Acts/ActsEvent/ActsEvent/ActsEventDict.h
index 7e86de6d16fe98d6c3bfc46be643c69afce3a868..83a53ff54c5f5041ff7946110f6ef7aa813da14e 100644
--- a/Tracking/Acts/ActsEvent/ActsEvent/ActsEventDict.h
+++ b/Tracking/Acts/ActsEvent/ActsEvent/ActsEventDict.h
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
 */
 
 #ifndef ACTSEVENT_DICT_H
@@ -33,5 +33,13 @@ namespace {
   };
 }
 
+#include "ActsEvent/TrackContainer.h"
+namespace {
+  struct GCCXML_DUMMY_ELACTSTRK_TRACKCONTAINER {
+     ActsTrk::TrackContainer              m_dest;
+     ElementLink<ActsTrk::TrackContainer> m_linkToDest;
+     std::vector<ElementLink<ActsTrk::TrackContainer> > m_two;
+  };
+}
 
 #endif
diff --git a/Tracking/Acts/ActsEvent/ActsEvent/TrackContainer.h b/Tracking/Acts/ActsEvent/ActsEvent/TrackContainer.h
index eca05dc3d51a6a51102b477f1ca76121f3b3773b..777ee27b803a37f5765626eda6d203a466af8429 100644
--- a/Tracking/Acts/ActsEvent/ActsEvent/TrackContainer.h
+++ b/Tracking/Acts/ActsEvent/ActsEvent/TrackContainer.h
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
 */
 
 #ifndef ACTSTRKEVENT_TRACKCONTAINER_H
@@ -23,9 +23,95 @@ struct DataLinkHolder {
   const T* operator->() const { return m_link.cptr(); }
 };
 
-using TrackContainer =
-    Acts::TrackContainer<ActsTrk::TrackBackend, ActsTrk::TrackStateBackend,
-                         ActsTrk::DataLinkHolder>;
+using TrackContainerBase = Acts::TrackContainer<ActsTrk::TrackBackend, ActsTrk::TrackStateBackend,
+                                                ActsTrk::DataLinkHolder>;
+
+class  TrackContainer :
+   public  TrackContainerBase
+{
+public:
+   using TrackContainerBase::TrackContainerBase;
+   using value_type = ConstTrackProxy;
+   ConstTrackProxy operator[](unsigned int index) const {
+      return getTrack(index);
+   }
+
+   // Special indexing policy which will dereference element links
+   // into an std::optional rather than a pointer or reference to an
+   // existing element in the destination collection. This is
+   // needed because the Acts track container does have physical
+   // representations of tracks, but only creats proxy objects
+   // for tracks which are created on demand.
+   class IndexingPolicy
+   {
+   public:
+      /// The type we get when we dereference a link, and derived types.
+      using ElementType = std::optional<ConstTrackProxy>;
+      struct ConstTrackProxyPtr {
+         ConstTrackProxyPtr(const ElementType *src) {
+            if (src) {
+               m_proxy = *src;
+            }
+         }
+         ConstTrackProxyPtr(const ConstTrackProxyPtr &) = default;
+         ConstTrackProxyPtr(ConstTrackProxyPtr &&) = default;
+         ConstTrackProxyPtr(const ConstTrackProxy &val) :m_proxy(val) {}
+         ConstTrackProxyPtr(ConstTrackProxy &&val) : m_proxy(std::move(val)) {}
+
+         ConstTrackProxy operator*() const {
+            return m_proxy.value();
+         }
+         const ConstTrackProxy *operator->() const {
+            return &m_proxy.value();
+         }
+         bool operator!() const {
+            return !m_proxy.has_value();
+         }
+         ConstTrackProxyPtr &operator=(const ElementType *src) {
+            if (src) {
+               m_proxy = *src;
+            }
+            else {
+               m_proxy.reset();
+            }
+            return *this;
+         }
+
+         std::optional<ConstTrackProxy> m_proxy;
+      };
+      using ElementConstReference = std::optional<ConstTrackProxy>;
+      using ElementConstPointer = ConstTrackProxyPtr;
+
+      /// The type of an index, as provided to or returned from a link.
+      using index_type = TrackContainerBase::IndexType;
+
+      /// The type of an index, as stored internally within a link.
+      using stored_index_type = index_type;
+
+      static bool isValid (const stored_index_type& index) {
+         return index != ConstTrackProxy::kInvalid;
+      }
+
+      static index_type storedToExternal (stored_index_type index) {
+         return index;
+      }
+      static void reset (stored_index_type& index) {
+         index=ConstTrackProxy::kInvalid;
+      }
+      static
+      ElementType lookup(const stored_index_type& index, const TrackContainerBase& container) {
+         return container.getTrack(index);
+      }
+
+      static void
+      reverseLookup([[maybe_unused]] const TrackContainerBase& container,
+                    ElementConstReference element,
+                    index_type& index) {
+         index= element.has_value() ? element.value().index() : ConstTrackProxy::kInvalid;
+      }
+   };
+
+};
 
 struct MutableTrackContainer
     : public Acts::TrackContainer<ActsTrk::MutableTrackBackend,
@@ -40,6 +126,14 @@ struct MutableTrackContainer
 
 }  // namespace ActsTrk
 
+// register special indexing policy for element links to Acts tracks i.e.
+// ElementLink<ActsTrk::TrackContainer>
+template <>
+struct DefaultIndexingPolicy <ActsTrk::TrackContainer  > {
+   using type = ActsTrk::TrackContainer::IndexingPolicy;
+};
+
+
 #include "AthenaKernel/CLASS_DEF.h"
 CLASS_DEF(ActsTrk::TrackContainer, 1210898253, 1)
 #endif
diff --git a/Tracking/Acts/ActsEvent/ActsEvent/selection.xml b/Tracking/Acts/ActsEvent/ActsEvent/selection.xml
index eac0a17091ee32fa5fa1f147061737f87512c2ae..609610e20b6a9014b00665ca99327d36abb1e3cd 100644
--- a/Tracking/Acts/ActsEvent/ActsEvent/selection.xml
+++ b/Tracking/Acts/ActsEvent/ActsEvent/selection.xml
@@ -1,4 +1,4 @@
-<!-- Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration -->
+<!-- Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration -->
 <lcgdict>
 
   <class name="Seed" />
@@ -9,5 +9,17 @@
   <class name="BoundTrackParametersContainer"
 	 id="07B10B02-CEF7-4FF9-91D5-569F506C46AE" />
 
+  <class name="SG::GenericElementLinkBase<ActsTrk::TrackContainer::IndexingPolicy>" />
+  <class name="ElementLink<ActsTrk::TrackContainer>" />
+  <read sourceClass="ElementLink<ActsTrk::TrackContainer>" version="[1-]"
+        targetClass="ElementLink<ActsTrk::TrackContainer>" source=""
+        target="" >
+    <![CDATA[
+       // Let the object prepare for being used:
+       if (newObj) newObj->toTransient();
+    ]]>
+  </read>
+
+  <class name="std::vector<ElementLink<ActsTrk::TrackContainer> >" />
 </lcgdict>
 
diff --git a/Tracking/Acts/ActsMonitoring/src/TrackParticleAnalysisAlg.cxx b/Tracking/Acts/ActsMonitoring/src/TrackParticleAnalysisAlg.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..22d8c1f1c3b136021c7da2a5f2aa912fa01b3800
--- /dev/null
+++ b/Tracking/Acts/ActsMonitoring/src/TrackParticleAnalysisAlg.cxx
@@ -0,0 +1,130 @@
+/*
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "TrackParticleAnalysisAlg.h"
+#include "ActsEvent/TrackContainer.h"
+#include "SGTools/CurrentEventStore.h"
+#include <iostream>
+#include <sstream>
+#include <iomanip>
+
+namespace {
+   int getSummaryValue(const xAOD::TrackParticle &track_particle, xAOD::SummaryType summary_type) {
+      uint8_t tmp;
+      return track_particle.summaryValue(tmp, summary_type) ? static_cast<int>(tmp) : -1;
+   }
+}
+
+
+namespace ActsTrk {
+
+  TrackParticleAnalysisAlg::TrackParticleAnalysisAlg(const std::string& name, ISvcLocator* pSvcLocator)
+    : AthMonitorAlgorithm(name, pSvcLocator)
+  {}
+
+  StatusCode TrackParticleAnalysisAlg::initialize() {
+    ATH_MSG_DEBUG("Initializing " << name() << " ...");
+
+    ATH_MSG_VERBOSE("Properties:");
+    ATH_MSG_VERBOSE(m_tracksKey);
+
+    ATH_CHECK(m_tracksKey.initialize());
+
+    ATH_MSG_VERBOSE("Monitoring settings ...");
+    ATH_MSG_VERBOSE(m_monGroupName);
+
+    return AthMonitorAlgorithm::initialize();
+  }
+
+  StatusCode TrackParticleAnalysisAlg::fillHistograms(const EventContext& ctx) const {
+    ATH_MSG_DEBUG( "Filling Histograms for " << name() << " ... " );
+
+    // Retrieve the tracks
+    SG::ReadHandle<xAOD::TrackParticleContainer> trackParticleskHandle = SG::makeHandle(m_tracksKey, ctx);
+    ATH_CHECK(trackParticleskHandle.isValid());
+    const xAOD::TrackParticleContainer *track_particles = trackParticleskHandle.cptr();
+
+    if (m_monitorTrackStateCounts) {
+       for (const xAOD::TrackParticle *track_particle : *track_particles) {
+          monitorTrackStateCounts(*track_particle);
+       }
+    }
+    auto monitor_pt = Monitored::Collection("pt", *track_particles,
+                                            [](const xAOD::TrackParticle *tp){return static_cast<double>(tp->pt()); } );
+    auto monitor_eta = Monitored::Collection("eta", *track_particles,
+                                             [](const xAOD::TrackParticle *tp){return static_cast<double>(tp->eta()); } );
+
+    auto monitor_pixelHits = Monitored::Collection("pixelHits", *track_particles,
+                                                   [](const xAOD::TrackParticle *tp){
+                                                      return getSummaryValue(*tp,xAOD::numberOfPixelHits); });
+
+    auto monitor_innermostHits = Monitored::Collection("innermostHits", *track_particles,
+                                                       [](const xAOD::TrackParticle *tp){
+                                                          return getSummaryValue(*tp,xAOD::numberOfInnermostPixelLayerHits); });
+
+    auto monitor_nextToInnermostHits = Monitored::Collection("nextToInnermostHits", *track_particles,
+                                                       [](const xAOD::TrackParticle *tp){
+                                                          return getSummaryValue(*tp,xAOD::numberOfNextToInnermostPixelLayerHits);});
+
+    auto monitor_expectInnermost = Monitored::Collection("expectInnermostHit", *track_particles,
+                                             [](const xAOD::TrackParticle *tp){
+                                                return getSummaryValue(*tp,xAOD::expectInnermostPixelLayerHit); });
+
+    auto monitor_expectNextToInnermost = Monitored::Collection("expectNextToInnermostHit", *track_particles,
+                                             [](const xAOD::TrackParticle *tp){
+                                                return getSummaryValue(*tp,xAOD::expectNextToInnermostPixelLayerHit); });
+
+    fill(m_monGroupName.value(),monitor_pt,monitor_eta, monitor_pixelHits,  monitor_innermostHits, monitor_nextToInnermostHits,
+         monitor_expectInnermost, monitor_expectNextToInnermost);
+    return StatusCode::SUCCESS;
+  }
+
+  void TrackParticleAnalysisAlg::monitorTrackStateCounts(const xAOD::TrackParticle &track_particle) const {
+     static const SG::AuxElement::ConstAccessor<ElementLink<ActsTrk::TrackContainer> > actsTrackLink("actsTrack");
+
+     ElementLink<ActsTrk::TrackContainer> link_to_track = actsTrackLink(track_particle);
+     // to ensure that the code does not suggest something stupid (i.e. creating an unnecessary copy)
+     static_assert( std::is_same<ElementLink<ActsTrk::TrackContainer>::ElementConstReference,
+                    std::optional<ActsTrk::TrackContainer::ConstTrackProxy> >::value);
+     std::optional<ActsTrk::TrackContainer::ConstTrackProxy> optional_track = *link_to_track;
+     if (optional_track.has_value()) {
+        ActsTrk::TrackContainer::ConstTrackProxy track = optional_track.value();
+        std::array<uint8_t, Acts::NumTrackStateFlags+1> counts{};
+
+        const ActsTrk::TrackContainer::ConstTrackProxy::IndexType
+           lastMeasurementIndex = track.tipIndex();
+
+        track.container().trackStateContainer().visitBackwards(
+                 lastMeasurementIndex,
+                 [&counts
+                  ](const typename ActsTrk::TrackStateBackend::ConstTrackStateProxy &state) -> void
+                 {
+                    Acts::ConstTrackStateType flag = state.typeFlags();
+                    ++counts[Acts::NumTrackStateFlags];
+                    for (unsigned int flag_i=0; flag_i<Acts::NumTrackStateFlags; ++flag_i) {
+                       if (flag.test(flag_i)) {
+                          if (flag_i == Acts::TrackStateFlag::HoleFlag) {
+                             if (!state.hasReferenceSurface() || !state.referenceSurface().associatedDetectorElement()) continue;
+                          }
+                          ++counts[flag_i];
+                       }
+                    }
+                 });
+        auto monitor_states = Monitored::Scalar<int>("States",counts[Acts::TrackStateFlag::NumTrackStateFlags]);
+        auto monitor_measurement = Monitored::Scalar<int>("Measurements",counts[Acts::TrackStateFlag::MeasurementFlag]);
+        auto monitor_parameter = Monitored::Scalar<int>("Parameters",counts[Acts::TrackStateFlag::ParameterFlag]);
+        auto monitor_outlier = Monitored::Scalar<int>("Outliers",counts[Acts::TrackStateFlag::OutlierFlag]);
+        auto monitor_hole = Monitored::Scalar<int>("Holes",counts[Acts::TrackStateFlag::HoleFlag]);
+        auto monitor_material = Monitored::Scalar<int>("MaterialStates",counts[Acts::TrackStateFlag::MaterialFlag]);
+        auto monitor_sharedHit = Monitored::Scalar<int>("SharedHits",counts[Acts::TrackStateFlag::SharedHitFlag]);
+        fill(m_monGroupName.value(), monitor_states, monitor_measurement, monitor_parameter, monitor_outlier, monitor_hole,
+             monitor_material, monitor_sharedHit);
+     }
+     else {
+        ATH_MSG_WARNING("Invalid track link for particle  " << track_particle.index());
+     }
+  }
+
+
+}
diff --git a/Tracking/Acts/ActsMonitoring/src/TrackParticleAnalysisAlg.h b/Tracking/Acts/ActsMonitoring/src/TrackParticleAnalysisAlg.h
new file mode 100644
index 0000000000000000000000000000000000000000..f7e2c6b448fcfbc9318b7db7e9b8f84dcba836d5
--- /dev/null
+++ b/Tracking/Acts/ActsMonitoring/src/TrackParticleAnalysisAlg.h
@@ -0,0 +1,37 @@
+/*
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
+*/
+
+#ifndef ACTSTRKANALYSIS_TRACKPARTICLEANALYSISALG_H
+#define ACTSTRKANALYSIS_TRACKPARTICLEANALYSISALG_H
+
+#include "AthenaMonitoring/AthMonitorAlgorithm.h"
+#include "xAODTracking/TrackParticleContainer.h"
+#include "StoreGate/ReadHandleKey.h"
+
+namespace ActsTrk {
+
+  class TrackParticleAnalysisAlg final :
+    public AthMonitorAlgorithm {
+  public:
+    TrackParticleAnalysisAlg(const std::string& name, ISvcLocator* pSvcLocator);
+    virtual ~TrackParticleAnalysisAlg() override = default;
+
+    virtual StatusCode initialize() override;
+    virtual StatusCode fillHistograms(const EventContext& ctx) const override;
+
+  private:
+    void monitorTrackStateCounts(const xAOD::TrackParticle &track_particle) const;
+
+    SG::ReadHandleKey<xAOD::TrackParticleContainer> m_tracksKey {this, "TrackParticleLocation", "",
+        "Input track particle collection"};
+    Gaudi::Property< std::string > m_monGroupName
+      {this, "MonGroupName", "TrackParticleAnalysisAlg"};
+
+    Gaudi::Property< bool > m_monitorTrackStateCounts
+      {this, "MonitorTrackStateCounts", true};
+  };
+
+}
+
+#endif
diff --git a/Tracking/Acts/ActsMonitoring/src/components/ActsMonitoring_entries.cxx b/Tracking/Acts/ActsMonitoring/src/components/ActsMonitoring_entries.cxx
index e427cc6d925a07cf94bf6c410cc2cc5cbac3381a..65301e256f785cdb5ed2d673a51310295ff6e267 100644
--- a/Tracking/Acts/ActsMonitoring/src/components/ActsMonitoring_entries.cxx
+++ b/Tracking/Acts/ActsMonitoring/src/components/ActsMonitoring_entries.cxx
@@ -1,5 +1,5 @@
 /*
-  Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
+  Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
 */
 
 // Algs
@@ -11,6 +11,7 @@
 #include "src/EstimatedTrackParamsAnalysisAlg.h"
 #include "src/SeedingAlgorithmAnalysisAlg.h"
 #include "src/TrackAnalysisAlg.h"
+#include "src/TrackParticleAnalysisAlg.h"
 // Tools
 #include "src/PhysValTool.h"
 
@@ -23,5 +24,6 @@ DECLARE_COMPONENT( ActsTrk::SeedAnalysisAlg )
 DECLARE_COMPONENT( ActsTrk::SeedingAlgorithmAnalysisAlg )
 DECLARE_COMPONENT( ActsTrk::EstimatedTrackParamsAnalysisAlg )
 DECLARE_COMPONENT( ActsTrk::TrackAnalysisAlg )
+DECLARE_COMPONENT( ActsTrk::TrackParticleAnalysisAlg )
 // Tools
 DECLARE_COMPONENT( ActsTrk::PhysValTool )
diff --git a/Tracking/Acts/ActsMonitoring/test/ActsReadEDM.py b/Tracking/Acts/ActsMonitoring/test/ActsReadEDM.py
index 4a69de9e65c48dc634caff6059e31c8e807d25a6..0fd14a704345d083cc28f0bc847ed19f76738dee 100755
--- a/Tracking/Acts/ActsMonitoring/test/ActsReadEDM.py
+++ b/Tracking/Acts/ActsMonitoring/test/ActsReadEDM.py
@@ -3,6 +3,7 @@
 # Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
 
 if __name__ == "__main__":
+    import re
     from AthenaConfiguration.AllConfigFlags import initConfigFlags
     flags = initConfigFlags()
 
@@ -20,6 +21,8 @@ if __name__ == "__main__":
     flags.addFlag("readSpacePoints", False)
     flags.addFlag("readTracks", False)
     flags.addFlag("tracks", "")
+    flags.addFlag("readTrackParticles", False)
+    flags.addFlag("trackParticles", "ActsCombinedTracksParticlesAlt")
     flags.fillFromArgs()
     
     flags.lock()
@@ -58,6 +61,16 @@ if __name__ == "__main__":
                                               name=f"{track}AnalysisAlg",
                                               OutputLevel=2,
                                               TracksLocation=track))
+    if flags.readTrackParticles:
+        from ActsConfig.ActsAnalysisConfig import ActsTrackParticleAnalysisAlgCfg
+        for tp in flags.trackParticles.split(','):
+            src_track_name = re.search(r'^(.*)ParticlesAlt.*',tp).group(1)
+            acc.merge(ActsTrackParticleAnalysisAlgCfg(flags,
+                                                      name=f"{tp}AnalysisAlg",
+                                                      OutputLevel=2,
+                                                      TrackParticleLocation=tp,
+                                                      ExtraInputs={('ActsTrk::TrackContainer',src_track_name)}, # ensure scheduled after reader
+                                                      MonGroupName=f"{tp}Analysis"))
             
     acc.printConfig()
     status = acc.run()
diff --git a/Tracking/Acts/ActsTrackReconstruction/src/TrackToTrackParticleCnvAlg.cxx b/Tracking/Acts/ActsTrackReconstruction/src/TrackToTrackParticleCnvAlg.cxx
index 1ec188aa0c2522b60c758d28507e089d676ad64e..4cbcc2099e1e31888c4867cba08b3d01511935c6 100644
--- a/Tracking/Acts/ActsTrackReconstruction/src/TrackToTrackParticleCnvAlg.cxx
+++ b/Tracking/Acts/ActsTrackReconstruction/src/TrackToTrackParticleCnvAlg.cxx
@@ -11,6 +11,7 @@
 #include "Acts/Propagator/detail/JacobianEngine.hpp"
 #include "ActsInterop/Logger.h"
 
+#include "AthContainers/Decorator.h"
 #include "xAODTracking/TrackParticleAuxContainer.h"
 #include "MagFieldElements/AtlasFieldCache.h"
 #include "InDetReadoutGeometry/SiDetectorElement.h"
@@ -232,6 +233,8 @@ namespace ActsTrk
     HitSummaryData hitInfo;
 
     unsigned int converted_track_states=0;
+
+    static const SG::AuxElement::Decorator<ElementLink<ActsTrk::TrackContainer> > trackLink("actsTrack");
     
     using namespace Acts::UnitLiterals;
     for (const typename ActsTrk::TrackContainer::ConstTrackProxy &track : *tracksContainer) {
@@ -473,7 +476,13 @@ namespace ActsTrk
              ATH_MSG_ERROR("Invalid size of param element " << param.size() <<  " != 6" );
           }
        }
+
        track_particle->setTrackParameters(parametersVec);
+
+       // add element to link to the correspond track
+       trackLink(*track_particle)
+          = ElementLink<ActsTrk::TrackContainer>( tracksContainer,
+                                                  track.index() );
     }
     ATH_MSG_DEBUG( "Converted " <<  tracksContainer->size() << " acts tracks into " << track_particles->size()
                   << " track particles with parameters for " << converted_track_states << " track states.");