diff --git a/Hlt/RecoConf/python/RecoConf/data_from_file.py b/Hlt/RecoConf/python/RecoConf/data_from_file.py index acfaf65fe088945feafcfb8713ea3d439ed873b6..638e7b436b173bb402f9532184359c3dfb780ba9 100644 --- a/Hlt/RecoConf/python/RecoConf/data_from_file.py +++ b/Hlt/RecoConf/python/RecoConf/data_from_file.py @@ -62,7 +62,9 @@ from PyConf.Tools import ( def __packed_location_for_(unpacked): - m = {"/Event/MC": "/Event/pSim", "/Event/Rec": "/Event/pRec"} + m = {"/Event/Rec": "/Event/pRec"} + for spill in ["", "PrevPrev/", "Prev/", "Next/", "NextNext/", "LHCBackground/"]: + m["/Event/" + spill + "MC"] = "/Event/" + spill + "pSim" for k, v in m.items(): if unpacked.startswith(k): return v + unpacked.removeprefix(k) @@ -88,13 +90,15 @@ def _unpacker(forced_type, unpacked_loc, **kwargs): "LHCb::PackedMCRichOpticalPhotons": MCRichOpticalPhotonUnpacker, "LHCb::PackedMCRichDigitSummarys": MCRichDigitSummaryUnpacker, } + input_data = input_from_root_file( + __packed_location_for_(unpacked_loc), forced_type=forced_type + ) + output_data = force_location(unpacked_loc) return Algorithm( _unpack_configurable[forced_type], name=f"Unpack_{unpacked_loc.replace('/', '_')}", - outputs={"OutputName": force_location(unpacked_loc)}, - InputName=input_from_root_file( - __packed_location_for_(unpacked_loc), forced_type=forced_type - ), + outputs={"OutputName": output_data}, + InputName=input_data, **kwargs, ) @@ -209,48 +213,54 @@ def _get_mc_unpackers(): ), } - def _mc_unpacker(key, **kwargs): + def _mc_unpacker(key, spill, **kwargs): """Return unpacker that reads from file and unpacks to a forced output location.""" packed_type, location = _packed_mc_from_file[key] # The SmartRefs held by the unpacked MC objects only work if we unpack to these specific locations return _unpacker( - packed_type, "/Event/MC/" + location.removeprefix("MC"), **kwargs + packed_type, + "/Event/" + spill + "MC/" + location.removeprefix("MC"), + **kwargs, ) # Ordered so that dependents are unpacked first # Make sure that MC particles and MC vertices are unpacked together, # see https://gitlab.cern.ch/lhcb/LHCb/issues/57 for details. - return collections.OrderedDict( + unps = collections.OrderedDict( [ - ("MCRichDigitSummaries", _mc_unpacker("MCRichDigitSummaries")), ("MCParticles", get_mc_particles("/Event/MC/Particles")), ("MCVertices", get_mc_vertices("/Event/MC/Vertices")), - ("MCVPHits", _mc_unpacker("MCVPHits")), - ("MCUTHits", _mc_unpacker("MCUTHits")), - ("MCFTHits", _mc_unpacker("MCFTHits")), - ("MCRichHits", _mc_unpacker("MCRichHits")), - ("MCRichSegments", _mc_unpacker("MCRichSegments")), - ("MCRichTracks", _mc_unpacker("MCRichTracks")), - ("MCRichOpticalPhotons", _mc_unpacker("MCRichOpticalPhotons")), - ("MCEcalHits", _mc_unpacker("MCEcalHits")), - ("MCHcalHits", _mc_unpacker("MCHcalHits")), - ("MCMuonHits", _mc_unpacker("MCMuonHits")), ] ) + for spill in ["", "PrevPrev/", "Prev/", "NextNext/", "Next/", "LHCBackground/"]: + unps.update( + [ + (spill + "MCVPHits", _mc_unpacker("MCVPHits", spill)), + (spill + "MCUTHits", _mc_unpacker("MCUTHits", spill)), + (spill + "MCFTHits", _mc_unpacker("MCFTHits", spill)), + ( + spill + "MCRichDigitSummaries", + _mc_unpacker("MCRichDigitSummaries", spill), + ), + (spill + "MCRichHits", _mc_unpacker("MCRichHits", spill)), + (spill + "MCRichSegments", _mc_unpacker("MCRichSegments", spill)), + (spill + "MCRichTracks", _mc_unpacker("MCRichTracks", spill)), + ( + spill + "MCRichOpticalPhotons", + _mc_unpacker("MCRichOpticalPhotons", spill), + ), + (spill + "MCEcalHits", _mc_unpacker("MCEcalHits", spill)), + (spill + "MCHcalHits", _mc_unpacker("MCHcalHits", spill)), + (spill + "MCMuonHits", _mc_unpacker("MCMuonHits", spill)), + ] + ) + return unps # precomputed dict of mc unpackers, will be filled on first use _mc_unpackers = None -def reco_unpacker(key): - """Returns the adequate unpacker for the given key""" - global _reco_unpackers - if _reco_unpackers is None: - _reco_unpackers = _get_reco_unpackers() - return _reco_unpackers[key] - - def reco_unpackers(): global _reco_unpackers if _reco_unpackers is None: @@ -258,12 +268,9 @@ def reco_unpackers(): return _reco_unpackers -def mc_unpacker(key): +def reco_unpacker(key): """Returns the adequate unpacker for the given key""" - global _mc_unpackers - if _mc_unpackers is None: - _mc_unpackers = _get_mc_unpackers() - return _mc_unpackers[key] + return reco_unpackers()[key] def mc_unpackers(): @@ -273,6 +280,11 @@ def mc_unpackers(): return _mc_unpackers +def mc_unpacker(key): + """Returns the adequate unpacker for the given key""" + return mc_unpackers()[key] + + def boole_links_digits_mcparticles(key): """Return a dict of locations for MC linker tables (to mcparticles) created by Boole.""" locations = { diff --git a/Hlt/RecoConf/python/RecoConf/mc_checking.py b/Hlt/RecoConf/python/RecoConf/mc_checking.py index df6396d77984f75384d854aaaa488229dae69c3b..9c40f33e585b72040aac725bde3443dc07f24f18 100644 --- a/Hlt/RecoConf/python/RecoConf/mc_checking.py +++ b/Hlt/RecoConf/python/RecoConf/mc_checking.py @@ -40,6 +40,7 @@ from PyConf.Algorithms import ( VPFullCluster2MCParticleLinker, ) from PyConf.application import make_odin +from PyConf.components import force_location from PyConf.dataflow import DataHandle from PyConf.reading import get_mc_header, get_mc_track_info from PyConf.tonic import configurable @@ -525,14 +526,29 @@ def make_default_IdealStateCreator(public=False): uthits = mc_unpacker("MCUTHits") fthits = mc_unpacker("MCFTHits") - link_vp_hits = MCParticle2MCHitAlg(MCParticles=mcpart, MCHitPath=vphits) - link_ut_hits = MCParticle2MCHitAlg(MCParticles=mcpart, MCHitPath=uthits) - link_ft_hits = MCParticle2MCHitAlg(MCParticles=mcpart, MCHitPath=fthits) + vplinkloc = "Link/" + vphits.OutputName.location.replace("/Event/", "") + link_vp_hits = MCParticle2MCHitAlg( + MCParticles=mcpart, + MCHitPath=vphits.OutputName, + outputs={"OutputData": force_location(vplinkloc)}, + ) + utlinkloc = "Link/" + uthits.OutputName.location.replace("/Event/", "") + link_ut_hits = MCParticle2MCHitAlg( + MCParticles=mcpart, + MCHitPath=uthits, + outputs={"OutputData": force_location(utlinkloc)}, + ) + ftlinkloc = "Link/" + fthits.OutputName.location.replace("/Event/", "") + link_ft_hits = MCParticle2MCHitAlg( + MCParticles=mcpart, + MCHitPath=fthits, + outputs={"OutputData": force_location(ftlinkloc)}, + ) return IdealStateCreator( - Extrapolator=TrackMasterExtrapolator( - ExtraSelector=TrackDistanceExtraSelector(shortDist=0) - ), + # Extrapolator=TrackMasterExtrapolator( + # ExtraSelector=TrackDistanceExtraSelector(shortDist=0) + # ), MCVertices=mcvert, VPMCHits=vphits, VPMCHitLinks=link_vp_hits, diff --git a/Hlt/RecoConf/python/RecoConf/rich_data_monitoring.py b/Hlt/RecoConf/python/RecoConf/rich_data_monitoring.py index 1fcb0d842c114afa85e3c1d3ff75cc119f08cd8b..701d52fd58d68f9bad424acc4d240ecd7d93bec1 100644 --- a/Hlt/RecoConf/python/RecoConf/rich_data_monitoring.py +++ b/Hlt/RecoConf/python/RecoConf/rich_data_monitoring.py @@ -17,6 +17,9 @@ from PyConf.Algorithms import ( Rich__Future__Rec__Moni__GeometricalEfficiencies as GeomEffs, ) from PyConf.Algorithms import Rich__Future__Rec__Moni__MassHypoRings as MassRings +from PyConf.Algorithms import ( + Rich__Future__Rec__Moni__PhotonSIMDEfficiency as PhotSIMDEff, +) from PyConf.Algorithms import Rich__Future__Rec__Moni__PhotonYield as PhotonYield from PyConf.Algorithms import Rich__Future__Rec__Moni__PixelBackgrounds as PixBackgrds from PyConf.Algorithms import Rich__Future__Rec__Moni__PixelClusters as ClusterMoni @@ -253,6 +256,7 @@ def default_rich_monitors(moni_set="Standard"): "GeomEffs", "MassRings", "DumpDervDets", + "SIMDPerf", ], # The default set of monitors. "Standard": [ @@ -552,6 +556,17 @@ def make_rich_track_monitors( CherenkovAnglesLocation=conf["SignalCKAngles"], ) + # SIMD Eff + key = "SIMDPerf" + if key in monitors: + results[key] = PhotSIMDEff( + name="RichPhotonSIMDEff" + track_name, + Detectors=det_opts, + CherenkovPhotonLocation=conf["CherenkovPhotons"], + PhotonToParentsLocation=conf["PhotonToParents"], + TrackSegmentsLocation=conf["TrackSegments"], + ) + # RICH alignment histogram production and/or optionally, others tasks key = "MirrorAlign" if key in monitors: diff --git a/Hlt/RecoConf/python/RecoConf/rich_mc_checking.py b/Hlt/RecoConf/python/RecoConf/rich_mc_checking.py index a685418967ff05f50e079cf48a720b4e87414ca4..ccfd0db4f7034c9b8eb7674fe7d2ab4017b4b503 100644 --- a/Hlt/RecoConf/python/RecoConf/rich_mc_checking.py +++ b/Hlt/RecoConf/python/RecoConf/rich_mc_checking.py @@ -23,9 +23,15 @@ from PyConf.Algorithms import ( from PyConf.Algorithms import Rich__Future__Rec__MC__Moni__DetectorHits as DetectorHits from PyConf.Algorithms import Rich__Future__Rec__MC__Moni__OpticalPhotons as MCOptPhots from PyConf.Algorithms import Rich__Future__Rec__MC__Moni__PIDQC as PIDQC +from PyConf.Algorithms import ( + Rich__Future__Rec__MC__Moni__RichPIDTupleCreatorAlg as RichPIDTuple, +) from PyConf.Algorithms import ( Rich__Future__Rec__MC__Moni__SIMDPhotonCherenkovAngles as MCCKAngles, ) +from PyConf.Algorithms import ( + Rich__Future__Rec__MC__Moni__SIMDPhotonTime as MCTime, +) from PyConf.Algorithms import Rich__Future__Rec__MC__Moni__TrackResolution as MCTkRes from PyConf.Tools import TrackSelector from PyConf.utilities import ConfigurationError @@ -64,6 +70,45 @@ def default_rich_checking_options(): ############################################################################### +def make_track_links(tracks, useUT=True): + """ + Returns appropriate track to MC links + """ + + if useUT: + links_to_lhcbids = make_links_lhcbids_mcparticles_tracking_system() + else: + links_to_lhcbids = make_links_lhcbids_mcparticles_VP_FT() + return make_links_tracks_mcparticles(tracks, LinksToLHCbIDs=links_to_lhcbids) + + +############################################################################### + + +def track_to_MCP_rels(conf, useUT=True): + """ + Creates useful relations from painful linkers + """ + + # Track linker stuff + if "RichTrackMCPartLinks" not in conf.keys(): + # Build Track -> MCP links ourselves + links_to_tracks = make_track_links(conf["OriginalV1Tracks"], useUT) + else: + links_to_tracks = conf["RichTrackMCPartLinks"] + + # Make the RICH Track -> MCParticle relations table + return TkToMCPRels( + name="RichTkToMCPRelations_{hash}", + TracksLocation=conf["OriginalV1Tracks"], + MCParticlesLinkLocation=links_to_tracks, + MCParticlesLocation=mc_unpacker("MCParticles"), + ) + + +############################################################################### + + @configurable def default_rich_checkers(moni_set="Standard"): """ @@ -78,20 +123,20 @@ def default_rich_checkers(moni_set="Standard"): # To Do: Add time monitors here for 4D support monitors = { - "Expert": # Activates all montiors - [ + "Expert": [ "RichHits", "PIDPerformance", "PhotonCherenkovAngles", "CherenkovResolution", - "CKResParameterisation", "TrackResolution", "MCOpticalPhotons", + "Time", + "PIDTuple", ], "Standard": # The default set of monitors - ["PIDPerformance", "PhotonCherenkovAngles", "TrackResolution"], + ["PIDPerformance", "PhotonCherenkovAngles", "TrackResolution", "Time"], "OnlineMonitoring": # For monitoring at the pit - ["PIDPerformance", "PhotonCherenkovAngles", "TrackResolution"], + ["PIDPerformance", "PhotonCherenkovAngles", "TrackResolution", "Time"], "RefractiveIndex": # For ref index task [ "RichHits", @@ -99,7 +144,9 @@ def default_rich_checkers(moni_set="Standard"): "CherenkovResolution", "TrackResolution", "MCOpticalPhotons", + "Time", ], + "ResolutionParameterisation": ["CKResParameterisation"], "None": [], } @@ -143,26 +190,10 @@ def make_rich_checkers(conf, reco_opts, check_opts, moni_set="Standard"): checkers = default_rich_checkers(moni_set) # MC Info - mcps = mc_unpacker("MCParticles") richSummaries = mc_unpacker("MCRichDigitSummaries") - # Track linker stuff - if check_opts["UseUT"]: - links_to_lhcbids = make_links_lhcbids_mcparticles_tracking_system() - else: - links_to_lhcbids = make_links_lhcbids_mcparticles_VP_FT() - - links_to_tracks = make_links_tracks_mcparticles( - InputTracks=conf["OriginalV1Tracks"], LinksToLHCbIDs=links_to_lhcbids - ) - - # Make the RICH Track -> MCParticle relations table - tkMCPRels = TkToMCPRels( - name="RichTkToMCPRelations_{hash}", - TracksLocation=conf["OriginalV1Tracks"], - MCParticlesLinkLocation=links_to_tracks, - MCParticlesLocation=mcps, - ) + # Track to MCP relations + tkMCPRels = track_to_MCP_rels(conf, check_opts["UseUT"]) # Momentum cuts for plotting etc (by radiator) pCuts = {"MinP": (0.5 * GeV, 0.5 * GeV), "MaxP": (120.0 * GeV, 120.0 * GeV)} @@ -240,6 +271,8 @@ def make_rich_checkers(conf, reco_opts, check_opts, moni_set="Standard"): Detectors=det_opts, MinP=pCuts["MinP"], MaxP=pCuts["MaxP"], + TracksLocation=conf["InputTracks"], + RichDigitSummariesLocation=richSummaries, TrackSegmentsLocation=conf["TrackSegments"], CherenkovPhotonLocation=conf["CherenkovPhotons"], CherenkovAnglesLocation=conf["SignalCKAngles"], @@ -286,4 +319,37 @@ def make_rich_checkers(conf, reco_opts, check_opts, moni_set="Standard"): PhotonYieldLocation=conf["SignalYields"], ) + # 4D Time + key = "Time" + is4d = reco_opts["Enable4DReco"] + if key in checkers and (is4d[0] or is4d[1]): + results[key] = MCTime( + name="RiCKMCTime" + track_name, + Detectors=det_opts, + TrackSegmentsLocation=conf["TrackSegments"], + CherenkovPhotonLocation=conf["CherenkovPhotons"], + SummaryTracksLocation=conf["SummaryTracks"], + PhotonToParentsLocation=conf["PhotonToParents"], + TrackToMCParticlesRelations=tkMCPRels.TrackToMCParticlesRelations, + RichPixelClustersLocation=conf["RichClusters"], + RichSIMDPixelSummariesLocation=conf["RichSIMDPixels"], + RichDigitSummariesLocation=richSummaries, + TracksLocation=conf["InputTracks"], + ) + + # PID tuple + key = "PIDTuple" + if key in checkers: + # Momentum cuts for plotting etc (by radiator) + pCutsPID = [2.0 * GeV, 100 * GeV] + # Make a PID monitor for this selection + results[key] = RichPIDTuple( + name="RichPIDTuple", + NTupleLUN="RICHTUPLE1", + TrackSelector=TrackSelector(MinPCut=pCutsPID[0], MaxPCut=pCutsPID[1]), + TracksLocation=conf["InputTracks"], + RichPIDsLocation=conf["RichPIDs"], + TrackToMCParticlesRelations=track_to_MCP_rels(conf), + ) + return results diff --git a/Hlt/RecoConf/python/RecoConf/rich_reconstruction.py b/Hlt/RecoConf/python/RecoConf/rich_reconstruction.py index a2eddf3a5bf9d060abb9ebe73e5b8746f1c8d5b2..e79c6e83fc3c6d6acb81f2a1f7e7eb80429cdd2e 100644 --- a/Hlt/RecoConf/python/RecoConf/rich_reconstruction.py +++ b/Hlt/RecoConf/python/RecoConf/rich_reconstruction.py @@ -1,5 +1,5 @@ ############################################################################### -# (c) Copyright 2019-2022 CERN for the benefit of the LHCb Collaboration # +# (c) Copyright 2019-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". # @@ -8,6 +8,7 @@ # granted to it by virtue of its status as an Intergovernmental Organization # # or submit itself to any jurisdiction. # ############################################################################### + from Configurables import Rich__Future__ParticleProperties as PartProps from GaudiKernel.SystemOfUnits import GeV from PyConf import configurable @@ -117,6 +118,8 @@ def default_rich_reco_options(init_override_opts={}): ], # The RICH radiators to use "RichGases": ["Rich1Gas", "Rich2Gas"], + # enable 4D reco in each radiator + "Enable4DReco": (False, False), # Maximum number of clusters GEC cut "MaxPixelClusters": 200000, # Maximum number of tracks GEC cut @@ -135,9 +138,19 @@ def default_rich_reco_options(init_override_opts={}): "ApplyPixelClustering": (False, False), # Activate pixels in specific panels in each RICH only. "ActivatePanel": (True, True), + # Average hit time expected in each RICH (for 4D reco) + "averageHitTime": (13.03, 52.94), + # Course time window for hit selection (in ns) (for 4D reco) + "pixelTimeWindow": (1.0, 1.0), + # Inner/Outer region override options + "pixOverrideRegions": (False, False), + "InnerRegionsX": (250, 999999), + "InnerRegionsY": (300, 300), # =========================================================== # Track treatment options # =========================================================== + # Use UT ? + "UseUT": True, # Track Extrapolator type "TrackExtrapolator": TrackSTEPExtrapolator, # Tay tracing ring points (Max) @@ -156,6 +169,8 @@ def default_rich_reco_options(init_override_opts={}): "MaxP": (9e90 * GeV, 9e90 * GeV), # Minimum transverse momentum cut "MinPt": 0 * GeV, + # Track resolution scale factors + "TkCKResScaleFactors": (1.0, 1.0), # =========================================================== # Photon treatment options # =========================================================== @@ -174,6 +189,9 @@ def default_rich_reco_options(init_override_opts={}): "MinMaxTrackROICuts": defaultMinMaxTrackROICuts(), # Min Photon Probability cuts "MinPhotonProbabilityCuts": defaultMinPhotonProbabilityCuts(), + # Timing window for photon reconstruction (in ns) for (inner,outer) regions + # Only used if 4D reconstruction is active + "photonTimeWindow": ((0.25, 0.25), (0.25, 0.25)), # =========================================================== # Settings for the global PID minimisation # =========================================================== @@ -255,9 +273,9 @@ def shortTrackType(track_name): @configurable -def make_rich_pixels(options, make_raw=default_raw_banks): +def decode_rich_raw(options, make_raw=default_raw_banks): """ - Return pixel specific RICH data. + Configures the RICH raw bank decoding Args: options (dict): The processing options to use @@ -267,9 +285,6 @@ def make_rich_pixels(options, make_raw=default_raw_banks): dict of useful data locations. """ - # Configure the particle properties - configure_rich_particle_properties(options) - # The conf dict to return results = {} @@ -283,46 +298,63 @@ def make_rich_pixels(options, make_raw=default_raw_banks): results["RawBanks"] = rawBanks # Decode the Raw event to RichSmartIDs - richDecode = RichDecoder( + results["RichDecodedData"] = RichDecoder( name="RichRawDecoder_{hash}", RawBanks=rawBanks, Detectors=det_opts, Panels=options["ActivatePanel"], - ) - results["RichDecodedData"] = richDecode.DecodedDataLocation + ).DecodedDataLocation + + # finally return the results dict + return results + + +@configurable +def make_rich_clusters(options, decode=decode_rich_raw): + """ + Configures the RICH pixel clustering + + Args: + options (dict): The processing options to use + decode : raw decoder to use + + Returns: + dict of useful data locations. + """ + + # Get the detector options + det_opts = get_detector_bool_opts(options) + + # The conf dict to return + results = {} + + # Raw Bank decoding + results.update(decode(options)) # Run clustering - doClus = options["ApplyPixelClustering"] - richClus = RichClustering( + results["RichClusters"] = RichClustering( name="RichPixelClustering_{hash}", - ApplyPixelClustering=doClus, + ApplyPixelClustering=options["ApplyPixelClustering"], MaxClusters=options["MaxPixelClusters"], - DecodedDataLocation=richDecode.DecodedDataLocation, + DecodedDataLocation=results["RichDecodedData"], Detectors=det_opts, - ) - results["RichClusters"] = richClus.RichPixelClustersLocation - - # Make the SIMD pixel objects - simdPixels = RichSIMDPixels( - name="RichSIMDPixels_{hash}", - NoClustering=(not doClus[0] and not doClus[1]), - RichPixelClustersLocation=richClus.RichPixelClustersLocation, - ) - results["RichSIMDPixels"] = simdPixels.RichSIMDPixelSummariesLocation + # 4D reco + Enable4D=options["Enable4DReco"], + AvHitTime=options["averageHitTime"], + TimeWindow=options["pixelTimeWindow"], + ).RichPixelClustersLocation return results @configurable -def make_rich_tracks(track_name, input_tracks, options): +def make_rich_pixels(options, clusters=make_rich_clusters): """ - Return tracking specific RICH data from input tracks. + Return all pixel specific RICH data. Args: - track_name (str): The track name to assign to this configuration - input_tracks (dict): Input track objects to process. - n_ring_point (3-tuple of int): Number points for mass hypothesis ring ray tracing (Aero,R1Gas,R2Gas). - options (dict): The processing options to use + options (dict): The processing options to use + clusters : Cluster maker to use Returns: dict of useful data locations. @@ -331,14 +363,46 @@ def make_rich_tracks(track_name, input_tracks, options): # Configure the particle properties configure_rich_particle_properties(options) - # The conf map to return + # The conf dict to return results = {} - # save the name for this set of results - results["TrackName"] = track_name + # Clustering + results.update(clusters(options)) - # Keep tabs on the input tracks used for this conf. - results["InputTracks"] = input_tracks + # Make the SIMD pixel objects + doClus = options["ApplyPixelClustering"] + timeWin = options["photonTimeWindow"] + simdPixels = RichSIMDPixels( + name="RichSIMDPixels_{hash}", + NoClustering=(not doClus[0] and not doClus[1]), + RichPixelClustersLocation=results["RichClusters"], + # 4D reco + Enable4D=options["Enable4DReco"], + AvHitTime=options["averageHitTime"], + CourseTimeWindow=options["pixelTimeWindow"], + InnerFineTimeWindow=timeWin[0], + OuterFineTimeWindow=timeWin[1], + # Emulation support + OverrideRegions=options["pixOverrideRegions"], + InnerPixelsX=options["InnerRegionsX"], + InnerPixelsY=options["InnerRegionsY"], + ) + results["RichSIMDPixels"] = simdPixels.RichSIMDPixelSummariesLocation + + return results + + +@configurable +def make_rich_track_segments(track_name, input_tracks, options, results): + """ + Make RICH track radiator segments + + Args: + track_name (str): The track name to assign to this configuration + input_tracks (dict): Input track objects to process. + options (dict): The processing options to use + results (dict): The results dict to fill + """ # The detector options det_opts = get_detector_bool_opts(options, track_name) @@ -358,25 +422,55 @@ def make_rich_tracks(track_name, input_tracks, options): results["InitialTrackToSegments"] = segments.TrackToSegmentsLocation results["SegmentToTracks"] = segments.SegmentToTrackLocation + +@configurable +def make_rich_segment_panel_points(track_name, options, results): + """ + Determine RICH track panel points + + Args: + track_name (str): The track name to assign to this configuration + options (dict): The processing options to use + results (dict): The results dict to fill + """ + + # The detector options + det_opts = get_detector_bool_opts(options, track_name) + # Intersections of segments with PD panels tkGloPnts = TkPanelGlobalPoints( name="RichTrackGloPoints" + track_name + "_{hash}", Detectors=det_opts, - TrackSegmentsLocation=segments.TrackSegmentsLocation, + TrackSegmentsLocation=results["TrackSegments"], ) tkLocPnts = TkPanelLocalPoints( name="RichTrackLocPoints" + track_name + "_{hash}", TrackGlobalPointsLocation=tkGloPnts.TrackGlobalPointsLocation, - TrackSegmentsLocation=segments.TrackSegmentsLocation, + TrackSegmentsLocation=results["TrackSegments"], ) results["TrackGlobalPoints"] = tkGloPnts.TrackGlobalPointsLocation results["TrackLocalPoints"] = tkLocPnts.TrackLocalPointsLocation + +@configurable +def make_emitted_track_info(track_name, options, results): + """ + Build the track emitted level information + + Args: + track_name (str): The track name to assign to this configuration + options (dict): The processing options to use + results (dict): The results dict to fill + """ + + # The detector options + det_opts = get_detector_bool_opts(options, track_name) + # Emitted photon yields emitY = EmittedYields( name="RichEmittedYields" + track_name + "_{hash}", Detectors=det_opts, - TrackSegmentsLocation=segments.TrackSegmentsLocation, + TrackSegmentsLocation=results["TrackSegments"], ) results["EmittedYields"] = emitY.EmittedPhotonYieldLocation results["EmittedSpectra"] = emitY.EmittedPhotonSpectraLocation @@ -385,7 +479,7 @@ def make_rich_tracks(track_name, input_tracks, options): emitChAngles = EmittedCKAngles( name="RichEmittedCKAngles" + track_name + "_{hash}", Detectors=det_opts, - TrackSegmentsLocation=segments.TrackSegmentsLocation, + TrackSegmentsLocation=results["TrackSegments"], EmittedPhotonYieldLocation=emitY.EmittedPhotonYieldLocation, EmittedPhotonSpectraLocation=emitY.EmittedPhotonSpectraLocation, ) @@ -398,11 +492,25 @@ def make_rich_tracks(track_name, input_tracks, options): NRingPointsMin=options["NRayTracingRingPointsMin"], NRingPointsMax=options["NRayTracingRingPointsMax"], NewCKRingTol=options["RayTracingNewCKRingTol"], - TrackSegmentsLocation=segments.TrackSegmentsLocation, + TrackSegmentsLocation=results["TrackSegments"], CherenkovAnglesLocation=emitChAngles.EmittedCherenkovAnglesLocation, ) results["EmittedCKRings"] = emitMassCones.MassHypothesisRingsLocation + +@configurable +def make_detectable_track_info(track_name, options, results): + """ + Build the track detectable level information + + Args: + track_name (str): The track name to assign to this configuration + options (dict): The processing options to use + results (dict): The results dict to fill + """ + + # The detector options + det_opts = get_detector_bool_opts(options, track_name) # Detectable photon yields if options["DetectableYieldsPrecision"] == "Full": from PyConf.Algorithms import ( @@ -421,41 +529,62 @@ def make_rich_tracks(track_name, input_tracks, options): detY = DetectableYields( name="RichDetectableYields" + track_name + "_{hash}", Detectors=det_opts, - TrackSegmentsLocation=segments.TrackSegmentsLocation, - EmittedSpectraLocation=emitY.EmittedPhotonSpectraLocation, - MassHypothesisRingsLocation=emitMassCones.MassHypothesisRingsLocation, + TrackSegmentsLocation=results["TrackSegments"], + EmittedSpectraLocation=results["EmittedSpectra"], + MassHypothesisRingsLocation=results["EmittedCKRings"], ) results["DetectableYields"] = detY.DetectablePhotonYieldLocation results["DetectableSpectra"] = detY.DetectablePhotonSpectraLocation + +@configurable +def make_geom_eff_info(track_name, options, results): + """ + Build the track geometrical efficiency information + + Args: + track_name (str): The track name to assign to this configuration + options (dict): The processing options to use + results (dict): The results dict to fill + """ + + # The detector options + det_opts = get_detector_bool_opts(options, track_name) + # Geometrical Eff. geomEff = GeomEff( name="RichGeomEff" + track_name + "_{hash}", Detectors=det_opts, - TrackSegmentsLocation=segments.TrackSegmentsLocation, - CherenkovAnglesLocation=emitChAngles.EmittedCherenkovAnglesLocation, - MassHypothesisRingsLocation=emitMassCones.MassHypothesisRingsLocation, + TrackSegmentsLocation=results["TrackSegments"], + CherenkovAnglesLocation=results["EmittedCKAngles"], + MassHypothesisRingsLocation=results["EmittedCKRings"], ) results["GeomEffs"] = geomEff.GeomEffsLocation results["GeomEffsPerPD"] = geomEff.GeomEffsPerPDLocation results["SegmentPhotonFlags"] = geomEff.SegmentPhotonFlagsLocation - # Select final track segments - tkSel = SelectTrackSegments( - name="RichTkSegmentSel" + track_name + "_{hash}", - Detectors=det_opts, - InTrackToSegmentsLocation=segments.TrackToSegmentsLocation, - GeomEffsLocation=geomEff.GeomEffsLocation, - ) - results["SelectedTrackToSegments"] = tkSel.OutTrackToSegmentsLocation + +@configurable +def make_signal_track_info(track_name, options, results): + """ + Build the track signal level information + + Args: + track_name (str): The track name to assign to this configuration + options (dict): The processing options to use + results (dict): The results dict to fill + """ + + # The detector options + det_opts = get_detector_bool_opts(options, track_name) # Signal Photon Yields sigYields = SignalYields( name="RichSignalYields" + track_name + "_{hash}", Detectors=det_opts, - DetectablePhotonYieldLocation=detY.DetectablePhotonYieldLocation, - DetectablePhotonSpectraLocation=detY.DetectablePhotonSpectraLocation, - GeomEffsLocation=geomEff.GeomEffsLocation, + DetectablePhotonYieldLocation=results["DetectableYields"], + DetectablePhotonSpectraLocation=results["DetectableSpectra"], + GeomEffsLocation=results["GeomEffs"], ) results["SignalYields"] = sigYields.SignalPhotonYieldLocation results["SignalSpectra"] = sigYields.SignalPhotonSpectraLocation @@ -464,12 +593,27 @@ def make_rich_tracks(track_name, input_tracks, options): sigChAngles = SignalCherenkovAngles( name="RichSignalCKAngles" + track_name + "_{hash}", Detectors=det_opts, - TrackSegmentsLocation=segments.TrackSegmentsLocation, + TrackSegmentsLocation=results["TrackSegments"], SignalPhotonSpectraLocation=sigYields.SignalPhotonSpectraLocation, SignalPhotonYieldLocation=sigYields.SignalPhotonYieldLocation, ) results["SignalCKAngles"] = sigChAngles.SignalCherenkovAnglesLocation + +@configurable +def make_track_resolution_info(track_name, options, results): + """ + Build the track CK theta resolution information + + Args: + track_name (str): The track name to assign to this configuration + options (dict): The processing options to use + results (dict): The results dict to fill + """ + + # The detector options + det_opts = get_detector_bool_opts(options, track_name) + # Track Resolutions tktype = shortTrackType(track_name) if options["TkCKResTreatment"] == "Functional": @@ -477,9 +621,10 @@ def make_rich_tracks(track_name, input_tracks, options): name="RichCKResolutions" + track_name + "_{hash}", Detectors=det_opts, MaxCKThetaRes=defaultMaxCKThetaResolutions()[tktype], - TrackSegmentsLocation=segments.TrackSegmentsLocation, - SignalCherenkovAnglesLocation=sigChAngles.SignalCherenkovAnglesLocation, - MassHypothesisRingsLocation=emitMassCones.MassHypothesisRingsLocation, + TrackSegmentsLocation=results["TrackSegments"], + SignalCherenkovAnglesLocation=results["SignalCKAngles"], + MassHypothesisRingsLocation=results["EmittedCKRings"], + ScaleFactor=options["TkCKResScaleFactors"], ) elif options["TkCKResTreatment"] == "Parameterised": tkRes = ParamTrackCKRes( @@ -487,8 +632,9 @@ def make_rich_tracks(track_name, input_tracks, options): Detectors=det_opts, TrackType=track_name, MaxCKThetaRes=defaultMaxCKThetaResolutions()[tktype], - TrackSegmentsLocation=segments.TrackSegmentsLocation, - SignalCherenkovAnglesLocation=sigChAngles.SignalCherenkovAnglesLocation, + TrackSegmentsLocation=results["TrackSegments"], + SignalCherenkovAnglesLocation=results["SignalCKAngles"], + ScaleFactor=options["TkCKResScaleFactors"], ) else: raise ValueError( @@ -498,19 +644,34 @@ def make_rich_tracks(track_name, input_tracks, options): ) results["CherenkovResolutions"] = tkRes.CherenkovResolutionsLocation - return results - @configurable -def make_rich_photons(track_name, input_tracks, options, make_raw=default_raw_banks): +def make_rich_tracks( + track_name, + input_tracks, + options, + segments=make_rich_track_segments, + panel_points=make_rich_segment_panel_points, + emitted_info=make_emitted_track_info, + detectable_info=make_detectable_track_info, + geom_effs=make_geom_eff_info, + signal_info=make_signal_track_info, + track_resolutions=make_track_resolution_info, +): """ - Return reconstructed photon specific RICH data. + Return tracking specific RICH data from input tracks. Args: - track_name (str): The name to assign to this configuration - input_tracks (dict): The input tracks to process - options (dict): The processing options to use - make_raw : The entity that provides the RawBanks to use (??) + track_name (str): The track name to assign to this configuration + input_tracks (dict): Input track objects to process. + options (dict): The processing options to use + segments : Segment builder to use + panel_points : Builds for the track PD panel points + emitted_info : Builds the track emitted level information + detectable_info : Builds the track detectable level information + geom_effs : Builds the track geometrical efficiencies + signal_info : Builds the track signal level information + track_resolutions : Builds the track CK theta resolutions Returns: dict of useful data locations. @@ -519,21 +680,61 @@ def make_rich_photons(track_name, input_tracks, options, make_raw=default_raw_ba # Configure the particle properties configure_rich_particle_properties(options) - # The conf dict to return + # The conf map to return results = {} # save the name for this set of results results["TrackName"] = track_name - # pixel and track reco - pixel_conf = make_rich_pixels(make_raw=make_raw, options=options) - track_conf = make_rich_tracks( - track_name=track_name, input_tracks=input_tracks, options=options + # Keep tabs on the input tracks used for this conf. + results["InputTracks"] = input_tracks + + # The detector options + det_opts = get_detector_bool_opts(options, track_name) + + # Create radiator segments from input tracks + segments(track_name, input_tracks, options, results) + + # Determine PD panel points + panel_points(track_name, options, results) + + # Emitted info + emitted_info(track_name, options, results) + + # Detectable info + detectable_info(track_name, options, results) + + # Geom Effs + geom_effs(track_name, options, results) + + # Signal info + signal_info(track_name, options, results) + + # Track resolutions + track_resolutions(track_name, options, results) + + # Select final track segments + tkSel = SelectTrackSegments( + name="RichTkSegmentSel" + track_name + "_{hash}", + Detectors=det_opts, + InTrackToSegmentsLocation=results["InitialTrackToSegments"], + GeomEffsLocation=results["GeomEffs"], ) + results["SelectedTrackToSegments"] = tkSel.OutTrackToSegmentsLocation - # include in returned configuration - results.update(pixel_conf) - results.update(track_conf) + return results + + +@configurable +def make_photon_candidates(track_name, options, results): + """ + Build the CK photon candidates + + Args: + track_name (str): The track name to assign to this configuration + options (dict): The processing options to use + results (dict): The results dict to fill + """ # The detector options det_opts = get_detector_bool_opts(options, track_name) @@ -555,41 +756,147 @@ def make_rich_photons(track_name, input_tracks, options, make_raw=default_raw_ba MinAllowedCherenkovTheta=minMaxCKTheta["Min"], MaxAllowedCherenkovTheta=minMaxCKTheta["Max"], TruncateCKAngles=options["TruncateCKAngles"], - TrackSegmentsLocation=track_conf["TrackSegments"], - CherenkovAnglesLocation=track_conf["SignalCKAngles"], - CherenkovResolutionsLocation=track_conf["CherenkovResolutions"], - TrackLocalPointsLocation=track_conf["TrackLocalPoints"], - TrackToSegmentsLocation=track_conf["SelectedTrackToSegments"], - SegmentPhotonFlagsLocation=track_conf["SegmentPhotonFlags"], - RichSIMDPixelSummariesLocation=pixel_conf["RichSIMDPixels"], + TrackSegmentsLocation=results["TrackSegments"], + CherenkovAnglesLocation=results["SignalCKAngles"], + CherenkovResolutionsLocation=results["CherenkovResolutions"], + TrackLocalPointsLocation=results["TrackLocalPoints"], + TrackToSegmentsLocation=results["SelectedTrackToSegments"], + SegmentPhotonFlagsLocation=results["SegmentPhotonFlags"], + RichSIMDPixelSummariesLocation=results["RichSIMDPixels"], RejectAmbiguousPhotons=options["RejectAmbiguousPhotons"], SaveMirrorData=options["SaveMirrorData"], + # 4D reco + Enable4D=options["Enable4DReco"], ) results["CherenkovPhotons"] = photReco.CherenkovPhotonLocation results["PhotonToParents"] = photReco.PhotonToParentsLocation results["PhotonMirrorData"] = photReco.PhotonMirrorDataLocation + +@configurable +def make_photon_signals(track_name, options, results): + """ + Build the CK photon signal information + + Args: + track_name (str): The track name to assign to this configuration + options (dict): The processing options to use + results (dict): The results dict to fill + """ + + # The detector options + det_opts = get_detector_bool_opts(options, track_name) + # Predicted pixel signals + photSel = options["PhotonSelection"] photPredSig = PhotonPredSignal( name="RichPredPixelSignal" + track_name + "_{hash}", Detectors=det_opts, MinPhotonProbability=options["MinPhotonProbabilityCuts"][photSel], - RichSIMDPixelSummariesLocation=pixel_conf["RichSIMDPixels"], - TrackSegmentsLocation=track_conf["TrackSegments"], - CherenkovPhotonLocation=photReco.CherenkovPhotonLocation, - PhotonToParentsLocation=photReco.PhotonToParentsLocation, - CherenkovAnglesLocation=track_conf["SignalCKAngles"], - CherenkovResolutionsLocation=track_conf["CherenkovResolutions"], - PhotonYieldLocation=track_conf["DetectableYields"], + RichSIMDPixelSummariesLocation=results["RichSIMDPixels"], + TrackSegmentsLocation=results["TrackSegments"], + CherenkovPhotonLocation=results["CherenkovPhotons"], + PhotonToParentsLocation=results["PhotonToParents"], + CherenkovAnglesLocation=results["SignalCKAngles"], + CherenkovResolutionsLocation=results["CherenkovResolutions"], + PhotonYieldLocation=results["DetectableYields"], + # 4D reco + Enable4D=options["Enable4DReco"], ) results["PhotonSignals"] = photPredSig.PhotonSignalsLocation + +@configurable +def make_rich_photons( + track_name, + input_tracks, + options, + photon_candidates=make_photon_candidates, + photon_signals=make_photon_signals, + pixels=make_rich_pixels, + tracks=make_rich_tracks, +): + """ + Return RICH reconstructed photon candidates + + Args: + track_name (str): The name to assign to this configuration + input_tracks (dict): The input tracks to process + options (dict): The processing options to use + photon_candidates : Photon candidate builder + photon_signals : Builds the photon signal information + + Returns: + dict of useful data locations. + """ + + # Configure the particle properties + configure_rich_particle_properties(options) + + # The conf dict to return + results = {} + + # save the name for this set of results + results["TrackName"] = track_name + + # Configure track and pixel level reco + results.update(pixels(options=options)) + results.update( + tracks(track_name=track_name, input_tracks=input_tracks, options=options) + ) + + # Ensure original tracks are in results dict + v1tkkey = "OriginalV1Tracks" + if v1tkkey in options.keys() and v1tkkey not in results.keys(): + results[v1tkkey] = options[v1tkkey] + + # Make photon candidates + photon_candidates(track_name, options, results) + + # Photon signals + photon_signals(track_name, options, results) + return results +@configurable +def make_reco_summaries(track_name, options, results): + """ + Build the reconstruction summary objects + + Args: + track_name (str): The track name to assign to this configuration + options (dict): The processing options to use + results (dict): The results dict to fill + """ + + # The detector options + det_opts = get_detector_bool_opts(options, track_name) + + # Create working event summary of reco info + recSum = RecSummary( + name="RichRecSummary" + track_name + "_{hash}", + Detectors=det_opts, + TrackSegmentsLocation=results["TrackSegments"], + TrackToSegmentsLocation=results["SelectedTrackToSegments"], + PhotonToParentsLocation=results["PhotonToParents"], + DetectablePhotonYieldLocation=results["DetectableYields"], + SignalPhotonYieldLocation=results["SignalYields"], + PhotonSignalsLocation=results["PhotonSignals"], + RichSIMDPixelSummariesLocation=results["RichSIMDPixels"], + ) + results["SummaryTracks"] = recSum.SummaryTracksLocation + results["Summarypixels"] = recSum.SummaryPixelsLocation + + @configurable def make_rich_pids( - track_name, input_tracks, options, make_raw=default_raw_banks, location=None + track_name, + input_tracks, + options, + location=None, + reco_summaries=make_reco_summaries, + photons=make_rich_photons, ): """ Return RICH PID data. @@ -598,7 +905,6 @@ def make_rich_pids( track_name (str): The name to assign to this configuration input_tracks (dict): The input tracks to process options (dict): The processing options to use - make_raw : The entity that provides the RawBanks to use (??) Returns: dict of useful data locations. @@ -617,40 +923,20 @@ def make_rich_pids( # save the name for this set of results results["TrackName"] = track_name - # pixel and track reco - pixel_conf = make_rich_pixels(make_raw=make_raw, options=options) - track_conf = make_rich_tracks( - track_name=track_name, input_tracks=input_tracks, options=options - ) - photon_conf = make_rich_photons( - track_name=track_name, - input_tracks=input_tracks, - make_raw=make_raw, - options=options, + # photon reco + results.update( + photons( + track_name=track_name, + input_tracks=input_tracks, + options=options, + ) ) - # include in returned configuration - results.update(pixel_conf) - results.update(track_conf) - results.update(photon_conf) - # The detector options det_opts = get_detector_bool_opts(options, track_name) - # Create working event summary of reco info - recSum = RecSummary( - name="RichRecSummary" + track_name + "_{hash}", - Detectors=det_opts, - TrackSegmentsLocation=track_conf["TrackSegments"], - TrackToSegmentsLocation=track_conf["SelectedTrackToSegments"], - PhotonToParentsLocation=photon_conf["PhotonToParents"], - DetectablePhotonYieldLocation=track_conf["DetectableYields"], - SignalPhotonYieldLocation=track_conf["SignalYields"], - PhotonSignalsLocation=photon_conf["PhotonSignals"], - RichSIMDPixelSummariesLocation=pixel_conf["RichSIMDPixels"], - ) - results["SummaryTracks"] = recSum.SummaryTracksLocation - results["Summarypixels"] = recSum.SummaryPixelsLocation + # Reco summary objects + reco_summaries(track_name, options, results) # ================================================================== # RICH Global PID @@ -660,7 +946,7 @@ def make_rich_pids( gpidInit = GPIDInit( name="RichGPIDInit" + track_name + "_{hash}", Detectors=det_opts, - SummaryTracksLocation=recSum.SummaryTracksLocation, + SummaryTracksLocation=results["SummaryTracks"], ) # Cache PID and DLL locations by iteration @@ -686,11 +972,13 @@ def make_rich_pids( ThresholdBackground=options["PDBackThresholds"][it], # Input data TrackPIDHyposLocation=PIDs[it], - TrackToSegmentsLocation=track_conf["SelectedTrackToSegments"], - TrackSegmentsLocation=track_conf["TrackSegments"], - GeomEffsPerPDLocation=track_conf["GeomEffsPerPD"], - DetectablePhotonYieldLocation=track_conf["DetectableYields"], - RichSIMDPixelSummariesLocation=pixel_conf["RichSIMDPixels"], + TrackToSegmentsLocation=results["SelectedTrackToSegments"], + TrackSegmentsLocation=results["TrackSegments"], + GeomEffsPerPDLocation=results["GeomEffsPerPD"], + DetectablePhotonYieldLocation=results["DetectableYields"], + RichSIMDPixelSummariesLocation=results["RichSIMDPixels"], + # 4D Reco + Enable4D=options["Enable4DReco"], ) # Likelihood minimiser @@ -705,13 +993,13 @@ def make_rich_pids( MaxTrackChangesPerIt=options["MaxTrackChangesPerIt"][it], MinSignalForNoLLCalc=options["MinSignalForNoLLCalc"][it], # Input data - SummaryTracksLocation=recSum.SummaryTracksLocation, - SummaryPixelsLocation=recSum.SummaryPixelsLocation, + SummaryTracksLocation=results["SummaryTracks"], + SummaryPixelsLocation=results["Summarypixels"], PixelBackgroundsLocation=pixBkgs.PixelBackgroundsLocation, TrackDLLsInputLocation=DLLs[it], TrackPIDHyposInputLocation=PIDs[it], - PhotonToParentsLocation=photon_conf["PhotonToParents"], - PhotonSignalsLocation=photon_conf["PhotonSignals"], + PhotonToParentsLocation=results["PhotonToParents"], + PhotonSignalsLocation=results["PhotonSignals"], ) # Save the outputs for the next it @@ -728,8 +1016,8 @@ def make_rich_pids( Detectors=det_opts, PIDVersion=options["PIDVersion"], # Inputs - TracksLocation=track_conf["InputTracks"], - SummaryTracksLocation=recSum.SummaryTracksLocation, + TracksLocation=results["InputTracks"], + SummaryTracksLocation=results["SummaryTracks"], TrackPIDHyposInputLocation=PIDs[options["nLikelihoodIterations"]], TrackDLLsInputLocation=DLLs[options["nLikelihoodIterations"]], outputs={"RichPIDsLocation": location}, diff --git a/Hlt/RecoConf/python/RecoConf/rich_reconstruction_mc_cheat.py b/Hlt/RecoConf/python/RecoConf/rich_reconstruction_mc_cheat.py new file mode 100644 index 0000000000000000000000000000000000000000..a4b034487798c512e741d4fd42f291e27b19960a --- /dev/null +++ b/Hlt/RecoConf/python/RecoConf/rich_reconstruction_mc_cheat.py @@ -0,0 +1,267 @@ +############################################################################### +# (c) Copyright 2019-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 PyConf import configurable +from PyConf.Algorithms import ( + Rich__Future__MC__DecodedDataAddMCInfo as DecodedDataAddMCInfo, +) +from PyConf.Algorithms import ( + Rich__Future__MC__DecodedDataFromMCRichHits as RichMCDecoder, +) +from PyConf.Algorithms import ( + Rich__Future__Rec__MC__RichPhotonUseMCInfo as PhotonUseMCInfo, +) +from PyConf.Algorithms import ( + Rich__Future__Rec__MC__RichPixelUseMCInfo as PixUseMCInfo, +) +from PyConf.Algorithms import ( + Rich__Future__Rec__MC__RichSegmentAddTimeFromMC as AddSegTimeMC, +) +from PyConf.Algorithms import ( + Rich__Future__Rec__MC__TrSegMakerFromMCRichTracks as MCSegmentCreator, +) +from PyConf.components import force_location +from PyConf.reading import get_mc_track_info + +from RecoConf.data_from_file import mc_unpacker +from RecoConf.mc_checking import make_default_IdealStateCreator +from RecoConf.rich_mc_checking import make_track_links, track_to_MCP_rels +from RecoConf.rich_reconstruction import ( + decode_rich_raw, + get_detector_bool_opts, + make_photon_candidates, + make_rich_clusters, + make_rich_pixels, + make_rich_track_segments, +) + + +@configurable +def default_mc_reco_cheat_options(): + """ + Returns a dict of the default RICH 'MC cheated' reconstruction options + """ + + opts = { + "EmulateRawFromMC": False, + "RejectAllPixelBackgrounds": False, + "RejectScintillation": False, + "EmulateTracksFromMC": False, + "IncludeTimeInfo": True, + "AllowMultipleHits": True, + "MaxHitsPerChannel": (99999, 99999), + "IsDetDescMC": True, + "ReadoutEfficiency": (0.95, 0.95), + "InnerPixelQuantization": (2.8, 2.8), + "OuterPixelQuantization": (5.6, 5.6), + "InnerPixelEff": (1.0, 1.0), + "OuterPixelEff": (1.0, 1.0), + "UseMCPhotonCKThetaValues": False, + "photEmulatedResMC": ( + # Inner (R1,R2) + ( + "(0.00078+0.0012*(std::tanh(-x/5000.0)+1.0))", + "(0.00065+0.0011*(std::tanh(-x/5000.0)+1.0))", + ), + # Outer (R1,R2) + ( + "(0.00078+0.0012*(std::tanh(-x/5000.0)+1.0))", + "(0.00065+0.0011*(std::tanh(-x/5000.0)+1.0))", + ), + ), + } + + return opts + + +@configurable +def emulated_raw_from_MC(options): + """ + Emulates RawBank decoding from MC Hits + """ + + # The conf dict to return + results = {} + + results["RichDecodedData"] = RichMCDecoder( + name="RichDecodeFromMC_{hash}", + IsDetDescMC=options["IsDetDescMC"], + RejectAllBackgrounds=options["RejectAllPixelBackgrounds"], + RejectScintillation=options["RejectScintillation"], + IncludeTimeInfo=options["IncludeTimeInfo"], + AllowMultipleHits=options["AllowMultipleHits"], + MaxHitsPerChannel=options["MaxHitsPerChannel"], + ReadoutEfficiency=options["ReadoutEfficiency"], + MCRichHitsLocation=mc_unpacker("MCRichHits"), + PrevMCRichHitsLocation=mc_unpacker("Prev/MCRichHits"), + PrevPrevMCRichHitsLocation=mc_unpacker("PrevPrev/MCRichHits"), + NextMCRichHitsLocation=mc_unpacker("Next/MCRichHits"), + NextNextMCRichHitsLocation=mc_unpacker("NextNext/MCRichHits"), + LHCBackgroundMCRichHitsLocation=mc_unpacker("LHCBackground/MCRichHits"), + ).DecodedDataLocation + + return results + + +@configurable +def decoded_data_add_MC_time(options): + """ + Adds time information from MC to existing decoded data + """ + + # First run regular decoding + results = decode_rich_raw(options) + + # Add time info + addTime = DecodedDataAddMCInfo( + name="RichDecodedDataAddMCInfo_{hash}", + InDecodedDataLocation=results["RichDecodedData"], + MCRichHitsLocation=mc_unpacker("MCRichHits"), + PrevMCRichHitsLocation=mc_unpacker("Prev/MCRichHits"), + PrevPrevMCRichHitsLocation=mc_unpacker("PrevPrev/MCRichHits"), + NextMCRichHitsLocation=mc_unpacker("Next/MCRichHits"), + NextNextMCRichHitsLocation=mc_unpacker("NextNext/MCRichHits"), + LHCBackgroundMCRichHitsLocation=mc_unpacker("LHCBackground/MCRichHits"), + ) + + # Set output to be main decoded data + results["RichDecodedData"] = addTime.OutDecodedDataLocation + + # finally return the results dict + return results + + +@configurable +def make_rich_track_segments_from_MC(track_name, input_tracks, options, results): + """ + Make track segments from MC + """ + + # The detector options + det_opts = get_detector_bool_opts(options, track_name) + + # Create radiator segments from input tracks + segments = MCSegmentCreator( + name="RichTrackSegments" + track_name + "_{hash}", + MCParticlesLocation=mc_unpacker("MCParticles"), + MCVerticesLocation=mc_unpacker("MCVertices"), + MCRichHitsLocation=mc_unpacker("MCRichHits"), + MCRichSegmentsLocation=mc_unpacker("MCRichSegments"), + MCRichTracksLocation=mc_unpacker("MCRichTracks"), + MCRichOpticalPhotonsLocation=mc_unpacker("MCRichOpticalPhotons"), + MCPropertyLocation=get_mc_track_info(), + IdealStateCreator=make_default_IdealStateCreator(), + MinP=options["MinP"], + MaxP=options["MaxP"], + MinPt=options["MinPt"], + Detectors=det_opts, + outputs={ + "OutputTracksLocation": force_location(str(input_tracks)), + "MCParticlesLinkLocation": force_location("Link/" + str(input_tracks)), + "TrackSegmentsLocation": None, + "TrackToSegmentsLocation": None, + "SegmentToTrackLocation": None, + }, + ) + results["TrackSegments"] = segments.TrackSegmentsLocation + results["InitialTrackToSegments"] = segments.TrackToSegmentsLocation + results["SegmentToTracks"] = segments.SegmentToTrackLocation + results["InputTracks"] = segments.OutputTracksLocation + results["OriginalV1Tracks"] = segments.OutputTracksLocation + results["RichTrackMCPartLinks"] = segments.MCParticlesLinkLocation + + +@configurable +def make_rich_track_segments_add_MC_time(track_name, input_tracks, options, results): + """ + Add timestamps to existing (reco) track segments using MC + """ + + # First make regular reco segments + make_rich_track_segments(track_name, input_tracks, options, results) + + # Add MC time + addSegTime = AddSegTimeMC( + name="RichAddSegTimeFromMC" + track_name + "_{hash}", + InTrackSegmentsLocation=results["TrackSegments"], + TracksLocation=input_tracks, + SegmentToTrackLocation=results["SegmentToTracks"], + MCParticlesLinkLocation=make_track_links(input_tracks, options["UseUT"]), + MCParticlesLocation=mc_unpacker("MCParticles"), + ) + + # set new segments as main ones to use + results["TrackSegments"] = addSegTime.OutTrackSegmentsLocation + + +@configurable +def make_rich_pixels_with_mc_positions(options, clusters=make_rich_clusters): + """ + Make SIMD pixels with MC cheated positions + """ + + # First make regular SIMD pixels + results = make_rich_pixels(options, clusters) + + # Then cheat positions + mcPos = PixUseMCInfo( + name="RichSIMDPixelsUseMCInfo_{hash}", + UseMCPosition=True, + UseMCTime=False, + InnerPixelQuantization=options["InnerPixelQuantization"], + OuterPixelQuantization=options["OuterPixelQuantization"], + InnerPixelEff=options["InnerPixelEff"], + OuterPixelEff=options["OuterPixelEff"], + InRichSIMDPixelSummariesLocation=results["RichSIMDPixels"], + MCRichHitsLocation=mc_unpacker("MCRichHits"), + PrevMCRichHitsLocation=mc_unpacker("Prev/MCRichHits"), + PrevPrevMCRichHitsLocation=mc_unpacker("PrevPrev/MCRichHits"), + NextMCRichHitsLocation=mc_unpacker("Next/MCRichHits"), + NextNextMCRichHitsLocation=mc_unpacker("NextNext/MCRichHits"), + LHCBackgroundMCRichHitsLocation=mc_unpacker("LHCBackground/MCRichHits"), + ) + + # redirect downstream algorithms to updated SIMD pixels + results["RichSIMDPixels"] = mcPos.OutRichSIMDPixelSummariesLocation + + # finally return the results dict + return results + + +@configurable +def make_photon_candidates_with_mc_info(track_name, options, results): + """ + Return RICH reconstructed photon candidates with some MC cheating + """ + + # first make regular photons + make_photon_candidates(track_name, options, results) + + # Then update with MC + mcPhotInfo = PhotonUseMCInfo( + name="RichPhotonUseMCInfo_{hash}", + InCherenkovPhotonLocation=results["CherenkovPhotons"], + TrackToMCParticlesRelations=track_to_MCP_rels(results), + TracksLocation=results["InputTracks"], + SegmentToTrackLocation=results["SegmentToTracks"], + PhotonToParentsLocation=results["PhotonToParents"], + SignalCherenkovAnglesLocation=results["SignalCKAngles"], + RichSIMDPixelSummariesLocation=results["RichSIMDPixels"], + RichDigitSummariesLocation=mc_unpacker("MCRichDigitSummaries"), + MCRichHitsLocation=mc_unpacker("MCRichHits"), + MCRichOpticalPhotonsLocation=mc_unpacker("MCRichOpticalPhotons"), + CKThetaSmearFuncInner=options["photEmulatedResMC"][0], + CKThetaSmearFuncOuter=options["photEmulatedResMC"][1], + UseMCPhotonCKThetaValues=options["UseMCPhotonCKThetaValues"], + ) + + # Finally update the photon data handle to use downstream + results["CherenkovPhotons"] = mcPhotInfo.OutCherenkovPhotonLocation