From 4fb7c92ab994bdbc763c6275e5e2be9afb74af76 Mon Sep 17 00:00:00 2001
From: Maarten Van Veghel <maarten.vanveghel@cern.ch>
Date: Sun, 4 Jun 2023 11:30:22 +0200
Subject: [PATCH] reco config with split track containers

---
 .../options/hlt2_2or3bodytopo_realtime.py     |   7 +-
 .../python/Hlt2Conf/standard_particles.py     |  69 ++---
 .../sprucing/spruce_check_persistreco.py      |   6 +-
 .../Moore/persistence/truth_matching.py       |   4 +-
 .../python/RecoConf/data_from_file.py         |  30 +-
 .../python/RecoConf/hlt2_global_reco.py       | 283 ++++++------------
 Hlt/RecoConf/python/RecoConf/hlt2_tracking.py |  48 ++-
 .../python/RecoConf/muon_reconstruction.py    |  17 +-
 .../python/RecoConf/protoparticles.py         |   9 +-
 .../python/RecoConf/reco_objects_from_file.py |   8 +-
 .../python/RecoConf/reconstruction_objects.py |  17 +-
 .../python/RecoConf/rich_reconstruction.py    |  10 +-
 .../python/RecoConf/ttrack_selections_reco.py |  12 +-
 13 files changed, 232 insertions(+), 288 deletions(-)

diff --git a/Hlt/Hlt2Conf/options/hlt2_2or3bodytopo_realtime.py b/Hlt/Hlt2Conf/options/hlt2_2or3bodytopo_realtime.py
index 3378058fe78..b2253f54faf 100644
--- a/Hlt/Hlt2Conf/options/hlt2_2or3bodytopo_realtime.py
+++ b/Hlt/Hlt2Conf/options/hlt2_2or3bodytopo_realtime.py
@@ -19,7 +19,7 @@ from RecoConf.global_tools import stateProvider_with_simplified_geom
 from RecoConf.reconstruction_objects import reconstruction
 
 from RecoConf.hlt2_global_reco import reconstruction as hlt2_reconstruction
-from RecoConf.hlt2_global_reco import make_light_reco_pr_kf
+from RecoConf.hlt2_global_reco import make_light_reco_pr_kf, make_light_reco_pr_kf_without_UT
 
 from Hlt2Conf.lines.topological_b import threebody_line, twobody_line
 
@@ -58,9 +58,10 @@ def make_lines():
     return [twobody_line(persistreco=True), threebody_line(persistreco=True)]
 
 
+my_reco = make_light_reco_pr_kf
+
 public_tools = [stateProvider_with_simplified_geom()]
 with hlt2_reconstruction.bind(
-        make_reconstruction=make_light_reco_pr_kf), reconstruction.bind(
-            from_file=False):
+        make_reconstruction=my_reco), reconstruction.bind(from_file=False):
     config = run_moore(
         options, make_lines, public_tools, exclude_incompatible=False)
diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/standard_particles.py b/Hlt/Hlt2Conf/python/Hlt2Conf/standard_particles.py
index 4fc429277b9..964b0cdb785 100644
--- a/Hlt/Hlt2Conf/python/Hlt2Conf/standard_particles.py
+++ b/Hlt/Hlt2Conf/python/Hlt2Conf/standard_particles.py
@@ -60,16 +60,15 @@ from PyConf.Algorithms import (
 from PyConf import configurable
 from RecoConf.reconstruction_objects import (
     make_pvs, make_charged_protoparticles as _make_charged_protoparticles,
-    make_neutral_protoparticles as _make_neutral_protoparticles,
-    make_upstream_charged_protoparticles as
-    _make_upstream_charged_protoparticles)
+    make_neutral_protoparticles as _make_neutral_protoparticles)
+
+from RecoConf.ttrack_selections_reco import make_ttrack_protoparticles
 
 from Hlt2Conf.algorithms_thor import ParticleCombiner, ParticleFilter
 import Functors as F
 from Functors.math import in_range
 
 from RecoConf.core_algorithms import make_unique_id_generator
