From d698e76d1cd9117ac9d06e9073aac52880c6a137 Mon Sep 17 00:00:00 2001 From: Alex Pearce Date: Thu, 27 May 2021 20:19:35 +0200 Subject: [PATCH 01/10] Fix ETA compilation for LHCb::Particle. --- Phys/FunctorCore/Functors/TrackLike.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Phys/FunctorCore/Functors/TrackLike.h b/Phys/FunctorCore/Functors/TrackLike.h index dd0319ab1a..85ae8391e8 100644 --- a/Phys/FunctorCore/Functors/TrackLike.h +++ b/Phys/FunctorCore/Functors/TrackLike.h @@ -182,11 +182,13 @@ namespace Functors::Track { struct PseudoRapidity : public Function { template auto operator()( Data const& d ) const { - if constexpr ( is_legacy_particle ) return Sel::Utils::deref_if_ptr( d ).momentum().eta(); - if constexpr ( Sel::Utils::has_tracklike_API ) + if constexpr ( is_legacy_particle ) { + return Sel::Utils::deref_if_ptr( d ).momentum().eta(); + } else if constexpr ( Sel::Utils::has_tracklike_API ) { return d.pseudoRapidity( Sel::Utils::get_kinematic_state( d ) ); - else + } else { return d.pseudoRapidity(); + } } }; -- GitLab From 1f069e6f2fca57acdbddbfb9adc6070604b7fac8 Mon Sep 17 00:00:00 2001 From: Alex Pearce Date: Thu, 27 May 2021 20:20:27 +0200 Subject: [PATCH 02/10] Support basic and composite LHCb::Particle in chi^2 functors. --- Phys/FunctorCore/Functors/TrackLike.h | 40 +++++++++++++++++++-------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/Phys/FunctorCore/Functors/TrackLike.h b/Phys/FunctorCore/Functors/TrackLike.h index 85ae8391e8..9bf7fd1776 100644 --- a/Phys/FunctorCore/Functors/TrackLike.h +++ b/Phys/FunctorCore/Functors/TrackLike.h @@ -202,6 +202,10 @@ namespace Functors::Track { }; /** @brief chi^2/d.o.f., as defined by the chi2PerDoF accessor. + * + * If the input is a legacy LHCb::Particle with a track, the track object's + * accessor is used. If a track is not present but a vertex is the vertex's + * accessor is used. */ struct Chi2PerDoF : public Function { template @@ -209,7 +213,13 @@ namespace Functors::Track { auto const& particle = Sel::Utils::deref_if_ptr( d ); using Particle = std::decay_t; if constexpr ( Sel::Utils::is_legacy_particle ) { - return particle.endVertex()->chi2PerDoF(); + // Try accessing a track first + const auto value = Sel::Utils::get_track_property_from_particle( + d, []( auto&& t ) { return t->chi2PerDoF(); }, invalid_value ); + // If that failed, try accessing a vertex + if ( value == invalid_value && particle.endVertex() ) { return particle.endVertex()->chi2PerDoF(); } + // Input is either a charged or a neutral basic + return value; } else { return Sel::get::chi2PerDoF( particle ); } @@ -217,21 +227,27 @@ namespace Functors::Track { }; /** @brief chi^2, as defined by the chi2 accessor. + * + * If the input is a legacy LHCb::Particle with a track, the track object's + * accessor is used. If a track is not present but a vertex is the vertex's + * accessor is used. */ struct Chi2 : public Function { template auto operator()( Data const& d ) const { - return Sel::Utils::deref_if_ptr( d ).chi2(); - } - }; - - /** @brief chi^2/d.o.f., as defined by the chi2PerDoF accessor of the track - */ - struct TrChi2PerDoF : public Function { - template - auto operator()( Data const& d ) const { - return Sel::Utils::get_track_property_from_particle( - d, []( auto&& t ) { return t->chi2PerDoF(); }, invalid_value ); + auto const& particle = Sel::Utils::deref_if_ptr( d ); + using Particle = std::decay_t; + if constexpr ( is_legacy_particle ) { + // Try accessing a track first + const auto value = Sel::Utils::get_track_property_from_particle( + particle, []( auto&& t ) { return t->chi2(); }, invalid_value ); + // If that failed, try accessing a vertex + if ( value == invalid_value && particle.endVertex() ) { return particle.endVertex()->chi2(); } + // Input is either a charged or a neutral basic + return value; + } else { + return particle.chi2(); + } } }; -- GitLab From c57937ceb69bfaddf93bfe8bf5384d0d640a27c7 Mon Sep 17 00:00:00 2001 From: Alex Pearce Date: Thu, 27 May 2021 20:20:45 +0200 Subject: [PATCH 03/10] Support LHCb::Particle in ghost probability functor. --- Phys/FunctorCore/Functors/TrackLike.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Phys/FunctorCore/Functors/TrackLike.h b/Phys/FunctorCore/Functors/TrackLike.h index 9bf7fd1776..e20cb93597 100644 --- a/Phys/FunctorCore/Functors/TrackLike.h +++ b/Phys/FunctorCore/Functors/TrackLike.h @@ -268,7 +268,12 @@ namespace Functors::Track { struct GhostProbability : public Function { template auto operator()( Data const& d ) const { - return Sel::Utils::deref_if_ptr( d ).ghostProbability(); + if constexpr ( is_legacy_particle ) { + return Sel::Utils::get_track_property_from_particle( + d, []( auto&& t ) { return t->ghostProbability(); }, invalid_value ); + } else { + return Sel::Utils::deref_if_ptr( d ).ghostProbability(); + } } }; -- GitLab From 4e4ac8a4eba50ca45f040406c5849664d614b034 Mon Sep 17 00:00:00 2001 From: Alex Pearce Date: Thu, 27 May 2021 20:21:44 +0200 Subject: [PATCH 04/10] Support LHCb::Particle in MINIP functor. --- Phys/FunctorCore/Functors/TrackLike.h | 20 +++++--------------- Phys/SelKernel/SelKernel/Utilities.h | 21 +++++++++++++++++++++ 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/Phys/FunctorCore/Functors/TrackLike.h b/Phys/FunctorCore/Functors/TrackLike.h index e20cb93597..258f048f22 100644 --- a/Phys/FunctorCore/Functors/TrackLike.h +++ b/Phys/FunctorCore/Functors/TrackLike.h @@ -429,9 +429,10 @@ namespace Functors::detail { template auto operator()( VContainer const& vertices, TrackChunk const& track_chunk ) const { - auto&& state = track_chunk.closestToBeamState(); - auto&& state_pos = state.position(); - auto&& state_dir = state.slopes(); + auto const& track = Sel::Utils::get_track_from_particle_or_track( track_chunk ); + auto&& state = track.closestToBeamState(); + auto&& state_pos = state.position(); + auto&& state_dir = state.slopes(); using std::min; // so min() below works when float_v is plain float or double using std::sqrt; // ... using vector_t = std::decay_t; @@ -457,18 +458,7 @@ namespace Functors::detail { // Accept generic containers using std::begin; using std::end; - // Compatibility layer :( -- get something with value semantics; for the - // non-legacy case then 'track' and 'track_chunk' will be the same thing - auto const& track = [&]() -> decltype( auto ) { - if constexpr ( is_legacy_particle ) { - // this only works for chargedbasics.. - auto const* track = Sel::Utils::get_track_from_particle( track_chunk ); - assert( track ); - return *track; - } else { - return track_chunk; - } - }(); + auto const& track = Sel::Utils::get_track_from_particle_or_track( track_chunk ); // Figure out the return type (probably float or SIMDWrapper's float_v for now) using float_v = decltype( Sel::Utils::impactParameterChi2( track, vertices.front() ) ); static_assert( std::numeric_limits::is_specialized, diff --git a/Phys/SelKernel/SelKernel/Utilities.h b/Phys/SelKernel/SelKernel/Utilities.h index 7c47c53592..288f3213f8 100644 --- a/Phys/SelKernel/SelKernel/Utilities.h +++ b/Phys/SelKernel/SelKernel/Utilities.h @@ -208,6 +208,27 @@ namespace Sel::Utils { return track ? accessor( track ) : invalid; }; + /** Unwrap an LHCb::Particle to a LHCb::Track or return the argument + * + * This is a compatibility layer to allow for similar behaviour when operating + * on LHCb::Particle or on SOA objects. For the non-legacy case the argument + * is returned immediately. + * + * This method **assumes** any input LHCb::Particle represents a 'charged + * basic' object and has an associated LHCb::Track. + */ + template + auto& get_track_from_particle_or_track( T const& particle_or_track ) { + if constexpr ( is_legacy_particle ) { + auto const* track = Sel::Utils::get_track_from_particle( particle_or_track ); + // Catch none-charged-basic usage in debug builds + assert( track ); + return *track; + } else { + return particle_or_track; + } + } + /* when we have different accessor names, we use these indirection layers */ template auto threeMomentum( T const& item ) { -- GitLab From 89f2055dc5404ad746368dfbcb62d0a665878fd6 Mon Sep 17 00:00:00 2001 From: Alex Pearce Date: Thu, 27 May 2021 20:21:53 +0200 Subject: [PATCH 05/10] Add documentation to math functors. --- Phys/FunctorCore/python/Functors/math.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Phys/FunctorCore/python/Functors/math.py b/Phys/FunctorCore/python/Functors/math.py index e8417d093e..1eece65355 100644 --- a/Phys/FunctorCore/python/Functors/math.py +++ b/Phys/FunctorCore/python/Functors/math.py @@ -18,10 +18,11 @@ from Functors.grammar import WrappedBoundFunctor def log(functor): """Wrap the math::log functor. - This allows constructions such as: - import Functors.math as fmath - from Functors import PT - myFilter(Functor=(fmath.log(PT) > 1.f), ...) + Example usage:: + + >>> import Functors.math as fmath + >>> from Functors import PT + >>> fmath.log(PT) > 1.0 """ return WrappedBoundFunctor(top_level_namespace + 'math::log', header_prefix + 'Function.h', functor) @@ -31,6 +32,12 @@ def in_range(min_val, functor, max_val): """Wrap the math::in_range functor. This is efficient and only evaluates the functor once. + + Example usage:: + + >>> import Functors.math as fmath + >>> from Functors import MASS + >>> fmath.in_range(1.5 * GeV, MASS, 2.5 * GeV) """ return WrappedBoundFunctor(top_level_namespace + 'math::in_range', header_prefix + 'Function.h', min_val, functor, -- GitLab From 16fcacfd170f98f45f0ed9c0fd679c62b9c952ae Mon Sep 17 00:00:00 2001 From: Alex Pearce Date: Thu, 27 May 2021 20:21:53 +0200 Subject: [PATCH 06/10] Add documentation to math functors. --- Phys/FunctorCore/python/Functors/math.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Phys/FunctorCore/python/Functors/math.py b/Phys/FunctorCore/python/Functors/math.py index 1eece65355..eb816fa4cc 100644 --- a/Phys/FunctorCore/python/Functors/math.py +++ b/Phys/FunctorCore/python/Functors/math.py @@ -23,6 +23,7 @@ def log(functor): >>> import Functors.math as fmath >>> from Functors import PT >>> fmath.log(PT) > 1.0 + ('operator>( ::Functors::math::log( ... """ return WrappedBoundFunctor(top_level_namespace + 'math::log', header_prefix + 'Function.h', functor) @@ -37,7 +38,8 @@ def in_range(min_val, functor, max_val): >>> import Functors.math as fmath >>> from Functors import MASS - >>> fmath.in_range(1.5 * GeV, MASS, 2.5 * GeV) + >>> fmath.in_range(1500, MASS, 2500) + ('::Functors::math::in_range( ... """ return WrappedBoundFunctor(top_level_namespace + 'math::in_range', header_prefix + 'Function.h', min_val, functor, -- GitLab From 58ab499599f5b0fa3504ddefa446b2bd152d1650 Mon Sep 17 00:00:00 2001 From: Alex Pearce Date: Fri, 28 May 2021 10:11:06 +0200 Subject: [PATCH 07/10] Redefine MASS functor. The MASS functor now takes no arguments returns the result of the `mass()` method. The original MASS functor has been renamed to MASSWITHHYPOTHESES and requires a list of mass hypotheses which are assigned to the child particles. This functor no longer tries to defer to the `mass()` method. Closes #154. --- Phys/FunctorCore/Functors/Composite.h | 67 +++++++++---------- Phys/FunctorCore/python/Functors/__init__.py | 12 ++-- .../python/Functors/tests/categories.py | 4 +- .../tests/options/VertexCutFunctors.py | 4 +- .../tests/options/test_functors.py | 7 +- 5 files changed, 44 insertions(+), 50 deletions(-) diff --git a/Phys/FunctorCore/Functors/Composite.h b/Phys/FunctorCore/Functors/Composite.h index c12824548c..688dc181e7 100644 --- a/Phys/FunctorCore/Functors/Composite.h +++ b/Phys/FunctorCore/Functors/Composite.h @@ -249,46 +249,42 @@ namespace Functors::Composite { std::move( vertex_location ) ); } + /** @brief Calculate the composite mass using the given child mass hypotheses. */ template - struct Mass : public Function { + struct MassWithHypotheses : public Function { /** Create a mass functor with a list of mass hypotheses that can be a mix * of floating point values (in MeV) and names of particles. Particle * names are translated into mass values using the ParticlePropertySvc. */ - Mass( std::tuple mass_inputs ) : m_mass_inputs{std::move( mass_inputs )} {} + MassWithHypotheses( std::tuple mass_inputs ) : m_mass_inputs{std::move( mass_inputs )} {} void bind( TopLevelInfo& top_level ) { bind( top_level, std::index_sequence_for{} ); } template auto operator()( CombinationType const& combination ) const { - if constexpr ( Sel::type_traits::has_mass_v ) { - // If there's a mass accessor then just call it - return combination.mass(); + // Calculate the mass from the child 3-momenta and the given + // mass hypotheses. Start by checking we have the correct number. + if constexpr ( detail::has_static_NumChildren_v ) { + // Check the number of children at compile time if possible + static_assert( sizeof...( MassInputs ) == CombinationType::NumChildren, + "Wrong number of masses supplied to Mass functor." ); } else { - // Otherwise calculate the mass from the child 3-momenta and the given - // mass hypotheses. Start by checking we have the correct number. - if constexpr ( detail::has_static_NumChildren_v ) { - // Check the number of children at compile time if possible - static_assert( sizeof...( MassInputs ) == CombinationType::NumChildren, - "Wrong number of masses supplied to Mass functor." ); - } else { - // The composite type erases the number of children, so we can only - // make this check at runtime. - if ( UNLIKELY( sizeof...( MassInputs ) != combination.numChildren() ) ) { - throw GaudiException{"Mismatch between number of mass values given (" + - std::to_string( sizeof...( MassInputs ) ) + - ") and the number of children in the given object (" + - std::to_string( combination.numChildren() ) + ")", - "Functors::Composite::Mass", StatusCode::FAILURE}; - } + // The composite type erases the number of children, so we can only + // make this check at runtime. + if ( UNLIKELY( sizeof...( MassInputs ) != combination.numChildren() ) ) { + throw GaudiException{"Mismatch between number of mass values given (" + + std::to_string( sizeof...( MassInputs ) ) + + ") and the number of children in the given object (" + + std::to_string( combination.numChildren() ) + ")", + "Functors::Composite::Mass", StatusCode::FAILURE}; } - float E{0.f}; // TODO make SIMD-friendly? - using std::sqrt; - for ( std::size_t idau = 0; idau < m_mass_values.size(); ++idau ) { - E += sqrt( std::pow( combination.daughters.P( idau ), 2 ) + m_mass_values[idau] * m_mass_values[idau] ); - } - return sqrt( E * E - combination.threeMomentum().Mag2() ); } + float E{0.f}; // TODO make SIMD-friendly? + using std::sqrt; + for ( std::size_t idau = 0; idau < m_mass_values.size(); ++idau ) { + E += sqrt( std::pow( combination.daughters.P( idau ), 2 ) + m_mass_values[idau] * m_mass_values[idau] ); + } + return sqrt( E * E - combination.threeMomentum().Mag2() ); } private: @@ -320,16 +316,17 @@ namespace Functors::Composite { std::array m_mass_values{}; }; - struct CompositeMass : public Function { - /** This allows some error messages to be customised. It is not critical. */ - static constexpr auto name() { return "CompositeMass"; } + /** @brief Return the input object's mass as defined by an accessor. */ + struct Mass : public Function { + static constexpr auto name() { return "Mass"; } template - auto operator()( Particle const& composite ) const { - if constexpr ( Sel::Utils::is_legacy_particle ) - return composite.momentum().M(); - else - return composite.mass(); + auto operator()( Particle const& particle ) const { + if constexpr ( Sel::Utils::is_legacy_particle ) { + return particle.momentum().M(); + } else { + return particle.mass(); + } } }; diff --git a/Phys/FunctorCore/python/Functors/__init__.py b/Phys/FunctorCore/python/Functors/__init__.py index 620f2b4fde..d9a664c24f 100644 --- a/Phys/FunctorCore/python/Functors/__init__.py +++ b/Phys/FunctorCore/python/Functors/__init__.py @@ -116,14 +116,14 @@ MAX = Functor( "Calculate the maximum of the given functor value.", Params=[('Functor', 'The functor to find the maximum return value of.', BoundFunctor)]) -MASS = Functor( - 'MASS', - 'Composite::Mass', +MASSWITHHYPOTHESES = Functor( + 'MASSWITHHYPOTHESES', + 'Composite::MassWithHypotheses', 'Composite.h', - '''Invariant mass of a combined particle.''', + '''Invariant mass of a combined particle given child mass hypotheses.''', Params=[('Masses', 'Masses of the children', list)]) -COMPOSITEMASS = Functor('COMPOSITEMASS', 'Composite::CompositeMass', - 'Composite.h', 'Get the particle mass') +MASS = Functor('MASS', 'Composite::Mass', 'Composite.h', + 'Get the particle (composite or basic) mass.') DOCA = Functor( 'DOCA', "Combination::DistanceOfClosestApproach", diff --git a/Phys/FunctorCore/python/Functors/tests/categories.py b/Phys/FunctorCore/python/Functors/tests/categories.py index 62229531d4..0cf05a70aa 100644 --- a/Phys/FunctorCore/python/Functors/tests/categories.py +++ b/Phys/FunctorCore/python/Functors/tests/categories.py @@ -154,9 +154,7 @@ DICT = { }, 'Composite2Body': { 'Functors': [ - MASS(Masses=[497., 497.]), - MASS(Masses=[137., 'pi+']), - MASS(Masses=['mu+', 'mu-']), + MASS, ], 'Includes': ['Composite'], }, diff --git a/Phys/FunctorCore/tests/options/VertexCutFunctors.py b/Phys/FunctorCore/tests/options/VertexCutFunctors.py index b7be64eba9..792bb1f3d0 100644 --- a/Phys/FunctorCore/tests/options/VertexCutFunctors.py +++ b/Phys/FunctorCore/tests/options/VertexCutFunctors.py @@ -30,9 +30,7 @@ app.EvtMax = 0 #MessageSvc().OutputLevel = DEBUG #Basic declaration test -vertex_functors = [(MASS(Masses=[106., 106.]) > 200. * MeV), - (MASS(Masses=["mu+", 106.]) > 200. * MeV), - (MASS(Masses=["mu+", "mu-"]) > 200. * MeV), Z > -1000 * mm] +vertex_functors = [(MASS > 200. * MeV), Z > -1000 * mm] for vertex_functor in vertex_functors: functor_test = test_functor( diff --git a/Phys/FunctorCore/tests/options/test_functors.py b/Phys/FunctorCore/tests/options/test_functors.py index 0305c8c878..80d53432fd 100644 --- a/Phys/FunctorCore/tests/options/test_functors.py +++ b/Phys/FunctorCore/tests/options/test_functors.py @@ -107,9 +107,10 @@ only_combination_functors = [ ] only_composite_functors = [ - MASS(Masses=[497., 497.]), # assume we'll test with a 2-body vertex... - MASS(Masses=[137., 'pi+']), - MASS(Masses=["mu+", "mu-"]), + MASSWITHHYPOTHESES( + Masses=[497., 497.]), # assume we'll test with a 2-body vertex... + MASSWITHHYPOTHESES(Masses=[137., 'pi+']), + MASSWITHHYPOTHESES(Masses=["mu+", "mu-"]), BPVETA(DUMMY_DATA_DEP), BPVCORRM(Mass=497., Vertices=DUMMY_DATA_DEP), BPVDIRA(DUMMY_DATA_DEP), -- GitLab From efb47f75feba6545c39d72cc4ebc118c9fe0a5ee Mon Sep 17 00:00:00 2001 From: Alex Pearce Date: Fri, 28 May 2021 10:28:45 +0200 Subject: [PATCH 08/10] Don't support mass hypotheses in BPVCORRM. See #154. --- Phys/FunctorCore/Functors/Composite.h | 24 ++++--------------- Phys/FunctorCore/python/Functors/__init__.py | 7 ++---- .../python/Functors/tests/categories.py | 2 +- .../tests/options/test_functors.py | 2 +- 4 files changed, 8 insertions(+), 27 deletions(-) diff --git a/Phys/FunctorCore/Functors/Composite.h b/Phys/FunctorCore/Functors/Composite.h index 688dc181e7..721e7abece 100644 --- a/Phys/FunctorCore/Functors/Composite.h +++ b/Phys/FunctorCore/Functors/Composite.h @@ -141,9 +141,6 @@ namespace Functors::detail { }; struct CorrectedMass : public Function { - float m_mass_hypo; - CorrectedMass( float mass_hypo ) : m_mass_hypo{mass_hypo} {} - static constexpr auto name() { return "CorrectedMass"; } template @@ -158,20 +155,8 @@ namespace Functors::detail { auto const [mom, m2] = [&]() { if constexpr ( detail::has_threeMomentum_v && Sel::type_traits::has_mass2_v ) { return std::tuple{composite.threeMomentum(), composite.mass2()}; - } else if constexpr ( Sel::type_traits::has_momentum_v ) { - auto p4 = composite.momentum(); - return std::tuple{p4.Vect(), p4.M2()}; } else { - // Use m_mass_hypo to get the 4-momentum of the composite - std::array child_masses; - child_masses.fill( m_mass_hypo ); - Gaudi::LorentzVector p4; - // @todo This does too much work! - Gaudi::XYZPoint position; - Gaudi::SymMatrix3x3 poscov; - Gaudi::SymMatrix4x4 p4cov; - Gaudi::Matrix4x3 momposcov; - LHCb::TrackKernel::computeParticleParams( composite, child_masses, position, p4, poscov, p4cov, momposcov ); + auto p4 = composite.momentum(); return std::tuple{p4.Vect(), p4.M2()}; } }(); @@ -242,11 +227,10 @@ namespace Functors::Composite { return detail::DataDepWrapper{std::move( vertex_location )}; } - /** @brief Calculate the corrected mass, using the given mass hypothesis. */ + /** @brief Calculate the corrected mass. */ template - auto CorrectedMass( float assumed_mass, std::string vertex_location ) { - return detail::add_data_deps( detail::CorrectedMass{assumed_mass}, - std::move( vertex_location ) ); + auto CorrectedMass( std::string vertex_location ) { + return detail::DataDepWrapper{std::move( vertex_location )}; } /** @brief Calculate the composite mass using the given child mass hypotheses. */ diff --git a/Phys/FunctorCore/python/Functors/__init__.py b/Phys/FunctorCore/python/Functors/__init__.py index d9a664c24f..54b51174c6 100644 --- a/Phys/FunctorCore/python/Functors/__init__.py +++ b/Phys/FunctorCore/python/Functors/__init__.py @@ -200,11 +200,8 @@ BPVCORRM = Functor( 'BPVCORRM', 'Composite::CorrectedMass', 'Composite.h', - '''Compute the corrected mass of the composite using the given mass - hypotheses and the associated [primary] vertex.''', - Params=[('Mass', 'Mass hypotheses to apply to the composite children', - float), - ('Vertices', 'TES location of input [primary] vertices.', + 'Compute the corrected mass of the composite using the associated [primary] vertex.', + Params=[('Vertices', 'TES location of input [primary] vertices.', DataHandle)], TemplateParams=[('VerticesType', 'Input vertex container type')]) BPVDIRA = Functor( diff --git a/Phys/FunctorCore/python/Functors/tests/categories.py b/Phys/FunctorCore/python/Functors/tests/categories.py index 0cf05a70aa..531442c82b 100644 --- a/Phys/FunctorCore/python/Functors/tests/categories.py +++ b/Phys/FunctorCore/python/Functors/tests/categories.py @@ -148,7 +148,7 @@ DICT = { simple_wrap(BPVETA, 'PVs', 'Vertices'), simple_wrap(BPVDIRA, 'PVs', 'Vertices'), simple_wrap(BPVFDCHI2, 'PVs', 'Vertices'), - simple_wrap(partial(BPVCORRM, Mass=497.), 'PVs', 'Vertices'), + simple_wrap(BPVCORRM, 'PVs', 'Vertices'), ], 'Includes': ['Particle'], }, diff --git a/Phys/FunctorCore/tests/options/test_functors.py b/Phys/FunctorCore/tests/options/test_functors.py index 80d53432fd..036e1ae3c2 100644 --- a/Phys/FunctorCore/tests/options/test_functors.py +++ b/Phys/FunctorCore/tests/options/test_functors.py @@ -112,7 +112,7 @@ only_composite_functors = [ MASSWITHHYPOTHESES(Masses=[137., 'pi+']), MASSWITHHYPOTHESES(Masses=["mu+", "mu-"]), BPVETA(DUMMY_DATA_DEP), - BPVCORRM(Mass=497., Vertices=DUMMY_DATA_DEP), + BPVCORRM(Vertices=DUMMY_DATA_DEP), BPVDIRA(DUMMY_DATA_DEP), BPVFDCHI2(DUMMY_DATA_DEP), COMB( -- GitLab From 1192fa24369959b1cd76d7af1f557adcf08dc20b Mon Sep 17 00:00:00 2001 From: Alex Pearce Date: Fri, 28 May 2021 14:34:29 +0200 Subject: [PATCH 09/10] Remove unused import. --- Phys/FunctorCore/python/Functors/grammar.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Phys/FunctorCore/python/Functors/grammar.py b/Phys/FunctorCore/python/Functors/grammar.py index b1b827fdbf..78cf2cab5d 100644 --- a/Phys/FunctorCore/python/Functors/grammar.py +++ b/Phys/FunctorCore/python/Functors/grammar.py @@ -13,7 +13,6 @@ Pure-python classes used as building blocks of functor expressions. """ from builtins import zip from builtins import object -import json import logging from GaudiKernel.Configurable import Configurable from Functors.common import header_prefix, top_level_namespace -- GitLab From f2edfcb18ea2cae45b994a302510ff0f4646cfcf Mon Sep 17 00:00:00 2001 From: Alex Pearce Date: Fri, 28 May 2021 14:35:17 +0200 Subject: [PATCH 10/10] Propagate unbound functor docstring. Closes #201. --- Phys/FunctorCore/python/Functors/grammar.py | 36 +++++++++++++++------ 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/Phys/FunctorCore/python/Functors/grammar.py b/Phys/FunctorCore/python/Functors/grammar.py index 78cf2cab5d..3b8224f232 100644 --- a/Phys/FunctorCore/python/Functors/grammar.py +++ b/Phys/FunctorCore/python/Functors/grammar.py @@ -8,11 +8,8 @@ # granted to it by virtue of its status as an Intergovernmental Organization # # or submit itself to any jurisdiction. # ############################################################################### -"""@package Functors -Pure-python classes used as building blocks of functor expressions. -""" -from builtins import zip -from builtins import object +"""Pure-python classes used as building blocks of functor expressions.""" +from inspect import cleandoc import logging from GaudiKernel.Configurable import Configurable from Functors.common import header_prefix, top_level_namespace @@ -211,20 +208,39 @@ class FunctorBase(object): class BoundFunctor(FunctorBase, type( 'Configurable', (), {})): #hack to ensure that the BoundFunctor is parsed correctly - """A functor that has had all its parameters fully specified.""" + """A functor that has had all its parameters fully specified. + + Note: + + A instance of this class may have a docstring, accessible through the + ``__doc__`` property, that includes information of the original + 'un-bound' functor. However, this amended docstring won't appear when + calling ``help`` on the instance, so check ``__doc__`` explicitly for + more information. + + See `Rec#201 `_ for + details. + """ def __init__(self, code=None, nice_code=None, headers=[], inputs=None, - nice_name=None): + nice_name=None, + docs=""): self._strrep = nice_code # RUNNUMBER(ODIN=...) self._strname = nice_name # RUNNUMBER self._code = code # Unique DataHandle objects held by this functor self._inputs = list(set(inputs or [])) self._headers = headers + # Assign an instance-specific docstring, allowing bound functors to + # forward the docstring of the base functor. Note that this will *not* + # change the docstring displayed with ``help`` as that shows the class + # docstring + if docs: + self.__doc__ = f"{cleandoc(docs)}\n\n{cleandoc(self.__doc__)}" def __getstate__(self): """Return serialisation state for pickling. @@ -374,7 +390,8 @@ def Functor(representation, code=self.code(), nice_code=representation, headers=[self._header], - nice_name=representation) + nice_name=representation, + docs=brief_docs) def call_with_args(self, *args, **kwargs): """Convert this Functor object to a BoundFunctor, using the given @@ -506,7 +523,8 @@ def Functor(representation, nice_code=repr_str, headers=list(set(cpp_headers)), inputs=inputs, - nice_name=representation) + nice_name=representation, + docs=brief_docs) call_doc_lines = ["Keyword arguments:"] for argtuple in Params: -- GitLab