diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/isolation.py b/Hlt/Hlt2Conf/python/Hlt2Conf/isolation.py index 0a0a72b7344db199f661b7f283c56d96ea7fe578..91d25cb50406cebb88f61ad04528815d4238f07a 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/isolation.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/isolation.py @@ -11,6 +11,9 @@ # Central isolation algorithm from PyConf import configurable +from PyConf.Algorithms import ( + LHCb__Phys__RelationTables__FunctorsToRelTable as FunctorsToRelTable, +) from PyConf.Algorithms import SelectionFromRelationTable, WeightedRelTableAlg @@ -35,3 +38,23 @@ def extra_outputs_for_isolation(name, ref_particles, extra_particles, selection) extra_outputs = [(name, selection_to_persist.OutputLocation)] return extra_outputs + + +@configurable +def extra_outputs_for_relinfo_isolation( + name, + ref_particles, + extra_particles, + selection, + variables, +): + FunctorsRelTableAlg = FunctorsToRelTable( + InputCandidates=ref_particles, + InputOtherTracks=extra_particles, + Cut=selection, + Variables=variables, + ) + + extra_outputs = [(name, FunctorsRelTableAlg.OutputLocation)] + + return extra_outputs diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/bnoc/builders/bnoc_isolation.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/bnoc/builders/bnoc_isolation.py index c1c059dc0ae748c502a8468f1ba175af4177b292..5e0cc6384bf19fb40f595eda418a4995a2c56c15 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/bnoc/builders/bnoc_isolation.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/bnoc/builders/bnoc_isolation.py @@ -20,7 +20,10 @@ from RecoConf.standard_particles import ( make_up_pions, ) -from Hlt2Conf.isolation import extra_outputs_for_isolation +from Hlt2Conf.isolation import ( + extra_outputs_for_isolation, + extra_outputs_for_relinfo_isolation, +) @configurable @@ -127,3 +130,169 @@ def make_iso_particles( ) return iso_parts + + +@configurable +def make_reltable_for_cone_isolation( + names=[], + candidates=[], + cut=(F.SQRT @ F.DR2 < 1.5), + LongTrackIso=False, + TTrackIso=False, + DownstreamTrackIso=False, + UpstreamTrackIso=False, + NeutralIso=False, + PiZerosIso=False, + Variables=[], +): + """ + Add to the extra_outputs different kind of isolations by properly setting the given flag + Args: + names: List of names for reference particles + candidates: List of containers of reference particles to relate extra particles + cut: Predicate to select extra tracks used in computation of isolation information for a given table + LongTrackIso: Boolean value to make isolation with long tracks + TTrackIso: Boolean value to make isolation with tt tracks + DownstreamTrackIso: Boolean value to make isolation with downstream tracks + UpstreamTrackIso: Boolean value to make isolation with upstream tracks + NeutralIso: Boolean value to make isolation with neutral particles + PiZerosIso: Boolean value to make isolation with merged pi0 -> gamma gamma + Variables: List of variable names to be included, see definition at Rec/Phys/RelatedInfoTools/include/Phys/RelatedInfoFunctors.h + """ + extra_outputs = [] + assert len(names) == len(candidates), ( + "Different number of names and candidate containers for particle isolation!" + ) + + for name, cand in zip(names, candidates): + if LongTrackIso: + extra_outputs += extra_outputs_for_relinfo_isolation( + name=name + "_LongTrackIsolation", + extra_particles=make_long_pions(), + ref_particles=cand, + selection=cut, + variables=Variables, + ) + if TTrackIso: + extra_outputs += extra_outputs_for_relinfo_isolation( + name=name + "_TTrackIsolation", + extra_particles=make_ttrack_pions(), + ref_particles=cand, + selection=cut, + variables=Variables, + ) + if DownstreamTrackIso: + extra_outputs += extra_outputs_for_relinfo_isolation( + name=name + "_DownstreamTrackIsolation", + extra_particles=make_down_pions(), + ref_particles=cand, + selection=cut, + variables=Variables, + ) + if UpstreamTrackIso: + extra_outputs += extra_outputs_for_relinfo_isolation( + name=name + "_UpstreamTrackIsolation", + extra_particles=make_up_pions(), + ref_particles=cand, + selection=cut, + variables=Variables, + ) + if NeutralIso: + extra_outputs += extra_outputs_for_relinfo_isolation( + name=name + "_NeutralIsolation", + extra_particles=make_photons(), + ref_particles=cand, + selection=cut, + variables=Variables, + ) + if PiZerosIso: + extra_outputs += extra_outputs_for_relinfo_isolation( + name=name + "_PiZerosIsolation", + extra_particles=make_merged_pi0s(), + ref_particles=cand, + selection=cut, + variables=Variables, + ) + return extra_outputs + + +@configurable +def make_reltable_for_vertex_isolation( + names=[], + candidates=[], + cut=(F.SQRT @ F.DR2 < 1.5), + Variables=["SMALLEST_REF_ENDVERTEX_IPCHI2"], +): + """ + Add to the extra_outputs different kind of isolations by properly setting the given flag + Args: + names: List of names for reference particles + candidates: List of containers of reference particles to relate extra particles + cut: Predicate to select extra tracks used in computation of isolation information for a given table + Variables: List of variable names to be included, see definition at Rec/Phys/RelatedInfoTools/include/Phys/RelatedInfoFunctors.h + """ + extra_outputs = [] + assert len(names) == len(candidates), ( + "Different number of names and candidate containers for particle isolation!" + ) + + for name, cand in zip(names, candidates): + extra_outputs += extra_outputs_for_relinfo_isolation( + name=name + "_VertexIsolation", + extra_particles=make_long_pions(), + ref_particles=cand, + selection=cut, + variables=Variables, + ) + + return extra_outputs + + +@configurable +def make_iso_reltables( + line_alg, + name="B", + coneangles=[1.0], + LongTrackIso=False, + TTrackIso=False, + DownstreamTrackIso=False, + UpstreamTrackIso=False, + NeutralIso=False, + PiZerosIso=False, + coneVariables=[], + vertexIsoVariables=[], +): + candidate = line_alg + + reltables = [] + + if coneVariables: + for coneangle in coneangles: + cut = (F.SQRT @ F.DR2 < coneangle) & ~F.FIND_IN_TREE() + + cone_reltable = make_reltable_for_cone_isolation( + names=[name + f"_DeltaR_{str(coneangle).replace('.', '')}"], + candidates=[candidate], + cut=cut, + LongTrackIso=LongTrackIso, + TTrackIso=TTrackIso, + DownstreamTrackIso=DownstreamTrackIso, + UpstreamTrackIso=UpstreamTrackIso, + NeutralIso=NeutralIso, + PiZerosIso=PiZerosIso, + Variables=coneVariables, + ) + + reltables += cone_reltable + + if vertexIsoVariables: + cut = ~F.FIND_IN_TREE() + vertex_reltable = make_reltable_for_vertex_isolation( + names=[name], + candidates=[candidate], + cut=cut, + Variables=vertexIsoVariables, + ) + reltables += vertex_reltable + + return reltables diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/bnoc/hlt2_bnoc.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/bnoc/hlt2_bnoc.py index 62af85bdc00fa8ef959a27158ecad502be09a395..cf6bad1527736ee7fd718031f64287799a9ba18a 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/bnoc/hlt2_bnoc.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/bnoc/hlt2_bnoc.py @@ -680,6 +680,21 @@ isolation_lines = { "NeutralIso": True, "PiZerosIso": True, }, + "reltables_kwargs": { + "name": "B0", + "coneangles": [1.0, 1.8], + "vertexIsoVariables": ["SMALLEST_REF_ENDVERTEX_IPCHI2"], + "LongTrackIso": True, + "NeutralIso": True, + "coneVariables": [ + "CONE_MULT", + "CONE_PX", + "CONE_PY", + "CONE_PZ", + "CONE_ASYM_PT", + "CONE_DELTA_PHI", + ], + }, "raw_banks": ["VP", "UT", "FT", "Rich", "Muon", "Calo"], }, "BdsToPhiRho": { diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/bnoc/utils.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/bnoc/utils.py index bd4d74bd0c719ebfed025fbab53d3fc20900950e..2c633dc352f43b543f96a21255bf7c3d5c07f5b0 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/bnoc/utils.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/bnoc/utils.py @@ -23,7 +23,10 @@ from Moore.lines import Hlt2Line, SpruceLine from PyConf.utilities import ConfigurationError from Hlt2Conf.lines.bnoc.builders.basic_builder import Topo_prefilter_extra_outputs -from Hlt2Conf.lines.bnoc.builders.bnoc_isolation import make_iso_particles +from Hlt2Conf.lines.bnoc.builders.bnoc_isolation import ( + make_iso_particles, + make_iso_reltables, +) from Hlt2Conf.lines.bnoc.prefilters import bnoc_prefilters @@ -185,6 +188,7 @@ def make_generic_line( min_twobody_mva=0.1, min_threebody_mva=0.1, iso_kwargs=None, + reltables_kwargs=None, spruce_hlt2_filters=None, **kwargs, ): @@ -211,6 +215,7 @@ def make_generic_line( and not require_GEC and not require_topo and iso_kwargs is None + and reltables_kwargs is None ): @register_line_builder(line_dict) @@ -276,6 +281,8 @@ def make_generic_line( ) if iso_kwargs is not None: extra_outputs += make_iso_particles(line_alg[-1], **iso_kwargs) + if reltables_kwargs is not None: + extra_outputs += make_iso_reltables(line_alg[-1], **reltables_kwargs) return Line( name=name, diff --git a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/test/spruce_test.py b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/test/spruce_test.py index e0d2a431c0a2203f9a5763447815ca87110f7853..d673e044af0e264c33625e2317a56737e8a8355c 100644 --- a/Hlt/Hlt2Conf/python/Hlt2Conf/lines/test/spruce_test.py +++ b/Hlt/Hlt2Conf/python/Hlt2Conf/lines/test/spruce_test.py @@ -24,6 +24,7 @@ from RecoConf.reconstruction_objects import make_pvs, upfront_reconstruction from RecoConf.standard_particles import ( make_has_rich_long_kaons, make_has_rich_long_pions, + make_long_pions, make_photons, ) from SelAlgorithms.monitoring import histogram_1d, monitor @@ -343,6 +344,9 @@ def Test_extraoutputs_hlt2_line(name="Hlt2Test_ExtraOutputs", prescale=1): from Moore.persistence.persistreco import persistreco_line_outputs from RecoConf.reconstruction_objects import reconstruction +from Hlt2Conf.isolation import ( + extra_outputs_for_relinfo_isolation, +) from Hlt2Conf.lines.topological_b import make_filtered_topo_threebody @@ -354,6 +358,32 @@ def threebody_line(name="Hlt2Topo3Body", prescale=1): ] candidates = make_filtered_topo_threebody(MVACut=0.998) + + extra_objs += extra_outputs_for_relinfo_isolation( + name=name + "_LongTrackIsolation", + extra_particles=make_long_pions(), + ref_particles=candidates, + selection=~F.FIND_IN_TREE(), + variables=[ + "CONE_MULT", + "CONE_PX", + "CONE_PY", + "CONE_PZ", + "CONE_E", + "CONE_P", + "CONE_PT", + "CONE_ASYM_PX", + "CONE_ASYM_PY", + "CONE_ASYM_PZ", + "CONE_ASYM_E", + "CONE_ASYM_P", + "CONE_ASYM_PT", + "CONE_DELTA_ETA", + "CONE_DELTA_PHI", + "SMALLEST_REF_ENDVERTEX_IPCHI2", + ], + ) + return Hlt2Line( name=name, algs=[candidates], diff --git a/Hlt/Hlt2Conf/tests/options/sprucing/spruce_turbopass_overlap_check.py b/Hlt/Hlt2Conf/tests/options/sprucing/spruce_turbopass_overlap_check.py index 5d8dc2941947e2d5ca5ccffaeba7a6579d6da058..b7f3958eaf0cdcc7cc5d996493a10044edcd3df9 100644 --- a/Hlt/Hlt2Conf/tests/options/sprucing/spruce_turbopass_overlap_check.py +++ b/Hlt/Hlt2Conf/tests/options/sprucing/spruce_turbopass_overlap_check.py @@ -209,6 +209,26 @@ for ii in range(nevents): f"Selective reco locations propagated. There are {persistreco.size()} {reco}" ) + # Check Isolation P2InfoRelations is present + + isorelations = TES[ + f"/Event/Turbo/{linetwo}/{linetwo}_LongTrackIsolation/P2InfoRelations" + ].relations() + if not isorelations or isorelations.size() <= 0: + print("Isolation extra_outputs not as expected") + raise RuntimeError( + "Check ERROR - Isolation Reltables relations not propogated as expected" + ) + elif not isorelations[0].to() or isorelations[0].to().size() != 16: + print("Isolation extra_outputs not as expected") + raise RuntimeError( + f"Check ERROR - Isolation Reltables gives relation table with {isorelations[0].to().size()} variables, expected 16" + ) + else: + print( + f"Isolation P2InfoRelations in extra_outputs locations propagated. There are {isorelations.size()} relations with {isorelations[0].to().size()} variables saved for the first candidate." + ) + # Cannot check absense of CALO objs with linetwo # Check PV tracks diff --git a/Hlt/Moore/python/Moore/tests/lhcbintegrationtests_options_isolation.py b/Hlt/Moore/python/Moore/tests/lhcbintegrationtests_options_isolation.py new file mode 100644 index 0000000000000000000000000000000000000000..829e752fe0cba5e8eba0982e16b39bb92b2f9556 --- /dev/null +++ b/Hlt/Moore/python/Moore/tests/lhcbintegrationtests_options_isolation.py @@ -0,0 +1,39 @@ +############################################################################### +# (c) Copyright 2025 CERN for the benefit of the LHCb Collaboration # +# # +# This software is distributed under the terms of the GNU General Public # +# Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". # +# # +# 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 Hlt2Conf.lines.bnoc import all_lines +from Hlt2Conf.settings.defaults import get_default_hlt1_filter_code_for_hlt2 +from Moore import Options, run_moore +from RecoConf.global_tools import ( + stateProvider_with_simplified_geom, + trackMasterExtrapolator_with_simplified_geom, +) +from RecoConf.reconstruction_objects import reconstruction + + +def main(options: Options): + test_lines = ["BdsToPhiPhi"] + + def make_lines(): + lines = [ + all_lines["Hlt2BnoC_" + hlt2_line_name]() for hlt2_line_name in test_lines + ] + + return lines + + public_tools = [ + trackMasterExtrapolator_with_simplified_geom(), + stateProvider_with_simplified_geom(), + ] + with ( + reconstruction.bind(from_file=False), + get_default_hlt1_filter_code_for_hlt2.bind(code=""), + ): + return run_moore(options, make_lines, public_tools)