From 57300cbf7d23ee9a809cd749369369cf2206f9c6 Mon Sep 17 00:00:00 2001 From: Eduardo Rodrigues <eduardo.rodrigues@cern.ch> Date: Fri, 5 Mar 2021 08:23:25 +0000 Subject: [PATCH] Fixed formatting patch generated by https://gitlab.cern.ch/lhcb/DaVinci/-/jobs/12434240 --- .../python/DaVinci/common_particles.py | 167 +++++++ Phys/DaVinci/python/DaVinci/data_from_file.py | 422 ++++++++---------- .../python/DaVinci/filters_selectors.py | 167 +++++++ Phys/DaVinci/python/DaVinci/locations.py | 133 ++++++ Phys/DaVinci/python/DaVinci/optionChecker.py | 2 +- Phys/DaVinci/python/DaVinci/reco_objects.py | 16 +- .../python/DaVinci/standard_particles.py | 45 +- 7 files changed, 684 insertions(+), 268 deletions(-) create mode 100644 Phys/DaVinci/python/DaVinci/common_particles.py create mode 100644 Phys/DaVinci/python/DaVinci/filters_selectors.py create mode 100644 Phys/DaVinci/python/DaVinci/locations.py diff --git a/Phys/DaVinci/python/DaVinci/common_particles.py b/Phys/DaVinci/python/DaVinci/common_particles.py new file mode 100644 index 000000000..f8783effe --- /dev/null +++ b/Phys/DaVinci/python/DaVinci/common_particles.py @@ -0,0 +1,167 @@ +############################################################################### +# (c) Copyright 2021 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. # +############################################################################### +""" +Definitions of "common particles" very similar to those of Runs 1 & 2. +""" + +from PyConf.tonic import configurable +from PyConf.Algorithms import FunctionalParticleMaker +from PyConf.Algorithms import LHCb__Phys__ParticleMakers__PhotonMaker as PhotonMaker + +from .reco_objects import make_charged_protoparticles as _make_charged_protoparticles +from .reco_objects import make_neutral_protoparticles as _make_neutral_protoparticles +from .reco_objects import make_pvs as _make_pvs + +from .selectors import default_particle_cuts, default_track_cuts +from .selectors import get_long_track_selector, get_down_track_selector +from .filters import all_protoparticle_filter as standard_protoparticle_filter + +from .algorithms_pyconf import ParticleFilterWithPVs, ParticleCombinerWithPVs + +######### +# Helpers +######### + + +@configurable +def _make_particles(species, + make_protoparticles=_make_charged_protoparticles, + get_track_selector=get_long_track_selector, + make_protoparticle_filter=standard_protoparticle_filter): + """ + Helper configurable to create `LHCb::Particle`s from `LHCb::ProtoParticle`s. + + Args: + species (str): Particle species hypothesis accepted by + `FunctionalParticleMaker`, i.e. one of the strings + "pion", "kaon", "muon", "electron", "proton". + """ + particles = FunctionalParticleMaker( + ParticleID=species, + InputProtoParticles=make_protoparticles(), + TrackSelector=get_track_selector(), + ProtoParticleFilter=make_protoparticle_filter()).Particles + return particles + + +@configurable +def make_photons(make_neutral_protoparticles=_make_neutral_protoparticles, + pvs=_make_pvs, + **kwargs): + """ + Configurable to create photon `LHCb::Particle`s from `LHCb::ProtoParticle`s. + """ + particles = PhotonMaker( + InputProtoParticles=make_neutral_protoparticles(), + InputPrimaryVertices=pvs(), + **kwargs).Particles + return particles + + +def _make_std_loose_particles(particles, pvs, name): + return ParticleFilterWithPVs( + particles, pvs, name=name, Code=default_particle_cuts()) + + +####################### +# Bacic particle makers +####################### + + +def make_long_pions(): + return _make_particles(species="pion") + + +def make_long_kaons(): + return _make_particles(species="kaon") + + +def make_long_protons(): + return _make_particles(species="proton") + + +def make_long_muons(): + return _make_particles(species="muon") + + +def make_long_electrons_no_brem(): + return _make_particles(species="electron") + + +def make_down_pions(): + return _make_particles( + species="pion", get_track_selector=get_down_track_selector) + + +def make_down_kaons(): + return _make_particles( + species="kaon", get_track_selector=get_down_track_selector) + + +def make_down_protons(): + return _make_particles( + species="proton", get_track_selector=get_down_track_selector) + + +################################# +# Particle makers with loose cuts +################################# + + +@configurable +def make_std_loose_pions(): + with get_long_track_selector.bind( + Code=default_track_cuts()), standard_protoparticle_filter.bind( + Code='PP_HASRICH'): + return _make_std_loose_particles( + make_long_pions(), _make_pvs(), name='StdLoosePions') + + +@configurable +def make_std_loose_kaons(): + with get_long_track_selector.bind( + Code=default_track_cuts()), standard_protoparticle_filter.bind( + Code='PP_HASRICH'): + return _make_std_loose_particles( + make_long_kaons(), _make_pvs(), name='StdLooseKaons') + + +@configurable +def make_std_loose_protons(): + with get_long_track_selector.bind( + Code=default_track_cuts()), standard_protoparticle_filter.bind( + Code='PP_HASRICH'): + return _make_std_loose_particles( + make_long_protons(), _make_pvs(), name='StdLooseProtons') + + +def make_std_loose_muons(): + #with get_long_track_selector.bind(Code=default_track_cuts()): + return _make_std_loose_particles( + make_long_muons(), _make_pvs(), name='StdLooseMuons') + + +@configurable +def make_std_loose_jpsi2mumu(): + muons = make_std_loose_muons() + descriptors = ["J/psi(1S) -> mu+ mu-"] + daughters_code = {"mu+": "ALL", "mu-": "ALL"} + combination_code = "(ADAMASS('J/psi(1S)') < 100.*MeV) & (ADOCACHI2CUT(30,''))" + vertex_code = "(CHI2VX < 25.)" + + return ParticleCombinerWithPVs( + name="StdLooseJpsi2MuMu", + particles=muons, + pvs=_make_pvs(), + DecayDescriptors=descriptors, + DaughtersCuts=daughters_code, + CombinationCut=combination_code, + MotherCut=vertex_code) diff --git a/Phys/DaVinci/python/DaVinci/data_from_file.py b/Phys/DaVinci/python/DaVinci/data_from_file.py index 25ec69269..471effb02 100644 --- a/Phys/DaVinci/python/DaVinci/data_from_file.py +++ b/Phys/DaVinci/python/DaVinci/data_from_file.py @@ -1,5 +1,5 @@ ############################################################################### -# (c) Copyright 2019 CERN for the benefit of the LHCb Collaboration # +# (c) Copyright 2019-2021 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". # @@ -8,206 +8,129 @@ # granted to it by virtue of its status as an Intergovernmental Organization # # or submit itself to any jurisdiction. # ############################################################################### - -###### -### N.B. THIS FILE IS INTENDED TO AVOID DEPENDENCIES ON MOORE, -### IS NEEDED FOR TESTING PURPOSES AND NEEDS TO BE REMOVED IN PRODUCTION -###### -"""Load data from files and set up unpackers. +""" +Helper module with functions to load reco./MC data and linker tables from files, +and set up reconstruction and simulation unpackers. There are two things we have to deal with: 1. Loading the data from the file in to the TES, done by - Gaudi::Hive::FetchDataFromFile. + `Gaudi::Hive::FetchDataFromFile`. 2. Unpacking and preparing packed containers, if the 'reconstruction' is defined as the objects already present in the file. -In most LHCb applications, step 2 is done for you behind the scenes. The -DataOnDemandSvc is configured in LHCb/GaudiConf/DstConf.py to unpack containers -when they are requested. It also configures adding RICH, MUON, and combined PID -information to ProtoParticles when they're unpacked. Because we don't have the -DataOnDemandSvc here, we have to do this by hand. - -The interesting 'user-facing' exports of this module are: - -* [reco,mc]_from_file(): Dict of names to locations that can be loaded from a file. -* [reco,mc]_unpackers(): Dict from unpacked object name to Algorithm that produces a - container of those objects. +In most LHCb applications step 2 is done behind the scenes: +the `DataOnDemandSvc` is configured in `LHCb/GaudiConf/DstConf.py` +to unpack containers when they are requested. +It also configures adding RICH, MUON, and combined PID information to ProtoParticles +when the unpacking takes place. This module effectively does all these steps +explicitly because the `PyConf` framework does not rely (by construction!) +on the somewhat subtle and obscure `DataOnDemandSvc`. + +The interesting "user-facing" exports of this module are +`{reco,mc}_unpackers()`, which return a dict from unpacked object name to +a `PyConf.Algorithm` instance that produces a container of those objects. + + The locations are defined under `DaVinci.locations`. + +.. note:: + 1) The functions defined in this module rely on data paths used in Runs 1 & 2, + and may need a revision once the Run 3 event model is finalised + and the definition of what gets persisted gets formalised. + 2) Code very heavily relies on its Moore equivalent. Thank you, RTA team. """ from __future__ import absolute_import, division, print_function + import collections from Gaudi.Configuration import ERROR + +from Configurables import (UnpackCaloHypo, UnpackProtoParticle, + UnpackRecVertex, UnpackTrackFunctional, + UnpackMCParticle, UnpackMCVertex) from Configurables import ( DataPacking__Unpack_LHCb__MuonPIDPacker_, - DataPacking__Unpack_LHCb__RichPIDPacker_, UnpackCaloHypo, - UnpackProtoParticle, UnpackRecVertex, UnpackTrackFunctional, - UnpackMCParticle, UnpackMCVertex, DataPacking__Unpack_LHCb__MCVPHitPacker_ - as UnpackMCVPHit, DataPacking__Unpack_LHCb__MCUTHitPacker_ as - UnpackMCUTHit, DataPacking__Unpack_LHCb__MCFTHitPacker_ as UnpackMCFTHit, + DataPacking__Unpack_LHCb__RichPIDPacker_, + DataPacking__Unpack_LHCb__MCVPHitPacker_ as UnpackMCVPHit, + DataPacking__Unpack_LHCb__MCUTHitPacker_ as UnpackMCUTHit, + DataPacking__Unpack_LHCb__MCFTHitPacker_ as UnpackMCFTHit, DataPacking__Unpack_LHCb__MCRichHitPacker_ as UnpackMCRichHit, DataPacking__Unpack_LHCb__MCEcalHitPacker_ as UnpackMCEcalHit, DataPacking__Unpack_LHCb__MCHcalHitPacker_ as UnpackMCHcalHit, DataPacking__Unpack_LHCb__MCMuonHitPacker_ as UnpackMCMuonHit, DataPacking__Unpack_LHCb__MCRichDigitSummaryPacker_ as RichSumUnPack) -from PyConf.components import Algorithm, force_location -from PyConf.application import make_data_with_FetchDataFromFile from PyConf.Tools import (ChargedProtoParticleAddRichInfo, ChargedProtoParticleAddMuonInfo, ChargedProtoParticleAddCombineDLLs) +from PyConf.components import Algorithm, force_location +from PyConf.application import make_data_with_FetchDataFromFile -def packed_reco_from_file(): - return { - 'PackedPVs': '/Event/pRec/Vertex/Primary', - 'PackedCaloElectrons': '/Event/pRec/Calo/Electrons', - 'PackedCaloPhotons': '/Event/pRec/Calo/Photons', - 'PackedCaloMergedPi0s': '/Event/pRec/Calo/MergedPi0s', - 'PackedCaloSplitPhotons': '/Event/pRec/Calo/SplitPhotons', - 'PackedMuonPIDs': '/Event/pRec/Muon/MuonPID', - 'PackedRichPIDs': '/Event/pRec/Rich/PIDs', - 'PackedTracks': '/Event/pRec/Track/Best', - 'PackedMuonTracks': '/Event/pRec/Track/Muon', - 'PackedNeutralProtos': '/Event/pRec/ProtoP/Neutrals', - 'PackedChargedProtos': '/Event/pRec/ProtoP/Charged', - } - - -def packed_mc_from_file(): - return { - 'PackedMCParticles': '/Event/pSim/MCParticles', - 'PackedMCVertices': '/Event/pSim/MCVertices', - 'PackedMCVPHits': '/Event/pSim/VP/Hits', - 'PackedMCUTHits': '/Event/pSim/UT/Hits', - 'PackedMCFTHits': '/Event/pSim/FT/Hits', - 'PackedMCRichHits': '/Event/pSim/Rich/Hits', - 'PackedMCEcalHits': '/Event/pSim/Ecal/Hits', - 'PackedMCHcalHits': '/Event/pSim/Hcal/Hits', - 'PackedMCMuonHits': '/Event/pSim/Muon/Hits', - 'PackedMCRichDigitSummaries': '/Event/pSim/Rich/DigitSummaries', - } - - -def unpacked_reco_locations(): - # If the structure is not like this, pointers point to to the wrong place... - # The SmartRefs held by the unpacked MC objects only work if we unpack to these specific locations - locations = { - k: v.replace('pRec', 'Rec') - for k, v in packed_reco_from_file().items() - } - return locations - - -def unpacked_mc_locations(): - # If the structure is not like this, pointers point to to the wrong place... - # The SmartRefs held by the unpacked MC objects only work if we unpack to these specific locations - return { - 'PackedMCParticles': '/Event/MC/Particles', - 'PackedMCVertices': '/Event/MC/Vertices', - 'PackedMCVPHits': '/Event/MC/VP/Hits', - 'PackedMCUTHits': '/Event/MC/UT/Hits', - 'PackedMCFTHits': '/Event/MC/FT/Hits', - 'PackedMCRichHits': '/Event/MC/Rich/Hits', - 'PackedMCEcalHits': '/Event/MC/Ecal/Hits', - 'PackedMCHcalHits': '/Event/MC/Hcal/Hits', - 'PackedMCMuonHits': '/Event/MC/Muon/Hits', - 'PackedMCRichDigitSummaries': '/Event/MC/Rich/DigitSummaries', - } - - -def reco_from_file(): - # TODO(AP) should only add the packed data if we're running on Upgrade MC - # where Brunel has already been run - packed_data = packed_reco_from_file() - # raw_event = raw_event_from_file() - # We don't want any keys accidentally overwriting each other - # assert set(packed_data.keys()).intersection(set(raw_event.keys())) == set() - # return dict(list(packed_data.items()) + list(raw_event.items())) - return packed_data - - -def mc_from_file(): - # TODO(AP) should only add the packed data if we're running on Upgrade MC - # where Brunel has already been run - packed_data = packed_mc_from_file() - return packed_data - - -def reco_unpacker(key, configurable, name, **kwargs): - """Return unpacker that reads from file and unpacks to a forced output location.""" - packed_loc = reco_from_file()[key] - unpacked_loc = unpacked_reco_locations()[key] - alg = Algorithm( - configurable, - name=name, - outputs={'OutputName': force_location(unpacked_loc)}, - InputName=make_data_with_FetchDataFromFile(packed_loc), - **kwargs) - return alg - - -def mc_unpacker(key, configurable, name, **kwargs): - """Return unpacker that reads from file and unpacks to a forced output location.""" - packed_loc = mc_from_file()[key] - unpacked_loc = unpacked_mc_locations()[key] - alg = Algorithm( - configurable, - name=name, - outputs={'OutputName': force_location(unpacked_loc)}, - InputName=make_data_with_FetchDataFromFile(packed_loc), - **kwargs) - return alg - - -def make_mc_track_info(): - return make_data_with_FetchDataFromFile('/Event/MC/TrackInfo') +from .locations import (LocationsPackedReco, LocationsUnpackedReco) +from .locations import (LocationsPackedSim, LocationsUnpackedSim) +from .locations import (LocationsBooleMCParticleLinkers, + LocationsBooleMCHitsLinkers, LocationsBrunelMCLinkers, + LocationMCTrackInfo) +from .locations import enums_as_dict def reco_unpackers(): - muonPIDs = reco_unpacker('PackedMuonPIDs', + """ + Return a {object name: `PyConf.Algorithm` instance} `OrderedDict` + effectively mapping unpacked reconstruction object names to their respective + unpacked data. + The names (keys) are the following: + 'PVs', + 'Tracks', 'MuonTracks', + 'NeutralProtos', 'ChargedProtos', + 'CaloElectrons', 'CaloPhotons', 'CaloMergedPi0s', 'CaloSplitPhotons', + 'MuonPIDs', 'RichPIDs'. + """ + muonPIDs = reco_unpacker(LocationsPackedReco.PackedMuonPIDs.name, DataPacking__Unpack_LHCb__MuonPIDPacker_, - 'UnpackMuonPIDs') + "UnpackMuonPIDs") richPIDs = reco_unpacker( - 'PackedRichPIDs', + LocationsPackedReco.PackedRichPIDs.name, DataPacking__Unpack_LHCb__RichPIDPacker_, - 'UnpackRichPIDs', + "UnpackRichPIDs", OutputLevel=ERROR) # The OutputLevel above suppresses the following useless warnings (plus more?) # WARNING DataPacking::Unpack<LHCb::RichPIDPacker>:: Incorrect data version 0 for packing version > 3. Correcting data to version 2. # Ordered so that dependents are unpacked first d = collections.OrderedDict([ - ('PVs', reco_unpacker('PackedPVs', UnpackRecVertex, - 'UnpackRecVertices')), - ('CaloElectrons', - reco_unpacker('PackedCaloElectrons', UnpackCaloHypo, - 'UnpackCaloElectrons')), - ('CaloPhotons', - reco_unpacker('PackedCaloPhotons', UnpackCaloHypo, - 'UnpackCaloPhotons')), - ('CaloMergedPi0s', - reco_unpacker('PackedCaloMergedPi0s', UnpackCaloHypo, - 'UnpackCaloMergedPi0s')), - ('CaloSplitPhotons', - reco_unpacker('PackedCaloSplitPhotons', UnpackCaloHypo, - 'UnpackCaloSplitPhotons')), - ('MuonPIDs', muonPIDs), - ('RichPIDs', richPIDs), - ('Tracks', - reco_unpacker('PackedTracks', UnpackTrackFunctional, - 'UnpackBestTracks')), - ('MuonTracks', - reco_unpacker('PackedMuonTracks', UnpackTrackFunctional, - 'UnpackMuonTracks')), - ('NeutralProtos', - reco_unpacker('PackedNeutralProtos', UnpackProtoParticle, - 'UnpackNeutralProtos')), - ('ChargedProtos', + ("PVs", + reco_unpacker(LocationsPackedReco.PackedPVs.name, UnpackRecVertex, + "UnpackRecVertices")), + ("CaloElectrons", + reco_unpacker(LocationsPackedReco.PackedCaloElectrons.name, + UnpackCaloHypo, "UnpackCaloElectrons")), + ("CaloPhotons", + reco_unpacker(LocationsPackedReco.PackedCaloPhotons.name, + UnpackCaloHypo, "UnpackCaloPhotons")), + ("CaloMergedPi0s", + reco_unpacker(LocationsPackedReco.PackedCaloMergedPi0s.name, + UnpackCaloHypo, "UnpackCaloMergedPi0s")), + ("CaloSplitPhotons", + reco_unpacker(LocationsPackedReco.PackedCaloSplitPhotons.name, + UnpackCaloHypo, "UnpackCaloSplitPhotons")), + ("MuonPIDs", muonPIDs), + ("RichPIDs", richPIDs), + ("Tracks", + reco_unpacker(LocationsPackedReco.PackedTracks.name, + UnpackTrackFunctional, "UnpackBestTracks")), + ("MuonTracks", + reco_unpacker(LocationsPackedReco.PackedMuonTracks.name, + UnpackTrackFunctional, "UnpackMuonTracks")), + ("NeutralProtos", + reco_unpacker(LocationsPackedReco.PackedNeutralProtos.name, + UnpackProtoParticle, "UnpackNeutralProtos")), + ("ChargedProtos", reco_unpacker( - 'PackedChargedProtos', + LocationsPackedReco.PackedChargedProtos.name, UnpackProtoParticle, - 'UnpackChargedProtos', + "UnpackChargedProtos", AddInfo=[ ChargedProtoParticleAddRichInfo( InputRichPIDLocation=richPIDs.OutputName), @@ -219,103 +142,152 @@ def reco_unpackers(): # Make sure we have consistent names, and that we're unpacking everything # we load from the file - assert set(['Packed' + k for k in d.keys()]) - set( - packed_reco_from_file().keys()) == set() + assert set(["Packed" + k for k in d.keys()]) - set( + enums_as_dict(LocationsPackedReco).keys()) == set() return d def mc_unpackers(): + """ + Return a {object name: `PyConf.Algorithm` instance} `OrderedDict` + effectively mapping unpacked reconstruction object names to their respective + unpacked data. + The names (keys) are the following: + 'MCRichDigitSummaries', + 'MCParticles', 'MCVertices', + 'MCVPHits', 'MCUTHits', 'MCFTHits','MCRichHits', + 'MCEcalHits', 'MCHcalHits', 'MCMuonHits'. + """ # Ordered so that dependents are unpacked first - mc_vertices = mc_unpacker('PackedMCVertices', UnpackMCVertex, - 'UnpackMCVertices') + mc_vertices = mc_unpacker(LocationsPackedSim.PackedMCVertices.name, + UnpackMCVertex, "UnpackMCVertices") # Make sure that MC particles and MC vertices are unpacked together, # see https://gitlab.cern.ch/lhcb/LHCb/issues/57 for details. mc_particles = mc_unpacker( - 'PackedMCParticles', + LocationsPackedSim.PackedMCParticles.name, UnpackMCParticle, - 'UnpackMCParticles', + "UnpackMCParticles", ExtraInputs=[mc_vertices]) - mc_vp_hits = mc_unpacker('PackedMCVPHits', UnpackMCVPHit, 'UnpackMCVPHits') - mc_ut_hits = mc_unpacker('PackedMCUTHits', UnpackMCUTHit, 'UnpackMCUTHits') - mc_ft_hits = mc_unpacker('PackedMCFTHits', UnpackMCFTHit, 'UnpackMCFTHits') - mc_rich_hits = mc_unpacker('PackedMCRichHits', UnpackMCRichHit, - 'UnpackMCRichHits') - mc_ecal_hits = mc_unpacker('PackedMCEcalHits', UnpackMCEcalHit, - 'UnpackMCEcalHits') - mc_hcal_hits = mc_unpacker('PackedMCHcalHits', UnpackMCHcalHit, - 'UnpackMCHcalHits') - mc_muon_hits = mc_unpacker('PackedMCMuonHits', UnpackMCMuonHit, - 'UnpackMCMuonHits') - - # RICH Digit summaries - mc_rich_digit_sums = mc_unpacker('PackedMCRichDigitSummaries', - RichSumUnPack, "RichSumUnPack") + mc_vp_hits = mc_unpacker(LocationsPackedSim.PackedMCVPHits.name, + UnpackMCVPHit, "UnpackMCVPHits") + mc_ut_hits = mc_unpacker(LocationsPackedSim.PackedMCUTHits.name, + UnpackMCUTHit, "UnpackMCUTHits") + mc_ft_hits = mc_unpacker(LocationsPackedSim.PackedMCFTHits.name, + UnpackMCFTHit, "UnpackMCFTHits") + mc_rich_hits = mc_unpacker(LocationsPackedSim.PackedMCRichHits.name, + UnpackMCRichHit, "UnpackMCRichHits") + mc_ecal_hits = mc_unpacker(LocationsPackedSim.PackedMCEcalHits.name, + UnpackMCEcalHit, "UnpackMCEcalHits") + mc_hcal_hits = mc_unpacker(LocationsPackedSim.PackedMCHcalHits.name, + UnpackMCHcalHit, "UnpackMCHcalHits") + mc_muon_hits = mc_unpacker(LocationsPackedSim.PackedMCMuonHits.name, + UnpackMCMuonHit, "UnpackMCMuonHits") + + mc_rich_digit_sums = mc_unpacker( + LocationsPackedSim.PackedMCRichDigitSummaries.name, RichSumUnPack, + "RichSumUnPack") d = collections.OrderedDict([ - ('MCRichDigitSummaries', mc_rich_digit_sums), - ('MCParticles', mc_particles), - ('MCVertices', mc_vertices), - ('MCVPHits', mc_vp_hits), - ('MCUTHits', mc_ut_hits), - ('MCFTHits', mc_ft_hits), - ('MCRichHits', mc_rich_hits), - ('MCEcalHits', mc_ecal_hits), - ('MCHcalHits', mc_hcal_hits), - ('MCMuonHits', mc_muon_hits), + ("MCRichDigitSummaries", mc_rich_digit_sums), + ("MCParticles", mc_particles), + ("MCVertices", mc_vertices), + ("MCVPHits", mc_vp_hits), + ("MCUTHits", mc_ut_hits), + ("MCFTHits", mc_ft_hits), + ("MCRichHits", mc_rich_hits), + ("MCEcalHits", mc_ecal_hits), + ("MCHcalHits", mc_hcal_hits), + ("MCMuonHits", mc_muon_hits), ]) # Make sure we have consistent names, and that we're unpacking everything # we load from the file - assert set(['Packed' + k for k in d.keys()]) - set( - packed_mc_from_file().keys()) == set() + assert set(["Packed" + k for k in d.keys()]) - set( + enums_as_dict(LocationsPackedSim).keys()) == set() return d +def reco_unpacker(key, configurable, name, **kwargs): + """ + Return a reco. unpacker (`PyConf.Algorithm` instance) that reads from file + at `LocationsPackedReco[key]` and unpacks to the + forced output location `LocationsUnpackedReco[key]`. + """ + alg = Algorithm( + configurable, + name=name, + InputName=make_data_with_FetchDataFromFile( + LocationsPackedReco[key].value), + outputs={ + "OutputName": force_location(LocationsUnpackedReco[key].value) + }, + **kwargs) + return alg + + +def mc_unpacker(key, configurable, name, **kwargs): + """ + Return a sim. unpacker (`PyConf.Algorithm` instance) that reads from file + at `LocationsPackedSim[key]` and unpacks to the + forced output location `LocationsUnpackedSim[key]`. + """ + alg = Algorithm( + configurable, + name=name, + InputName=make_data_with_FetchDataFromFile( + LocationsPackedSim[key].value), + outputs={ + "OutputName": force_location(LocationsUnpackedSim[key].value) + }, + **kwargs) + return alg + + +def make_mc_track_info(): + """ + Return the MCTrackInfo data under `locations.LocationMCTrackInfo` + via `Gaudi::Hive::FetchDataFromFile`. + """ + return make_data_with_FetchDataFromFile(LocationMCTrackInfo) + + def boole_links_digits_mcparticles(): - """Return a dict of locations for MC linker tables (to mcparticles) created by Boole.""" - locations = { - "EcalDigits": "/Event/Link/Raw/Ecal/Digits", - "FTLiteClusters": "/Event/Link/Raw/FT/LiteClusters", - "HcalDigits": "/Event/Link/Raw/Hcal/Digits", - "MuonDigits": "/Event/Link/Raw/Muon/Digits", - "UTClusters": "/Event/Link/Raw/UT/Clusters", - "VPDigits": "/Event/Link/Raw/VP/Digits", - } + """ + Return a {TES_path: make_data_with_FetchDataFromFile(TES_path)} dict + of locations (`locations.LocationsBooleMCParticleLinkers`) for MC linker tables + (to `MCParticles`) created by Boole. + """ return { - key: make_data_with_FetchDataFromFile(loc) - for key, loc in locations.items() + loc.name: make_data_with_FetchDataFromFile(loc.value) + for loc in LocationsBooleMCParticleLinkers } def boole_links_digits_mchits(): - """Return a dict of locations for MC linker tables (to mchits) created by Boole. + """ + Return a {TES_path: make_data_with_FetchDataFromFile(TES_path)} dict + of locations (`locations.LocationsBooleMCHitsLinkers`) for MC linker tables + (to `MCHits`) created by Boole. - These locations are only propagated out of Boole for eXtendend DIGI and DST types. + These locations are only propagated and persisted out of Boole + for eXtendend DIGI and DST types. """ - locations = { - "FTLiteClusters": "/Event/Link/Raw/FT/LiteClusters2MCHits", - "UTClusters": "/Event/Link/Raw/UT/Clusters2MCHits", - "VPDigits": "/Event/Link/Raw/VP/Digits2MCHits", - } return { - key: make_data_with_FetchDataFromFile(loc) - for key, loc in locations.items() + loc.name: make_data_with_FetchDataFromFile(loc.value) + for loc in LocationsBooleMCHitsLinkers } def brunel_links(): - """Return a dict of locations for MC linker tables created by Brunel.""" - locations = { - "CaloElectrons": "/Event/Link/Rec/Calo/Electrons", - "CaloMergedPi0s": "/Event/Link/Rec/Calo/MergedPi0s", - "CaloPhotons": "/Event/Link/Rec/Calo/Photons", - "CaloSplitPhotons": "/Event/Link/Rec/Calo/SplitPhotons", - "Tracks": "/Event/Link/Rec/Track/Best", - } + """ + Return a {TES_path: make_data_with_FetchDataFromFile(TES_path)} dict + of locations (`locations.LocationsBrunelMCLinkers`) for MC linker tables + created by Brunel. + """ return { - key: make_data_with_FetchDataFromFile(loc) - for key, loc in locations.items() + loc.name: make_data_with_FetchDataFromFile(loc.value) + for loc in LocationsBrunelMCLinkers } diff --git a/Phys/DaVinci/python/DaVinci/filters_selectors.py b/Phys/DaVinci/python/DaVinci/filters_selectors.py new file mode 100644 index 000000000..b301bbb7b --- /dev/null +++ b/Phys/DaVinci/python/DaVinci/filters_selectors.py @@ -0,0 +1,167 @@ +############################################################################### +# (c) Copyright 2021 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. # +############################################################################### +""" +Definitions of: + +- `Particle` and `ProtoParticle` filters. +- Track selectors. +- Default cuts à la runs 1&2 common particles. +""" +from __future__ import absolute_import, division, print_function + +from PyConf.tonic import configurable +from PyConf.Tools import LoKi__Hybrid__ProtoParticleFilter as ProtoParticleFilter +from PyConf.Tools import LoKi__Hybrid__TrackSelector as TrackSelector + +from .hacks import patched_hybrid_tool + +######################### +# Helpers to combine cuts +######################### + + +def require_all(*cuts): + """ + Return a cut string requiring all (string) arguments. + + Example: + + >>> require_all('PT > {pt_min}', 'DLLK < {dllk_max}') + '(PT > {pt_min}) & (DLLK < {dllk_max})' + """ + return " & ".join(["({})".format(c) for c in cuts]) + + +def require_any(*cuts): + """ + Return a cut string requiring at least one of the (string) arguments passes. + + Example: + + >>> require_any('M < 8*GeV', 'PT > 3*GeV') + '(M < 8*GeV) | (PT > 3*GeV)' + """ + return " | ".join(["({})".format(c) for c in cuts]) + + +####################### +# Protoparticle filters +####################### + + +@configurable +def all_protoparticle_filter(Code="PP_ALL", **kwargs): + """ + Get a `LoKi__Hybrid__ProtoParticleFilter` instance + that by default selects all protoparticles. + + Args: + Code (str): The "Code" argument to pass to the filter tool. + Default = "PP_ALL". + kwargs: Keyword arguments accepted by `LoKi__Hybrid__Tool`. + + Returns: + `LoKi__Hybrid__ProtoParticleFilter` instance wrapped as a `PyConf.components.Tool`. + """ + return ProtoParticleFilter( + Code=Code, Factory=patched_hybrid_tool("PPFactory"), **kwargs) + + +################# +# Track selectors +################# + + +@configurable +def get_all_track_selector(Code="TrALL", **kwargs): + """ + Get a `LoKi__Hybrid__TrackSelector` instance + that by default selects all tracks. + + Args: + Code (str): The "Code" argument to pass to the tool. + Default = "TrALL". + kwargs: Keyword arguments accepted by `LoKi__Hybrid__TrackSelector`. + + Returns: + `LoKi__Hybrid__TrackSelector` instance wrapped as a `PyConf.components.Tool`. + """ + return TrackSelector(Code=Code, **kwargs) + + +@configurable +def get_long_track_selector(Code='TrALL', **kwargs): + """ + Get a `LoKi__Hybrid__TrackSelector` instance + that by default selects all long tracks. + + Args: + Code (str): The "Code" argument to pass to the tool. + Default = "TrALL & TrLONG". + kwargs: Keyword arguments accepted by `LoKi__Hybrid__TrackSelector`. + + Returns: + `LoKi__Hybrid__TrackSelector` instance wrapped as a `PyConf.components.Tool`. + """ + return TrackSelector(Code=require_all("TrLONG", Code), **kwargs) + + +@configurable +def get_down_track_selector(Code='TrALL', **kwargs): + """ + Get a `LoKi__Hybrid__TrackSelector` instance + that by default selects all downstream tracks. + + Args: + Code (str): The "Code" argument to pass to the tool. + Default = "TrALL & TrDOWNSTREAM". + kwargs: Keyword arguments accepted by `LoKi__Hybrid__TrackSelector`. + + Returns: + `LoKi__Hybrid__TrackSelector` instance wrapped as a `PyConf.components.Tool`. + """ + return TrackSelector(Code=require_all("TrDOWNSTREAM", Code), **kwargs) + + +@configurable +def get_upstream_track_selector(Code='TrALL', **kwargs): + """ + Get a `LoKi__Hybrid__TrackSelector` instance + that by default selects all upstream tracks. + + Args: + Code (str): The "Code" argument to pass to the tool. + Default = "TrALL & TrUPSTREAM". + kwargs: Keyword arguments accepted by `LoKi__Hybrid__TrackSelector`. + + Returns: + `LoKi__Hybrid__TrackSelector` instance wrapped as a `PyConf.components.Tool`. + """ + return TrackSelector(Code=require_all("TrUPSTREAM", Code), **kwargs) + + +################################# +# Default track and particle cuts +################################# + + +def default_track_cuts(): + """ + Return a string with the default track cuts. + """ + return require_all("TrCHI2<5", "~TrCLONE") + + +def default_particle_cuts(): + """ + Return a string with the default particle standard loose cuts. + """ + return require_all("PT>250*MeV", "MIPCHI2DV(PRIMARY)>4.") diff --git a/Phys/DaVinci/python/DaVinci/locations.py b/Phys/DaVinci/python/DaVinci/locations.py new file mode 100644 index 000000000..38a104274 --- /dev/null +++ b/Phys/DaVinci/python/DaVinci/locations.py @@ -0,0 +1,133 @@ +############################################################################### +# (c) Copyright 2021 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. # +############################################################################### +""" +Definitions of enums specifying the standard locations of +packed and unpacked objects, and various linker tables. + +.. note:: + These locations are what has been used in Runs 1 & 2, + and may need a revision once the Run 3 event model is finalised + and the definition of what gets persisted gets formalised. +""" +from __future__ import absolute_import, division, print_function + +from enum import Enum + + +class LocationsPackedReco(Enum): + """ + Locations of packed reconstruction objects, stored under "/Event/pRec". + """ + PackedPVs = "/Event/pRec/Vertex/Primary" + PackedCaloElectrons = "/Event/pRec/Calo/Electrons" + PackedCaloPhotons = "/Event/pRec/Calo/Photons" + PackedCaloMergedPi0s = "/Event/pRec/Calo/MergedPi0s" + PackedCaloSplitPhotons = "/Event/pRec/Calo/SplitPhotons" + PackedMuonPIDs = "/Event/pRec/Muon/MuonPID" + PackedRichPIDs = "/Event/pRec/Rich/PIDs" + PackedTracks = "/Event/pRec/Track/Best" + PackedMuonTracks = "/Event/pRec/Track/Muon" + PackedNeutralProtos = "/Event/pRec/ProtoP/Neutrals" + PackedChargedProtos = "/Event/pRec/ProtoP/Charged" + + +LocationsUnpackedReco = Enum( + "LocationsUnpackedReco", + {e.name: e.value.replace("pRec", "Rec") + for e in LocationsPackedReco}) +LocationsUnpackedReco.__doc__ = """ +Locations of packed reconstruction objects, stored under "/Event/pRec". +""" + + +class LocationsPackedSim(Enum): + """ + Locations of packed simulation objects, stored under "/Event/pSim". + """ + PackedMCParticles = "/Event/pSim/MCParticles" + PackedMCVertices = "/Event/pSim/MCVertices" + PackedMCVPHits = "/Event/pSim/VP/Hits" + PackedMCUTHits = "/Event/pSim/UT/Hits" + PackedMCFTHits = "/Event/pSim/FT/Hits" + PackedMCRichHits = "/Event/pSim/Rich/Hits" + PackedMCEcalHits = "/Event/pSim/Ecal/Hits" + PackedMCHcalHits = "/Event/pSim/Hcal/Hits" + PackedMCMuonHits = "/Event/pSim/Muon/Hits" + PackedMCRichDigitSummaries = "/Event/pSim/Rich/DigitSummaries" + + +class LocationsUnpackedSim(Enum): + """ + Locations of unpacked simulation objects, stored under "/Event/MC". + """ + PackedMCParticles = "/Event/MC/Particles" + PackedMCVertices = "/Event/MC/Vertices" + PackedMCVPHits = "/Event/MC/VP/Hits" + PackedMCUTHits = "/Event/MC/UT/Hits" + PackedMCFTHits = "/Event/MC/FT/Hits" + PackedMCRichHits = "/Event/MC/Rich/Hits" + PackedMCEcalHits = "/Event/MC/Ecal/Hits" + PackedMCHcalHits = "/Event/MC/Hcal/Hits" + PackedMCMuonHits = "/Event/MC/Muon/Hits" + PackedMCRichDigitSummaries = "/Event/MC/Rich/DigitSummaries" + + +# Location of MCTrackInfo objects +LocationMCTrackInfo = "/Event/MC/TrackInfo" + + +class LocationsBooleMCParticleLinkers(Enum): + """ + Locations of MC linker tables to MCParticles created by Boole. + """ + EcalDigits = "/Event/Link/Raw/Ecal/Digits" + FTLiteClusters = "/Event/Link/Raw/FT/LiteClusters" + HcalDigits = "/Event/Link/Raw/Hcal/Digits" + MuonDigits = "/Event/Link/Raw/Muon/Digits" + UTClusters = "/Event/Link/Raw/UT/Clusters" + VPDigits = "/Event/Link/Raw/VP/Digits" + + +class LocationsBooleMCHitsLinkers(Enum): + """ + Locations for MC linker tables to MCHits created by Boole. + + These locations are only propagated out of Boole for eXtendend DIGI and DST types. + """ + FTLiteClusters = "/Event/Link/Raw/FT/LiteClusters2MCHits" + UTClusters = "/Event/Link/Raw/UT/Clusters2MCHits" + VPDigits = "/Event/Link/Raw/VP/Digits2MCHits" + + +class LocationsBrunelMCLinkers(Enum): + """ + Locations of MC linker tables created by Brunel. + """ + CaloElectrons = "/Event/Link/Rec/Calo/Electrons" + CaloMergedPi0s = "/Event/Link/Rec/Calo/MergedPi0s" + CaloPhotons = "/Event/Link/Rec/Calo/Photons" + CaloSplitPhotons = "/Event/Link/Rec/Calo/SplitPhotons" + Tracks = "/Event/Link/Rec/Track/Best" + + +def enums_as_dict(enums): + """ + Return a {name: value} dict of all enum members. + + Example: + + >>> class MyEnum(Enum): + a = 1 + b = 2 + >>> enums_as_dict(MyEnum) + {'a': 1, 'b': 2} + """ + return {e.name: e.value for e in enums} diff --git a/Phys/DaVinci/python/DaVinci/optionChecker.py b/Phys/DaVinci/python/DaVinci/optionChecker.py index 9475f3249..9b21a3b31 100644 --- a/Phys/DaVinci/python/DaVinci/optionChecker.py +++ b/Phys/DaVinci/python/DaVinci/optionChecker.py @@ -89,7 +89,7 @@ def print_allowed_option_values(allowedValues, name=None): print(set_color("green") + "Known job option configurations and allowed values:") for name, values in allowedValues.iteritems(): - print("%s \t : %s" % (name, value), set_color("plain")) + print("%s \t : %s" % (name, values), set_color("plain")) else: print(set_color("green") + "Allowed values for DaVinci option %s:" % name) diff --git a/Phys/DaVinci/python/DaVinci/reco_objects.py b/Phys/DaVinci/python/DaVinci/reco_objects.py index 87931be7f..d0f599033 100644 --- a/Phys/DaVinci/python/DaVinci/reco_objects.py +++ b/Phys/DaVinci/python/DaVinci/reco_objects.py @@ -8,11 +8,16 @@ # granted to it by virtue of its status as an Intergovernmental Organization # # or submit itself to any jurisdiction. # ############################################################################### +""" +Configurables to make the various reconstruction objects from the +packed data on file. -###### -### N.B. THIS FILE IS INTENDED TO AVOID DEPENDENCIES ON MOORE, -### IS NEEDED FOR TESTING PURPOSES AND NEEDS TO BE REMOVED IN PRODUCTION -###### +.. note:: + 1) What is defined here relies on data paths used in Runs 1 & 2, + and may need a revision once the Run 3 event model is finalised + and the definition of what gets persisted gets formalised. + 2) Code very heavily relies on its Moore equivalent. Thank you, RTA team. +""" from PyConf import configurable from .data_from_file import reco_unpackers @@ -54,7 +59,8 @@ def make_tracks(): def reconstruction(from_file=True): """Return reconstruction objects. - Note it is advised to use this function if more than one object is needed, + Note: + It is advised to use this function if more than one object is needed, rather than the accessors below as it makes the configuration slower. """ # removed reco since it will not be done in DV diff --git a/Phys/DaVinci/python/DaVinci/standard_particles.py b/Phys/DaVinci/python/DaVinci/standard_particles.py index 96d6097d7..36e38bcb3 100644 --- a/Phys/DaVinci/python/DaVinci/standard_particles.py +++ b/Phys/DaVinci/python/DaVinci/standard_particles.py @@ -19,18 +19,13 @@ from __future__ import absolute_import, division, print_function from GaudiKernel.SystemOfUnits import GeV, MeV, mm, picosecond -from PyConf.Algorithms import ( - FunctionalParticleMaker, - LHCb__Phys__ParticleMakers__PhotonMaker as PhotonMaker, - LHCb__Phys__ParticleMakers__MergedPi0Maker as MergedPi0Maker, - Proto2ChargedBasic, -) -from PyConf.Tools import (LoKi__Hybrid__ProtoParticleFilter as - ProtoParticleFilter, LoKi__Hybrid__TrackSelector as - TrackSelector) - from PyConf import configurable +from PyConf.Algorithms import ( + FunctionalParticleMaker, LHCb__Phys__ParticleMakers__PhotonMaker as + PhotonMaker, LHCb__Phys__ParticleMakers__MergedPi0Maker as MergedPi0Maker, + Proto2ChargedBasic) + from .algorithms_pyconf import ( require_all, ParticleFilter, @@ -40,7 +35,9 @@ from .algorithms_pyconf import ( NeutralParticleCombinerWithPVs, ) -from .hacks import patched_hybrid_tool +from .selectors import get_all_track_selector, get_long_track_selector, get_down_track_selector +from .filters import all_protoparticle_filter as standard_protoparticle_filter + from .reco_objects import ( make_charged_protoparticles as _make_charged_protoparticles, make_pvs as _make_pvs, make_neutral_protoparticles as _make_neutral_protoparticles) @@ -49,32 +46,6 @@ _KAON0_M = 497.611 * MeV # +/- 0.013, PDG, PR D98, 030001 and 2019 update _LAMBDA_M = 1115.683 * MeV # +/- 0.006, PDG, PR D98, 030001 and 2019 update -@configurable -def get_all_track_selector(Code='TrALL', **kwargs): - return TrackSelector(Code=Code, **kwargs) - - -@configurable -def get_long_track_selector(Code='TrALL', **kwargs): - return TrackSelector(Code=require_all('TrLONG', Code), **kwargs) - - -@configurable -def get_down_track_selector(Code='TrALL', **kwargs): - return TrackSelector(Code=require_all('TrDOWNSTREAM', Code), **kwargs) - - -@configurable -def get_upstream_track_selector(Code='TrALL', **kwargs): - return TrackSelector(Code=require_all('TrUPSTREAM', Code), **kwargs) - - -@configurable -def standard_protoparticle_filter(Code='PP_ALL', **kwargs): - return ProtoParticleFilter( - Code=Code, Factory=patched_hybrid_tool('PPFactory'), **kwargs) - - @configurable def _make_particles(species, make_protoparticles=_make_charged_protoparticles, -- GitLab