-from RecoConf.ttrack_selections_reco import make_ttrack_protoparticles
 
 masses = {
     'pi0': 134.9768 *
@@ -121,13 +120,15 @@ def standard_protoparticle_filter(Code=F.ALL):
 def _make_particles(species,
                     CheckPID=True,
                     make_protoparticles=_make_charged_protoparticles,
-                    get_track_selector=get_long_track_selector,
+                    track_type=None,
+                    get_track_selector=get_all_track_selector,
                     make_protoparticle_filter=standard_protoparticle_filter):
     """ creates LHCb::Particles from LHCb::ProtoParticles """
     tp = get_track_selector()
     pp = make_protoparticle_filter()
     particles = FunctionalParticleMaker(
-        InputProtoParticles=make_protoparticles(),
+        InputProtoParticles=make_protoparticles()
+        if track_type is None else make_protoparticles(track_type=track_type),
         ParticleID=species,
         CheckPID=CheckPID,
         TrackPredicate=tp,
@@ -140,12 +141,13 @@ def _make_particles(species,
 def _make_ChargedBasics(
         species,
         make_protoparticles=_make_charged_protoparticles,
-        get_track_selector=get_long_track_selector,
+        track_type='Long',
+        get_track_selector=get_all_track_selector,
         make_protoparticle_filter=standard_protoparticle_filter):
     """ creates LHCb::v2::ChargedBasics from LHCb::ProtoParticles """
     particles = Proto2ChargedBasic(
         InputUniqueIDGenerator=make_unique_id_generator(),
-        InputProtoParticles=make_protoparticles(),
+        InputProtoParticles=make_protoparticles(track_type),
         ParticleID=species,
         TrackPredicate=get_track_selector(),
         ProtoParticlePredicate=make_protoparticle_filter(),
@@ -157,7 +159,7 @@ def _make_ChargedBasics(
 def _make_all_ChargedBasics(species):
     return _make_ChargedBasics(
         species=species,
-        get_track_selector=get_all_track_selector,
+        track_type=None,
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
@@ -165,7 +167,7 @@ def _make_all_ChargedBasics(species):
 def _make_long_ChargedBasics(species):
     return _make_ChargedBasics(
         species=species,
-        get_track_selector=get_long_track_selector,
+        track_type='Long',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
@@ -237,7 +239,7 @@ def make_photons(make_neutral_protoparticles=_make_neutral_protoparticles,
 def make_long_electrons_no_brem():
     return _make_particles(
         species="electron",
-        get_track_selector=get_long_track_selector,
+        track_type='Long',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
@@ -255,56 +257,56 @@ def make_long_electrons_with_brem(bremadder='SelectiveBremAdder'):
 def make_long_pions():
     return _make_particles(
         species="pion",
-        get_track_selector=get_long_track_selector,
+        track_type='Long',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_long_kaons():
     return _make_particles(
         species="kaon",
-        get_track_selector=get_long_track_selector,
+        track_type='Long',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_long_protons():
     return _make_particles(
         species="proton",
-        get_track_selector=get_long_track_selector,
+        track_type='Long',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_long_muons():
     return _make_particles(
         species="muon",
-        get_track_selector=get_long_track_selector,
+        track_type='Long',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_long_sigmaps():
     return _make_particles(
         species="sigmap",
-        get_track_selector=get_long_track_selector,
+        track_type='Long',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_long_sigmams():
     return _make_particles(
         species="sigmam",
-        get_track_selector=get_long_track_selector,
+        track_type='Long',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_long_xis():
     return _make_particles(
         species="xim",
-        get_track_selector=get_long_track_selector,
+        track_type='Long',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_long_omegas():
     return _make_particles(
         species="omegam",
-        get_track_selector=get_long_track_selector,
+        track_type='Long',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
@@ -319,40 +321,35 @@ def make_ismuon_long_muon():
 def make_up_electrons_no_brem():
     return _make_particles(
         species="electron",
-        make_protoparticles=_make_upstream_charged_protoparticles,
-        get_track_selector=get_upstream_track_selector,
+        track_type='Upstream',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_up_pions():
     return _make_particles(
         species="pion",
-        make_protoparticles=_make_upstream_charged_protoparticles,
-        get_track_selector=get_upstream_track_selector,
+        track_type='Upstream',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_up_kaons():
     return _make_particles(
         species="kaon",
-        make_protoparticles=_make_upstream_charged_protoparticles,
-        get_track_selector=get_upstream_track_selector,
+        track_type='Upstream',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_up_protons():
     return _make_particles(
         species="proton",
-        make_protoparticles=_make_upstream_charged_protoparticles,
-        get_track_selector=get_upstream_track_selector,
+        track_type='Upstream',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_up_muons():
     return _make_particles(
         species="muon",
-        make_protoparticles=_make_upstream_charged_protoparticles,
-        get_track_selector=get_upstream_track_selector,
+        track_type='Upstream',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
@@ -360,42 +357,42 @@ def make_up_muons():
 def make_down_pions():
     return _make_particles(
         species="pion",
-        get_track_selector=get_down_track_selector,
+        track_type='Downstream',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_down_kaons():
     return _make_particles(
         species="kaon",
-        get_track_selector=get_down_track_selector,
+        track_type='Downstream',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_down_protons():
     return _make_particles(
         species="proton",
-        get_track_selector=get_down_track_selector,
+        track_type='Downstream',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_down_electrons():
     return _make_particles(
         species="electron",
-        get_track_selector=get_down_track_selector,
+        track_type='Downstream',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_down_electrons_no_brem():
     return _make_particles(
         species="electron",
-        get_track_selector=get_down_track_selector,
+        track_type='Downstream',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
 def make_down_muons():
     return _make_particles(
         species="muon",
-        get_track_selector=get_down_track_selector,
+        track_type='Downstream',
         make_protoparticle_filter=standard_protoparticle_filter)
 
 
@@ -403,7 +400,6 @@ def make_down_muons():
 def make_ttrack_pions():
     return _make_particles(
         species="pion",
-        get_track_selector=get_ttrack_track_selector,
         make_protoparticles=make_ttrack_protoparticles,
         make_protoparticle_filter=standard_protoparticle_filter)
 
@@ -411,7 +407,6 @@ def make_ttrack_pions():
 def make_ttrack_protons():
     return _make_particles(
         species="proton",
-        get_track_selector=get_ttrack_track_selector,
         make_protoparticles=make_ttrack_protoparticles,
         make_protoparticle_filter=standard_protoparticle_filter)
 
diff --git a/Hlt/Hlt2Conf/tests/options/sprucing/spruce_check_persistreco.py b/Hlt/Hlt2Conf/tests/options/sprucing/spruce_check_persistreco.py
index 8de813582d8..858da13efe8 100644
--- a/Hlt/Hlt2Conf/tests/options/sprucing/spruce_check_persistreco.py
+++ b/Hlt/Hlt2Conf/tests/options/sprucing/spruce_check_persistreco.py
@@ -103,9 +103,9 @@ for ii in range(nevents):
     check_persistreco(TES, locations.values(), N=2, unexpected_locs=[])
 
     # Specific check for persistreco
-    persistedCharged = TES['/Event/Spruce/HLT2/Rec/ProtoP/Charged'].size()
-    persistedTracks = TES['/Event/Spruce/HLT2/Rec/Track/Best'].size()
-    if persistedTracks < 100 or persistedCharged < 100:
+    persistedCharged = TES['/Event/Spruce/HLT2/Rec/ProtoP/Long'].size()
+    persistedTracks = TES['/Event/Spruce/HLT2/Rec/Track/BestLong'].size()
+    if persistedTracks < 20 or persistedCharged < 20:
         error("persistreco objects not being saved correctly.")
 
     # Check the Rich (=9) RawBank is present
diff --git a/Hlt/Moore/python/Moore/persistence/truth_matching.py b/Hlt/Moore/python/Moore/persistence/truth_matching.py
index 79325870bf4..2faf309bc24 100644
--- a/Hlt/Moore/python/Moore/persistence/truth_matching.py
+++ b/Hlt/Moore/python/Moore/persistence/truth_matching.py
@@ -165,8 +165,8 @@ def _match_charged(charged, charged_brunel, mc_particles):
         tracks = _collect_dependencies(dh.producer, TRACK_CONTAINER_T)
         assert len(
             tracks
-        ) == 1, "Unexpected number of track dependencies - expected 1, got " + len(
-            tracks) + " instead"
+        ) == 1, "Unexpected number of track dependencies - expected 1, got " + str(
+            len(tracks)) + " instead"
         tracks = dict(v1=tracks.pop())
         assoc_tracks = mc_checking.make_links_tracks_mcparticles(
             InputTracks=tracks,
diff --git a/Hlt/RecoConf/python/RecoConf/data_from_file.py b/Hlt/RecoConf/python/RecoConf/data_from_file.py
index 1130e09643f..274b116bf60 100644
--- a/Hlt/RecoConf/python/RecoConf/data_from_file.py
+++ b/Hlt/RecoConf/python/RecoConf/data_from_file.py
@@ -53,10 +53,13 @@ def _packed_reco_from_file():
         'PackedCaloPhotons': '/Event/pRec/Calo/Photons',
         'PackedCaloMergedPi0s': '/Event/pRec/Calo/MergedPi0s',
         'PackedCaloSplitPhotons': '/Event/pRec/Calo/SplitPhotons',
-        'PackedTracks': '/Event/pRec/Track/Best',
+        'PackedLongTracks': '/Event/pRec/Track/BestLong',
+        'PackedDownstreamTracks': '/Event/pRec/Track/BestDownstream',
         'PackedUpstreamTracks': '/Event/pRec/Track/BestUpstream',
+        'PackedUnfittedTtracks': '/Event/pRec/Track/UnfittedTtrack',
         'PackedNeutralProtos': '/Event/pRec/ProtoP/Neutrals',
-        'PackedChargedProtos': '/Event/pRec/ProtoP/Charged',
+        'PackedLongProtos': '/Event/pRec/ProtoP/Long',
+        'PackedDownstreamProtos': '/Event/pRec/ProtoP/Downstream',
         'PackedUpstreamProtos': '/Event/pRec/ProtoP/Upstream',
     }
 
@@ -188,19 +191,32 @@ def reco_unpackers():
         ('CaloSplitPhotons',
          reco_unpacker('PackedCaloSplitPhotons', UnpackCaloHypo,
                        'UnpackCaloSplitPhotons')),
-        ('Tracks',
-         reco_unpacker('PackedTracks', UnpackTrack, 'UnpackBestTracks')),
+        ('LongTracks',
+         reco_unpacker('PackedLongTracks', UnpackTrack,
+                       'UnpackBestLongTracks')),
+        ('DownstreamTracks',
+         reco_unpacker('PackedDownstreamTracks', UnpackTrack,
+                       'UnpackBestDownstreamTracks')),
         ('UpstreamTracks',
          reco_unpacker('PackedUpstreamTracks', UnpackTrack,
                        'UnpackBestUpstreamTracks')),
+        ('UnfittedTtracks',
+         reco_unpacker('PackedUnfittedTtracks', UnpackTrack,
+                       'UnpackUnfittedTtracks')),
         ('NeutralProtos',
          reco_unpacker('PackedNeutralProtos', UnpackProtoParticle,
                        'UnpackNeutralProtos')),
-        ('ChargedProtos',
+        ('LongProtos',
          reco_unpacker(
-             'PackedChargedProtos',
+             'PackedLongProtos',
              UnpackProtoParticle,
-             'UnpackChargedProtos',
+             'UnpackLongProtos',
+         )),
+        ('DownstreamProtos',
+         reco_unpacker(
+             'PackedDownstreamProtos',
+             UnpackProtoParticle,
+             'UnpackDownstreamProtos',
          )),
         ('UpstreamProtos',
          reco_unpacker(
diff --git a/Hlt/RecoConf/python/RecoConf/hlt2_global_reco.py b/Hlt/RecoConf/python/RecoConf/hlt2_global_reco.py
index 5ecc18b9757..86f54fd82a8 100644
--- a/Hlt/RecoConf/python/RecoConf/hlt2_global_reco.py
+++ b/Hlt/RecoConf/python/RecoConf/hlt2_global_reco.py
@@ -1,5 +1,5 @@
 ###############################################################################
-# (c) Copyright 2020-2021 CERN for the benefit of the LHCb Collaboration      #
+# (c) Copyright 2020-2023 CERN for the benefit of the LHCb Collaboration      #
 #                                                                             #
 # This software is distributed under the terms of the GNU General Public      #
 # Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   #
@@ -14,7 +14,7 @@ The 'global' reconstruction is that which produces the final output of the
 'full' HLT2 reconstruction: charged and neutral ProtoParticle containers.
 """
 from .hlt1_tracking import make_reco_pvs, make_FTRawBankDecoder_clusters
-from .hlt2_tracking import make_hlt2_tracks, make_hlt2_tracks_without_UT, convert_tracks_to_v3_from_v1
+from .hlt2_tracking import make_hlt2_tracks, make_hlt2_tracks_without_UT, convert_tracks_to_v3_from_v1, get_persisted_tracks_per_type
 from .rich_reconstruction import make_rich_pids, make_all_rich_pids, default_rich_reco_options, make_merged_rich_pids
 from .calorimeter_reconstruction import make_calo
 from .muon_reconstruction import make_all_muon_pids, make_merged_muon_pids, make_conv_muon_pids
@@ -25,85 +25,11 @@ from .protoparticles import (
 )
 
 from PyConf import configurable
-from PyConf.packing import persisted_location
+from PyConf.packing import persisted_location, persisted_proto_track_types
 from PyConf.Algorithms import (
     TrackContainerCopy, RecSummaryMaker, TracksEmptyProducer,
-    TrackContainersMergerShared, PVsEmptyProducer, RecVertexEmptyProducer)
-
-
-# TODO make things configurable, if needed
-@configurable
-def make_legacy_reconstruction():
-    """Return reconstruction objects of the default reconstruction.
-    """
-    # Tracks
-    hlt2_tracks = make_hlt2_tracks()
-    best_tracks = hlt2_tracks["Best"]
-    velo_tracks = hlt2_tracks["Velo"]
-
-    rich_and_charged_proto_track_types = ["Long", "Downstream"]
-
-    # PVs
-    pvs = make_reco_pvs(velo_tracks, persisted_location('PVs'))
-
-    # RICH
-    richRecConfs = make_all_rich_pids(best_tracks, default_rich_reco_options())
-    # Merge them for now to be compatible with Brunel.
-    rich_pids = make_merged_rich_pids(richRecConfs)
-
-    # Calo, enable v3 track versions
-    tracks_v3, trackrels = convert_tracks_to_v3_from_v1(
-        best_tracks["v1"],
-        track_types=rich_and_charged_proto_track_types + ["Ttrack"])
-    calo_pids = make_calo(tracks_v3, pvs["v3"], trackrels=trackrels)
-
-    # Muons
-    muonRecConfs = make_all_muon_pids(
-        tracks=tracks_v3, track_types=rich_and_charged_proto_track_types)
-    # Convert them to KeyedContainer for ProtoParticle storage
-    muon_conv, muon_tracks = make_conv_muon_pids(
-        muonRecConfs,
-        best_tracks,
-        track_types=rich_and_charged_proto_track_types)
-    # Merge them for now to be compatible with Brunel.
-    muon_pids = make_merged_muon_pids(muon_conv)
-    muonpid_tracklists = [muon_tracks['Long'], muon_tracks['Downstream']]
-    merge_muontracks = TrackContainerCopy(
-        name="CreateMuonPIDTrackContainer_{hash}",
-        inputLocations=muonpid_tracklists).outputLocation
-
-    charged_protos = make_charged_protoparticles_from(
-        tracks=best_tracks,
-        rich_pids=rich_pids,
-        calo_pids=calo_pids,
-        muon_pids=muon_pids,
-        track_types=rich_and_charged_proto_track_types,
-        location=persisted_location('ChargedProtos'))
-
-    neutral_protos = make_neutral_protoparticles_from(
-        calo_pids=calo_pids, location=persisted_location('NeutralProtos'))
-
-    return {
-        "ChargedProtos": charged_protos,
-        "UpstreamProtos": charged_protos,
-        "NeutralProtos": neutral_protos['ProtoParticleLocation'],
-        "IsPhoton": neutral_protos['IsPhoton'],
-        "IsNotH": neutral_protos['IsNotH'],
-        "AllTrackHandles": hlt2_tracks,
-        "Tracks": best_tracks["v1"],
-        "VeloTracks": velo_tracks["v1"],
-        "MuonPIDTracks": merge_muontracks,
-        "PVs": pvs["v3"],
-        "PVs_v1": pvs["v1"],
-        # The full data flow is functional, we only keep this for compatibility with reco from file.
-        "UpfrontReconstruction": [],
-        "CaloElectrons": calo_pids["v1_electrons"],
-        "CaloPhotons": calo_pids["v1_photons"],
-        "CaloMergedPi0s": calo_pids["v1_mergedPi0s"],
-        "CaloSplitPhotons": calo_pids["v1_splitPhotons"],
-        "MuonPIDs": muon_pids,
-        "RichPIDs": rich_pids,
-    }
+    TrackContainersMergerShared, PVsEmptyProducer, RecVertexEmptyProducer,
+    ProtoContainersSharedMerger)
 
 
 @configurable
@@ -117,113 +43,79 @@ def make_light_reconstruction(usePatPVFuture=False,
     """Return reconstruction objects of the fastest or the light reconstruction, with possibility to skip the UT
 
     """
-    # Tracks
-    merger_list = []
-    rich_and_charged_proto_track_types = []
-    if skipUT:
-        hlt2_tracks = make_hlt2_tracks_without_UT(
-            light_reco=True, fast_reco=fastReco, use_pr_kf=use_pr_kf)
-        merger_list = [
-            hlt2_tracks["BestLong"]["v1"], hlt2_tracks["SeedDecloned"]["v1"]
-        ]
-        rich_and_charged_proto_track_types = ["Long"]
-    else:
-        hlt2_tracks = make_hlt2_tracks(
-            light_reco=True, fast_reco=fastReco, use_pr_kf=use_pr_kf)
-        merger_list = [
-            hlt2_tracks["BestLong"]["v1"], hlt2_tracks["BestDownstream"]["v1"],
-            hlt2_tracks["SeedDecloned"]["v1"]
-        ]
-        rich_and_charged_proto_track_types = ["Long", "Downstream"]
-
-    velo_tracks = hlt2_tracks["Velo"]
-
-    merge_tracks = TrackContainerCopy(
+    rich_and_charged_proto_track_types = ["Long"] if skipUT else [
+        "Long", "Downstream", "Upstream"
+    ]
+    hlt2_tracks_maker = make_hlt2_tracks_without_UT if skipUT else make_hlt2_tracks
+    hlt2_tracks = hlt2_tracks_maker(
+        light_reco=True, fast_reco=fastReco, use_pr_kf=use_pr_kf)
+
+    persisted_tracks = get_persisted_tracks_per_type(hlt2_tracks, persist=True)
+    velo_tracks = hlt2_tracks['Velo']
+
+    best_tracks = TrackContainersMergerShared(
         name="CreateBestTrackContainer_{hash}",
-        inputLocations=merger_list,
-        outputs={'outputLocation': persisted_location('Tracks')})
-
-    best_tracks = {"v1": merge_tracks.outputLocation}
-
-    tracks_v3, trackrels = convert_tracks_to_v3_from_v1(
-        best_tracks["v1"],
-        track_types=rich_and_charged_proto_track_types + ["Ttrack"])
-
-    # split/unmerged track conversion
-    # TODO should be for all track types like this (or rather within the convert function)
-    best_upstream = hlt2_tracks.get(
-        'BestUpstream', {"v1": TracksEmptyProducer(name="FakeUpstreamTracks")})
-
-    upstream_tracks = TrackContainerCopy(
-        name="CreateBestUpstreamContainer_{hash}",
-        inputLocations=[best_upstream['v1']],
-        outputs={
-            'outputLocation': persisted_location('UpstreamTracks')
-        }).outputLocation
-
-    upstream_v3, upstream_rels = convert_tracks_to_v3_from_v1(
-        upstream_tracks,
-        track_types=["Upstream"],
-    )
-    tracks_v3.update(upstream_v3)
-    trackrels.update(upstream_rels)
+        InputLocations=list(persisted_tracks.values())).OutputLocation
+
+    # track conversion to v3 for muon / calo
+    tracks_v3, tracks_rels = convert_tracks_to_v3_from_v1(persisted_tracks)
 
     # PVs
     pvs = make_reco_pvs(velo_tracks, persisted_location('PVs'))
 
-    # RICH
-    rich_pids = {'Merged': None, 'Upstream': None}
-    if not skipRich:
-        rich_options = default_rich_reco_options()
-        richRecConfs = make_all_rich_pids(
-            best_tracks,
-            rich_options,
-            make_pids=make_rich_pids,
-            track_types=rich_and_charged_proto_track_types)
-        # specifically for optional upstream
-        rich_pids['Upstream'] = make_rich_pids('Upstream', upstream_tracks,
-                                               rich_options)['RichPIDs']
-        # Merge them for now to be backwards compatible.
-        rich_pids['Merged'] = make_merged_rich_pids(
-            richRecConfs, rich_and_charged_proto_track_types, location=None)
-
-    # Calo, enable v3 track versions
+    # calorimeter
     calo_pids = make_calo(
-        tracks_v3, pvs["v3"], trackrels=trackrels) if not skipCalo else None
+        tracks_v3, pvs["v3"], trackrels=tracks_rels) if not skipCalo else None
 
     # Muons
-    muon_pids = None
+    muon_pids = {}
+    muon_tracks = {}
     if not skipMuon:
         muonRecConfs = make_all_muon_pids(
             tracks=tracks_v3, track_types=rich_and_charged_proto_track_types)
-        muon_conv, muon_split_tracks = make_conv_muon_pids(
+        muon_pids, muon_tracks = make_conv_muon_pids(
             muonRecConfs,
-            best_tracks,
+            persisted_tracks,
             track_types=rich_and_charged_proto_track_types)
-        muon_tracks = TrackContainersMergerShared(
+        # merged versions
+        muon_tracks['Merged'] = TrackContainersMergerShared(
             name="CreateMuonPIDTrackContainer_{hash}",
-            InputLocations=list(muon_split_tracks.values())).OutputLocation
-        muon_pids = make_merged_muon_pids(muon_conv)
-
-    # default charged protoparticles
-    charged_protos = make_charged_protoparticles_from(
-        tracks=best_tracks,
-        rich_pids=rich_pids['Merged'],
-        calo_pids=calo_pids,
-        muon_pids=muon_pids,
-        track_types=rich_and_charged_proto_track_types,
-        location=persisted_location('ChargedProtos'))
-
-    # optional/split upstream protoparticles
-    # TODO should be for all track types like this
-    upstream_protos = make_charged_protoparticles_from(
-        name="FunctionalChargedProtoParticleMakerUpstream_{hash}",
-        tracks={"v1": upstream_tracks},
-        rich_pids=rich_pids['Upstream'],
-        calo_pids=calo_pids,
-        muon_pids=None,
-        track_types=['Upstream'],
-        location=persisted_location('UpstreamProtos'))
+            InputLocations=list(muon_tracks.values())).OutputLocation
+        muon_pids['Merged'] = make_merged_muon_pids(muon_pids)
+
+    # RICH
+    rich_pids = {}
+    if not skipRich:
+        rich_options = default_rich_reco_options()
+        for track_type in rich_and_charged_proto_track_types:
+            rich_pids[track_type] = make_rich_pids(
+                track_type,
+                persisted_tracks[track_type],
+                rich_options,
+                location=persisted_location(
+                    f'{track_type}RichPIDs'))['RichPIDs']
+        # merged versions
+        rich_pids['Merged'] = make_merged_rich_pids(
+            rich_pids, rich_and_charged_proto_track_types,
+            location=None) if not skipRich else None
+
+    # split-per-track-type protoparticles
+    charged_protos = {}
+    for track_type in rich_and_charged_proto_track_types:
+        charged_protos[track_type] = make_charged_protoparticles_from(
+            name=f"FunctionalChargedProtoParticleMaker{track_type}" +
+            "_{hash}",
+            tracks=persisted_tracks[track_type],
+            rich_pids=rich_pids.get(track_type, None),
+            calo_pids=calo_pids,
+            muon_pids=muon_pids.get(track_type, None),
+            track_types=[track_type],
+            location=persisted_location(f'{track_type}Protos'))
+
+    # merged access to all (split) protos (sharedobjectscontainer)
+    charged_protos['Merged'] = ProtoContainersSharedMerger(
+        name="CreateChargedProtosContainer_{hash}",
+        InputLocations=list(charged_protos.values())).OutputLocation
 
     # neutral protoparticles
     neutral_protos = make_neutral_protoparticles_from(
@@ -234,47 +126,44 @@ def make_light_reconstruction(usePatPVFuture=False,
     ####
     output = {
         "AllTrackHandles": hlt2_tracks,
-        "Tracks": best_tracks["v1"],
-        "UpstreamTracks": upstream_tracks,
+        "Tracks": best_tracks,
         "VeloTracks": velo_tracks["v1"],
+        "UnfittedTtracks": persisted_tracks['UnfittedTtrack'],
         "PVs": pvs["v3"],
         "PVs_v1": pvs["v1"],
-        "ChargedProtos": charged_protos,
-        "UpstreamProtos": upstream_protos,
+        "RichPIDs": rich_pids.get('Merged', None),
+        "MuonPIDs": muon_pids.get('Merged', None),
+        "MuonPIDTracks": muon_tracks.get('Merged', None),
+        "ChargedProtos": charged_protos['Merged'],
         "NeutralProtos": neutral_protos['ProtoParticleLocation'],
         "IsPhoton": neutral_protos['IsPhoton'],
         "IsNotH": neutral_protos['IsNotH'],
         "UpfrontReconstruction": []
     }
 
-    if skipUT:
-        extra_tracks_dict = {
-            "BestLong": hlt2_tracks["BestLong"],
-            "BestSeed": hlt2_tracks["BestSeed"]
-        }
+    for track_type, tracks in persisted_tracks.items():
+        output[f'{track_type}Tracks'] = tracks
 
-        output.update(extra_tracks_dict)
+    for track_type, protos in charged_protos.items():
+        output[f'{track_type}RichPIDs'] = rich_pids.get(track_type, None)
+        output[f'{track_type}MuonPIDs'] = muon_pids.get(track_type, None)
+        output[f'{track_type}Protos'] = protos
 
     if not skipCalo:
-        caloDict = {
+        output.update({
             "CaloElectrons": calo_pids["v1_electrons"],
             "CaloPhotons": calo_pids["v1_photons"],
             "CaloMergedPi0s": calo_pids["v1_mergedPi0s"],
             "CaloSplitPhotons": calo_pids["v1_splitPhotons"]
-        }
-
-        output.update(caloDict)
+        })
 
-    if not skipRich:
-        richDict = {
-            "RichPIDs": rich_pids['Merged'],
-            "UpstreamRichPIDs": rich_pids['Upstream'],
-        }
-        output.update(richDict)
+    # set persistence accordingly
+    persisted_proto_track_types.global_bind(
+        track_types=rich_and_charged_proto_track_types)
 
-    if not skipMuon:
-        muonDict = {"MuonPIDTracks": muon_tracks, "MuonPIDs": muon_pids}
-        output.update(muonDict)
+    # for now to keep ttrack config as is alive
+    if "BestSeed" in hlt2_tracks:
+        output["BestSeed"] = hlt2_tracks["BestSeed"]["v1"]
 
     return output
 
@@ -367,14 +256,14 @@ def reconstruction(make_reconstruction=make_light_reco_pr_kf_without_UT):
     return reco_output
 
 
-def make_charged_protoparticles():
+def make_charged_protoparticles(track_type):
     """Return a DataHandle to the container of charged ProtoParticles.
 
     The charged ProtoParticle making is not functional, and so the sequence of algorithms
     which produces the full information at this location needs to be scheduled explicitly
     by including the `upfront_reconstruction` method in the file in the control flow.
     """
-    return reconstruction()["ChargedProtos"]
+    return reconstruction()[track_type]
 
 
 def upfront_reconstruction():
diff --git a/Hlt/RecoConf/python/RecoConf/hlt2_tracking.py b/Hlt/RecoConf/python/RecoConf/hlt2_tracking.py
index 6d088a8809e..6f3e8ac38d1 100644
--- a/Hlt/RecoConf/python/RecoConf/hlt2_tracking.py
+++ b/Hlt/RecoConf/python/RecoConf/hlt2_tracking.py
@@ -26,6 +26,7 @@ from RecoConf.hlt1_tracking import (
     make_PrStorePrUTHits_empty_hits, make_PrStoreUTHit_empty_hits,
     get_global_measurement_provider)
 
+from PyConf.packing import persisted_location
 from PyConf.Algorithms import (
     PrForwardTrackingVelo, PrForwardTracking, PrVeloUT, PrHybridSeeding,
     PrMatchNN, PrCheatedSciFiTracking, PrLongLivedTracking,
@@ -1489,15 +1490,56 @@ def get_fast_hlt2_tracks_without_UT():
     }
 
 
+def get_persisted_tracks_per_type(hlt2_tracks, persist=True):
+    """Return dictionary of best tracks per track type
+
+    """
+    persisted_tracks_config = {
+        'Long': {
+            'key': 'BestLong',
+            'location': 'LongTracks'
+        },
+        'Downstream': {
+            'key': 'BestDownstream',
+            'location': 'DownstreamTracks'
+        },
+        'Upstream': {
+            'key': 'BestUpstream',
+            'location': 'UpstreamTracks'
+        },
+        'UnfittedTtrack': {
+            'key': 'SeedDecloned',
+            'location': 'UnfittedTtracks'
+        },
+    }
+
+    tracks = {}
+    for track_type, config in persisted_tracks_config.items():
+        if config['key'] in hlt2_tracks:
+            # TODO preferably this should be at the moment of producing
+            # or algorithm that just copies something to other location
+            in_tracks = hlt2_tracks[config['key']]['v1']
+            tracks[track_type] = TrackContainersMerger(
+                name=f"CreateBest{track_type}Container" + "_{hash}",
+                InputLocations=[in_tracks],
+                outputs={
+                    'OutputLocation': persisted_location(config['location'])
+                }).OutputLocation if persist else in_tracks
+    return tracks
+
+
 def convert_tracks_to_v3_from_v1(tracks_v1,
-                                 track_types,
+                                 track_types=None,
                                  shared_container=False):
     tracks = dict()
     trackrels = dict()
     Converter = TrackSOAFromSharedV1 if shared_container else TrackSOAFromV1
-    for tracktype in track_types:
+    relevant_track_types = track_types if track_types is not None else list(
+        tracks_v1.keys())
+    for tracktype in relevant_track_types:
         trackconverter = Converter(
-            InputTracks=tracks_v1,
+            InputTracks=tracks_v1[tracktype]
+            if type(tracks_v1) is dict else tracks_v1,
             InputUniqueIDGenerator=make_unique_id_generator(),
             RestrictToType=tracktype)
         tracks[tracktype] = trackconverter.OutputTracks
diff --git a/Hlt/RecoConf/python/RecoConf/muon_reconstruction.py b/Hlt/RecoConf/python/RecoConf/muon_reconstruction.py
index 538fb7b1646..533b117c12f 100644
--- a/Hlt/RecoConf/python/RecoConf/muon_reconstruction.py
+++ b/Hlt/RecoConf/python/RecoConf/muon_reconstruction.py
@@ -17,6 +17,7 @@ from PyConf.Algorithms import (
 
 from .hlt2_muonid import make_muon_ids
 from RecoConf.core_algorithms import make_unique_id_generator
+from PyConf.packing import persisted_location
 
 
 @configurable
@@ -46,7 +47,7 @@ def make_all_muon_pids(tracks, track_types=['Long', 'Downstream']):
 
 def make_conv_muon_pids(muonPidsConfs,
                         best_tracks,
-                        light_reco=False,
+                        light_reco=True,
                         track_types=['Long', 'Downstream'],
                         output_locations={
                             'Tracks': {},
@@ -54,17 +55,19 @@ def make_conv_muon_pids(muonPidsConfs,
                         }):
     muon_pids_v1 = dict()
     muon_tracks = dict()
-    for track_type in track_types:
+    relevant_track_types = list(set(track_types) & {'Long', 'Downstream'})
+    for track_type in relevant_track_types:
         # Add muon hits of MuonPID to "muontracks"
         muon_tracks[track_type] = MuonPIDV2ToMuonTracks(
             name="MuonPIDV2ToMuonTracks_" + track_type + "_{hash}",
             InputMuonPIDs=muonPidsConfs[track_type],
-            InputTracks=best_tracks[track_type]["v1"]
+            InputTracks=best_tracks[track_type]
             if light_reco else best_tracks['v1'],
             RestrictToType=track_type,
             outputs={
                 'OutputMuonTracks':
-                output_locations['Tracks'].get(track_type, None)
+                persisted_location(f'{track_type}MuonTracks')
+                #output_locations['Tracks'].get(track_type, None)
             }).OutputMuonTracks
 
         # Convert to Keyed Container for ProtoParticle
@@ -72,12 +75,12 @@ def make_conv_muon_pids(muonPidsConfs,
             name="fromV2MuonPIDV1MuonPID" + track_type + "_{hash}",
             InputMuonPIDs=muonPidsConfs[track_type],
             InputMuonTracks=muon_tracks[track_type],
-            InputTracks=best_tracks[track_type]["v1"]
+            InputTracks=best_tracks[track_type]
             if light_reco else best_tracks['v1'],
             RestrictToType=track_type,
             outputs={
-                'OutputMuonPIDs': output_locations['PIDs'].get(
-                    track_type, None)
+                'OutputMuonPIDs': persisted_location(f'{track_type}MuonPIDs')
+                #output_locations['PIDs'].get(track_type, None)
             }).OutputMuonPIDs
     return muon_pids_v1, muon_tracks
 
diff --git a/Hlt/RecoConf/python/RecoConf/protoparticles.py b/Hlt/RecoConf/python/RecoConf/protoparticles.py
index 5ac6b3758a5..0e22b78545f 100644
--- a/Hlt/RecoConf/python/RecoConf/protoparticles.py
+++ b/Hlt/RecoConf/python/RecoConf/protoparticles.py
@@ -1,5 +1,5 @@
 ###############################################################################
-# (c) Copyright 2019 CERN for the benefit of the LHCb Collaboration           #
+# (c) Copyright 2023 CERN for the benefit of the LHCb Collaboration           #
 #                                                                             #
 # This software is distributed under the terms of the GNU General Public      #
 # Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING".   #
@@ -107,6 +107,9 @@ def make_charged_protoparticles(tracks,
         rich_pids: input rich_pids created from tracks
         calo_pids: input calo_pids from Calorimeter reconstruction
         muon_pids: input muon_pids created from tracks
+        track_types: accepted track types
+        location: location of output (for persistency etc.)
+        name: name of algorithm
 
     Returns:
         dict with DataHandle to the container of charged ProtoParticles and the sequence of algorithms to create them.
@@ -180,6 +183,7 @@ def make_charged_protoparticles(tracks,
     if ("Upstream" in track_types and (rich_pids is not None)) or (
         ("Downstream" in track_types or "Long" in track_types) and
         (calo_pids is not None) and (rich_pids is not None) and
+        (calo_pids is not None) and (rich_pids is not None) and
         (muon_pids is not None)):
         # Set up ann pids
         addInfo += [
@@ -197,8 +201,7 @@ def make_charged_protoparticles(tracks,
     is_list = type(tracks) is list
     charged_protos = FunctionalChargedProtoParticleMaker(
         name=name,
-        Inputs=[track["v1"]
-                for track in tracks] if is_list else [tracks["v1"]],
+        Inputs=[track["v1"] for track in tracks] if is_list else [tracks],
         Code=F.require_any(*[track_predicates[i] for i in track_types]),
         AddInfo=addInfo,
         outputs={'Output': location})
diff --git a/Hlt/RecoConf/python/RecoConf/reco_objects_from_file.py b/Hlt/RecoConf/python/RecoConf/reco_objects_from_file.py
index 569b0fc7c32..188cd7e9a92 100644
--- a/Hlt/RecoConf/python/RecoConf/reco_objects_from_file.py
+++ b/Hlt/RecoConf/python/RecoConf/reco_objects_from_file.py
@@ -41,8 +41,8 @@ def reconstruction():
     return m
 
 
-def make_charged_protoparticles():
-    return reconstruction()['ChargedProtos']
+def make_charged_protoparticles(track_type='Charged'):
+    return reconstruction()[f'{track_type}Protos']
 
 
 def make_upstream_charged_protoparticles():
@@ -89,5 +89,5 @@ def make_pvs():
     return reconstruction()['PVs']
 
 
-def make_tracks():
-    return reconstruction()['Tracks']
+def make_tracks(track_type='Tracks'):
+    return reconstruction()[track_type]
diff --git a/Hlt/RecoConf/python/RecoConf/reconstruction_objects.py b/Hlt/RecoConf/python/RecoConf/reconstruction_objects.py
index 66dfe89b2f5..b75384f7621 100644
--- a/Hlt/RecoConf/python/RecoConf/reconstruction_objects.py
+++ b/Hlt/RecoConf/python/RecoConf/reconstruction_objects.py
@@ -62,18 +62,11 @@ def upfront_reconstruction():
     return reconstruction()["UpfrontReconstruction"]
 
 
-def make_charged_protoparticles():
+def make_charged_protoparticles(track_type='Charged'):
     """Return a DataHandle to the container of charged ProtoParticles.
 
     """
-    return reconstruction()["ChargedProtos"]
-
-
-def make_upstream_charged_protoparticles():
-    """Return a DataHandle to the container of upstream ProtoParticles.
-
-    """
-    return reconstruction()['UpstreamProtos']
+    return reconstruction()[f'{track_type}Protos']
 
 
 def make_neutral_protoparticles():
@@ -129,11 +122,11 @@ def make_pvs_v1():
     return reconstruction()["PVs_v1"]
 
 
-def make_tracks():
-    """Return a DataHandle to the container of all tracks
+def make_tracks(track_type='Tracks'):
+    """Return a DataHandle to the container of (all) tracks
 
     """
-    return reconstruction()["Tracks"]
+    return reconstruction()[track_type]
 
 
 def make_rich_pids():
diff --git a/Hlt/RecoConf/python/RecoConf/rich_reconstruction.py b/Hlt/RecoConf/python/RecoConf/rich_reconstruction.py
index 089cd806dfd..8d33847ac88 100644
--- a/Hlt/RecoConf/python/RecoConf/rich_reconstruction.py
+++ b/Hlt/RecoConf/python/RecoConf/rich_reconstruction.py
@@ -601,6 +601,9 @@ def make_rich_pids(track_name,
         dict of useful data locations.
     """
 
+    # make sure track names are consistent
+    if track_name is 'Ttrack': track_name = 'Seed'
+
     # Configure the particle properties
     configure_rich_particle_properties(options)
 
@@ -756,14 +759,11 @@ def make_all_rich_pids(best_tracks,
     return richRecConfs
 
 
-def make_merged_rich_pids(richRecConfs,
+def make_merged_rich_pids(rich_pids,
                           track_types=['Long', 'Downstream'],
                           location=None):
     merged_pids = MergePIDs(
-        InputLocations=[
-            conf["RichPIDs"] for track_type, conf in richRecConfs.items()
-            if track_type in track_types
-        ],
+        InputLocations=[v for k, v in rich_pids.items() if k in track_types],
         outputs={
             'OutputLocation': location
         }).OutputLocation
diff --git a/Hlt/RecoConf/python/RecoConf/ttrack_selections_reco.py b/Hlt/RecoConf/python/RecoConf/ttrack_selections_reco.py
index f22846814f9..87756b58130 100644
--- a/Hlt/RecoConf/python/RecoConf/ttrack_selections_reco.py
+++ b/Hlt/RecoConf/python/RecoConf/ttrack_selections_reco.py
@@ -55,10 +55,11 @@ def make_good_ttracks(global_reco=reconstruction,
                       filter_code=F.require_all(F.TRACKISTTRACK, F.P > 2000,
                                                 F.P < 500000),
                       refit_tracks=True):
-    if "BestLong" in global_reco() and "BestSeed" in global_reco():
+    reco = global_reco()
+    if "LongTracks" and "BestSeed" in reco:
         # this is for the default reconstruction in Hlt2. Sprucing not currently supported
-        best_long = global_reco()["BestLong"]["v1"]
-        fitted_seed = global_reco()["BestSeed"]["v1"]
+        best_long = reco["LongTracks"]
+        fitted_seed = reco["BestSeed"]
 
         best_seed = kill_clones(
             inputTracks=fitted_seed, referenceTracks=[best_long])
@@ -146,7 +147,8 @@ def make_ttrack_reco(global_reco, make_ttracks, skipRich=False,
 
 # define protoparticle maker for T tracks that uses the above reconstruction
 @configurable
-def make_ttrack_protoparticles(name="TTrackSel_ProtoParticleMaker",
+def make_ttrack_protoparticles(track_type='Ttrack',
+                               name="TTrackSel_ProtoParticleMaker",
                                make_ttrack_reco=make_ttrack_reco,
                                make_ttracks=make_good_ttracks,
                                make_global_reco=reconstruction):
@@ -158,7 +160,7 @@ def make_ttrack_protoparticles(name="TTrackSel_ProtoParticleMaker",
     ttrack_reco = make_ttrack_reco(global_reco, make_ttracks)
 
     return make_charged_protoparticles(
-        tracks=ttrack_reco["good_ttracks"],
+        tracks=ttrack_reco["good_ttracks"]['v1'],
         rich_pids=ttrack_reco["rich_pids"],
         calo_pids=ttrack_reco["calo_pids"],
         muon_pids=None,  # TODO: add muon PIDs when compatible with T tracks
-- 
GitLab