diff --git a/CaloFuture/CaloFutureMoniDst/src/ChargedPIDsChecker.cpp b/CaloFuture/CaloFutureMoniDst/src/ChargedPIDsChecker.cpp
index 4515b4404a1fff9b7e0c72185455230966eac7c7..0e467bbe7213f8ec5a7aa928c6cf9c8d071fd250 100644
--- a/CaloFuture/CaloFutureMoniDst/src/ChargedPIDsChecker.cpp
+++ b/CaloFuture/CaloFutureMoniDst/src/ChargedPIDsChecker.cpp
@@ -30,7 +30,7 @@ namespace LHCb::Calo::Algorithms {
 
   namespace {
     using Track      = LHCb::Event::v1::Track;
-    using ChargedPID = LHCb::Event::Calo::v1::ChargedPID;
+    using ChargedPID = LHCb::Event::Calo::v1::CaloChargedPID;
     using BremInfo   = LHCb::Event::Calo::v1::BremInfo;
 
     using Tracks      = LHCb::Event::v1::Tracks;
diff --git a/CaloFuture/CaloFutureReco/python/CaloFutureReco/Configuration.py b/CaloFuture/CaloFutureReco/python/CaloFutureReco/Configuration.py
index e1902efd6f88b02f7e12eccaa35b2298e50d4ae6..4eb37d5888577831271d55cb5150baaeeffc1a0e 100644
--- a/CaloFuture/CaloFutureReco/python/CaloFutureReco/Configuration.py
+++ b/CaloFuture/CaloFutureReco/python/CaloFutureReco/Configuration.py
@@ -611,11 +611,11 @@ class CaloFutureProcessor(CaloFutureRecoConf, LHCbConfigurableUser):
                     AttributeError, 'PositionEnergyMasks contains unknown tag '
                     + tag + ' -should be in' + knownMasks)
 
-        from Configurables import (
-            GaudiSequencer, ChargedProtoParticleAddEcalInfo,
-            ChargedProtoParticleAddBremInfo, ChargedProtoParticleAddHcalInfo,
-            ChargedProtoParticleAddCombineDLLs,
-            FunctionalChargedProtoParticleMaker)
+        from Configurables import (GaudiSequencer,
+                                   ChargedProtoParticleAddCaloInfo,
+                                   ChargedProtoParticleAddBremInfo,
+                                   ChargedProtoParticleAddCombineDLLs,
+                                   FunctionalChargedProtoParticleMaker)
 
         fullSeq = []
 
@@ -711,27 +711,26 @@ class CaloFutureProcessor(CaloFutureRecoConf, LHCbConfigurableUser):
                 maker.AddInfo += [t]
                 return t
 
-            ecal = addInfo(ChargedProtoParticleAddEcalInfo, "AddEcal")
+            calos = addInfo(ChargedProtoParticleAddCaloInfo, "AddCalo")
 
             from Configurables import LHCb__Converters__Calo__Hypo__v1__fromV2 as HypoConverter
-            ecal_converter1 = HypoConverter("HypoConverter_" + ecal.getName())
+            ecal_converter1 = HypoConverter("HypoConverter_" + calos.getName())
             ecal_converter1.InputHypos = "PleaseSetMeCorrectly"
             ecal_converter1.InputClusters = "Rec/Converted/Calo/EcalClusters"
-            ecal_converter1.OutputHypos = "/Event/Rec/Converted/" + ecal.getName(
+            ecal_converter1.OutputHypos = "/Event/Rec/Converted/" + calos.getName(
             ) + "/Hypos"
 
             from Configurables import LHCb__Converters__Calo__Hypo2TrackTable__v1__fromV2 as HypoTableConverter
             ecal_converter2 = HypoTableConverter("HypoTableConveter_" +
-                                                 ecal.getName())
-            ecal_converter2.InputTable = ecal.InputElectronMatchLocation
+                                                 calos.getName())
+            ecal_converter2.InputTable = calos.InputElectronMatchLocation
             ecal_converter2.InputHypotheses = ecal_converter1.OutputHypos
-            ecal_converter2.OutputTable = "/Event/Rec/Converted/" + ecal.getName(
+            ecal_converter2.OutputTable = "/Event/Rec/Converted/" + calos.getName(
             ) + "/HypoTable"
 
-            ecal.InputElectronMatchLocation = ecal_converter2.OutputTable  # HypoTrTable2D
+            calos.InputElectronMatchLocation = ecal_converter2.OutputTable  # HypoTrTable2D
 
             brem = addInfo(ChargedProtoParticleAddBremInfo, "AddBrem")
-            hcal = addInfo(ChargedProtoParticleAddHcalInfo, "AddHcal")
             comb = addInfo(ChargedProtoParticleAddCombineDLLs,
                            "AddCombineDLLs")
 
diff --git a/CaloFuture/CaloFutureTools/src/ChargedPIDsConverter.cpp b/CaloFuture/CaloFutureTools/src/ChargedPIDsConverter.cpp
index bc59d12c03578a20c46953001f61314c589bef04..362d847ece71458429cb1bfb887333fb520134ac 100644
--- a/CaloFuture/CaloFutureTools/src/ChargedPIDsConverter.cpp
+++ b/CaloFuture/CaloFutureTools/src/ChargedPIDsConverter.cpp
@@ -55,7 +55,7 @@ namespace LHCb::Calo {
   // ============================================================================
   // Specific class definitions
   // ============================================================================
-  using ChargedPID_v1 = LHCb::Event::Calo::v1::ChargedPID;
+  using ChargedPID_v1 = LHCb::Event::Calo::v1::CaloChargedPID;
   using ChargedPID_v2 = LHCb::Event::Calo::v2::ChargedPID;
   using BremInfo_v1   = LHCb::Event::Calo::v1::BremInfo;
   using BremInfo_v2   = LHCb::Event::Calo::v2::BremInfo;
diff --git a/Phys/FlavourTagging/src/Run2OSElectronTagger.cpp b/Phys/FlavourTagging/src/Run2OSElectronTagger.cpp
index d55b89df9c38cad16fd44036178e3152e714cc04..fa9ee833046784fb2c7c9fa842e9b8d3cf1c2fb4 100644
--- a/Phys/FlavourTagging/src/Run2OSElectronTagger.cpp
+++ b/Phys/FlavourTagging/src/Run2OSElectronTagger.cpp
@@ -170,11 +170,12 @@ std::optional<LHCb::Tagger> Run2OSElectronTagger::performTagging( const LHCb::Pa
     if ( tagEOverP > m_maxEOverP ) continue;
 
     const bool   isMuon      = tagProto->muonPID() ? tagProto->muonPID()->IsMuon() : false;
-    const double tagProbNNk  = tagProto->info( LHCb::ProtoParticle::ProbNNk, -1000.0 );
-    const double tagProbNNpi = tagProto->info( LHCb::ProtoParticle::ProbNNpi, -1000.0 );
-    const double tagProbNNp  = tagProto->info( LHCb::ProtoParticle::ProbNNp, -1000.0 );
-    const double tagProbNNmu = tagProto->info( LHCb::ProtoParticle::ProbNNmu, -1000.0 );
-    const double tagProbNNe  = tagProto->info( LHCb::ProtoParticle::ProbNNe, -1000.0 );
+    auto const   gpid        = tagProto->globalChargedPID();
+    const double tagProbNNk  = gpid ? gpid->ProbNNk() : -1000.0;
+    const double tagProbNNpi = gpid ? gpid->ProbNNpi() : -1000.0;
+    const double tagProbNNp  = gpid ? gpid->ProbNNp() : -1000.0;
+    const double tagProbNNmu = gpid ? gpid->ProbNNmu() : -1000.0;
+    const double tagProbNNe  = gpid ? gpid->ProbNNe() : -1000.0;
 
     if ( isMuon ) continue;
     if ( tagProbNNk > m_maxProbNNk ) continue;
@@ -214,7 +215,7 @@ std::optional<LHCb::Tagger> Run2OSElectronTagger::performTagging( const LHCb::Pa
     const double minIpSig = std::sqrt( minIp->second );
     if ( minIpSig < m_minIpSigTagPileUpVertices ) continue;
 
-    const double tagProbNNghost = tagProto->info( LHCb::ProtoParticle::ProbNNghost, -1000.0 );
+    const double tagProbNNghost = gpid ? gpid->ProbNNghost() : -1000.0;
 
     // auto bestVertex = m_DVAlgorithm->bestVertex( tagCand, m_DVAlgorithm->geometry() );
     // std::optional<std::pair<double, double>> bestVertexIp =
diff --git a/Phys/FlavourTagging/src/Run2OSKaonTagger.cpp b/Phys/FlavourTagging/src/Run2OSKaonTagger.cpp
index 28240a780afd0e555e69b4e8470c736aec7f97e4..e4c6b6623f85e2d6944edb0a9088d88a589e9c0c 100644
--- a/Phys/FlavourTagging/src/Run2OSKaonTagger.cpp
+++ b/Phys/FlavourTagging/src/Run2OSKaonTagger.cpp
@@ -157,11 +157,12 @@ std::optional<LHCb::Tagger> Run2OSKaonTagger::performTagging( const LHCb::Partic
     const double               tagTrackChi2PerDof = tagTrack->chi2PerDoF();
 
     const bool   isMuon      = tagProto->muonPID() ? tagProto->muonPID()->IsMuon() : false;
-    const double tagProbNNk  = tagProto->info( LHCb::ProtoParticle::ProbNNk, -1000.0 );
-    const double tagProbNNpi = tagProto->info( LHCb::ProtoParticle::ProbNNpi, -1000.0 );
-    const double tagProbNNp  = tagProto->info( LHCb::ProtoParticle::ProbNNp, -1000.0 );
-    const double tagProbNNmu = tagProto->info( LHCb::ProtoParticle::ProbNNmu, -1000.0 );
-    const double tagProbNNe  = tagProto->info( LHCb::ProtoParticle::ProbNNe, -1000.0 );
+    auto const   gpid        = tagProto->globalChargedPID();
+    const double tagProbNNk  = gpid ? gpid->ProbNNk() : -1000.0;
+    const double tagProbNNpi = gpid ? gpid->ProbNNpi() : -1000.0;
+    const double tagProbNNp  = gpid ? gpid->ProbNNp() : -1000.0;
+    const double tagProbNNmu = gpid ? gpid->ProbNNmu() : -1000.0;
+    const double tagProbNNe  = gpid ? gpid->ProbNNe() : -1000.0;
 
     if ( isMuon ) continue;
     if ( tagProbNNk < m_minProbNNk ) continue;
diff --git a/Phys/FlavourTagging/src/Run2OSMuonTagger.cpp b/Phys/FlavourTagging/src/Run2OSMuonTagger.cpp
index 9a90d82e13b3eb89d7317bb0781134dbf9c88464..69071b11e25418449370fce7c091809531bf5e8c 100644
--- a/Phys/FlavourTagging/src/Run2OSMuonTagger.cpp
+++ b/Phys/FlavourTagging/src/Run2OSMuonTagger.cpp
@@ -150,12 +150,13 @@ std::optional<LHCb::Tagger> Run2OSMuonTagger::performTagging( const LHCb::Partic
     const LHCb::ProtoParticle* tagProto = tagCand->proto();
     const LHCb::Track*         tagTrack = tagProto->track();
 
-    const bool   isMuon      = tagProto->muonPID() ? tagProto->muonPID()->IsMuon() : false;
-    const double tagProbNNk  = tagProto->info( LHCb::ProtoParticle::ProbNNk, -1000.0 );
-    const double tagProbNNpi = tagProto->info( LHCb::ProtoParticle::ProbNNpi, -1000.0 );
-    const double tagProbNNp  = tagProto->info( LHCb::ProtoParticle::ProbNNp, -1000.0 );
-    const double tagProbNNmu = tagProto->info( LHCb::ProtoParticle::ProbNNmu, -1000.0 );
-    const double tagProbNNe  = tagProto->info( LHCb::ProtoParticle::ProbNNe, -1000.0 );
+    const bool isMuon      = tagProto->muonPID() ? tagProto->muonPID()->IsMuon() : false;
+    auto const gpid        = tagProto->globalChargedPID();
+    auto const tagProbNNk  = gpid ? gpid->ProbNNk() : -1000.0;
+    auto const tagProbNNpi = gpid ? gpid->ProbNNpi() : -1000.0;
+    auto const tagProbNNp  = gpid ? gpid->ProbNNp() : -1000.0;
+    auto const tagProbNNmu = gpid ? gpid->ProbNNmu() : -1000.0;
+    auto const tagProbNNe  = gpid ? gpid->ProbNNe() : -1000.0;
 
     if ( !isMuon ) continue;
     if ( tagProbNNk > m_maxProbNNk ) continue;
diff --git a/Phys/FlavourTagging/src/Run2SSKaonTagger.cpp b/Phys/FlavourTagging/src/Run2SSKaonTagger.cpp
index 805443f258ee66fe43f7fcf6a91a3f60386dd4e7..434592686dff265bb2d394fdb0e87e2a903237db 100644
--- a/Phys/FlavourTagging/src/Run2SSKaonTagger.cpp
+++ b/Phys/FlavourTagging/src/Run2SSKaonTagger.cpp
@@ -180,9 +180,10 @@ std::optional<LHCb::Tagger> Run2SSKaonTagger::performTagging( const LHCb::Partic
     const double btag_relPt = sqrt( tag_mom.P() * tag_mom.P() - btag_rel * btag_rel );
 
     const LHCb::ProtoParticle* tagProto     = tagCand.particle->proto();
-    const double               tagPIDk      = tagProto->info( LHCb::ProtoParticle::ProbNNk, -1000.0 );
-    const double               tagPIDpi     = tagProto->info( LHCb::ProtoParticle::ProbNNpi, -1000.0 );
-    const double               tagPIDp      = tagProto->info( LHCb::ProtoParticle::ProbNNp, -1000.0 );
+    auto const                 gpid         = tagProto->globalChargedPID();
+    const double               tagPIDk      = gpid ? gpid->ProbNNk() : -1000.0;
+    const double               tagPIDpi     = gpid ? gpid->ProbNNpi() : -1000.0;
+    const double               tagPIDp      = gpid ? gpid->ProbNNp() : -1000.0;
     const LHCb::Track*         tagTrack     = tagProto->track();
     const double               tagTrackChi2 = tagTrack->chi2PerDoF();
 
diff --git a/Phys/FlavourTagging/src/Run2SSPionTagger.cpp b/Phys/FlavourTagging/src/Run2SSPionTagger.cpp
index 4248a802ad338197d11221c3acd2a5a808383d7c..43037ca11be2c2f31d70417cbde8a13a3b71183d 100644
--- a/Phys/FlavourTagging/src/Run2SSPionTagger.cpp
+++ b/Phys/FlavourTagging/src/Run2SSPionTagger.cpp
@@ -161,7 +161,8 @@ std::optional<LHCb::Tagger> Run2SSPionTagger::performTagging( const LHCb::Partic
     // inputs for the BDT
     const double               dR           = std::sqrt( deltaEta * deltaEta + deltaPhi * deltaPhi );
     const LHCb::ProtoParticle* tagProto     = tagCand->proto();
-    const double               tagPIDk      = tagProto->info( LHCb::ProtoParticle::additionalInfo::CombDLLk, -1000.0 );
+    auto const                 gpid         = tagProto->globalChargedPID();
+    const double               tagPIDk      = gpid ? gpid->CombDLLk() : -1000.0;
     const LHCb::Track*         tagTrack     = tagProto->track();
     const double               tagTrackChi2 = tagTrack->chi2PerDoF();
     const double               tagGhostProb = tagTrack->ghostProbability();
diff --git a/Phys/FlavourTagging/src/Run2SSProtonTagger.cpp b/Phys/FlavourTagging/src/Run2SSProtonTagger.cpp
index ff18c4ad29b54d7c8f6c98e948ad55dc4d77ed7a..cea44afa7c2ab23c24d27edd60e815e7c4db16ec 100644
--- a/Phys/FlavourTagging/src/Run2SSProtonTagger.cpp
+++ b/Phys/FlavourTagging/src/Run2SSProtonTagger.cpp
@@ -175,7 +175,8 @@ std::optional<LHCb::Tagger> Run2SSProtonTagger::performTagging( const LHCb::Part
     // inputs for the BDT
     const double               dR       = std::sqrt( deltaEta * deltaEta + deltaPhi * deltaPhi );
     const LHCb::ProtoParticle* tagProto = tagCand->proto();
-    const double               tagPIDp  = tagProto->info( LHCb::ProtoParticle::additionalInfo::CombDLLp, -1000.0 );
+    auto const                 gpid     = tagProto->globalChargedPID();
+    const double               tagPIDp  = gpid ? gpid->CombDLLp() : -1000.0;
 
     // MVA
     const double BDT = m_classifier->getClassifierValue(
diff --git a/Phys/FunctorCore/include/Functors/NeutralLike.h b/Phys/FunctorCore/include/Functors/NeutralLike.h
index 1bb33952e8013c60e16f90ae366b03d6eaf8ece2..221bf50872ccfe9dfdbbaecf06d8c2429b802837 100644
--- a/Phys/FunctorCore/include/Functors/NeutralLike.h
+++ b/Phys/FunctorCore/include/Functors/NeutralLike.h
@@ -11,6 +11,7 @@
 #pragma once
 #include "Event/ProtoParticle.h"
 #include "Functors/Function.h"
+#include "Functors/PID.h"
 
 namespace Functors::detail {
 
@@ -21,26 +22,6 @@ namespace Functors::detail {
 
 namespace Functors::Neutral {
 
-  /** @brief generic form using old Additional info. Superseded by functors using info in the NeutralPID object.
-   * For practical usage, add e.g. using ClusterMass_t = ExtraInfo_t<LHCb::ProtoParticle::additionalInfo::ClusterMass>;
-   */
-  template <LHCb::ProtoParticle::additionalInfo ai>
-  struct ExtraInfo_t : public Function {
-    template <typename T>
-    auto operator()( const T& d ) const {
-      if constexpr ( std::is_pointer_v<T> ) {
-        return operator()( *d ); // return itself with class
-      } else if constexpr ( detail::has_proto_v<T> ) {
-        LHCb::ProtoParticle const* pp = d.proto();
-        return pp ? pp->info( ai, invalid_value ) : invalid_value;
-      } else {
-        static_assert( detail::ai_always_false<T>, "The type T does not have a `proto()` member function"
-                                                   "-- sorry, not supported" );
-        return invalid_value;
-      }
-    }
-  };
-
   /** @brief The below functors use the information stored in NeutralPID objects
    */
   template <typename T, T ( LHCb::NeutralPID::*fun )() const>
@@ -50,20 +31,14 @@ namespace Functors::Neutral {
 
     Functors::Optional<T> operator()( LHCb::ProtoParticle const& pp ) const {
       auto pid = pp.neutralPID();
-      if ( !pid ) return std::nullopt;
-      return ( *this )( *pid );
+      return pid ? ( *this )( *pid ) : std::nullopt;
     }
 
     Functors::Optional<T> operator()( LHCb::Particle const& p ) const {
-      auto pp = p.proto();
-      if ( !pp ) return std::nullopt;
-      return ( *this )( *pp );
+      return p.proto() ? ( *this )( *p.proto() ) : std::nullopt;
     }
 
-    Functors::Optional<T> operator()( LHCb::Particle const* p ) const {
-      if ( !p ) return std::nullopt;
-      return ( *this )( *p );
-    }
+    Functors::Optional<T> operator()( LHCb::Particle const* p ) const { return ( *this )( *p ); }
   };
 
   using IsPhoton_t         = NeutralPID_Accessor_t<float, &LHCb::NeutralPID::IsPhoton>;
@@ -71,10 +46,12 @@ namespace Functors::Neutral {
   using ShowerShape_t      = NeutralPID_Accessor_t<float, &LHCb::NeutralPID::ShowerShape>;
   using NeutralE19_t       = NeutralPID_Accessor_t<float, &LHCb::NeutralPID::CaloNeutralE19>;
   using NeutralE49_t       = NeutralPID_Accessor_t<float, &LHCb::NeutralPID::CaloNeutralE49>;
-  using NeutralID_t        = NeutralPID_Accessor_t<int, &LHCb::NeutralPID::CaloNeutralID>;
   using Saturation_t       = NeutralPID_Accessor_t<int, &LHCb::NeutralPID::Saturation>;
   using NeutralHcal2Ecal_t = NeutralPID_Accessor_t<float, &LHCb::NeutralPID::CaloNeutralHcal2Ecal>;
   using NeutralEcal_t      = NeutralPID_Accessor_t<float, &LHCb::NeutralPID::CaloNeutralEcal>;
   using ClusterMass_t      = NeutralPID_Accessor_t<float, &LHCb::NeutralPID::ClusterMass>;
 
+  using NeutralID_t =
+      Functors::PID::details::CellIDFunc<&LHCb::ProtoParticle::neutralPID, &LHCb::NeutralPID::CaloNeutralID>;
+
 } // namespace Functors::Neutral
diff --git a/Phys/FunctorCore/include/Functors/PID.h b/Phys/FunctorCore/include/Functors/PID.h
index 43d3ecc24f2a4a3f7752e34fef4390be05b10b23..d612f48bd703c3f150cc0d0d347626d04d4ab423 100644
--- a/Phys/FunctorCore/include/Functors/PID.h
+++ b/Phys/FunctorCore/include/Functors/PID.h
@@ -9,6 +9,7 @@
 * or submit itself to any jurisdiction.                                       *
 \*****************************************************************************/
 #pragma once
+#include "Detector/Calo/CaloCellID.h"
 #include "Event/ProtoParticle.h"
 #include "Functors/Function.h"
 #include "Functors/Utilities.h"
@@ -185,4 +186,97 @@ namespace Functors::PID {
     }
   };
 
+  // CALO
+  //
+  using namespace LHCb::Event::Calo::v1;
+
+  namespace details {
+
+    template <auto obj, auto fn, auto... args>
+    struct CaloPredicate : Predicate {
+      bool operator()( LHCb::ProtoParticle const& p ) const {
+        auto const* pid = std::invoke( obj, p );
+        return pid ? std::invoke( fn, *pid, args... ) : false;
+      }
+      auto operator()( LHCb::ProtoParticle const* p ) const { return p ? ( *this )( *p ) : false; }
+      auto operator()( LHCb::Particle const& p ) const { return ( *this )( p.proto() ); }
+      auto operator()( LHCb::Particle const* p ) const { return ( *this )( *p ); }
+    };
+
+    template <auto obj, auto acc, auto fn, auto... args>
+    struct CaloFunction : Function {
+      auto operator()( LHCb::ProtoParticle const& p ) const {
+        auto const* pid = std::invoke( obj, p );
+        return pid && std::invoke( acc, *pid ) ? Functors::Optional{std::invoke( fn, *pid, args... )} : std::nullopt;
+      }
+      auto operator()( LHCb::ProtoParticle const* p ) const { return p ? ( *this )( *p ) : std::nullopt; }
+      auto operator()( LHCb::Particle const& p ) const { return ( *this )( p.proto() ); }
+      auto operator()( LHCb::Particle const* p ) const { return ( *this )( *p ); }
+    };
+
+    template <auto obj, auto fn, auto... args>
+    struct CellIDFunc : Function {
+      unsigned operator()( LHCb::ProtoParticle const& p ) const {
+        auto const* pid = std::invoke( obj, p );
+        return pid ? std::invoke( fn, *pid, args... ).all() : 0u;
+      }
+      auto operator()( LHCb::ProtoParticle const* p ) const { return p ? ( *this )( *p ) : 0u; }
+      auto operator()( LHCb::Particle const& p ) const { return ( *this )( p.proto() ); }
+      auto operator()( LHCb::Particle const* p ) const { return ( *this )( *p ); }
+    };
+
+    template <auto fn, auto... args>
+    using EcalFunc = CaloFunction<&LHCb::ProtoParticle::caloChargedPID, &CaloChargedPID::InEcal, fn, args...>;
+
+    template <auto fn, auto... args>
+    using HcalFunc = CaloFunction<&LHCb::ProtoParticle::caloChargedPID, &CaloChargedPID::InHcal, fn, args...>;
+
+    template <auto fn, auto... args>
+    using BremFunc = CaloFunction<&LHCb::ProtoParticle::bremInfo, &BremInfo::InBrem, fn, args...>;
+
+  } // namespace details
+
+  using InEcal = details::CaloPredicate<&LHCb::ProtoParticle::caloChargedPID, &CaloChargedPID::InEcal>;
+  using InHcal = details::CaloPredicate<&LHCb::ProtoParticle::caloChargedPID, &CaloChargedPID::InHcal>;
+
+  using InBrem  = details::CaloPredicate<&LHCb::ProtoParticle::bremInfo, &BremInfo::InBrem>;
+  using HasBrem = details::CaloPredicate<&LHCb::ProtoParticle::bremInfo, &BremInfo::HasBrem>;
+
+  using EcalPIDe          = details::EcalFunc<&CaloChargedPID::EcalPIDe>;
+  using EcalPIDmu         = details::EcalFunc<&CaloChargedPID::EcalPIDmu>;
+  using ElectronShowerEoP = details::EcalFunc<&CaloChargedPID::ElectronShowerEoP>;
+  using ElectronShowerDLL = details::EcalFunc<&CaloChargedPID::ElectronShowerDLL>;
+  using ElectronMatch     = details::EcalFunc<&CaloChargedPID::ElectronMatch>;
+  using ElectronEnergy    = details::EcalFunc<&CaloChargedPID::ElectronEnergy>;
+  using ElectronID        = details::CellIDFunc<&LHCb::ProtoParticle::caloChargedPID, &CaloChargedPID::ElectronID>;
+  using ClusterID         = details::CellIDFunc<&LHCb::ProtoParticle::caloChargedPID, &CaloChargedPID::ClusterID>;
+
+  using HcalPIDe  = details::HcalFunc<&CaloChargedPID::HcalPIDe>;
+  using HcalPIDmu = details::HcalFunc<&CaloChargedPID::HcalPIDmu>;
+  using HcalEoP   = details::HcalFunc<&CaloChargedPID::HcalEoP>;
+
+  using BremEnergy           = details::BremFunc<&BremInfo::BremEnergy>;
+  using BremPIDe             = details::BremFunc<&BremInfo::BremPIDe>;
+  using BremBendCorr         = details::BremFunc<&BremInfo::BremBendingCorrection>;
+  using BremHypoMatch        = details::BremFunc<&BremInfo::BremHypoMatch>;
+  using BremHypoEnergy       = details::BremFunc<&BremInfo::BremHypoEnergy>;
+  using BremHypoDeltaX       = details::BremFunc<&BremInfo::BremHypoDeltaX>;
+  using BremTrackBasedEnergy = details::BremFunc<&BremInfo::BremTrackBasedEnergy>;
+  using BremHypoID           = details::CellIDFunc<&LHCb::ProtoParticle::bremInfo, &BremInfo::BremHypoID>;
+
+  struct ClusterMatch : public Function {
+    Functors::Optional<float> operator()( LHCb::ProtoParticle const& p ) const {
+      if ( p.track() ) {
+        auto pid = p.caloChargedPID();
+        return pid ? Functors::Optional{pid->ClusterMatch()} : invalid_value;
+      } else {
+        auto pid = p.neutralPID();
+        return pid ? Functors::Optional{pid->CaloTrMatch()} : std::nullopt;
+      }
+    }
+    auto operator()( LHCb::ProtoParticle const* p ) const { return p ? ( *this )( *p ) : std::nullopt; }
+    auto operator()( LHCb::Particle const& p ) const { return ( *this )( p.proto() ); }
+    auto operator()( LHCb::Particle const* p ) const { return ( *this )( *p ); }
+  };
+
 } // namespace Functors::PID
diff --git a/Phys/FunctorCore/include/Functors/Particle.h b/Phys/FunctorCore/include/Functors/Particle.h
index 3a2d1d33b532f8b783725bd87a9241ae3ac776f3..b101bd3d2b828286821308fbceda942bf1b6dee2 100644
--- a/Phys/FunctorCore/include/Functors/Particle.h
+++ b/Phys/FunctorCore/include/Functors/Particle.h
@@ -199,24 +199,6 @@ namespace Functors::Particle {
     bool operator()( LHCb::ProtoParticle const* pp ) const { return pp && ( *this )( *pp ); }
   };
 
-  struct PPHasEcalInfo : public Predicate {
-    bool operator()( LHCb::ProtoParticle const& pp ) const {
-      constexpr auto ecal_info = std::array{
-          LHCb::ProtoParticle::additionalInfo::EcalPIDe,          LHCb::ProtoParticle::additionalInfo::BremPIDe,
-          LHCb::ProtoParticle::additionalInfo::EcalPIDmu,         LHCb::ProtoParticle::additionalInfo::CaloTrMatch,
-          LHCb::ProtoParticle::additionalInfo::CaloElectronMatch, LHCb::ProtoParticle::additionalInfo::CaloBremMatch,
-          LHCb::ProtoParticle::additionalInfo::CaloChargedEcal,   LHCb::ProtoParticle::additionalInfo::CaloDepositID,
-          LHCb::ProtoParticle::additionalInfo::ShowerShape,       LHCb::ProtoParticle::additionalInfo::ClusterMass,
-          LHCb::ProtoParticle::additionalInfo::CaloNeutralEcal,   LHCb::ProtoParticle::additionalInfo::CaloEcalE,
-          LHCb::ProtoParticle::additionalInfo::CaloEcalChi2,      LHCb::ProtoParticle::additionalInfo::CaloBremChi2,
-          LHCb::ProtoParticle::additionalInfo::CaloClusChi2,      LHCb::ProtoParticle::additionalInfo::CaloTrajectoryL,
-          LHCb::ProtoParticle::additionalInfo::PhotonID};
-      return !LHCb::essentiallyZero( pp.info( LHCb::ProtoParticle::additionalInfo::InAccEcal, 0 ) ) &&
-             std::any_of( ecal_info.begin(), ecal_info.end(), [&]( auto i ) { return pp.hasInfo( i ); } );
-    }
-    bool operator()( LHCb::ProtoParticle const* pp ) const { return ( *this )( *pp ); }
-  };
-
   /**
    * @brief Check if the "TriggerResult_t" object is TOS
    */
diff --git a/Phys/FunctorCore/include/Functors/TrackLike.h b/Phys/FunctorCore/include/Functors/TrackLike.h
index bb4d03f01d5b470d1462c0a2debf81d4b003684c..36575edff8183c904cd9eef4700256512fa7410c 100644
--- a/Phys/FunctorCore/include/Functors/TrackLike.h
+++ b/Phys/FunctorCore/include/Functors/TrackLike.h
@@ -48,68 +48,39 @@ namespace Functors::detail {
    */
   enum struct Pid { electron, muon, pion, kaon, proton, deuteron, ghost };
 
-  constexpr LHCb::ProtoParticle::additionalInfo to_ppai( Pid pid ) {
-    switch ( pid ) {
-    case Pid::electron:
-      return LHCb::ProtoParticle::additionalInfo::ProbNNe;
-    case Pid::muon:
-      return LHCb::ProtoParticle::additionalInfo::ProbNNmu;
-    case Pid::pion:
-      return LHCb::ProtoParticle::additionalInfo::ProbNNpi;
-    case Pid::kaon:
-      return LHCb::ProtoParticle::additionalInfo::ProbNNk;
-    case Pid::proton:
-      return LHCb::ProtoParticle::additionalInfo::ProbNNp;
-    case Pid::deuteron:
-      return LHCb::ProtoParticle::additionalInfo::ProbNNd;
-    case Pid::ghost:
-      return LHCb::ProtoParticle::additionalInfo::ProbNNghost;
-    }
-    throw std::domain_error{"impossible PID type"};
-  }
-
-  constexpr LHCb::ProtoParticle::additionalInfo to_ppai_combdll( Pid pid ) {
-    switch ( pid ) {
-    case Pid::muon:
-      return LHCb::ProtoParticle::additionalInfo::CombDLLmu;
-    case Pid::proton:
-      return LHCb::ProtoParticle::additionalInfo::CombDLLp;
-    case Pid::electron:
-      return LHCb::ProtoParticle::additionalInfo::CombDLLe;
-    case Pid::kaon:
-      return LHCb::ProtoParticle::additionalInfo::CombDLLk;
-    case Pid::pion:
-      return LHCb::ProtoParticle::additionalInfo::CombDLLpi;
-    default:
-      throw std::domain_error{"unsupported PID type"};
-    }
-  }
-
   template <Pid pid, typename T>
   constexpr auto combDLL( const T& d ) {
     if constexpr ( is_legacy_particle<T> ) {
       auto const* pp = Sel::Utils::deref_if_ptr( d ).proto();
-      return ( pp ) ? combDLL<pid>( *pp ) : std::nullopt;
+      return ( pp && pp->globalChargedPID() ) ? combDLL<pid>( *( pp->globalChargedPID() ) ) : std::nullopt;
     } else if constexpr ( detail::is_proto_particle<T> ) {
-      constexpr auto id = to_ppai_combdll( pid );
-      return Sel::Utils::deref_if_ptr( d ).hasInfo( id )
-                 ? Functors::Optional{Sel::Utils::deref_if_ptr( d ).info( id, 0. )}
-                 : std::nullopt;
+      auto gpid = Sel::Utils::deref_if_ptr( d ).globalChargedPID();
+      return gpid ? detail::combDLL<pid>( *gpid ) : std::nullopt;
     } else {
+      Functors::Optional<decltype( d.CombDLLe() )> dll = std::nullopt;
       switch ( pid ) {
       case Pid::electron:
-        return d.CombDLLe();
+        dll = d.CombDLLe();
+        break;
       case Pid::muon:
-        return d.CombDLLmu();
+        dll = d.CombDLLmu();
+        break;
       case Pid::pion:
-        return d.CombDLLpi();
+        dll = d.CombDLLpi();
+        break;
       case Pid::kaon:
-        return d.CombDLLk();
+        dll = d.CombDLLk();
+        break;
       case Pid::proton:
-        return d.CombDLLp();
+        dll = d.CombDLLp();
+        break;
+      case Pid::deuteron:
+        dll = d.CombDLLd();
+        break;
       default:
         throw std::domain_error{"impossible PID type"};
       }
+      return dll;
     }
   }
 
@@ -126,6 +97,42 @@ namespace Functors::detail {
   template <Pid id, typename T>
   constexpr bool has_probNN_v = has_probNN<id>::template value<T>;
 
+  template <Pid pid, typename T>
+  constexpr auto probNN( const T& d ) {
+    if constexpr ( is_legacy_particle<T> ) {
+      auto const* pp = Sel::Utils::deref_if_ptr( d ).proto();
+      return ( !pp || !pp->globalChargedPID() ) ? probNN<pid>( *( pp->globalChargedPID() ) ) : std::nullopt;
+    } else {
+      Functors::Optional<float> output = std::nullopt;
+      switch ( pid ) {
+      case Pid::electron:
+        output = d.ProbNNe();
+        break;
+      case Pid::muon:
+        output = d.ProbNNmu();
+        break;
+      case Pid::pion:
+        output = d.ProbNNpi();
+        break;
+      case Pid::kaon:
+        output = d.ProbNNk();
+        break;
+      case Pid::proton:
+        output = d.ProbNNp();
+        break;
+      case Pid::deuteron:
+        output = d.ProbNNd();
+        break;
+      case Pid::ghost:
+        output = d.ProbNNghost();
+        break;
+      default:
+        throw std::domain_error{"impossible PID type"};
+      }
+      return output.value() != LHCb::GlobalChargedPID::DefaultProbNN ? output : std::nullopt;
+    }
+  }
+
   template <typename StatePos_t, typename StateDir_t, typename VertexPos_t>
   [[gnu::always_inline]] inline auto impactParameterSquared( StatePos_t const& state_pos, StateDir_t const& state_dir,
                                                              VertexPos_t const& vertex ) {
@@ -208,132 +215,6 @@ namespace Functors::Track {
     }
   };
 
-  using additInfo = LHCb::ProtoParticle::additionalInfo;
-  /** @brief Bool and Numerical (Float and Integer) AdditionalInfo, get information from Calo Additional Info and from
-   * Particle v2
-   */
-  template <additInfo ai>
-  struct BoolAdditionalInfo : public Predicate {
-    auto operator()( LHCb::Particle const& p ) const {
-      auto const* pp = p.proto();
-      if ( !pp ) { return false; }
-      return !LHCb::essentiallyZero( pp->info( ai, 0 ) );
-    }
-
-    auto operator()( LHCb::Particle const* p ) const { return ( *this )( *p ); }
-
-    template <typename Data>
-    auto operator()( Data const& d ) const {
-      switch ( ai ) {
-      case additInfo::CaloHasBrem:
-        return d.HasBrem();
-      case additInfo::InAccEcal:
-        return d.InEcal();
-      case additInfo::InAccHcal:
-        return d.InHcal();
-      case additInfo::InAccBrem:
-        return d.InBrem();
-      default:
-        throw std::domain_error{"Invalid additional info"};
-      }
-    }
-  };
-
-  using HasBrem = BoolAdditionalInfo<additInfo::CaloHasBrem>;
-  using InEcal  = BoolAdditionalInfo<additInfo::InAccEcal>;
-  using InHcal  = BoolAdditionalInfo<additInfo::InAccHcal>;
-  using InBrem  = BoolAdditionalInfo<additInfo::InAccBrem>;
-
-  template <additInfo ai>
-  struct NumAdditionalInfo : public Function {
-    auto operator()( LHCb::Particle const& p ) const {
-      auto const* pp = p.proto();
-      return ( pp && pp->hasInfo( ai ) ) ? Functors::Optional{pp->info( ai, 0.f )} : std::nullopt;
-    }
-
-    auto operator()( LHCb::Particle const* p ) const { return ( *this )( *p ); }
-
-    template <typename Data>
-    auto operator()( Data const& d ) const {
-      switch ( ai ) {
-      case additInfo::CaloBremEnergy:
-        return d.BremEnergy();
-      case additInfo::CaloBremBendingCorr:
-        return d.BremBendingCorrection();
-      case additInfo::BremPIDe:
-        return d.BremPIDe();
-      case additInfo::EcalPIDe:
-        return d.EcalPIDe();
-      case additInfo::EcalPIDmu:
-        return d.EcalPIDmu();
-      case additInfo::HcalPIDe:
-        return d.HcalPIDe();
-      case additInfo::HcalPIDmu:
-        return d.HcalPIDmu();
-      case additInfo::CaloEoverP:
-        return d.ElectronShowerEoP();
-      case additInfo::ElectronShowerDLL:
-        return d.ElectronShowerDLL();
-      case additInfo::CaloTrMatch:
-        return d.ClusterMatch();
-      case additInfo::CaloElectronMatch:
-        return d.ElectronMatch();
-      case additInfo::CaloBremHypoID:
-        return d.BremHypoID();
-      case additInfo::CaloBremMatch:
-        return d.BremHypoMatch();
-      case additInfo::CaloChargedEcal:
-        return d.ElectronEnergy();
-      case additInfo::CaloBremHypoEnergy:
-        return d.BremHypoEnergy();
-      case additInfo::CaloBremHypoDeltaX:
-        return d.BremHypoDeltaX();
-      case additInfo::CaloBremTBEnergy:
-        return d.BremTrackBasedEnergy();
-      default:
-        throw std::domain_error{"Invalid additional info"};
-      }
-    }
-  };
-
-  using BremEnergy           = NumAdditionalInfo<additInfo::CaloBremEnergy>;
-  using BremBendCorr         = NumAdditionalInfo<additInfo::CaloBremBendingCorr>;
-  using BremPIDe             = NumAdditionalInfo<additInfo::BremPIDe>;
-  using EcalPIDe             = NumAdditionalInfo<additInfo::EcalPIDe>;
-  using EcalPIDmu            = NumAdditionalInfo<additInfo::EcalPIDmu>;
-  using HcalPIDe             = NumAdditionalInfo<additInfo::HcalPIDe>;
-  using HcalPIDmu            = NumAdditionalInfo<additInfo::HcalPIDmu>;
-  using ElectronShowerEoP    = NumAdditionalInfo<additInfo::CaloEoverP>;
-  using ElectronShowerDLL    = NumAdditionalInfo<additInfo::ElectronShowerDLL>;
-  using ElectronMatch        = NumAdditionalInfo<additInfo::CaloElectronMatch>;
-  using BremHypoMatch        = NumAdditionalInfo<additInfo::CaloBremMatch>;
-  using ElectronEnergy       = NumAdditionalInfo<additInfo::CaloChargedEcal>;
-  using BremHypoID           = NumAdditionalInfo<additInfo::CaloBremHypoID>;
-  using BremHypoEnergy       = NumAdditionalInfo<additInfo::CaloBremHypoEnergy>;
-  using BremHypoDeltaX       = NumAdditionalInfo<additInfo::CaloBremHypoDeltaX>;
-  using BremTrackBasedEnergy = NumAdditionalInfo<additInfo::CaloBremTBEnergy>;
-  using ElectronID           = NumAdditionalInfo<additInfo::CaloChargedID>;
-
-  struct ClusterMatch : public Function {
-    Functors::Optional<double> operator()( LHCb::Particle const& p ) const {
-      auto const* pp = p.proto();
-      if ( !pp ) { return std::nullopt; }
-      if ( nullptr != pp->track() ) {
-        return ( pp->hasInfo( additInfo::CaloTrMatch ) ) ? pp->info( additInfo::CaloTrMatch, 0.f ) : invalid_value;
-      } else {
-        auto pid = pp->neutralPID();
-        if ( !pid ) return std::nullopt;
-        return pid->CaloTrMatch();
-      }
-    }
-    auto operator()( LHCb::Particle const* p ) const { return ( *this )( *p ); }
-
-    template <typename Data>
-    auto operator()( Data const& d ) const {
-      return d.ClusterMatch();
-    }
-  };
-
   /** @brief HasBremAdded, as defined by the HasBremAdded accessor.
    */
   struct HasBremAdded : public Predicate {
@@ -344,34 +225,6 @@ namespace Functors::Track {
     auto operator()( LHCb::Particle const* p ) const { return ( *this )( *p ); }
   };
 
-  /** @brief HcalEoP, as defined by the HcalEoP accessor.
-   */
-  struct HcalEoP : public Function {
-    auto operator()( LHCb::Particle const& p ) const {
-      auto const* pp    = p.proto();
-      auto const* track = pp ? pp->track() : nullptr;
-      return ( pp && pp->hasInfo( additInfo::CaloHcalE ) && !LHCb::essentiallyZero( track->p() ) )
-                 ? Functors::Optional{( pp->info( additInfo::CaloHcalE, 0.f ) ) / ( track->p() )}
-                 : std::nullopt;
-    }
-
-    auto operator()( LHCb::Particle const* p ) const { return ( *this )( *p ); }
-
-    template <typename Data>
-    auto operator()( Data const& d ) const {
-      return d.HcalEoP();
-    }
-  };
-
-  /** @brief ClusterID, as defined by the accessor of the same name.
-   */
-  struct ClusterID : public Function {
-    template <typename Data>
-    auto operator()( Data const& d ) const {
-      return d.ClusterID();
-    }
-  };
-
   /** @brief Number of degrees of freedom, as defined by the nDoF accessor.
    */
   struct nDoF : public Function {
@@ -494,10 +347,8 @@ namespace Functors::Track {
         auto        pp = Sel::Utils::deref_if_ptr( d ).proto();
         return pp ? operator()( *pp ) : std::nullopt;
       } else if constexpr ( detail::is_proto_particle<T> ) {
-        auto constexpr pid = to_ppai( id );
-        return Sel::Utils::deref_if_ptr( d ).hasInfo( pid )
-                   ? Functors::Optional{Sel::Utils::deref_if_ptr( d ).info( pid, 0. )}
-                   : std::nullopt;
+        auto gpid = Sel::Utils::deref_if_ptr( d ).globalChargedPID();
+        return gpid ? detail::probNN<id>( *gpid ) : std::nullopt;
       } else {
         throw GaudiException{"The type T neither has a `proto()` member function nor a `probNN<id>`() member function "
                              "-- sorry, not supported",
diff --git a/Phys/FunctorCore/python/Functors/__init__.py b/Phys/FunctorCore/python/Functors/__init__.py
index 19e7120eb2487ffa9ce6cbc7a13c8a8d753428e3..eb768bd6e872ef5bbaec7eeb413daf34a853f85e 100644
--- a/Phys/FunctorCore/python/Functors/__init__.py
+++ b/Phys/FunctorCore/python/Functors/__init__.py
@@ -768,7 +768,7 @@ MUONCATBOOST = Functor(
                 Functor's call operator expects a particle like object.""")
 
 HASBREM = Functor(
-    'HASBREM', "Track::HasBrem", """Has non-zero brem momentum-recovery energy.
+    'HASBREM', "PID::HasBrem", """Has non-zero brem momentum-recovery energy.
 
                  Functor's call operator expects a track like object.""")
 HASBREMADDED = Functor(
@@ -778,44 +778,44 @@ HASBREMADDED = Functor(
                  Functor's call operator expects a track like object in v1 event model."""
 )
 INECAL = Functor(
-    'INECAL', "Track::InEcal", """In Ecal acceptance.
+    'INECAL', "PID::InEcal", """In Ecal acceptance.
 
                  Functor's call operator expects a track like object.""")
 INHCAL = Functor(
-    'INHCAL', "Track::InHcal", """In Hcal acceptance.
+    'INHCAL', "PID::InHcal", """In Hcal acceptance.
 
                  Functor's call operator expects a track like object.""")
 INBREM = Functor(
-    'INBREM', "Track::InBrem", """In Brem acceptance.
+    'INBREM', "PID::InBrem", """In Brem acceptance.
 
                  Functor's call operator expects a track like object.""")
 BREMENERGY = Functor(
-    'BREMENERGY', "Track::BremEnergy", """Brem momentum-recovery energy.
+    'BREMENERGY', "PID::BremEnergy", """Brem momentum-recovery energy.
 
                      Functor's call operator expects a track like object.""")
 BREMBENDCORR = Functor(
-    'BREMBENDCORR', "Track::BremBendCorr",
+    'BREMBENDCORR', "PID::BremBendCorr",
     """Correction factor accounting for bending biases in track due to brem.
 
     Functor's call operator expects a track like object.""")
 BREMPIDE = Functor(
-    'BREMPIDE', "Track::BremPIDe", """Brem-based DLL for electron-ID.
+    'BREMPIDE', "PID::BremPIDe", """Brem-based DLL for electron-ID.
 
                    Functor's call operator expects a track like object.""")
 ECALPIDE = Functor(
-    'ECALPIDE', "Track::EcalPIDe", """Ecal-based DLL for electron-ID.
+    'ECALPIDE', "PID::EcalPIDe", """Ecal-based DLL for electron-ID.
 
                    Functor's call operator expects a track like object.""")
 ECALPIDMU = Functor(
-    'ECALPIDMU', "Track::EcalPIDmu", """Ecal-based DLL for mu-ID.
+    'ECALPIDMU', "PID::EcalPIDmu", """Ecal-based DLL for mu-ID.
 
                     Functor's call operator expects a track like object.""")
 HCALPIDE = Functor(
-    'HCALPIDE', "Track::HcalPIDe", """Hcal-based DLL for electron-ID.
+    'HCALPIDE', "PID::HcalPIDe", """Hcal-based DLL for electron-ID.
 
                    Functor's call operator expects a track like object.""")
 HCALPIDMU = Functor(
-    'HCALPIDMU', "Track::HcalPIDmu", """Hcal-based DLL for mu-ID.
+    'HCALPIDMU', "PID::HcalPIDmu", """Hcal-based DLL for mu-ID.
 
                     Functor's call operator expects a track like object.""")
 RICH_DLL_E = Functor(
@@ -895,66 +895,70 @@ RICH_THRESHOLD_DE = Functor(
 
                     Functor's call operator expects a track like object.""")
 ELECTRONSHOWEREOP = Functor(
-    'ELECTRONSHOWEREOP', "Track::ElectronShowerEoP",
+    'ELECTRONSHOWEREOP', "PID::ElectronShowerEoP",
     """Electron energy/momentum with track-based cell selection.
 
     Functor's call operator expects a track like object.""")
 CLUSTERMATCH_CHI2 = Functor(
-    'CLUSTERMATCH', "Track::ClusterMatch",
+    'CLUSTERMATCH', "PID::ClusterMatch",
     """CaloID estimator : 2D chi2 for Track/CaloCluster matching (neutral + charged).
 
     Functor's call operator expects a particle like object.""")
 ELECTRONMATCH_CHI2 = Functor(
-    'ELECTRONMATCH', "Track::ElectronMatch",
+    'ELECTRONMATCH', "PID::ElectronMatch",
     """CaloID estimator : 3D chi2 for Track/CaloHypo(e) matching (charged).
 
     Functor's call operator expects a track like object.""")
 BREMHYPOID = Functor(
-    'BREMHYPOID', "Track::BremHypoID",
+    'BREMHYPOID', "PID::BremHypoID",
     """All significant bits representation of CellID (32bits), i.e. CellID.all(), for CaloHypo (photon) associated to track for brem recovery
+    0 is invalid/unavailable (see Detector/Calo/include/Detector/Calo/CaloCellID.h)
 
     Functor's call operator expects a track like object.""")
 BREMHYPOMATCH_CHI2 = Functor(
-    'BREMHYPOMATCH', "Track::BremHypoMatch",
+    'BREMHYPOMATCH', "PID::BremHypoMatch",
     """2D chi2 of CaloHypo (photon) associated to track for brem recovery
 
     Functor's call operator expects a track like object.""")
 ELECTRONENERGY = Functor(
-    'ELECTRONENERGY', "Track::ElectronEnergy",
+    'ELECTRONENERGY', "PID::ElectronEnergy",
     """Cluster energy associated to CaloHypo (charged)
 
     Functor's call operator expects a track like object.""")
 BREMHYPOENERGY = Functor(
-    'BREMHYPOENERGY', "Track::BremHypoEnergy",
+    'BREMHYPOENERGY', "PID::BremHypoEnergy",
     """Energy of CaloHypo (photon) associated to track for brem recovery.
 
     Functor's call operator expects a track like object.""")
 BREMHYPODELTAX = Functor(
-    'BREMHYPODELTAX', "Track::BremHypoDeltaX",
+    'BREMHYPODELTAX', "PID::BremHypoDeltaX",
     """Test statistic of being first-state like of CaloHypo (photon) for brem recovery
 
     Functor's call operator expects a track like object.""")
 BREMTRACKBASEDENERGY = Functor(
-    'BREMTRACKBASEDENERGY', "Track::BremTrackBasedEnergy",
+    'BREMTRACKBASEDENERGY', "PID::BremTrackBasedEnergy",
     """Track-based brem energy determination
 
     Functor's call operator expects a track like object.""")
 ELECTRONID = Functor(
-    'ELECTRONID', "Track::ElectronID",
+    'ELECTRONID', "PID::ElectronID",
     """All significant bits representation of CellID (32bits), i.e. CellID.all(), associated to CaloHypo seed (electron hypo)
+    0 is invalid/unavailable (see Detector/Calo/include/Detector/Calo/CaloCellID.h)
 
     Functor's call operator expects a track like object.""")
+
 HCALEOP = Functor(
-    'HCALEOP', "Track::HcalEoP", """Hcal energy deposit over momentum (track)
+    'HCALEOP', "PID::HcalEoP", """Hcal energy deposit over momentum (track)
 
                   Functor's call operator expects a track like object.""")
 CLUSTERID = Functor(
-    'CLUSTERID', "Track::ClusterID",
+    'CLUSTERID', "PID::ClusterID",
     """CellID.all() of the best matching cluster for a given reconstructed track.
+    0 is invalid/unavailable (see Detector/Calo/include/Detector/Calo/CaloCellID.h)
 
-    Functor's call operator expects a track like object in v2 event model.""")
+    Functor's call operator expects a track like object.""")
 ELECTRONSHOWERDLL = Functor(
-    'ELECTRONSHOWERDLL', "Track::ElectronShowerDLL",
+    'ELECTRONSHOWERDLL', "PID::ElectronShowerDLL",
     """Summed per-cell E/p DLL (electron versus pion) with track-based cell selection and energy estimation.
 
     Functor's call operator expects a track like object.""")
@@ -1126,11 +1130,6 @@ PPHASMUONINFO = Functor(
 
                         Functor's call operator expects a protoparticle.
                         """)
-PPHASECALINFO = Functor(
-    "PPHASECALINFO", "Particle::PPHasEcalInfo", """ProtoParticle has ECAL info
-
-                        Functor's call operator expects a protoparticle.
-                        """)
 
 # generic
 ALL = Functor('ALL', "AcceptAll",
@@ -2360,20 +2359,12 @@ CALO_NEUTRAL_4TO9_ENERGY_RATIO = Functor(
     Note:
     Functor will return -1000 if protoparticle doesn't have a NeutralPID object."""
 )
-
-INT_CALO_NEUTRAL_ID = Functor(
+CALO_NEUTRAL_ID = Functor(
     'CALO_NEUTRAL_ID', "Neutral::NeutralID_t",
     """Retrieve bitwise information (32bits) of all CALO neutral seed CellIDs.
+    0 is invalid/unavailable (see Detector/Calo/include/Detector/Calo/CaloCellID.h)
 
-    Functor's call operator expects an LHCb::Particle or ProtoParticle.
-
-    Note:
-    Since the return is an int, it is necessary to add a custom invalid_value through F.VALUE_OR(invalid_value) @....
-    Can be done manually in each usage or with below functor CALO_NEUTRAL_ID."""
-)
-
-CALO_NEUTRAL_ID = VALUE_OR(-1000) @ INT_CALO_NEUTRAL_ID
-
+    Functor's call operator expects an LHCb::Particle or ProtoParticle.""")
 CALO_NEUTRAL_ECAL_ENERGY = Functor(
     'CALO_NEUTRAL_ECAL_ENERGY', "Neutral::NeutralEcal_t",
     """Retrieve the ECAL cluster energy associated to the neutral CaloHypo.
diff --git a/Phys/FunctorCore/tests/src/TestFunctors.cpp b/Phys/FunctorCore/tests/src/TestFunctors.cpp
index 0c74a2ae647b86cf06778758e72d3928f513b543..1671f5ab8c4525c0477c4785a5c2478d7ab80732 100644
--- a/Phys/FunctorCore/tests/src/TestFunctors.cpp
+++ b/Phys/FunctorCore/tests/src/TestFunctors.cpp
@@ -390,25 +390,26 @@ BOOST_AUTO_TEST_CASE( test_ai_functors ) {
           ( Functors::Neutral::NeutralE49_t{} > 0.1f ) & ( Functors::Neutral::Saturation_t{} > 0.1f ) );
 
   // test calo and rich track functors
-  testPointerAndObject(
-      std::vector<int>{11, 22, 211},
-      ( Functors::Track::HasBrem{} ) & ( Functors::Track::HasBremAdded{} ) & ( Functors::Track::InEcal{} ) &
-          ( Functors::Track::InHcal{} ) & ( Functors::Track::InBrem{} ) & ( Functors::Track::BremEnergy{} > 0.f ) &
-          ( Functors::Track::BremBendCorr{} > 0.f ) & ( Functors::Track::BremPIDe{} > 0.f ) &
-          ( Functors::Track::EcalPIDe{} > 0.f ) & ( Functors::Track::EcalPIDmu{} > 0.f ) &
-          ( Functors::Track::HcalPIDe{} > 0.f ) & ( Functors::Track::HcalPIDmu{} > 0.f ) &
-          ( Functors::Track::ElectronShowerEoP{} > 0.f ) & ( Functors::Track::ElectronShowerDLL{} > 0.f ) &
-          ( Functors::Track::ClusterMatch{} > 0.f ) & ( Functors::Track::ElectronMatch{} > 0.f ) &
-          ( Functors::Track::BremHypoMatch{} > 0.f ) & ( Functors::Track::ElectronEnergy{} > 0.f ) &
-          ( Functors::Track::BremHypoEnergy{} > 0.f ) & ( Functors::Track::BremHypoDeltaX{} > 0.f ) &
-          ( Functors::Track::BremHypoID{} > 0 ) & ( Functors::Track::BremTrackBasedEnergy{} > 0.f ) &
-          ( Functors::Track::ElectronID{} > 0 ) & ( Functors::Track::HcalEoP{} > 0.f ) &
-          ( Functors::PID::RichDLLe{} > 0.f ) & ( Functors::PID::RichDLLmu{} > 0.f ) &
-          ( Functors::PID::RichDLLp{} > 0.f ) & ( Functors::PID::RichDLLk{} > 0.f ) &
-          ( Functors::PID::RichDLLpi{} > 0.f ) & ( Functors::PID::RichDLLd{} > 0.f ) &
-          ( Functors::PID::RichDLLbt{} > 0.f ) & ( Functors::PID::RichScaledDLLe{} > 0.f ) &
-          ( Functors::PID::RichScaledDLLmu{} > 0.f ) & ( Functors::PID::RichThresholdEl{} ) &
-          ( Functors::PID::Rich1GasUsed{} ) & ( Functors::PID::Rich2GasUsed{} ) );
+  testPointerAndObject( std::vector<int>{11, 22, 211},
+                        ( Functors::PID::HasBrem{} ) & ( Functors::Track::HasBremAdded{} ) &
+                            ( Functors::PID::InEcal{} ) & ( Functors::PID::ClusterID{} > 0 ) &
+                            ( Functors::PID::InHcal{} ) & ( Functors::PID::InBrem{} ) &
+                            ( Functors::PID::BremEnergy{} > 0.f ) & ( Functors::PID::BremBendCorr{} > 0.f ) &
+                            ( Functors::PID::BremPIDe{} > 0.f ) & ( Functors::PID::EcalPIDe{} > 0.f ) &
+                            ( Functors::PID::EcalPIDmu{} > 0.f ) & ( Functors::PID::HcalPIDe{} > 0.f ) &
+                            ( Functors::PID::HcalPIDmu{} > 0.f ) & ( Functors::PID::ElectronShowerEoP{} > 0.f ) &
+                            ( Functors::PID::ElectronShowerDLL{} > 0.f ) & ( Functors::PID::ClusterMatch{} > 0.f ) &
+                            ( Functors::PID::ElectronMatch{} > 0.f ) & ( Functors::PID::BremHypoMatch{} > 0.f ) &
+                            ( Functors::PID::ElectronEnergy{} > 0.f ) & ( Functors::PID::BremHypoEnergy{} > 0.f ) &
+                            ( Functors::PID::BremHypoDeltaX{} > 0.f ) & ( Functors::PID::BremHypoID{} > 0 ) &
+                            ( Functors::PID::BremTrackBasedEnergy{} > 0.f ) & ( Functors::PID::ElectronID{} > 0 ) &
+                            ( Functors::PID::HcalEoP{} > 0.f ) & ( Functors::PID::RichDLLe{} > 0.f ) &
+                            ( Functors::PID::RichDLLmu{} > 0.f ) & ( Functors::PID::RichDLLp{} > 0.f ) &
+                            ( Functors::PID::RichDLLk{} > 0.f ) & ( Functors::PID::RichDLLpi{} > 0.f ) &
+                            ( Functors::PID::RichDLLd{} > 0.f ) & ( Functors::PID::RichDLLbt{} > 0.f ) &
+                            ( Functors::PID::RichScaledDLLe{} > 0.f ) & ( Functors::PID::RichScaledDLLmu{} > 0.f ) &
+                            ( Functors::PID::RichThresholdEl{} ) & ( Functors::PID::Rich1GasUsed{} ) &
+                            ( Functors::PID::Rich2GasUsed{} ) );
 }
 
 BOOST_AUTO_TEST_CASE( test_1trackmva_functor ) {
@@ -491,13 +492,6 @@ BOOST_AUTO_TEST_CASE( test_general_track_functor ) {
   BOOST_CHECK_EQUAL( REFERENCEPOINT( STATES( track ).front() ).x().cast(), 0 );
 }
 
-BOOST_AUTO_TEST_CASE( test_calo_track_functors ) {
-  Functors::Track::ClusterID CLUSTERID;
-
-  DummyTrack track{10.f, 10.f, true, true};
-  BOOST_CHECK_EQUAL( CLUSTERID( track ), 1 );
-}
-
 BOOST_AUTO_TEST_CASE( test_brem_functors ) {
   auto const BREM           = Functors::Track::Bremsstrahlung{};
   auto const PZ_WITH_BREM   = chain( PZ, BREM );
@@ -514,10 +508,12 @@ BOOST_AUTO_TEST_CASE( test_brem_functors ) {
 
   auto etrack1 = LHCb::Track();
   auto eproto1 = LHCb::ProtoParticle();
+  auto ebrem1  = LHCb::Event::Calo::v1::BremInfo();
+  ebrem1.setInBrem( true );
+  ebrem1.setHasBrem( true );
+  ebrem1.setBremEnergy( 2. * Gaudi::Units::GeV );
   eproto1.setTrack( &etrack1 );
-  eproto1.addInfo( LHCb::ProtoParticle::additionalInfo::InAccBrem, 1 );
-  eproto1.addInfo( LHCb::ProtoParticle::additionalInfo::CaloHasBrem, 1 );
-  eproto1.addInfo( LHCb::ProtoParticle::additionalInfo::CaloBremEnergy, 2. * Gaudi::Units::GeV );
+  eproto1.setBremInfo( &ebrem1 );
   Gaudi::LorentzVector emom1     = {0., 0., 10. * Gaudi::Units::GeV, 10. * Gaudi::Units::GeV};
   auto                 electron1 = LHCb::Particle( LHCb::ParticleID{11} );
   electron1.setMomentum( emom1 );
@@ -526,10 +522,12 @@ BOOST_AUTO_TEST_CASE( test_brem_functors ) {
 
   auto etrack2 = LHCb::Track();
   auto eproto2 = LHCb::ProtoParticle();
+  auto ebrem2  = LHCb::Event::Calo::v1::BremInfo();
+  ebrem2.setInBrem( true );
+  ebrem2.setHasBrem( true );
+  ebrem2.setBremEnergy( 3. * Gaudi::Units::GeV );
   eproto2.setTrack( &etrack2 );
-  eproto2.addInfo( LHCb::ProtoParticle::additionalInfo::InAccBrem, 1 );
-  eproto2.addInfo( LHCb::ProtoParticle::additionalInfo::CaloHasBrem, 1 );
-  eproto2.addInfo( LHCb::ProtoParticle::additionalInfo::CaloBremEnergy, 3. * Gaudi::Units::GeV );
+  eproto2.setBremInfo( &ebrem2 );
   Gaudi::LorentzVector emom2     = {0., 0., -9. * Gaudi::Units::GeV, 9. * Gaudi::Units::GeV};
   auto                 electron2 = LHCb::Particle( LHCb::ParticleID{-11} );
   electron2.setMomentum( emom2 );
@@ -2600,11 +2598,9 @@ BOOST_AUTO_TEST_CASE( test_get_proto_functor ) {
   // create protoparticle
   LHCb::ProtoParticle proto;
 
-  Functors::Particle::PPHasEcalInfo PPHASECAL;
   Functors::Particle::PPHasMuonInfo PPHASMUON;
   Functors::Particle::PPHasRich     PPHASRICH;
 
-  BOOST_CHECK_EQUAL( PPHASECAL( proto ), false );
   BOOST_CHECK_EQUAL( PPHASMUON( proto ), false );
   BOOST_CHECK_EQUAL( PPHASRICH( proto ), false );
 }
@@ -2674,6 +2670,7 @@ BOOST_AUTO_TEST_CASE( test_get_track_functor ) {
     part.field<ChargedBasicsTag::CombDLL>( ChargedBasicsTag::Hypo::pi ).set( std::numeric_limits<float>::lowest() );
     part.field<ChargedBasicsTag::CombDLL>( ChargedBasicsTag::Hypo::K ).set( std::numeric_limits<float>::lowest() );
     part.field<ChargedBasicsTag::CombDLL>( ChargedBasicsTag::Hypo::mu ).set( std::numeric_limits<float>::lowest() );
+    part.field<ChargedBasicsTag::CombDLL>( ChargedBasicsTag::Hypo::d ).set( std::numeric_limits<float>::lowest() );
   }
 
   auto const& truev3_threemom      = tracks.scalar()[0].threeMomentum( StateLocation::ClosestToBeam );
diff --git a/Phys/ParticleCombiners/tests/src/test_thor_combiner.cpp b/Phys/ParticleCombiners/tests/src/test_thor_combiner.cpp
index dc3d71a94ca7627c06d309686ecec77c967f0860..25e355886a5ffe82a3c58f5c2f5143fd2317f0b5 100644
--- a/Phys/ParticleCombiners/tests/src/test_thor_combiner.cpp
+++ b/Phys/ParticleCombiners/tests/src/test_thor_combiner.cpp
@@ -412,6 +412,7 @@ auto generate_particles( std::size_t n_elements, LHCb::UniqueIDGenerator const&
     part.field<ChargedBasicsTag::CombDLL>( ChargedBasicsTag::Hypo::pi ).set( std::numeric_limits<float>::lowest() );
     part.field<ChargedBasicsTag::CombDLL>( ChargedBasicsTag::Hypo::K ).set( std::numeric_limits<float>::lowest() );
     part.field<ChargedBasicsTag::CombDLL>( ChargedBasicsTag::Hypo::mu ).set( std::numeric_limits<float>::lowest() );
+    part.field<ChargedBasicsTag::CombDLL>( ChargedBasicsTag::Hypo::d ).set( std::numeric_limits<float>::lowest() );
     part.field<ChargedBasicsTag::Track>().set( i );
   }
 
diff --git a/Phys/ParticleConverters/src/Particle_to_Particle_v2.cpp b/Phys/ParticleConverters/src/Particle_to_Particle_v2.cpp
index 9ae2d6a100fb32327ec9642f28dfeed1fe0b818f..3b7791bb652b3104833700f41c21c16a2b1f6395 100644
--- a/Phys/ParticleConverters/src/Particle_to_Particle_v2.cpp
+++ b/Phys/ParticleConverters/src/Particle_to_Particle_v2.cpp
@@ -157,16 +157,19 @@ public:
         }
 
         // combdlls
+        auto const pid = proto->globalChargedPID();
         part.field<ChargedBasicsTag::CombDLL>( ChargedBasicsTag::Hypo::p )
-            .set( proto->info( LHCb::ProtoParticle::additionalInfo::CombDLLp, std::numeric_limits<float>::lowest() ) );
+            .set( pid ? pid->CombDLLp() : std::numeric_limits<float>::lowest() );
         part.field<ChargedBasicsTag::CombDLL>( ChargedBasicsTag::Hypo::e )
-            .set( proto->info( LHCb::ProtoParticle::additionalInfo::CombDLLe, std::numeric_limits<float>::lowest() ) );
+            .set( pid ? pid->CombDLLe() : std::numeric_limits<float>::lowest() );
         part.field<ChargedBasicsTag::CombDLL>( ChargedBasicsTag::Hypo::pi )
-            .set( proto->info( LHCb::ProtoParticle::additionalInfo::CombDLLpi, std::numeric_limits<float>::lowest() ) );
+            .set( pid ? pid->CombDLLpi() : std::numeric_limits<float>::lowest() );
         part.field<ChargedBasicsTag::CombDLL>( ChargedBasicsTag::Hypo::K )
-            .set( proto->info( LHCb::ProtoParticle::additionalInfo::CombDLLk, std::numeric_limits<float>::lowest() ) );
+            .set( pid ? pid->CombDLLk() : std::numeric_limits<float>::lowest() );
         part.field<ChargedBasicsTag::CombDLL>( ChargedBasicsTag::Hypo::mu )
-            .set( proto->info( LHCb::ProtoParticle::additionalInfo::CombDLLmu, std::numeric_limits<float>::lowest() ) );
+            .set( pid ? pid->CombDLLmu() : std::numeric_limits<float>::lowest() );
+        part.field<ChargedBasicsTag::CombDLL>( ChargedBasicsTag::Hypo::d )
+            .set( pid ? pid->CombDLLd() : std::numeric_limits<float>::lowest() );
       }
       if ( prop == m_particle_prop ) {
         ++nparticles;
diff --git a/Phys/ParticleMaker/src/ParticleMakerForParticleFlow.cpp b/Phys/ParticleMaker/src/ParticleMakerForParticleFlow.cpp
index 87b06ef0e0554dd805058d7a287e5282b3586a93..2950feb9836ffaef67d3344ec6913eed57ec43f4 100644
--- a/Phys/ParticleMaker/src/ParticleMakerForParticleFlow.cpp
+++ b/Phys/ParticleMaker/src/ParticleMakerForParticleFlow.cpp
@@ -236,11 +236,12 @@ LHCb::Particles ParticleMakerForParticleFlow::operator()( LHCb::ProtoParticle::R
 
     // Find the PID corresponding to the best ProbNN hypothesis
     Pid::Array<float> prob;
-    prob( Pid::Key::pi ) = proto->info( LHCb::ProtoParticle::additionalInfo::ProbNNpi, 0 );
-    prob( Pid::Key::K )  = proto->info( LHCb::ProtoParticle::additionalInfo::ProbNNk, 0 );
-    prob( Pid::Key::mu ) = proto->info( LHCb::ProtoParticle::additionalInfo::ProbNNmu, 0 );
-    prob( Pid::Key::e )  = proto->info( LHCb::ProtoParticle::additionalInfo::ProbNNe, 0 );
-    prob( Pid::Key::p )  = proto->info( LHCb::ProtoParticle::additionalInfo::ProbNNp, 0 );
+    auto const        gpid = proto->globalChargedPID();
+    prob( Pid::Key::pi )   = gpid ? gpid->ProbNNpi() : 0.;
+    prob( Pid::Key::K )    = gpid ? gpid->ProbNNk() : 0.;
+    prob( Pid::Key::mu )   = gpid ? gpid->ProbNNmu() : 0.;
+    prob( Pid::Key::e )    = gpid ? gpid->ProbNNe() : 0.;
+    prob( Pid::Key::p )    = gpid ? gpid->ProbNNp() : 0.;
 
     auto bestID = bestPid( prob );
 
diff --git a/Phys/TisTosTobbing/src/lib/ParticleTisTos.cpp b/Phys/TisTosTobbing/src/lib/ParticleTisTos.cpp
index b7e2e1c6e29235b6b4da840f8b117fd3202952a3..fe1014d18c34e623792ef5c425b2ab41d895b214 100644
--- a/Phys/TisTosTobbing/src/lib/ParticleTisTos.cpp
+++ b/Phys/TisTosTobbing/src/lib/ParticleTisTos.cpp
@@ -80,7 +80,7 @@ std::vector<LHCb::LHCbID> ParticleTisTos::protoParticleHits( const ProtoParticle
 
   std::vector<LHCbID> hits;
   const Track*        onit = pp.track();
-  if ( 0 != onit ) {
+  if ( onit ) {
     hits.insert( hits.end(), onit->lhcbIDs().begin(), onit->lhcbIDs().end() );
     if ( msgLevel( MSG::VERBOSE ) ) verbose() << " protoParticleHits copied track hits " << endmsg;
     if ( extend && m_projectTracksToCalo ) {
@@ -94,25 +94,21 @@ std::vector<LHCb::LHCbID> ParticleTisTos::protoParticleHits( const ProtoParticle
     if ( msgLevel( MSG::VERBOSE ) ) verbose() << " protoParticleHits ECAL VIA CaloHypo " << endmsg;
 
     // new Apr 6, 2012:  collect calo cell IDs from ProtoParticle::ExtraInfo if available
-    LHCb::Detector::Calo::CellID caloCell( (unsigned int)0 );
+    LHCb::Detector::Calo::CellID caloCell{};
     bool                         caloOK( false );
 
-    if ( 0 != onit ) {
+    if ( onit ) {
       caloOK = true; // don't care about calo info for charged if not found (we also project tracks!)
-      if ( m_caloClustForCharged )
-        caloCell = LHCb::Detector::Calo::CellID(
-            (unsigned int)pp.info( LHCb::ProtoParticle::additionalInfo::CaloChargedID, 0 ) );
-      // deb info() << " charged " << caloCell.all() << endmsg;
+      if ( auto pid = pp.caloChargedPID(); m_caloClustForCharged && pid )
+        caloCell = pid->ElectronID() ? pid->ElectronID() : pid->ClusterID();
     } else {
-      if ( m_caloClustForNeutral ) {
-        caloCell = LHCb::Detector::Calo::CellID(
-            (unsigned int)pp.info( LHCb::ProtoParticle::additionalInfo::CaloNeutralID, 0 ) );
-        // deb info() << " neutral " << caloCell.all() << endmsg;
+      if ( auto pid = pp.neutralPID(); m_caloClustForNeutral && pid ) {
+        caloCell = pid->CaloNeutralID();
       } else {
         caloOK = true; // don't want calo info for neutrals
       }
     }
-    if ( caloCell.all() != 0 ) {
+    if ( caloCell ) {
       caloOK = true;
       hits.push_back( caloCell );
       if ( extend ) {
@@ -133,7 +129,7 @@ std::vector<LHCb::LHCbID> ParticleTisTos::protoParticleHits( const ProtoParticle
             // deb info() << " inside hypo passed clusters " << endmsg;
             LHCb::Detector::Calo::CellID centerCell, centerCell1, dummyCell;
             //       next if always false: left in for historical reasons
-            if ( 0 != onit && m_caloClustForCharged ) {
+            if ( onit && m_caloClustForCharged ) {
               if ( LHCb::CaloHypo::Hypothesis::EmCharged == hypo->hypothesis() ) {
                 // deb info() << " charged hypo " << endmsg;
                 if ( msgLevel( MSG::VERBOSE ) ) verbose() << " protoParticleHits EmCharged " << endmsg;
@@ -206,7 +202,7 @@ std::vector<LHCb::LHCbID> ParticleTisTos::protoParticleHits( const ProtoParticle
   }
 
   // add muon hits only if needed
-  if ( ( m_TOSFrac[kMuon] > 0.0 ) && ( 0 != onit ) ) {
+  if ( ( m_TOSFrac[kMuon] > 0.0 ) && onit ) {
     if ( msgLevel( MSG::VERBOSE ) ) verbose() << " protoParticleHits fs trying for muons " << endmsg;
     const LHCb::MuonPID* muid = pp.muonPID();
     if ( muid != 0 ) {
@@ -267,14 +263,14 @@ bool ParticleTisTos::addToSignal( const LHCb::Particle& particle ) {
     }
   } else {
     const ProtoParticle* pp = particle.proto();
-    if ( 0 != pp ) {
+    if ( pp ) {
       if ( msgLevel( MSG::VERBOSE ) ) verbose() << " addToSignal with Particle PROTOPARTICLE " << endmsg;
       // deb info() << particle << endmsg;
       if ( addToSignal( *pp ) ) sigModified = true;
     } else {
-      Warning(
-          "Particle passed as signal has no daughters and ProtoParticle is not accessible; TisTossing is not possible",
-          StatusCode::SUCCESS, 3 )
+      Warning( "Particle passed as signal has no daughters and ProtoParticle is not accessible; TisTossing is not "
+               "possible",
+               StatusCode::SUCCESS, 3 )
           .ignore();
     }
   }
@@ -481,7 +477,7 @@ unsigned int ParticleTisTos::tisTos( const LHCb::Particle& particle ) {
   } else {
     // non-composite daughter
     const ProtoParticle* pp = particle.proto();
-    if ( 0 != pp ) { return tisTosSortedHits( protoParticleHits( *pp ) ); }
+    if ( pp ) { return tisTosSortedHits( protoParticleHits( *pp ) ); }
   }
   return 0;
 }
@@ -554,7 +550,7 @@ std::string ParticleTisTos::analysisReport( const LHCb::Particle& particle ) {
   } else {
     // non-composite daughter
     const ProtoParticle* pp = particle.proto();
-    if ( 0 != pp ) {
+    if ( pp ) {
       report << offset() << " ProtoParticle " << analysisReportSortedHits( protoParticleHits( *pp ) ) << std::endl;
       return report.str();
     }
@@ -578,7 +574,7 @@ bool ParticleTisTos::tos( const LHCb::Particle& particle ) {
     return true;
   } else {
     const ProtoParticle* pp = particle.proto();
-    if ( 0 != pp ) { return tosSortedHits( protoParticleHits( *pp ) ); }
+    if ( pp ) { return tosSortedHits( protoParticleHits( *pp ) ); }
   }
   return false;
 }
@@ -599,7 +595,7 @@ bool ParticleTisTos::tis( const LHCb::Particle& particle ) {
     return true;
   } else {
     const ProtoParticle* pp = particle.proto();
-    if ( 0 != pp ) { return tisSortedHits( protoParticleHits( *pp ) ); }
+    if ( pp ) { return tisSortedHits( protoParticleHits( *pp ) ); }
   }
   return false;
 }
@@ -617,7 +613,7 @@ bool ParticleTisTos::tus( const LHCb::Particle& particle ) {
     return false;
   } else {
     const ProtoParticle* pp = particle.proto();
-    if ( 0 != pp ) {
+    if ( pp ) {
       if ( m_compositeTPSviaPartialTOSonly ) {
         return tosSortedHits( protoParticleHits( *pp ) );
       } else {
diff --git a/Pr/PrMCTools/src/PrVeloHeavyFlavourTrackingChecker.cpp b/Pr/PrMCTools/src/PrVeloHeavyFlavourTrackingChecker.cpp
index 76e54145b2418f932cb3931d55b57edeb8043161..554e0b105d8dc90808b76f1d0d6211ba67ba01ea 100644
--- a/Pr/PrMCTools/src/PrVeloHeavyFlavourTrackingChecker.cpp
+++ b/Pr/PrMCTools/src/PrVeloHeavyFlavourTrackingChecker.cpp
@@ -414,8 +414,9 @@ void PrVeloHeavyFlavourTrackingChecker::operator()( MCParticles const& mcparts,
       sc &= tuple->column( compbase + pbase + "PT", pion ? pion->momentum().Pt() : 0. );
       sc &= tuple->column( compbase + pbase + "IP", ip );
       sc &= tuple->column( compbase + pbase + "IPChi2", ipchi2 );
-      sc &= tuple->column( compbase + pbase + "PIDK",
-                           pion ? pion->proto()->info( LHCb::ProtoParticle::additionalInfo::CombDLLk, 0. ) : 0. );
+      sc &= tuple->column(
+          compbase + pbase + "PIDK",
+          pion ? ( pion->proto()->globalChargedPID() ? pion->proto()->globalChargedPID()->CombDLLk() : 0. ) : 0. );
     }
     // track info
     std::string hfbase = base + "HeavyFlavourTracking_";
diff --git a/Rec/ChargedProtoANNPID/src/ChargedProtoParticleAddANNPIDInfo.cpp b/Rec/ChargedProtoANNPID/src/ChargedProtoParticleAddANNPIDInfo.cpp
index c619f58bf9614c0d5fd52eeececea1a497c5f9e9..49c444cf92b0b21c6931dcb0761a14109b40d289 100644
--- a/Rec/ChargedProtoANNPID/src/ChargedProtoParticleAddANNPIDInfo.cpp
+++ b/Rec/ChargedProtoANNPID/src/ChargedProtoParticleAddANNPIDInfo.cpp
@@ -23,6 +23,71 @@
 #include "fmt/format.h"
 #include <memory>
 
+namespace {
+  void setProbNN( LHCb::GlobalChargedPID* pid, LHCb::ProtoParticle::additionalInfo pidtype, float value ) {
+    if ( !pid ) return;
+    using Info = LHCb::ProtoParticle::additionalInfo;
+    switch ( pidtype ) {
+    case Info::ProbNNe:
+      pid->setProbNNe( value );
+      break;
+    case Info::ProbNNmu:
+      pid->setProbNNmu( value );
+      break;
+    case Info::ProbNNpi:
+      pid->setProbNNpi( value );
+      break;
+    case Info::ProbNNk:
+      pid->setProbNNk( value );
+      break;
+    case Info::ProbNNp:
+      pid->setProbNNmu( value );
+      break;
+    case Info::ProbNNd:
+      pid->setProbNNd( value );
+      break;
+    case Info::ProbNNghost:
+      pid->setProbNNghost( value );
+      break;
+    default:
+      break;
+    }
+  }
+
+  std::optional<float> getProbNN( LHCb::GlobalChargedPID const* pid, LHCb::ProtoParticle::additionalInfo pidtype ) {
+    if ( !pid ) return std::nullopt;
+    using Info                 = LHCb::ProtoParticle::additionalInfo;
+    std::optional<float> value = std::nullopt;
+    switch ( pidtype ) {
+    case Info::ProbNNe:
+      value = pid->ProbNNe();
+      break;
+    case Info::ProbNNmu:
+      value = pid->ProbNNmu();
+      break;
+    case Info::ProbNNpi:
+      value = pid->ProbNNpi();
+      break;
+    case Info::ProbNNk:
+      value = pid->ProbNNk();
+      break;
+    case Info::ProbNNp:
+      value = pid->ProbNNp();
+      break;
+    case Info::ProbNNd:
+      value = pid->ProbNNd();
+      break;
+    case Info::ProbNNghost:
+      value = pid->ProbNNghost();
+      break;
+    default:
+      break;
+    }
+    if ( value && *value == LHCb::GlobalChargedPID::DefaultProbNN ) value = std::nullopt;
+    return value;
+  }
+} // namespace
+
 namespace ANNGlobalPID {
 
   //-----------------------------------------------------------------------------
@@ -75,12 +140,14 @@ namespace ANNGlobalPID {
         if ( !proto->track()->checkType( m_tkType ) ) continue;
 
         // Clear current ANN PID information
-        if ( proto->hasInfo( m_protoInfo ) ) {
+        LHCb::GlobalChargedPID* pid = proto->globalChargedPID();
+        if ( proto->hasInfo( m_protoInfo ) || getProbNN( pid, m_protoInfo ) ) {
           //       std::ostringstream mess;
           //       mess << "ProtoParticle already has '" << m_protoInfo
           //            << "' information -> Replacing.";
           //       Warning( mess.str(), StatusCode::SUCCESS, 1 ).ignore();
           proto->eraseInfo( m_protoInfo );
+          setProbNN( pid, m_protoInfo, LHCb::GlobalChargedPID::DefaultProbNN );
         }
 
         // ANN Track Selection.
@@ -99,7 +166,7 @@ namespace ANNGlobalPID {
         }
 
         // add to protoparticle
-        proto->addInfo( m_protoInfo, nnOut );
+        setProbNN( pid, m_protoInfo, nnOut );
 
       } // loop over protos
 
diff --git a/Rec/GlobalReco/python/GlobalReco/Configuration.py b/Rec/GlobalReco/python/GlobalReco/Configuration.py
index 6b4bff62fb233c4c887a22823225646513e7f0f3..cde94c96ab708088258ffce97ea2e229b82b6002 100644
--- a/Rec/GlobalReco/python/GlobalReco/Configuration.py
+++ b/Rec/GlobalReco/python/GlobalReco/Configuration.py
@@ -167,8 +167,7 @@ class GlobalRecoConf(LHCbConfigurableUser):
         from Configurables import (
             GaudiSequencer, FunctionalChargedProtoParticleMaker,
             ChargedProtoParticleAddRichInfo, ChargedProtoParticleAddMuonInfo,
-            ChargedProtoParticleAddEcalInfo, ChargedProtoParticleAddBremInfo,
-            ChargedProtoParticleAddHcalInfo,
+            ChargedProtoParticleAddCaloInfo, ChargedProtoParticleAddBremInfo,
             ChargedProtoParticleAddCombineDLLs, DelegatingTrackSelector)
         cseq = GaudiSequencer("ChargedProtoParticles")
         seq.Members += [cseq]
@@ -193,30 +192,28 @@ class GlobalRecoConf(LHCbConfigurableUser):
         if not self.getProp("NoSpdPrs"):
             raise RuntimeError("NoPrsSpd false not supported in Run 3")
 
-        ecal = addInfo(charged, ChargedProtoParticleAddEcalInfo, "AddEcal")
+        calo = addInfo(charged, ChargedProtoParticleAddCaloInfo, "AddCalo")
 
         ecal_converter1 = converterForHypo(
             "/Event/Rec/Calo/Electrons"
         )  # FIXME: use the propery of the producer... (ClassifyPhotonElectronalg)
         ecal_converter2 = converterForHypo2TrackTable(
-            ecal.InputElectronMatchLocation, ecal_converter1.OutputHypos)
+            calo.InputElectronMatchLocation, ecal_converter1.OutputHypos)
 
-        ecal.InputElectronMatchLocation = ecal_converter2.OutputTable  # HypoTrTable2D
+        calo.InputElectronMatchLocation = ecal_converter2.OutputTable  # HypoTrTable2D
 
         from Configurables import CaloFutureHypoEstimator
-        ecal_hypo = ecal.addTool(CaloFutureHypoEstimator,
+        ecal_hypo = calo.addTool(CaloFutureHypoEstimator,
                                  "CaloFutureHypoEstimator")
-        ecal_hypo.ElectronMatchLocation = ecal.InputElectronMatchLocation
-        ecal_hypo.BremMatchLocation = ecal.InputElectronMatchLocation
+        ecal_hypo.ElectronMatchLocation = calo.InputElectronMatchLocation
+        ecal_hypo.BremMatchLocation = calo.InputElectronMatchLocation
 
         brem = addInfo(charged, ChargedProtoParticleAddBremInfo, "AddBrem")
         brem.InputBremMatchLocation = ecal_converter2.OutputTable  # HypoTrTable2D    --  Rec/Calo/ElectronMatch
         brem_hypo = brem.addTool(CaloFutureHypoEstimator,
                                  "CaloFutureHypoEstimator")
         brem_hypo.BremMatchLocation = brem.InputBremMatchLocation
-        brem_hypo.ElectronMatchLocation = ecal.InputElectronMatchLocation
-
-        hcal = addInfo(charged, ChargedProtoParticleAddHcalInfo, "AddHcal")
+        brem_hypo.ElectronMatchLocation = calo.InputElectronMatchLocation
 
         # Fill the Combined DLL information in the charged protoparticles
         combine = addInfo(charged, ChargedProtoParticleAddCombineDLLs,
@@ -253,9 +250,8 @@ class GlobalRecoConf(LHCbConfigurableUser):
             charged.OutputLevel = level
             rich.OutputLevel = level
             muon.OutputLevel = level
-            ecal.OutputLevel = level
+            calo.OutputLevel = level
             brem.OutputLevel = level
-            hcal.OutputLevel = level
             combine.OutputLevel = level
             neutral.OutputLevel = level
 
diff --git a/Rec/GlobalReco/src/ChargedProtoParticleFilteredCopyAlg.cpp b/Rec/GlobalReco/src/ChargedProtoParticleFilteredCopyAlg.cpp
index 5bd20de912c6596ec70366a44c5bcc02a20b18f8..95dbd8c68298efc89abdf52b3981d92df393ee1e 100644
--- a/Rec/GlobalReco/src/ChargedProtoParticleFilteredCopyAlg.cpp
+++ b/Rec/GlobalReco/src/ChargedProtoParticleFilteredCopyAlg.cpp
@@ -18,7 +18,9 @@
 #include "LHCbAlgs/Transformer.h"
 
 namespace {
-  using OutData = std::tuple<LHCb::ProtoParticles, LHCb::Tracks, LHCb::RichPIDs, LHCb::MuonPIDs, LHCb::Tracks>;
+  using OutData =
+      std::tuple<LHCb::ProtoParticles, LHCb::Tracks, LHCb::RichPIDs, LHCb::MuonPIDs, LHCb::Tracks,
+                 LHCb::Event::Calo::v1::CaloChargedPIDs, LHCb::Event::Calo::v1::BremInfos, LHCb::GlobalChargedPIDs>;
 
   struct ProtoParticlePredicate {
     using Signature                    = bool( const LHCb::ProtoParticle& );
@@ -44,7 +46,8 @@ namespace LHCb {
         : with_functors( name, pSvc, {KeyValue( "InputProtos", "" )},
                          {KeyValue( "OutputProtos", "" ), KeyValue( "OutputTracks", "" ),
                           KeyValue( "OutputRichPIDs", "" ), KeyValue( "OutputMuonPIDs", "" ),
-                          KeyValue( "OutputMuonTracks", "" )} ) {}
+                          KeyValue( "OutputMuonTracks", "" ), KeyValue( "OutputCaloChargedPIDs", "" ),
+                          KeyValue( "OutputBremInfos", "" ), KeyValue( "OutputGlobalChargedPIDs", "" )} ) {}
 
     OutData operator()( ProtoParticle::Range const& ) const override;
 
@@ -59,7 +62,7 @@ namespace LHCb {
 
 OutData LHCb::ChargedProtoParticleFilteredCopyAlg::operator()( ProtoParticle::Range const& input_protos ) const {
   OutData result;
-  auto& [out_protos, out_tracks, out_rpids, out_mpids, out_mtracks] = result;
+  auto& [out_protos, out_tracks, out_rpids, out_mpids, out_mtracks, out_cpids, out_binfos, out_gpids] = result;
 
   // set upper limit on container size/capacity
   auto max_size            = input_protos.size();
@@ -117,6 +120,31 @@ OutData LHCb::ChargedProtoParticleFilteredCopyAlg::operator()( ProtoParticle::Ra
       out_proto->setMuonPID( out_mpids.object( mpid_key ) );
     }
 
+    // copy brem, calochargedpid and global charged pid objects if available
+    auto const* input_binfo = input_proto->bremInfo();
+    if ( input_binfo ) {
+      auto out_binfo = std::make_unique<LHCb::Event::Calo::v1::BremInfo>( *input_binfo );
+      out_binfo->setIDTrack( out_track );
+      out_proto->setBremInfo( out_binfo.get() );
+      out_binfos.insert( out_binfo.release() );
+    }
+
+    auto const* input_cpid = input_proto->caloChargedPID();
+    if ( input_cpid ) {
+      auto out_cpid = std::make_unique<LHCb::Event::Calo::v1::CaloChargedPID>( *input_cpid );
+      out_cpid->setIDTrack( out_track );
+      out_proto->setCaloChargedPID( out_cpid.get() );
+      out_cpids.insert( out_cpid.release() );
+    }
+
+    auto const* input_gpid = input_proto->globalChargedPID();
+    if ( input_gpid ) {
+      auto out_gpid = std::make_unique<LHCb::GlobalChargedPID>( *input_gpid );
+      out_gpid->setIDTrack( out_track );
+      out_proto->setGlobalChargedPID( out_gpid.get() );
+      out_gpids.insert( out_gpid.release() );
+    }
+
     // NOTE: calo info to be added externally, as electron/photon containers are unpacked separately
 
     // move to containers
diff --git a/Rec/GlobalReco/src/FunctionalChargedProtoParticleMaker.cpp b/Rec/GlobalReco/src/FunctionalChargedProtoParticleMaker.cpp
index 737b42984f82799510d0fb892e9fafee0e51a911..db8538785dd5e5f33cab54ec138a225c2d26d43c 100644
--- a/Rec/GlobalReco/src/FunctionalChargedProtoParticleMaker.cpp
+++ b/Rec/GlobalReco/src/FunctionalChargedProtoParticleMaker.cpp
@@ -31,6 +31,8 @@
  */
 
 namespace {
+  using Output = std::tuple<LHCb::ProtoParticles, LHCb::GlobalChargedPIDs>;
+
   struct TrackPredicate {
     using Signature                    = bool( const LHCb::Track& );
     static constexpr auto PropertyName = "Code";
@@ -38,27 +40,26 @@ namespace {
 } // namespace
 
 class FunctionalChargedProtoParticleMaker final
-    : public with_functors<
-          LHCb::Algorithm::MergingTransformer<
-              LHCb::ProtoParticles( const Gaudi::Functional::vector_of_const_<LHCb::Track::Range>& ranges ),
-              LHCb::Algorithm::Traits::usesConditions<>>,
-          TrackPredicate> {
+    : public with_functors<LHCb::Algorithm::MergingMultiTransformer<
+                               Output( const Gaudi::Functional::vector_of_const_<LHCb::Track::Range>& ranges ),
+                               LHCb::Algorithm::Traits::usesConditions<>>,
+                           TrackPredicate> {
 
 public:
   /// Standard constructor
   FunctionalChargedProtoParticleMaker( const std::string& name, ISvcLocator* pSvcLocator )
       : with_functors( name, pSvcLocator, //
                        {"Inputs", {LHCb::TrackLocation::Default}},
-                       KeyValue{"Output", LHCb::ProtoParticleLocation::Charged} ) {}
+                       {KeyValue{"Output", LHCb::ProtoParticleLocation::Charged}, KeyValue{"OutputPIDs", ""}} ) {}
 
-  LHCb::ProtoParticles
-  operator()( const Gaudi::Functional::vector_of_const_<LHCb::Track::Range>& ranges ) const override {
+  Output operator()( const Gaudi::Functional::vector_of_const_<LHCb::Track::Range>& ranges ) const override {
     if ( !m_det.get().geometry() ) { throw GaudiException( "Could not load geometry", name(), StatusCode::FAILURE ); }
     auto& geometry = *m_det.get().geometry();
     // make output container
-    LHCb::ProtoParticles protos;
-    auto const&          track_pred = getFunctor<TrackPredicate>();
-    const bool           useTkKey   = ( ranges.size() == 1 );
+    Output result;
+    auto& [protos, pids]   = result;
+    auto const& track_pred = getFunctor<TrackPredicate>();
+    const bool  useTkKey   = ( ranges.size() == 1 );
     // Loop over tracks container
     for ( const auto& tracks : ranges ) {
       protos.reserve( protos.size() + tracks.size() );
@@ -81,7 +82,11 @@ public:
       }
     }
     for ( const auto& addInfo : m_addInfo ) ( *addInfo )( protos, geometry ).ignore();
-    return protos;
+    // fill GlobalChargedPID object if made and transfer ownership
+    for ( LHCb::ProtoParticle* proto : protos ) {
+      if ( LHCb::GlobalChargedPID* pid = proto->globalChargedPID(); pid ) pids.insert( std::move( pid ) );
+    }
+    return result;
   }
 
 private: