diff --git a/GaudiAlg/include/GaudiAlg/MergingTransformer.h b/GaudiAlg/include/GaudiAlg/MergingTransformer.h index 6bcc8177eb5133b6adc800b0f5c764c6be3afb5c..c60b1538e5ae5238003f37a00d26bc207d7072a2 100644 --- a/GaudiAlg/include/GaudiAlg/MergingTransformer.h +++ b/GaudiAlg/include/GaudiAlg/MergingTransformer.h @@ -279,6 +279,87 @@ namespace Gaudi::Functional { }, Gaudi::Details::Property::ImmediatelyInvokeHandler{true}} {} + // Many of the same -> N with filter functionality + template <typename Signature, typename Traits_ = Traits::BaseClass_t<Gaudi::Algorithm>> + struct MergingMultiTransformerFilter; + + template <typename... Outs, typename In, typename Traits_> + struct MergingMultiTransformerFilter<std::tuple<Outs...>( vector_of_const_<In> const& ), Traits_> + : details::DataHandleMixin<std::tuple<Outs...>, std::tuple<>, Traits_> { + + private: + using base_class = details::DataHandleMixin<std::tuple<Outs...>, std::tuple<>, Traits_>; + + public: + using KeyValue = typename base_class::KeyValue; + using KeyValues = typename base_class::KeyValues; + using OutKeys = std::array<KeyValue, sizeof...( Outs )>; + + MergingMultiTransformerFilter( std::string const& name, ISvcLocator* locator, KeyValues const& inputs, + OutKeys const& outputs ); + + // accessor to input Locations + std::string const& inputLocation( unsigned int n ) const { return m_inputLocations.value()[n]; } + unsigned int inputLocationSize() const { return m_inputLocations.value().size(); } + + // derived classes can NOT implement execute + StatusCode execute( EventContext const& ) const override final { + vector_of_const_<In> ins; + ins.reserve( m_inputs.size() ); + std::transform( m_inputs.begin(), m_inputs.end(), std::back_inserter( ins ), + details::details2::get_from_handle<In>{} ); + try { + return std::apply( + [&]( auto&... outhandle ) { + GF_SUPPRESS_SPURIOUS_CLANG_WARNING_BEGIN + return std::apply( + [&outhandle...]( bool passed, auto&&... data ) { + ( details::put( outhandle, std::forward<decltype( data )>( data ) ), ... ); + return passed; + }, + ( *this )( std::as_const( ins ) ) ); + GF_SUPPRESS_SPURIOUS_CLANG_WARNING_END + }, + this->m_outputs ) + ? FilterDecision::PASSED + : FilterDecision::FAILED; + } catch ( GaudiException& e ) { + ( e.code() ? this->warning() : this->error() ) << e.message() << endmsg; + return e.code(); + } + } + + virtual std::tuple<bool, Outs...> operator()( const vector_of_const_<In>& inputs ) const = 0; + + private: + // if In is a pointer, it signals optional (as opposed to mandatory) input + template <typename T> + using InputHandle_t = details::InputHandle_t<Traits_, typename std::remove_pointer<T>::type>; + std::vector<InputHandle_t<In>> m_inputs; // and make the handles properties instead... + Gaudi::Property<std::vector<std::string>> m_inputLocations; // TODO/FIXME: remove this duplication... + // TODO/FIXME: replace vector of string property + call-back with a + // vector<handle> property ... as soon as declareProperty can deal with that. + }; + + template <typename... Outs, typename In, typename Traits_> + MergingMultiTransformerFilter<std::tuple<Outs...>( const vector_of_const_<In>& ), + Traits_>::MergingMultiTransformerFilter( std::string const& name, + ISvcLocator* pSvcLocator, + KeyValues const& inputs, + OutKeys const& outputs ) + : base_class( name, pSvcLocator, outputs ) + , m_inputLocations{ + this, inputs.first, inputs.second, + [=]( Gaudi::Details::PropertyBase& ) { + this->m_inputs = details::make_vector_of_handles<decltype( this->m_inputs )>( this, m_inputLocations ); + if ( std::is_pointer_v<In> ) { // handle constructor does not (yet) allow to set + // optional flag... so do it + // explicitly here... + std::for_each( this->m_inputs.begin(), this->m_inputs.end(), []( auto& h ) { h.setOptional( true ); } ); + } + }, + Gaudi::Details::Property::ImmediatelyInvokeHandler{true}} {} + } // namespace Gaudi::Functional #endif diff --git a/GaudiExamples/options/FunctionalAlgorithms/MultiMergers.py b/GaudiExamples/options/FunctionalAlgorithms/MultiMergers.py new file mode 100644 index 0000000000000000000000000000000000000000..e80b0953c20a87fdc935250f6179b280b9ec1ce6 --- /dev/null +++ b/GaudiExamples/options/FunctionalAlgorithms/MultiMergers.py @@ -0,0 +1,56 @@ +##################################################################################### +# (c) Copyright 2021 CERN for the benefit of the LHCb and ATLAS collaborations # +# # +# This software is distributed under the terms of the Apache version 2 licence, # +# copied verbatim in the file "LICENSE". # +# # +# In applying this licence, CERN does not waive the privileges and immunities # +# granted to it by virtue of its status as an Intergovernmental Organization # +# or submit itself to any jurisdiction. # +##################################################################################### +from Configurables import ( + ApplicationMgr, + EvtStoreSvc, + Gaudi__Examples__IntDataProducer as IntDataProducer, + is2ff_merger, + is2ff_merger_filter, +) + +int_a = IntDataProducer("IntAProducer", Value=2, OutputLocation="/Event/IntA") +int_b = IntDataProducer("IntBProducer", Value=3, OutputLocation="/Event/IntB") +merger = is2ff_merger( + InputInts=[str(int_a.OutputLocation), + str(int_b.OutputLocation)]) +# This filter should set its status to 'passed' as 2 * 2 * 3 > 10 +merger_filter_passing = is2ff_merger_filter( + "MergerFilterPassing", + InputInts=[ + str(int_a.OutputLocation), + str(int_a.OutputLocation), + str(int_b.OutputLocation), + ], + O1="/Event/MF/Float1", + O2="/Event/MF/Float2") +# This filter should set its status to 'failed' as 2 * 3 < 10 +merger_filter_failing = is2ff_merger_filter( + "MergerFilterFailing", + InputInts=[ + str(int_a.OutputLocation), + str(int_b.OutputLocation), + ], + O1="/Event/MFSwapped/Float1", + O2="/Event/MFSwapped/Float2") + +app = ApplicationMgr( + EvtMax=2, + EvtSel="NONE", + ExtSvc=[EvtStoreSvc("EventDataSvc")], + HistogramPersistency="NONE", + TopAlg=[ + int_a, + int_b, + merger, + merger_filter_passing, + merger_filter_failing, + ], +) diff --git a/GaudiExamples/src/FunctionalAlgorithms/MakeAndConsume.cpp b/GaudiExamples/src/FunctionalAlgorithms/MakeAndConsume.cpp index fe9b1c4ccd5610c914ca55d7b156319c03a1ec3c..a31c6fed5e14d44cbdd8141b4b07ec5c33f24dde 100644 --- a/GaudiExamples/src/FunctionalAlgorithms/MakeAndConsume.cpp +++ b/GaudiExamples/src/FunctionalAlgorithms/MakeAndConsume.cpp @@ -52,9 +52,11 @@ namespace Gaudi::Examples { : Producer( name, svcLoc, KeyValue( "OutputLocation", "/Event/MyInt" ) ) {} int operator()() const override { - info() << "executing IntDataProducer, storing 7 into " << outputLocation() << endmsg; - return 7; + info() << "executing IntDataProducer, storing " << m_value.value() << " into " << outputLocation() << endmsg; + return m_value; } + + Gaudi::Property<int> m_value{this, "Value", 7, "The integer value to produce."}; }; DECLARE_COMPONENT( IntDataProducer ) diff --git a/GaudiExamples/src/FunctionalAlgorithms/merging_transformer.cpp b/GaudiExamples/src/FunctionalAlgorithms/merging_transformer.cpp index dcab536b962145392e1d45f65e780eb9e7a5efd5..729e01192cbde3f440ce9b8dd4eb48886b00d3be 100644 --- a/GaudiExamples/src/FunctionalAlgorithms/merging_transformer.cpp +++ b/GaudiExamples/src/FunctionalAlgorithms/merging_transformer.cpp @@ -11,9 +11,11 @@ #include "GaudiAlg/MergingTransformer.h" #include <string> -using ints = Gaudi::Functional::vector_of_const_<int>; -using out_t = std::tuple<float, float>; -using is2ff_merger_base = Gaudi::Functional::MergingMultiTransformer<out_t( ints const& )>; +using ints = Gaudi::Functional::vector_of_const_<int>; +using out_t = std::tuple<float, float>; +using is2ff_merger_base = Gaudi::Functional::MergingMultiTransformer<out_t( ints const& )>; +using is2ff_merger_filter_base = Gaudi::Functional::MergingMultiTransformerFilter<out_t( ints const& )>; +using filter_out_t = std::tuple<bool, float, float>; struct is2ff_merger : public is2ff_merger_base { is2ff_merger( std::string const& name, ISvcLocator* pSvcLocator ) @@ -24,13 +26,35 @@ struct is2ff_merger : public is2ff_merger_base { float f1 = 1, f2 = 1; for ( auto i : is ) { - info() << "i: " << i; + info() << "i: " << i << " "; f1 *= i; f2 *= 1.f / i; } info() << endmsg; - return out_t{f1, f2}; + return {f1, f2}; } }; DECLARE_COMPONENT( is2ff_merger ) + +struct is2ff_merger_filter : public is2ff_merger_filter_base { + is2ff_merger_filter( std::string const& name, ISvcLocator* pSvcLocator ) + : is2ff_merger_filter_base( name, pSvcLocator, {"InputInts", {"firstInt", "secondInt"}}, + {KeyValue{"O1", "firstFloat"}, KeyValue{"O2", "secondFloat"}} ) {} + + filter_out_t operator()( ints const& is ) const override { + float f1 = 1, f2 = 1; + + for ( auto i : is ) { + info() << "i: " << i << " "; + f1 *= i; + f2 *= 1.f / i; + } + info() << endmsg; + auto filter_passed = f1 > 10; + info() << "Filter " << ( filter_passed ? "passed" : "failed" ) << endmsg; + return {filter_passed, f1, f2}; + } +}; + +DECLARE_COMPONENT( is2ff_merger_filter ) diff --git a/GaudiExamples/tests/qmtest/gaudiexamples.qms/functional_algorithms.qms/multi_mergers.qmt b/GaudiExamples/tests/qmtest/gaudiexamples.qms/functional_algorithms.qms/multi_mergers.qmt new file mode 100644 index 0000000000000000000000000000000000000000..810aa03f2a40ea19e758fa715a01f21a144f9e07 --- /dev/null +++ b/GaudiExamples/tests/qmtest/gaudiexamples.qms/functional_algorithms.qms/multi_mergers.qmt @@ -0,0 +1,17 @@ +<?xml version="1.0" ?><!DOCTYPE extension PUBLIC '-//QM/2.3/Extension//EN' 'http://www.codesourcery.com/qm/dtds/2.3/-//qm/2.3/extension//en.dtd'> +<!-- + (c) Copyright 2021 CERN for the benefit of the LHCb and ATLAS collaborations + + This software is distributed under the terms of the Apache version 2 licence, + copied verbatim in the file "LICENSE". + + In applying this licence, CERN does not waive the privileges and immunities + granted to it by virtue of its status as an Intergovernmental Organization + or submit itself to any jurisdiction. +--> +<extension class="GaudiTest.GaudiExeTest" kind="test"> +<argument name="program"><text>gaudirun.py</text></argument> +<argument name="args"><set><text>../../options/FunctionalAlgorithms/MultiMergers.py</text></set></argument> + <argument name="use_temp_dir"><enumeral>true</enumeral></argument> + <argument name="reference"><text>refs/MultiMergers.ref</text></argument> +</extension> diff --git a/GaudiExamples/tests/qmtest/refs/MultiMergers.ref b/GaudiExamples/tests/qmtest/refs/MultiMergers.ref new file mode 100644 index 0000000000000000000000000000000000000000..b95f492fb458719d1e88f402c2747376a7ac3f5c --- /dev/null +++ b/GaudiExamples/tests/qmtest/refs/MultiMergers.ref @@ -0,0 +1,30 @@ +# --> Including file '/home/apearce/stack-g/Gaudi/GaudiExamples/options/FunctionalAlgorithms/MultiMergers.py' +# <-- End of file '/home/apearce/stack-g/Gaudi/GaudiExamples/options/FunctionalAlgorithms/MultiMergers.py' +ApplicationMgr SUCCESS +==================================================================================================================================== + Welcome to ApplicationMgr (GaudiCoreSvc v35r3) + running on lbquantaperf01 on Fri Apr 30 10:45:07 2021 +==================================================================================================================================== +ApplicationMgr INFO Application Manager Configured successfully +EventLoopMgr WARNING Unable to locate service "EventSelector" +EventLoopMgr WARNING No events will be processed from external input. +ApplicationMgr INFO Application Manager Initialized successfully +ApplicationMgr INFO Application Manager Started successfully +IntAProducer INFO executing IntDataProducer, storing 2 into /Event/IntA +IntBProducer INFO executing IntDataProducer, storing 3 into /Event/IntB +is2ff_merger INFO i: 2 i: 3 +MergerFilterPas... INFO i: 2 i: 2 i: 3 +MergerFilterPas... INFO Filter passed +MergerFilterFai... INFO i: 2 i: 3 +MergerFilterFai... INFO Filter failed +IntAProducer INFO executing IntDataProducer, storing 2 into /Event/IntA +IntBProducer INFO executing IntDataProducer, storing 3 into /Event/IntB +is2ff_merger INFO i: 2 i: 3 +MergerFilterPas... INFO i: 2 i: 2 i: 3 +MergerFilterPas... INFO Filter passed +MergerFilterFai... INFO i: 2 i: 3 +MergerFilterFai... INFO Filter failed +ApplicationMgr INFO Application Manager Stopped successfully +EventLoopMgr INFO Histograms converted successfully according to request. +ApplicationMgr INFO Application Manager Finalized successfully +ApplicationMgr INFO Application Manager Terminated successfully