diff --git a/PWGCF/Femto/Core/collisionTagger.h b/PWGCF/Femto/Core/collisionTagger.h new file mode 100644 index 00000000000..9d2bf28a0c5 --- /dev/null +++ b/PWGCF/Femto/Core/collisionTagger.h @@ -0,0 +1,1016 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license 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. + +/// \file collisionTagger.h +/// \brief Shared machinery for the self-configuring collision tagger (TableProducer/femtoProducerCollisionTagger.cxx) +/// and for pair/triplet tasks that want to read back their own assigned tag bit(s). +/// +/// The tagger walks the running workflow's device list once, and for every matching pair/triplet +/// task instance it finds, assigns the next free bit(s) of FColTags::collisionTag to that +/// instance's particle role(s), in discovery order. A consuming task cannot read the tagger's +/// bit assignment back out of it directly: RunningWorkflowInfo only exposes each device's +/// CLI/JSON-resolved option values, a static snapshot taken once at workflow construction -- it is +/// never updated by what another device's init() computes at runtime, and there is no ordering +/// guarantee between different devices' init() calls regardless. So instead, discoverTaggedRoles() +/// is the single canonical implementation of the bit-assignment walk, included and called +/// identically by the tagger (to assign bits and fill FColTags) and by every consuming task (to +/// recompute, from the same static device list, which bit(s) ended up belonging to its own device +/// name). Keeping this walk in one place is what keeps the two sides from silently drifting apart. +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#ifndef PWGCF_FEMTO_CORE_COLLISIONTAGGER_H_ +#define PWGCF_FEMTO_CORE_COLLISIONTAGGER_H_ + +#include "PWGCF/Femto/Core/dataTypes.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace o2::analysis::femto::collisiontagger +{ + +constexpr int MaxTagBits = 8 * sizeof(datatypes::CollisionTagType); + +// Supporting an arbitrary number of same-type "subwagons" (same task, different cuts, all running +// in one train) rules out reusing Partition for the cut check itself: it is a compile-time +// construct, permanently bound to one specific Configurable object's fields. So instead, each +// matching sibling device's configured cut VALUES are copied into a plain value struct (no +// Configurable, no Partition), one push_back per discovered subwagon, and checked by hand against +// each particle -- the same approach femtoDreamCollisionMasker.cxx uses for its own multi-variant +// support. The check functions below are a direct, faithful port of the corresponding +// MAKE_*_PARTITION macro in Core/partitions.h, just expressed as a runtime check instead of a +// compiled Gandiva expression. +struct TrackSelectionValues { + int chargeAbs = 1; + int chargeSign = 1; + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + datatypes::TrackMaskType maskLowMomentum = 0; + datatypes::TrackMaskType maskHighMomentum = 0; + datatypes::TrackMaskType rejectionMaskLowMomentum = 0; + datatypes::TrackMaskType rejectionMaskHighMomentum = 0; + float pidThres = 1e6f; +}; + +// Despite the name, this shape (sign + pt + eta + phi + mass window + flat mask) is NOT +// V0-specific: MAKE_CASCADE_PARTITION, MAKE_SIGMA_PARTITION, MAKE_SIGMAPLUS_PARTITION, +// MAKE_D0_PARTITION, and MAKE_CHARM3PRONG_PARTITION in Core/partitions.h are all byte-for-byte +// identical to MAKE_LAMBDA_PARTITION, and CascadeMaskType/KinkMaskType/CharmHadronMaskType are +// all, like V0MaskType, uint32_t -- so this one struct (and lambdaPasses()/readV0Selection() +// below) is reused verbatim for Xi/Omega (cascade), Sigma/SigmaPlus (kink), and D0/Lc +// (charm hadron) selections too, not just Lambda. Only SigmaPlusPcm needs its own struct +// (KinkPcmSelectionValues below), for the one extra mlScoreMin field it has beyond this shape. +struct V0SelectionValues { + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::V0MaskType mask = 0; + int sign = 0; // only used for lambdas; k0shorts carry a dummy value for compatibility +}; + +// one bit = one pair type for one task instance: both sides' selections, read together, since the +// bit means "both are present", not "particle 1 is present" or "particle 2 is present" separately +struct TrackTrackVariant { + TrackSelectionValues sel1; + TrackSelectionValues sel2; + bool sameSpecies = false; + int bit = 0; +}; + +// sel1/sel2 (not trackSel/v0Sel) for the same reason TrackTrackVariant above uses sel1/sel2: so +// tagCrossVariants() below can be written once, generically, for any cross-type pair -- naming +// the fields after what they ARE (track selection, v0 selection) would tie the generic helper's +// code to this one particle-type combination. +struct TrackV0Variant { + TrackSelectionValues sel1; + V0SelectionValues sel2; + int bit = 0; +}; + +// SigmaPlusPcm's selection is V0SelectionValues plus one extra field (mlScoreMin), so it needs +// its own value struct, check function (kinkPcmPasses, below readV0Selection), and variant type +// -- everything else about it (sign/pt/eta/phi/mass/mask) is identical to V0SelectionValues. +struct KinkPcmSelectionValues { + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::KinkMaskType mask = 0; + int sign = 0; + float mlScoreMin = -1.f; +}; + +struct TrackKinkPcmVariant { + TrackSelectionValues sel1; + KinkPcmSelectionValues sel2; + int bit = 0; +}; + +// Same-type pair where BOTH legs use the V0SelectionValues shape: covers v0-v0 (lambda-lambda, +// k0short-k0short, lambda-k0short) and d0-d0 (same shape, see V0SelectionValues above), checked +// with tagPairVariants() when sel1/sel2 are the same species (sameSpecies-aware) or tagCrossVariants() +// when they are not (e.g. lambda-k0short: sameSpecies is meaningless there and left false/unused). +struct V0V0Variant { + V0SelectionValues sel1; + V0SelectionValues sel2; + bool sameSpecies = false; + int bit = 0; +}; + +// Phi -> K+K-, Rho0 -> pi+pi-: both daughters are the same species, so MAKE_RESONANCE_0_PARTITON +// (Core/partitions.h) only needs one pair of momentum-threshold-gated daughter masks, applied to +// whichever of the two stored columns (maskPosDau/maskNegDau) belongs to the daughter that was +// above/below threshold at build time (posDauHasHighMomentum/negDauHasHighMomentum, read straight +// off the candidate -- these switches are baked in at production, not re-derived here). +struct TwoTrackResonanceSelectionValues { + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::TrackMaskType dauMaskBelowThres = 0; + datatypes::TrackMaskType dauMaskAboveThres = 0; +}; + +// Kstar0 -> K+pi-, Kstar0bar -> K-pi+: the physical pos/neg daughter's species flips with the +// candidate's charge sign (see MAKE_RESONANCE_1_PARTITON in Core/partitions.h), so the daughter +// masks are named by species (kaon/pion) rather than by charge, and sign picks which hypothesis a +// given candidate's signedPt must match. +struct Kstar0SelectionValues { + int sign = 0; + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::TrackMaskType kaonDauMaskBelowThres = 0; + datatypes::TrackMaskType kaonDauMaskAboveThres = 0; + datatypes::TrackMaskType pionDauMaskBelowThres = 0; + datatypes::TrackMaskType pionDauMaskAboveThres = 0; +}; + +// Pi0/Eta -> gamma gamma: photon daughters have no momentum-dependent PID switch and no pos/neg +// distinction, so (unlike TwoTrackResonanceSelectionValues above) there is only one flat mask, +// already the AND of both legs' own masks at production time (see femtotwophotonresonances::MaskDau +// in FemtoTables.h) -- same shape as V0SelectionValues/k0shortPasses, just plain pt (no sign) and a +// PhotonMaskType mask instead of V0MaskType. +struct TwoPhotonResonanceSelectionValues { + float ptMin = 0.f; + float ptMax = 1e6f; + float etaMin = -1e6f; + float etaMax = 1e6f; + float phiMin = 0.f; + float phiMax = 1.f * o2::constants::math::TwoPI; + float massMin = 0.f; + float massMax = 1e6f; + datatypes::PhotonMaskType mask = 0; +}; + +// Cross-type pair variants for the three resonance-pairing tasks: track-twoTrackResonance (Phi/Rho0 +// share one shape, Kstar0 needs its own because of the kaon/pion mask swap), track-twoPhotonResonance +// (Pi0/Eta), and v0-twoTrackResonance (lambda/k0short x phi/rho0/kstar0 -- same split as track-side). +struct TrackTwoTrackResonanceVariant { + TrackSelectionValues sel1; + TwoTrackResonanceSelectionValues sel2; + int bit = 0; +}; + +struct TrackKstar0Variant { + TrackSelectionValues sel1; + Kstar0SelectionValues sel2; + int bit = 0; +}; + +struct TrackTwoPhotonResonanceVariant { + TrackSelectionValues sel1; + TwoPhotonResonanceSelectionValues sel2; + int bit = 0; +}; + +struct V0TwoTrackResonanceVariant { + V0SelectionValues sel1; + TwoTrackResonanceSelectionValues sel2; + int bit = 0; +}; + +struct V0Kstar0Variant { + V0SelectionValues sel1; + Kstar0SelectionValues sel2; + int bit = 0; +}; + +// Triplet variants, mirroring the "123 same, 12 same, or all different" pattern +// TripletTrackTrackTrackBuilder/TripletTrackTrackV0Builder/TripletTrackTrackCascadeBuilder use +// (Core/tripletBuilder.h, confMixing.particle123AreSameSpecies / particle12AreSameSpecies -- only +// these two combinations are supported, never e.g. "1&3 same"). Same-type (track-track-track): +// all three roles share one table, so same123 needs 3 distinct entries passing sel1 (sel2/sel3 +// ignored, mirrored onto sel1 at discovery time -- same trick as TrackTrackVariant's sameSpecies +// above), same12 needs 2 distinct passing sel1 plus 1 more passing sel3 (sel2 ignored). +struct TrackTrackTrackVariant { + TrackSelectionValues sel1; + TrackSelectionValues sel2; + TrackSelectionValues sel3; + bool same123 = false; + bool same12 = false; + int bit = 0; +}; + +// Cross-type (track-track-v0, track-track-cascade): roles 1&2 share the track table, role 3 is a +// different table (v0 or cascade, hence V0SelectionValues shape) -- only same12 is possible here +// (no same123 since role 3 is never the same type as roles 1&2). +struct TrackTrackV0Variant { + TrackSelectionValues sel1; + TrackSelectionValues sel2; + V0SelectionValues sel3; + bool same12 = false; + int bit = 0; +}; + +inline TrackSelectionValues readTrackSelection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + TrackSelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".chargeAbs") { + v.chargeAbs = option.defaultValue.get(); + } else if (option.name == prefix + ".chargeSign") { + v.chargeSign = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".maskLowMomentum") { + v.maskLowMomentum = option.defaultValue.get(); + } else if (option.name == prefix + ".maskHighMomentum") { + v.maskHighMomentum = option.defaultValue.get(); + } else if (option.name == prefix + ".rejectionMaskLowMomentum") { + v.rejectionMaskLowMomentum = option.defaultValue.get(); + } else if (option.name == prefix + ".rejectionMaskHighMomentum") { + v.rejectionMaskHighMomentum = option.defaultValue.get(); + } else if (option.name == prefix + ".pidThres") { + v.pidThres = option.defaultValue.get(); + } + } + return v; +} + +inline V0SelectionValues readV0Selection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + V0SelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".mask") { + v.mask = option.defaultValue.get(); + } else if (option.name == prefix + ".sign") { + v.sign = option.defaultValue.get(); + } + } + return v; +} + +inline KinkPcmSelectionValues readKinkPcmSelection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + KinkPcmSelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".mask") { + v.mask = option.defaultValue.get(); + } else if (option.name == prefix + ".sign") { + v.sign = option.defaultValue.get(); + } else if (option.name == prefix + ".mlScoreMin") { + v.mlScoreMin = option.defaultValue.get(); + } + } + return v; +} + +inline TwoTrackResonanceSelectionValues readTwoTrackResonanceSelection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + TwoTrackResonanceSelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".dauMaskBelowThres") { + v.dauMaskBelowThres = option.defaultValue.get(); + } else if (option.name == prefix + ".dauMaskAboveThres") { + v.dauMaskAboveThres = option.defaultValue.get(); + } + } + return v; +} + +inline Kstar0SelectionValues readKstar0Selection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + Kstar0SelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".sign") { + v.sign = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".kaonDauMaskBelowThres") { + v.kaonDauMaskBelowThres = option.defaultValue.get(); + } else if (option.name == prefix + ".kaonDauMaskAboveThres") { + v.kaonDauMaskAboveThres = option.defaultValue.get(); + } else if (option.name == prefix + ".pionDauMaskBelowThres") { + v.pionDauMaskBelowThres = option.defaultValue.get(); + } else if (option.name == prefix + ".pionDauMaskAboveThres") { + v.pionDauMaskAboveThres = option.defaultValue.get(); + } + } + return v; +} + +inline TwoPhotonResonanceSelectionValues readTwoPhotonResonanceSelection(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + TwoPhotonResonanceSelectionValues v; + for (auto const& option : device.options) { + if (option.name == prefix + ".ptMin") { + v.ptMin = option.defaultValue.get(); + } else if (option.name == prefix + ".ptMax") { + v.ptMax = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMin") { + v.etaMin = option.defaultValue.get(); + } else if (option.name == prefix + ".etaMax") { + v.etaMax = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMin") { + v.phiMin = option.defaultValue.get(); + } else if (option.name == prefix + ".phiMax") { + v.phiMax = option.defaultValue.get(); + } else if (option.name == prefix + ".massMin") { + v.massMin = option.defaultValue.get(); + } else if (option.name == prefix + ".massMax") { + v.massMax = option.defaultValue.get(); + } else if (option.name == prefix + ".dauMask") { + v.mask = option.defaultValue.get(); + } + } + return v; +} + +// reads the current value of a plain (non-group-prefixed) boolean option on a sibling device, +// e.g. one of its doprocessXXX-backed PROCESS_SWITCH flags +inline bool hasEnabledOption(o2::framework::DeviceSpec const& device, std::string const& name) +{ + for (auto const& option : device.options) { + if (option.name == name) { + return option.defaultValue.get(); + } + } + return false; +} + +// true if ANY enabled option on this device has a name starting with `prefix` -- used instead of +// hasEnabledOption() where a pair/triplet task has several *WithTagging* process functions that +// all consume the same tag bit (processMixedEventWithTagging, processMixedEventWithTaggingAndMass, +// ...AndEventShape, ...AndFitVars, ...): matching the shared prefix means enabling any one of +// those variants correctly triggers bit assignment, instead of only the base one happening to +// have the exact name this check was written against. +inline bool hasAnyEnabledOptionStartingWith(o2::framework::DeviceSpec const& device, std::string const& prefix) +{ + for (auto const& option : device.options) { + if (option.name.rfind(prefix, 0) == 0 && option.defaultValue.get()) { + return true; + } + } + return false; +} + +// mirrors MAKE_TRACK_PARTITION(selection) in Core/partitions.h +template +bool trackPasses(T const& track, TrackSelectionValues const& sel) +{ + if (sel.chargeSign != 0 && ((sel.chargeSign > 0) != (track.signedPt() > 0.f))) { + return false; + } + float absPt = std::fabs(sel.chargeAbs * track.signedPt()); + if (!(absPt > sel.ptMin && absPt < sel.ptMax)) { + return false; + } + if (!(track.eta() > sel.etaMin && track.eta() < sel.etaMax)) { + return false; + } + if (!(track.phi() > sel.phiMin && track.phi() < sel.phiMax)) { + return false; + } + float momentum = absPt * std::cosh(track.eta()); + if (momentum <= sel.pidThres) { + return (track.mask() & sel.maskLowMomentum) == sel.maskLowMomentum && + (track.mask() & sel.rejectionMaskLowMomentum) == static_cast(0); + } + return (track.mask() & sel.maskHighMomentum) == sel.maskHighMomentum && + (track.mask() & sel.rejectionMaskHighMomentum) == static_cast(0); +} + +// mirrors MAKE_LAMBDA_PARTITION(selection) in Core/partitions.h -- also mirrors +// MAKE_CASCADE_PARTITION/MAKE_SIGMA_PARTITION/MAKE_SIGMAPLUS_PARTITION/MAKE_D0_PARTITION/ +// MAKE_CHARM3PRONG_PARTITION, which are byte-for-byte identical, so this same function is reused +// as-is for Xi/Omega, Sigma/SigmaPlus, and D0/Lc candidates too (see the comment on +// V0SelectionValues above). +template +bool lambdaPasses(T const& v0, V0SelectionValues const& sel) +{ + if (sel.sign != 0 && ((sel.sign > 0) != (v0.signedPt() > 0.f))) { + return false; + } + float absPt = std::fabs(v0.signedPt()); + if (!(absPt > sel.ptMin && absPt < sel.ptMax)) { + return false; + } + if (!(v0.eta() > sel.etaMin && v0.eta() < sel.etaMax)) { + return false; + } + if (!(v0.phi() > sel.phiMin && v0.phi() < sel.phiMax)) { + return false; + } + if (!(v0.mass() > sel.massMin && v0.mass() < sel.massMax)) { + return false; + } + return (v0.mask() & sel.mask) == sel.mask; +} + +// mirrors MAKE_SIGMAPLUSPCM_PARTITION(selection) in Core/partitions.h -- lambdaPasses() above +// plus the one extra mlScoreMin check. +template +bool kinkPcmPasses(T const& kink, KinkPcmSelectionValues const& sel) +{ + if (sel.sign != 0 && ((sel.sign > 0) != (kink.signedPt() > 0.f))) { + return false; + } + float absPt = std::fabs(kink.signedPt()); + if (!(absPt > sel.ptMin && absPt < sel.ptMax)) { + return false; + } + if (!(kink.eta() > sel.etaMin && kink.eta() < sel.etaMax)) { + return false; + } + if (!(kink.phi() > sel.phiMin && kink.phi() < sel.phiMax)) { + return false; + } + if (!(kink.mass() > sel.massMin && kink.mass() < sel.massMax)) { + return false; + } + if (!(kink.mlScore() >= sel.mlScoreMin)) { + return false; + } + return (kink.mask() & sel.mask) == sel.mask; +} + +// mirrors MAKE_K0SHORT_PARTITION(selection) in Core/partitions.h +template +bool k0shortPasses(T const& v0, V0SelectionValues const& sel) +{ + if (!(v0.pt() > sel.ptMin && v0.pt() < sel.ptMax)) { + return false; + } + if (!(v0.eta() > sel.etaMin && v0.eta() < sel.etaMax)) { + return false; + } + if (!(v0.phi() > sel.phiMin && v0.phi() < sel.phiMax)) { + return false; + } + if (!(v0.mass() > sel.massMin && v0.mass() < sel.massMax)) { + return false; + } + return (v0.mask() & sel.mask) == sel.mask; +} + +// mirrors MAKE_RESONANCE_0_PARTITON(selection) in Core/partitions.h (Phi, Rho0) +template +bool twoTrackResonancePasses(T const& res, TwoTrackResonanceSelectionValues const& sel) +{ + if (!(res.pt() > sel.ptMin && res.pt() < sel.ptMax)) { + return false; + } + if (!(res.eta() > sel.etaMin && res.eta() < sel.etaMax)) { + return false; + } + if (!(res.phi() > sel.phiMin && res.phi() < sel.phiMax)) { + return false; + } + if (!(res.mass() > sel.massMin && res.mass() < sel.massMax)) { + return false; + } + datatypes::TrackMaskType posMask = res.posDauHasHighMomentum() ? sel.dauMaskAboveThres : sel.dauMaskBelowThres; + if ((res.maskPosDau() & posMask) != posMask) { + return false; + } + datatypes::TrackMaskType negMask = res.negDauHasHighMomentum() ? sel.dauMaskAboveThres : sel.dauMaskBelowThres; + return (res.maskNegDau() & negMask) == negMask; +} + +// mirrors MAKE_RESONANCE_1_PARTITON(selection) in Core/partitions.h (Kstar0/Kstar0bar) -- pos/neg +// daughter <-> kaon/pion resolved from the candidate's own signedPt, same as the real builder. +template +bool kstar0Passes(T const& kstar0, Kstar0SelectionValues const& sel) +{ + if (sel.sign != 0 && ((sel.sign > 0) != (kstar0.signedPt() > 0.f))) { + return false; + } + float absPt = std::fabs(kstar0.signedPt()); + if (!(absPt > sel.ptMin && absPt < sel.ptMax)) { + return false; + } + if (!(kstar0.eta() > sel.etaMin && kstar0.eta() < sel.etaMax)) { + return false; + } + if (!(kstar0.phi() > sel.phiMin && kstar0.phi() < sel.phiMax)) { + return false; + } + if (!(kstar0.mass() > sel.massMin && kstar0.mass() < sel.massMax)) { + return false; + } + bool posIsKaon = kstar0.signedPt() > 0.f; + datatypes::TrackMaskType posMaskThres = posIsKaon + ? (kstar0.posDauHasHighMomentum() ? sel.kaonDauMaskAboveThres : sel.kaonDauMaskBelowThres) + : (kstar0.posDauHasHighMomentum() ? sel.pionDauMaskAboveThres : sel.pionDauMaskBelowThres); + if ((kstar0.maskPosDau() & posMaskThres) != posMaskThres) { + return false; + } + datatypes::TrackMaskType negMaskThres = posIsKaon + ? (kstar0.negDauHasHighMomentum() ? sel.pionDauMaskAboveThres : sel.pionDauMaskBelowThres) + : (kstar0.negDauHasHighMomentum() ? sel.kaonDauMaskAboveThres : sel.kaonDauMaskBelowThres); + return (kstar0.maskNegDau() & negMaskThres) == negMaskThres; +} + +// mirrors MAKE_TWOPHOTONRESONANCE_PARTITION(selection) in Core/partitions.h (Pi0, Eta) +template +bool twoPhotonResonancePasses(T const& res, TwoPhotonResonanceSelectionValues const& sel) +{ + if (!(res.pt() > sel.ptMin && res.pt() < sel.ptMax)) { + return false; + } + if (!(res.eta() > sel.etaMin && res.eta() < sel.etaMax)) { + return false; + } + if (!(res.phi() > sel.phiMin && res.phi() < sel.phiMax)) { + return false; + } + if (!(res.mass() > sel.massMin && res.mass() < sel.massMax)) { + return false; + } + return (res.maskDau() & sel.mask) == sel.mask; +} + +using CollisionTagBitset = std::bitset; + +// true if at least `needed` entries in `range` satisfy `passes(entry, sel)`. The one primitive +// every per-collision tagging check below is built from; a new particle type only ever needs a +// new `passes` function (trackPasses/lambdaPasses/k0shortPasses above are examples), never a new +// copy of this loop. +template +inline bool hasAtLeast(Range const& range, Selection const& sel, PassesFn&& passes, int needed = 1) +{ + int count = 0; + for (auto const& entry : range) { + if (passes(entry, sel) && ++count >= needed) { + return true; + } + } + return false; +} + +// Sets tag.set(v.bit) for every variant where `range` has what it takes to form a SAME-TYPE pair +// (both v.sel1 and v.sel2 satisfied -- or, for a same-species pair, 2 distinct entries passing +// the one shared selection v.sel2). Reusable for any future same-type pair (V0V0, D0D0, ...) -- +// just a different `variants`/`range`/`passes` combination, the counting logic doesn't change. +template +inline void tagPairVariants(CollisionTagBitset& tag, std::vector const& variants, Range const& range, PassesFn&& passes) +{ + for (auto const& v : variants) { + bool ok1 = hasAtLeast(range, v.sel1, passes, 1); + bool ok2 = hasAtLeast(range, v.sel2, passes, v.sameSpecies ? 2 : 1); + if (ok1 && ok2) { + tag.set(v.bit); + } + } +} + +// Sets tag.set(v.bit) for every variant where range1 has what it takes for role 1 (v.sel1, +// checked with passes1) AND range2 has what it takes for role 2 (v.sel2, checked with passes2). +// Reusable for any future cross-type pair (track-cascade, track-kink, track-D0, ...) -- just a +// different `variants`/ranges/passes combination. +template +inline void tagCrossVariants(CollisionTagBitset& tag, std::vector const& variants, Range1 const& range1, PassesFn1&& passes1, Range2 const& range2, PassesFn2&& passes2) +{ + for (auto const& v : variants) { + if (hasAtLeast(range1, v.sel1, passes1, 1) && hasAtLeast(range2, v.sel2, passes2, 1)) { + tag.set(v.bit); + } + } +} + +// One-call convenience for the common case of a single tagPairVariants() check immediately +// followed by producing the tag row -- covers any task type whose process function only ever +// needs one same-type-pair check per collision (e.g. processTrackTrack). +template +inline void tagAndProduce(Produces& producedTags, std::vector const& variants, Range const& range, PassesFn&& passes) +{ + CollisionTagBitset tag; + tagPairVariants(tag, variants, range, passes); + producedTags(static_cast(tag.to_ullong())); +} + +// Sets tag.set(v.bit) for every variant where `range` has what it takes to form a SAME-TYPE +// triplet (track-track-track): same123 needs 3 distinct entries passing sel1; same12 needs 2 +// distinct passing sel1 plus 1 more passing sel3; otherwise sel1/sel2/sel3 each need their own +// independent match. Mirrors TripletTrackTrackTrackBuilder exactly (see TrackTrackTrackVariant). +template +inline void tagTripletVariants(CollisionTagBitset& tag, std::vector const& variants, Range const& range, PassesFn&& passes) +{ + for (auto const& v : variants) { + bool ok = false; + if (v.same123) { + ok = hasAtLeast(range, v.sel1, passes, 3); + } else if (v.same12) { + ok = hasAtLeast(range, v.sel1, passes, 2) && hasAtLeast(range, v.sel3, passes, 1); + } else { + ok = hasAtLeast(range, v.sel1, passes, 1) && hasAtLeast(range, v.sel2, passes, 1) && hasAtLeast(range, v.sel3, passes, 1); + } + if (ok) { + tag.set(v.bit); + } + } +} + +// Sets tag.set(v.bit) for every variant where range1 (roles 1&2, one table) and range2 (role 3, a +// different table) have what it takes to form a CROSS-TYPE triplet (track-track-v0, +// track-track-cascade): same12 needs 2 distinct entries in range1 passing sel1; otherwise role 1 +// and role 2 each need their own independent match in range1. Mirrors +// TripletTrackTrackV0Builder/TripletTrackTrackCascadeBuilder exactly (see TrackTrackV0Variant). +template +inline void tagCrossTripletVariants(CollisionTagBitset& tag, std::vector const& variants, Range1 const& range1, PassesFn1&& passes1, Range2 const& range2, PassesFn2&& passes2) +{ + for (auto const& v : variants) { + bool ok = v.same12 ? hasAtLeast(range1, v.sel1, passes1, 2) + : (hasAtLeast(range1, v.sel1, passes1, 1) && hasAtLeast(range1, v.sel2, passes1, 1)); + if (ok && hasAtLeast(range2, v.sel3, passes2, 1)) { + tag.set(v.bit); + } + } +} + +// Role names and device-name substrings, declared once: a consuming task needs the exact same +// role spelling discoverTaggedRoles() below uses, to look its own bit back up via findOwnBit() -- +// fed in as ConfTagging's template argument below, so a task's role is fixed at compile time +// rather than a loose string repeated at every call site. Declared as named `char` arrays, not +// `const char*` pointing at string literals: a pointer non-type template argument must be the +// address of a named object with static storage duration, and the standard specifically +// disallows forming that address from a string literal directly (two identical literals aren't +// guaranteed the same address). A named array has none of that ambiguity, and decays to +// `const char*` on its own wherever a plain pointer is expected (ConfTagging +// below, the std::string construction in discoverTaggedRoles(), etc.). `inline` (not just +// `constexpr`) matters too: without it, a namespace-scope variable has internal linkage, so each +// translation unit would get its own copy at its own address -- fine for a value, but the same +// template instantiation must denote the same entity everywhere it's used. `inline` guarantees +// one shared address. The device-name substrings are, unavoidably, the literal +// o2physics_add_dpl_workflow() names from the respective CMakeLists.txt -- there is no way to +// derive those at compile time, so they are hardcoded here, exactly once, rather than repeated at +// every call site. +namespace role +{ +inline constexpr char TrackTrackPair[] = "TrackTrackPair"; +inline constexpr char TrackLambdaPair[] = "TrackLambdaPair"; +inline constexpr char TrackK0shortPair[] = "TrackK0shortPair"; +inline constexpr char TrackXiPair[] = "TrackXiPair"; +inline constexpr char TrackOmegaPair[] = "TrackOmegaPair"; +inline constexpr char TrackSigmaPair[] = "TrackSigmaPair"; +inline constexpr char TrackSigmaPlusPair[] = "TrackSigmaPlusPair"; +inline constexpr char TrackSigmaPlusPcmPair[] = "TrackSigmaPlusPcmPair"; +inline constexpr char TrackD0Pair[] = "TrackD0Pair"; +inline constexpr char TrackLcPair[] = "TrackLcPair"; +inline constexpr char LambdaLambdaPair[] = "LambdaLambdaPair"; +inline constexpr char K0shortK0shortPair[] = "K0shortK0shortPair"; +inline constexpr char LambdaK0shortPair[] = "LambdaK0shortPair"; +inline constexpr char D0D0Pair[] = "D0D0Pair"; +inline constexpr char TrackPhiPair[] = "TrackPhiPair"; +inline constexpr char TrackKstar0Pair[] = "TrackKstar0Pair"; +inline constexpr char TrackRho0Pair[] = "TrackRho0Pair"; +inline constexpr char TrackPi0Pair[] = "TrackPi0Pair"; +inline constexpr char TrackEtaPair[] = "TrackEtaPair"; +inline constexpr char LambdaPhiPair[] = "LambdaPhiPair"; +inline constexpr char LambdaKstar0Pair[] = "LambdaKstar0Pair"; +inline constexpr char LambdaRho0Pair[] = "LambdaRho0Pair"; +inline constexpr char K0shortPhiPair[] = "K0shortPhiPair"; +inline constexpr char K0shortKstar0Pair[] = "K0shortKstar0Pair"; +inline constexpr char K0shortRho0Pair[] = "K0shortRho0Pair"; +inline constexpr char TrackTrackTrackTriplet[] = "TrackTrackTrackTriplet"; +inline constexpr char TrackTrackLambdaTriplet[] = "TrackTrackLambdaTriplet"; +inline constexpr char TrackTrackXiTriplet[] = "TrackTrackXiTriplet"; +inline constexpr char TrackTrackOmegaTriplet[] = "TrackTrackOmegaTriplet"; +} // namespace role + +namespace devicename +{ +constexpr const char* FemtoPairTrackTrack = "femto-pair-track-track"; +constexpr const char* FemtoPairTrackV0 = "femto-pair-track-v0"; +constexpr const char* FemtoPairTrackCascade = "femto-pair-track-cascade"; +constexpr const char* FemtoPairTrackKink = "femto-pair-track-kink"; +constexpr const char* FemtoPairTrackCharmHadron = "femto-pair-track-charm-hadron"; +constexpr const char* FemtoPairV0V0 = "femto-pair-v0-v0"; +constexpr const char* FemtoPairD0D0 = "femto-pair-d0-d0"; +constexpr const char* FemtoPairTrackTwoTrackResonance = "femto-pair-track-two-track-resonance"; +constexpr const char* FemtoPairTrackTwoPhotonResonance = "femto-pair-track-two-photon-resonance"; +constexpr const char* FemtoPairV0TwoTrackResonance = "femto-pair-v0-two-track-resonance"; +constexpr const char* FemtoTripletTrackTrackTrack = "femto-triplet-track-track-track"; +constexpr const char* FemtoTripletTrackTrackV0 = "femto-triplet-track-track-v0"; +constexpr const char* FemtoTripletTrackTrackCascade = "femto-triplet-track-track-cascade"; +} // namespace devicename + +// One assigned bit of FColTags::collisionTag: `deviceName` is the matched sibling device's own +// (unique) DPL device name, `role` names which particle-selection group of that device the bit +// covers (e.g. "TrackSelection1", "LambdaSelection1"), and `bit` is its position in collisionTag. +// One assigned bit means "this collision has what it takes to form a pair of this TYPE": both the +// particle-1-role candidate and the particle-2-role candidate (or, for a same-species pair, at +// least 2 distinct candidates passing the one shared selection) are present. One bit per pair type +// per task instance -- not one bit per particle role -- since during mixing the same bit is +// checked identically on both the anchor and the partner collision (either one could end up +// supplying either role across the full mixing sweep). +struct TaggedRole { + std::string deviceName; + std::string role; + int bit = 0; +}; + +// The single canonical bit-assignment walk: scans the workflow's device list in order and, for +// every recognized pair/triplet task instance found, consumes the next free bit(s) for its +// role(s) -- exactly what femtoProducerCollisionTagger.cxx uses to decide what to tag, and what a +// consuming task calls to recompute (from the same static device list) which bit(s) ended up +// being its own. Keep this in sync with femtoProducerCollisionTagger.cxx's supported task types: whichever +// task types are added there must be added here identically, in the same order, or the tagger and +// its consumers will silently disagree about which bit means what. +inline std::vector discoverTaggedRoles(o2::framework::RunningWorkflowInfo const& workflows) +{ + std::vector roles; + int counter = 0; + for (o2::framework::DeviceSpec const& device : workflows.devices) { + if (device.name.find(devicename::FemtoPairTrackTrack) != std::string::npos) { + // Gated on whether ANY of the *WithTagging* process functions that actually consume this + // bit is enabled (processMixedEventWithTagging itself, or one of the + // ...AndMass/...AndEventShape/...AndFitVars combined variants -- all share the same role + // and tag bit, since the tag-bit check and the extra joined columns are independent + // concerns). An instance that enables none of them will never look this bit up, so + // assigning it one anyway would just waste a slot out of the hard-capped budget + // (MaxTagBits below) for nothing. + if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackTrackPair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackV0) != std::string::npos) { + // Each species gates independently on its own *MixedEventWithTagging* switch family -- + // Lambda and K0short are activated completely independently of each other (e.g. + // same-event-only for one species, tagged-mixed-event for the other is a legitimate + // combination), and, same as TrackTrackPair above, same-event/Mc/WithFitVars-without-tagging + // variants are irrelevant here since none of them ever consume a tag bit. + if (hasAnyEnabledOptionStartingWith(device, "processLambdaMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackLambdaPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackK0shortPair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackCascade) != std::string::npos) { + // Xi and Omega are mutually exclusive on a single instance (the task itself fatals if + // both are enabled), but gating independently costs nothing and needs no special-casing + // here. + if (hasAnyEnabledOptionStartingWith(device, "processXiMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackXiPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processOmegaMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackOmegaPair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackKink) != std::string::npos) { + if (hasAnyEnabledOptionStartingWith(device, "processSigmaMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackSigmaPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processSigmaPlusMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackSigmaPlusPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processSigmaPlusPcmMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackSigmaPlusPcmPair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackCharmHadron) != std::string::npos) { + if (hasAnyEnabledOptionStartingWith(device, "processD0MixedEventWithTagging")) { + roles.push_back({device.name, role::TrackD0Pair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processLcMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackLcPair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairV0V0) != std::string::npos) { + // Lambda-lambda, k0short-k0short and lambda-k0short are mutually exclusive on a single + // instance (the task itself fatals if more than one is enabled), but gating independently + // costs nothing and needs no special-casing here. + if (hasAnyEnabledOptionStartingWith(device, "processLambdaLambdaMixedEventWithTagging")) { + roles.push_back({device.name, role::LambdaLambdaPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortK0shortMixedEventWithTagging")) { + roles.push_back({device.name, role::K0shortK0shortPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processLambdaK0shortMixedEventWithTagging")) { + roles.push_back({device.name, role::LambdaK0shortPair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairD0D0) != std::string::npos) { + // Only one pair type possible per instance (sign on D0Selection1/2 picks the hypothesis), so + // this task's WithTagging process function is just "processMixedEventWithTagging", same local + // name as femtoPairTrackTrack.cxx's -- fine since option names are scoped per-device. + if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { + roles.push_back({device.name, role::D0D0Pair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackTwoTrackResonance) != std::string::npos) { + // Phi, Kstar0 and Rho0 are mutually exclusive on a single instance (the task itself fatals + // if more than one is enabled), but gating independently costs nothing here either. + if (hasAnyEnabledOptionStartingWith(device, "processPhiMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackPhiPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processKstar0MixedEventWithTagging")) { + roles.push_back({device.name, role::TrackKstar0Pair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processRho0MixedEventWithTagging")) { + roles.push_back({device.name, role::TrackRho0Pair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairTrackTwoPhotonResonance) != std::string::npos) { + // Pi0 and Eta are mutually exclusive on a single instance, same as above. + if (hasAnyEnabledOptionStartingWith(device, "processPi0MixedEventWithTagging")) { + roles.push_back({device.name, role::TrackPi0Pair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processEtaMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackEtaPair, counter++}); + } + } else if (device.name.find(devicename::FemtoPairV0TwoTrackResonance) != std::string::npos) { + // All six v0 x resonance combinations are mutually exclusive on a single instance (the task + // itself fatals if more than one is enabled), same as above. + if (hasAnyEnabledOptionStartingWith(device, "processLambdaPhiMixedEventWithTagging")) { + roles.push_back({device.name, role::LambdaPhiPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processLambdaKstar0MixedEventWithTagging")) { + roles.push_back({device.name, role::LambdaKstar0Pair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processLambdaRho0MixedEventWithTagging")) { + roles.push_back({device.name, role::LambdaRho0Pair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortPhiMixedEventWithTagging")) { + roles.push_back({device.name, role::K0shortPhiPair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortKstar0MixedEventWithTagging")) { + roles.push_back({device.name, role::K0shortKstar0Pair, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processK0shortRho0MixedEventWithTagging")) { + roles.push_back({device.name, role::K0shortRho0Pair, counter++}); + } + } else if (device.name.find(devicename::FemtoTripletTrackTrackTrack) != std::string::npos) { + if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackTrackTrackTriplet, counter++}); + } + } else if (device.name.find(devicename::FemtoTripletTrackTrackV0) != std::string::npos) { + if (hasAnyEnabledOptionStartingWith(device, "processMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackTrackLambdaTriplet, counter++}); + } + } else if (device.name.find(devicename::FemtoTripletTrackTrackCascade) != std::string::npos) { + // Xi and Omega are mutually exclusive on a single instance (the task itself fatals if both + // are enabled), but gating independently costs nothing here either. + if (hasAnyEnabledOptionStartingWith(device, "processXiMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackTrackXiTriplet, counter++}); + } + if (hasAnyEnabledOptionStartingWith(device, "processOmegaMixedEventWithTagging")) { + roles.push_back({device.name, role::TrackTrackOmegaTriplet, counter++}); + } + } + // TODO: add the remaining triplet task types (mc-particle triplets, ...), following the same + // pattern. Whatever is added here must also be added to femtoProducerCollisionTagger.cxx's + // init(), in the same order, since that is where the selection VALUES behind each of these + // bits actually get read and checked. + } + return roles; +} + +// Convenience for a consuming task: find the bit this device's own `role` was assigned, or -1 if +// it was never reached (e.g. this device's own process switch wasn't enabled on the tagger, so it +// never requested this role at all). +inline int findOwnBit(std::vector const& roles, std::string const& ownDeviceName, std::string const& role) +{ + for (auto const& r : roles) { + if (r.deviceName == ownDeviceName && r.role == role) { + return r.bit; + } + } + return -1; +} + +// Carries a consuming task's own, fixed pair-type role (Role, one of the role:: constants above, +// supplied at the instantiation site -- e.g. ConfTagging confTagging;) and +// the bit that role was assigned, discovered once in init() into `internalTagMask` (not read from +// the JSON config). Bundling both together means a call like findOwnBit(roles, ownDevice.name, +// confTagging.Role) always uses the exact role this same object's mask gets set for, instead of +// the role being a second, independently-spelled literal at the call site. Every task that opts +// into tagger-filtered mixing instantiates one of these; wrapped in a ConfigurableGroup (rather +// than a bare Configurable) so more fields can be added here later without having to touch every +// consumer. `prefix` folds in Role itself: a task with more than one pair type (e.g. +// femtoPairTrackV0.cxx's Lambda and K0short, each needing their own tag bit) instantiates +// ConfTagging twice, and without this, both would register the exact same CLI/JSON option name +// ("Tagging.internalTagMask" twice over), which DPL rejects at startup as an ambiguous option. +template +struct ConfTagging : o2::framework::ConfigurableGroup { + std::string prefix = std::string("Tagging") + role; + static constexpr char const* Role = role; + o2::framework::Configurable internalTagMask{"internalTagMask", 0, "internal: discovered collision tagger bitmask, set automatically in init() -- do not set manually"}; +}; + +} // namespace o2::analysis::femto::collisiontagger + +#endif // PWGCF_FEMTO_CORE_COLLISIONTAGGER_H_ diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index 4816b1527b7..651e591d3d6 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -143,6 +143,18 @@ DECLARE_SOA_TABLE_STAGED_VERSIONED(FColMasks_001, "FCOLMASK", 1, //! collision m using FColMasks = FColMasks_001; using StoredFColMasks = StoredFColMasks_001; +// table for collision tags: one bit per (wagon, pair type) combination the collision tagger was +// self-configured to check, set if the collision has what it takes to form at least one pair of +// that type (both particle-1-role and particle-2-role candidates present, or, for a same-species +// pair, at least 2 distinct candidates passing the one shared selection). Used to pre-filter the +// mixed-event pool instead of discovering empty collisions only after slicing. Which bit means +// what is assigned at self-configuration time and logged by the tagger; see +// PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx. +DECLARE_SOA_TABLE_STAGED_VERSIONED(FColTags_001, "FCOLTAG", 1, //! collision tags + femtocollisions::CollisionTag); +using FColTags = FColTags_001; +using StoredFColTags = StoredFColTags_001; + DECLARE_SOA_TABLE_STAGED_VERSIONED(FColSphericities_001, "FCOLSPHERICITY", 1, //! sphericity femtocollisions::Sphericity); using FColSphericities = FColSphericities_001; diff --git a/PWGCF/Femto/TableProducer/CMakeLists.txt b/PWGCF/Femto/TableProducer/CMakeLists.txt index 9658d1b354f..d804d6e39fa 100644 --- a/PWGCF/Femto/TableProducer/CMakeLists.txt +++ b/PWGCF/Femto/TableProducer/CMakeLists.txt @@ -14,6 +14,11 @@ o2physics_add_dpl_workflow(femto-producer PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::EventFilteringUtils O2Physics::PWGEMPhotonMesonCore COMPONENT_NAME Analysis) +o2physics_add_dpl_workflow(femto-producer-collision-tagger + SOURCES ./femtoProducerCollisionTagger.cxx + PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore + COMPONENT_NAME Analysis) + o2physics_add_dpl_workflow(femto-producer-derived-to-derived SOURCES ./femtoProducerDerivedToDerived.cxx PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore diff --git a/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx b/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx new file mode 100644 index 00000000000..2fca898ea81 --- /dev/null +++ b/PWGCF/Femto/TableProducer/femtoProducerCollisionTagger.cxx @@ -0,0 +1,704 @@ +// Copyright 2019-2025 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license 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. + +/// \file femtoProducerCollisionTagger.cxx +/// \brief Self-configuring task that tags femto collisions with a bit per (pair/triplet task +/// instance, pair type) found in the same workflow, set if the collision has what it +/// takes to form at least one pair of that type: both the particle-1-role and +/// particle-2-role candidate present (or, for a same-species pair, at least 2 distinct +/// candidates passing the one shared selection). Used to pre-filter the mixed-event pool +/// instead of discovering empty collisions only after slicing. Supports any number of +/// same-type "subwagons" (the same task run several times with different cuts in one +/// train) -- bits are assigned in discovery order. +/// \author Anton Riedel, TU München, anton.riedel@tum.de + +#include "PWGCF/Femto/Core/collisionTagger.h" +#include "PWGCF/Femto/Core/dataTypes.h" +#include "PWGCF/Femto/DataModel/FemtoTables.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace o2::analysis::femto; + +struct FemtoProducerCollisionTagger { + o2::framework::Produces producedTags; + + // Process functions below take a single collision ITERATOR (not the whole table) as their + // first argument, with the track/v0 tables as later arguments -- the same auto-grouping-by- + // fColId mechanism every processSameEvent() in this codebase already relies on, so tracks/ + // lambdas/k0shorts arrive pre-sliced to that one collision with no manual Preslice/SliceCache/ + // sliceBy() needed here at all. + using FemtoCollisions = o2::soa::Join; + using FemtoCollision = FemtoCollisions::iterator; + using FemtoTracks = o2::soa::Join; + using FemtoLambdas = o2::soa::Join; + using FemtoK0shorts = o2::soa::Join; + using FemtoXis = o2::soa::Join; + using FemtoOmegas = o2::soa::Join; + using FemtoSigmas = o2::soa::Join; + using FemtoSigmaPlus = o2::soa::Join; + using FemtoSigmaPlusPcm = o2::soa::Join; + using FemtoD0s = o2::soa::Join; + using FemtoLcs = o2::soa::Join; + using FemtoPhis = o2::soa::Join; + using FemtoKstar0s = o2::soa::Join; + using FemtoRho0s = o2::soa::Join; + using FemtoPi0s = o2::soa::Join; + using FemtoEtas = o2::soa::Join; + + // --- femto-pair-track-track: one bit per instance, "a track-track pair is possible here" --- + std::vector mTrackTrackVariants; + + // --- femto-pair-track-v0: one bit per instance per enabled v0 species --- + std::vector mTrackLambdaVariants; + std::vector mTrackK0shortVariants; + + // --- femto-pair-track-cascade: one bit per instance per enabled cascade species --- + std::vector mTrackXiVariants; + std::vector mTrackOmegaVariants; + + // --- femto-pair-track-kink: one bit per instance per enabled kink species --- + std::vector mTrackSigmaVariants; + std::vector mTrackSigmaPlusVariants; + std::vector mTrackSigmaPlusPcmVariants; + + // --- femto-pair-track-charm-hadron: one bit per instance per enabled charm hadron species --- + std::vector mTrackD0Variants; + std::vector mTrackLcVariants; + + // --- femto-pair-v0-v0: one bit per instance per enabled v0-v0 combination --- + std::vector mLambdaLambdaVariants; + std::vector mK0shortK0shortVariants; + std::vector mLambdaK0shortVariants; + + // --- femto-pair-d0-d0: one bit per instance --- + std::vector mD0D0Variants; + + // --- femto-pair-track-two-track-resonance: one bit per instance per enabled species --- + std::vector mTrackPhiVariants; + std::vector mTrackKstar0Variants; + std::vector mTrackRho0Variants; + + // --- femto-pair-track-two-photon-resonance: one bit per instance per enabled species --- + std::vector mTrackPi0Variants; + std::vector mTrackEtaVariants; + + // --- femto-pair-v0-two-track-resonance: one bit per instance per enabled combination --- + std::vector mLambdaPhiVariants; + std::vector mLambdaKstar0Variants; + std::vector mLambdaRho0Variants; + std::vector mK0shortPhiVariants; + std::vector mK0shortKstar0Variants; + std::vector mK0shortRho0Variants; + + // --- femto-triplet-track-track-track: one bit per instance --- + std::vector mTrackTrackTrackVariants; + + // --- femto-triplet-track-track-v0: one bit per instance --- + std::vector mTrackTrackLambdaVariants; + + // --- femto-triplet-track-track-cascade: one bit per instance per enabled cascade species --- + std::vector mTrackTrackXiVariants; + std::vector mTrackTrackOmegaVariants; + + void init(o2::framework::InitContext& context) + { + // FColTags gets exactly one row written per collision; if more than one process function were + // enabled, each would try to write its own row for the same collision. Until FColTags is + // extended to carry one column per wagon type, only one process function may run at a time -- + // run separate tagger instances (with separate output files/merges) for separate wagon types. + int activeProcesses = static_cast(doprocessTrackTrack) + static_cast(doprocessTrackV0) + static_cast(doprocessTrackCascade) + static_cast(doprocessTrackKink) + static_cast(doprocessTrackSigmaPlusPcm) + static_cast(doprocessTrackD0) + static_cast(doprocessTrackLc) + static_cast(doprocessV0V0) + static_cast(doprocessD0D0) + static_cast(doprocessTrackTwoTrackResonance) + static_cast(doprocessTrackTwoPhotonResonance) + static_cast(doprocessV0TwoTrackResonance) + static_cast(doprocessTripletTrackTrackTrack) + static_cast(doprocessTripletTrackTrackV0) + static_cast(doprocessTripletTrackTrackCascade); + if (activeProcesses != 1) { + LOG(fatal) << "Exactly one process function must be enabled on the collision tagger, found " << activeProcesses << ". Breaking..."; + } + + LOG(info) << "*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*"; + LOG(info) << " Collision tagger self-configuration "; + LOG(info) << "*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*+-+*"; + + // discoverTaggedRoles() is the single canonical bit-assignment walk, shared with every + // consuming pair/triplet task (see Core/collisionTagger.h) -- it assigns bits to every + // recognized task instance found in the workflow, regardless of which of THIS tagger + // instance's own process functions is enabled, so that bit numbers stay globally consistent + // even when multiple tagger instances (one per wagon type) run side by side. + auto& workflows = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + + auto findDevice = [&workflows](std::string const& name) -> o2::framework::DeviceSpec const& { + for (auto const& device : workflows.devices) { + if (device.name == name) { + return device; + } + } + LOG(fatal) << "Device " << name << " vanished between discovery and lookup. Breaking..."; + return workflows.devices.front(); // unreachable, silences -Wreturn-type + }; + + for (auto const& r : roles) { + LOG(info) << " bit " << r.bit << " = " << r.deviceName << " " << r.role; + if (doprocessTrackTrack && r.role == collisiontagger::role::TrackTrackPair && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackTrack) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackTrackVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); + // When sameSpecies is on, the real pair task's own dispatcher (PairTrackTrackBuilder:: + // processMixedEvent in pairBuilder.h) uses partition1 for BOTH roles and never consults + // TrackSelection2 at all -- mirror that here instead of reading TrackSelection2 off the + // device, since the user is only expected to configure TrackSelection1 in that mode and + // TrackSelection2 may be left at an unrelated default. + v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readTrackSelection(device, "TrackSelection2"); + v.bit = r.bit; + mTrackTrackVariants.push_back(v); + } else if (doprocessTrackV0 && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackV0) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::TrackLambdaPair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.bit = r.bit; + mTrackLambdaVariants.push_back(v); + } else if (r.role == collisiontagger::role::TrackK0shortPair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.bit = r.bit; + mTrackK0shortVariants.push_back(v); + } + } else if (doprocessTrackCascade && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackCascade) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::TrackXiPair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "XiSelection"); + v.bit = r.bit; + mTrackXiVariants.push_back(v); + } else if (r.role == collisiontagger::role::TrackOmegaPair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "OmegaSelection"); + v.bit = r.bit; + mTrackOmegaVariants.push_back(v); + } + } else if ((doprocessTrackKink || doprocessTrackSigmaPlusPcm) && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackKink) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::TrackSigmaPair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "SigmaSelection1"); + v.bit = r.bit; + mTrackSigmaVariants.push_back(v); + } else if (r.role == collisiontagger::role::TrackSigmaPlusPair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "SigmaPlusSelection1"); + v.bit = r.bit; + mTrackSigmaPlusVariants.push_back(v); + } else if (r.role == collisiontagger::role::TrackSigmaPlusPcmPair) { + collisiontagger::TrackKinkPcmVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readKinkPcmSelection(device, "SigmaPlusPcmSelection1"); + v.bit = r.bit; + mTrackSigmaPlusPcmVariants.push_back(v); + } + } else if ((doprocessTrackD0 || doprocessTrackLc) && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackCharmHadron) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::TrackD0Pair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "D0Selection1"); + v.bit = r.bit; + mTrackD0Variants.push_back(v); + } else if (r.role == collisiontagger::role::TrackLcPair) { + collisiontagger::TrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "LcSelection1"); + v.bit = r.bit; + mTrackLcVariants.push_back(v); + } + } else if (doprocessV0V0 && r.deviceName.find(collisiontagger::devicename::FemtoPairV0V0) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::LambdaLambdaPair) { + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); + // Mirror PairV0V0Builder::processMixedEvent(), which uses partition1 for BOTH roles and + // never consults LambdaSelection2 at all when sameSpecies -- same override as + // TrackTrackPair above. + v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readV0Selection(device, "LambdaSelection2"); + v.bit = r.bit; + mLambdaLambdaVariants.push_back(v); + } else if (r.role == collisiontagger::role::K0shortK0shortPair) { + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.sameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); + v.sel2 = v.sameSpecies ? v.sel1 : collisiontagger::readV0Selection(device, "K0shortSelection2"); + v.bit = r.bit; + mK0shortK0shortVariants.push_back(v); + } else if (r.role == collisiontagger::role::LambdaK0shortPair) { + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sel2 = collisiontagger::readV0Selection(device, "K0shortSelection2"); + v.bit = r.bit; + mLambdaK0shortVariants.push_back(v); + } + } else if (doprocessD0D0 && r.deviceName.find(collisiontagger::devicename::FemtoPairD0D0) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::V0V0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "D0Selection1"); + collisiontagger::V0SelectionValues sel2Raw = collisiontagger::readV0Selection(device, "D0Selection2"); + bool confSameSpecies = collisiontagger::hasEnabledOption(device, "Mixing.sameSpecies"); + // PairD0D0Builder::init() forces sameSpecies off when the two signs disagree (see + // pairBuilder.h) -- mirror that override here too, otherwise a sign-flip-only d0-d0bar + // config would be mis-tagged as a same-species pair. + v.sameSpecies = confSameSpecies && (v.sel1.sign == sel2Raw.sign); + v.sel2 = v.sameSpecies ? v.sel1 : sel2Raw; + v.bit = r.bit; + mD0D0Variants.push_back(v); + } else if (doprocessTrackTwoTrackResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackTwoTrackResonance) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::TrackPhiPair) { + collisiontagger::TrackTwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "PhiSelection"); + v.bit = r.bit; + mTrackPhiVariants.push_back(v); + } else if (r.role == collisiontagger::role::TrackKstar0Pair) { + collisiontagger::TrackKstar0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readKstar0Selection(device, "Kstar0Selection"); + v.bit = r.bit; + mTrackKstar0Variants.push_back(v); + } else if (r.role == collisiontagger::role::TrackRho0Pair) { + collisiontagger::TrackTwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "Rho0Selection"); + v.bit = r.bit; + mTrackRho0Variants.push_back(v); + } + } else if (doprocessTrackTwoPhotonResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairTrackTwoPhotonResonance) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::TrackPi0Pair) { + collisiontagger::TrackTwoPhotonResonanceVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readTwoPhotonResonanceSelection(device, "Pi0Selection1"); + v.bit = r.bit; + mTrackPi0Variants.push_back(v); + } else if (r.role == collisiontagger::role::TrackEtaPair) { + collisiontagger::TrackTwoPhotonResonanceVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + v.sel2 = collisiontagger::readTwoPhotonResonanceSelection(device, "EtaSelection1"); + v.bit = r.bit; + mTrackEtaVariants.push_back(v); + } + } else if (doprocessV0TwoTrackResonance && r.deviceName.find(collisiontagger::devicename::FemtoPairV0TwoTrackResonance) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + if (r.role == collisiontagger::role::LambdaPhiPair) { + collisiontagger::V0TwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "PhiSelection"); + v.bit = r.bit; + mLambdaPhiVariants.push_back(v); + } else if (r.role == collisiontagger::role::LambdaKstar0Pair) { + collisiontagger::V0Kstar0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sel2 = collisiontagger::readKstar0Selection(device, "Kstar0Selection"); + v.bit = r.bit; + mLambdaKstar0Variants.push_back(v); + } else if (r.role == collisiontagger::role::LambdaRho0Pair) { + collisiontagger::V0TwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "Rho0Selection"); + v.bit = r.bit; + mLambdaRho0Variants.push_back(v); + } else if (r.role == collisiontagger::role::K0shortPhiPair) { + collisiontagger::V0TwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "PhiSelection"); + v.bit = r.bit; + mK0shortPhiVariants.push_back(v); + } else if (r.role == collisiontagger::role::K0shortKstar0Pair) { + collisiontagger::V0Kstar0Variant v; + v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.sel2 = collisiontagger::readKstar0Selection(device, "Kstar0Selection"); + v.bit = r.bit; + mK0shortKstar0Variants.push_back(v); + } else if (r.role == collisiontagger::role::K0shortRho0Pair) { + collisiontagger::V0TwoTrackResonanceVariant v; + v.sel1 = collisiontagger::readV0Selection(device, "K0shortSelection1"); + v.sel2 = collisiontagger::readTwoTrackResonanceSelection(device, "Rho0Selection"); + v.bit = r.bit; + mK0shortRho0Variants.push_back(v); + } + } else if (doprocessTripletTrackTrackTrack && r.deviceName.find(collisiontagger::devicename::FemtoTripletTrackTrackTrack) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackTrackTrackVariant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + collisiontagger::TrackSelectionValues sel2Raw = collisiontagger::readTrackSelection(device, "TrackSelection2"); + collisiontagger::TrackSelectionValues sel3Raw = collisiontagger::readTrackSelection(device, "TrackSelection3"); + v.same123 = collisiontagger::hasEnabledOption(device, "Mixing.particle123AreSameSpecies"); + v.same12 = collisiontagger::hasEnabledOption(device, "Mixing.particle12AreSameSpecies"); + // Mirror TripletTrackTrackTrackBuilder::processMixedEvent(), which ignores + // TrackSelection2/3 entirely when same123 (uses partition1 for all three roles), and + // ignores TrackSelection2 (but not 3) when same12 -- same override trick as + // TrackTrackVariant's sameSpecies above. + v.sel2 = (v.same123 || v.same12) ? v.sel1 : sel2Raw; + v.sel3 = v.same123 ? v.sel1 : sel3Raw; + v.bit = r.bit; + mTrackTrackTrackVariants.push_back(v); + } else if (doprocessTripletTrackTrackV0 && r.deviceName.find(collisiontagger::devicename::FemtoTripletTrackTrackV0) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackTrackV0Variant v; + v.sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + collisiontagger::TrackSelectionValues sel2Raw = collisiontagger::readTrackSelection(device, "TrackSelection2"); + v.same12 = collisiontagger::hasEnabledOption(device, "Mixing.particle12AreSameSpecies"); + v.sel2 = v.same12 ? v.sel1 : sel2Raw; + v.sel3 = collisiontagger::readV0Selection(device, "LambdaSelection1"); + v.bit = r.bit; + mTrackTrackLambdaVariants.push_back(v); + } else if (doprocessTripletTrackTrackCascade && r.deviceName.find(collisiontagger::devicename::FemtoTripletTrackTrackCascade) != std::string::npos) { + o2::framework::DeviceSpec const& device = findDevice(r.deviceName); + collisiontagger::TrackSelectionValues sel1 = collisiontagger::readTrackSelection(device, "TrackSelection1"); + collisiontagger::TrackSelectionValues sel2Raw = collisiontagger::readTrackSelection(device, "TrackSelection2"); + bool same12 = collisiontagger::hasEnabledOption(device, "Mixing.particle12AreSameSpecies"); + collisiontagger::TrackSelectionValues sel2 = same12 ? sel1 : sel2Raw; + if (r.role == collisiontagger::role::TrackTrackXiTriplet) { + collisiontagger::TrackTrackV0Variant v; + v.sel1 = sel1; + v.sel2 = sel2; + v.same12 = same12; + v.sel3 = collisiontagger::readV0Selection(device, "XiSelection"); + v.bit = r.bit; + mTrackTrackXiVariants.push_back(v); + } else if (r.role == collisiontagger::role::TrackTrackOmegaTriplet) { + collisiontagger::TrackTrackV0Variant v; + v.sel1 = sel1; + v.sel2 = sel2; + v.same12 = same12; + v.sel3 = collisiontagger::readV0Selection(device, "OmegaSelection"); + v.bit = r.bit; + mTrackTrackOmegaVariants.push_back(v); + } + } + // TODO: add the remaining triplet task types (mc-particle triplets, ...), following the + // same pattern. + } + + if (doprocessTrackTrack && mTrackTrackVariants.empty()) { + LOG(fatal) << "processTrackTrack is enabled, but no femto-pair-track-track device was found in this workflow."; + } + if (doprocessTrackV0 && mTrackLambdaVariants.empty() && mTrackK0shortVariants.empty()) { + LOG(fatal) << "processTrackV0 is enabled, but no femto-pair-track-v0 device (with processLambdaMixedEventWithTagging " + << "or processK0shortMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTrackCascade && mTrackXiVariants.empty() && mTrackOmegaVariants.empty()) { + LOG(fatal) << "processCascade is enabled, but no femto-pair-track-cascade device (with processXiMixedEventWithTagging " + << "or processOmegaMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTrackKink && mTrackSigmaVariants.empty() && mTrackSigmaPlusVariants.empty()) { + LOG(fatal) << "processTrackKink is enabled, but no femto-pair-track-kink device (with processSigmaMixedEventWithTagging/" + << "processSigmaPlusMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTrackSigmaPlusPcm && mTrackSigmaPlusPcmVariants.empty()) { + LOG(fatal) << "processTrackSigmaPlusPcm is enabled, but no femto-pair-track-kink device (with " + << "processSigmaPlusPcmMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTrackD0 && mTrackD0Variants.empty()) { + LOG(fatal) << "processTrackD0 is enabled, but no femto-pair-track-charm-hadron device (with processD0MixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessTrackLc && mTrackLcVariants.empty()) { + LOG(fatal) << "processTrackLc is enabled, but no femto-pair-track-charm-hadron device (with processLcMixedEventWithTagging " + << "enabled) was found in this workflow."; + } + if (doprocessV0V0 && mLambdaLambdaVariants.empty() && mK0shortK0shortVariants.empty() && mLambdaK0shortVariants.empty()) { + LOG(fatal) << "processV0V0 is enabled, but no femto-pair-v0-v0 device (with processLambdaLambdaMixedEventWithTagging/" + << "processK0shortK0shortMixedEventWithTagging/processLambdaK0shortMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessD0D0 && mD0D0Variants.empty()) { + LOG(fatal) << "processD0D0 is enabled, but no femto-pair-d0-d0 device (with processMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTrackTwoTrackResonance && mTrackPhiVariants.empty() && mTrackKstar0Variants.empty() && mTrackRho0Variants.empty()) { + LOG(fatal) << "processTrackTwoTrackResonance is enabled, but no femto-pair-track-two-track-resonance device (with " + << "processPhiMixedEventWithTagging/processKstar0MixedEventWithTagging/processRho0MixedEventWithTagging enabled) " + << "was found in this workflow."; + } + if (doprocessTrackTwoPhotonResonance && mTrackPi0Variants.empty() && mTrackEtaVariants.empty()) { + LOG(fatal) << "processTrackTwoPhotonResonance is enabled, but no femto-pair-track-two-photon-resonance device (with " + << "processPi0MixedEventWithTagging/processEtaMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessV0TwoTrackResonance && mLambdaPhiVariants.empty() && mLambdaKstar0Variants.empty() && mLambdaRho0Variants.empty() && + mK0shortPhiVariants.empty() && mK0shortKstar0Variants.empty() && mK0shortRho0Variants.empty()) { + LOG(fatal) << "processV0TwoTrackResonance is enabled, but no femto-pair-v0-two-track-resonance device (with one of the " + << "processXxxMixedEventWithTagging switches enabled) was found in this workflow."; + } + if (doprocessTripletTrackTrackTrack && mTrackTrackTrackVariants.empty()) { + LOG(fatal) << "processTripletTrackTrackTrack is enabled, but no femto-triplet-track-track-track device (with " + << "processMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTripletTrackTrackV0 && mTrackTrackLambdaVariants.empty()) { + LOG(fatal) << "processTripletTrackTrackV0 is enabled, but no femto-triplet-track-track-v0 device (with " + << "processMixedEventWithTagging enabled) was found in this workflow."; + } + if (doprocessTripletTrackTrackCascade && mTrackTrackXiVariants.empty() && mTrackTrackOmegaVariants.empty()) { + LOG(fatal) << "processTripletTrackTrackCascade is enabled, but no femto-triplet-track-track-cascade device (with " + << "processXiMixedEventWithTagging/processOmegaMixedEventWithTagging enabled) was found in this workflow."; + } + if (!roles.empty() && roles.back().bit >= collisiontagger::MaxTagBits) { + LOG(fatal) << "Too many tag bits requested (" << (roles.back().bit + 1) << "); collision tagger supports up to " << collisiontagger::MaxTagBits << "."; + } + } + + // `tracksThisCol` arrives already sliced to `col` (see FemtoCollision comment above) -- the + // entire per-collision tagging step is one call to tagAndProduce(), which builds the tag + // bitset, runs the same-type-pair check (both track roles' selections, or 2 distinct entries + // for a same-species pair) against every variant, and produces the row. + void processTrackTrack(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol) + { + collisiontagger::tagAndProduce(producedTags, mTrackTrackVariants, tracksThisCol, + [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackTrack, "Tag collisions for femto-pair-track-track wagon(s)", false); + + // Two species checked independently, both contributing to the one tag this collision gets -- + // can't collapse to a single tagAndProduce() call like processTrackTrack above (that helper + // assumes exactly one check per row), but each cross-type check (track role + v0 role) is + // still just one call to tagCrossVariants(). + void processTrackV0(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoLambdas const& lambdasThisCol, FemtoK0shorts const& k0shortsThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + if (!mTrackLambdaVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackLambdaVariants, tracksThisCol, trackPassesFn, lambdasThisCol, + [](auto const& lambda, auto const& sel) { return collisiontagger::lambdaPasses(lambda, sel); }); + } + if (!mTrackK0shortVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackK0shortVariants, tracksThisCol, trackPassesFn, k0shortsThisCol, + [](auto const& k0short, auto const& sel) { return collisiontagger::k0shortPasses(k0short, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackV0, "Tag collisions for femto-pair-track-v0 wagon(s)", false); + + void processTrackCascade(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoXis const& xisThisCol, FemtoOmegas const& omegasThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + auto cascadePassesFn = [](auto const& cascade, auto const& sel) { return collisiontagger::lambdaPasses(cascade, sel); }; + if (!mTrackXiVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackXiVariants, tracksThisCol, trackPassesFn, xisThisCol, cascadePassesFn); + } + if (!mTrackOmegaVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackOmegaVariants, tracksThisCol, trackPassesFn, omegasThisCol, cascadePassesFn); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackCascade, "Tag collisions for femto-pair-track-cascade wagon(s)", false); + + // Sigma and SigmaPlus (kink) tables are always filled together by the producer, but SigmaPlusPcm + // comes from a mutually exclusive producer process function (processTracksSigmaPlusPcmsRun3pp/Mc) -- + // so it gets its own process function below instead of joining this one, otherwise enabling this + // process would force the producer to supply a table combination it can never actually produce. + void processTrackKink(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoSigmas const& sigmasThisCol, FemtoSigmaPlus const& sigmaPlusThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + if (!mTrackSigmaVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackSigmaVariants, tracksThisCol, trackPassesFn, sigmasThisCol, + [](auto const& sigma, auto const& sel) { return collisiontagger::lambdaPasses(sigma, sel); }); + } + if (!mTrackSigmaPlusVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackSigmaPlusVariants, tracksThisCol, trackPassesFn, sigmaPlusThisCol, + [](auto const& sigmaPlus, auto const& sel) { return collisiontagger::lambdaPasses(sigmaPlus, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackKink, "Tag collisions for femto-pair-track-kink wagon(s) (Sigma/SigmaPlus)", false); + + void processTrackSigmaPlusPcm(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoSigmaPlusPcm const& sigmaPlusPcmThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackSigmaPlusPcmVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, sigmaPlusPcmThisCol, [](auto const& sigmaPlusPcm, auto const& sel) { return collisiontagger::kinkPcmPasses(sigmaPlusPcm, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackSigmaPlusPcm, "Tag collisions for femto-pair-track-kink wagon(s) (SigmaPlusPcm)", false); + + // D0 and Lc are also mutually exclusive on the producer side (separate process functions, counted + // together in its own "exactly one process enabled" check), so each gets its own process function + // here too, for the same reason as SigmaPlusPcm above. + void processTrackD0(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoD0s const& d0sThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackD0Variants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, d0sThisCol, [](auto const& d0, auto const& sel) { return collisiontagger::lambdaPasses(d0, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackD0, "Tag collisions for femto-pair-track-charm-hadron wagon(s) (D0)", false); + + void processTrackLc(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoLcs const& lcsThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossVariants(tag, mTrackLcVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, lcsThisCol, [](auto const& lc, auto const& sel) { return collisiontagger::lambdaPasses(lc, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackLc, "Tag collisions for femto-pair-track-charm-hadron wagon(s) (Lc)", false); + + // No track table involved here at all -- v0-v0 is checked purely between the two v0 tables + // themselves (lambda/k0short), same as D0D0 below is checked purely within FemtoD0s. + void processV0V0(FemtoCollision const& /*col*/, FemtoLambdas const& lambdasThisCol, FemtoK0shorts const& k0shortsThisCol) + { + collisiontagger::CollisionTagBitset tag; + if (!mLambdaLambdaVariants.empty()) { + collisiontagger::tagPairVariants(tag, mLambdaLambdaVariants, lambdasThisCol, + [](auto const& v0, auto const& sel) { return collisiontagger::lambdaPasses(v0, sel); }); + } + if (!mK0shortK0shortVariants.empty()) { + collisiontagger::tagPairVariants(tag, mK0shortK0shortVariants, k0shortsThisCol, + [](auto const& v0, auto const& sel) { return collisiontagger::k0shortPasses(v0, sel); }); + } + if (!mLambdaK0shortVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mLambdaK0shortVariants, lambdasThisCol, [](auto const& v0, auto const& sel) { return collisiontagger::lambdaPasses(v0, sel); }, k0shortsThisCol, [](auto const& v0, auto const& sel) { return collisiontagger::k0shortPasses(v0, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processV0V0, "Tag collisions for femto-pair-v0-v0 wagon(s)", false); + + void processD0D0(FemtoCollision const& /*col*/, FemtoD0s const& d0sThisCol) + { + collisiontagger::tagAndProduce(producedTags, mD0D0Variants, d0sThisCol, + [](auto const& d0, auto const& sel) { return collisiontagger::lambdaPasses(d0, sel); }); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processD0D0, "Tag collisions for femto-pair-d0-d0 wagon(s)", false); + + // femto-producer-resonances has no mutual-exclusivity gate between Phi/Kstar0/Rho0 (all three + // default to enabled, built from the same track pool with different PID hypotheses), so they can + // safely be combined into one process function here too. + void processTrackTwoTrackResonance(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoPhis const& phisThisCol, FemtoKstar0s const& kstar0sThisCol, FemtoRho0s const& rho0sThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + if (!mTrackPhiVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackPhiVariants, tracksThisCol, trackPassesFn, phisThisCol, + [](auto const& phi, auto const& sel) { return collisiontagger::twoTrackResonancePasses(phi, sel); }); + } + if (!mTrackKstar0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackKstar0Variants, tracksThisCol, trackPassesFn, kstar0sThisCol, + [](auto const& kstar0, auto const& sel) { return collisiontagger::kstar0Passes(kstar0, sel); }); + } + if (!mTrackRho0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackRho0Variants, tracksThisCol, trackPassesFn, rho0sThisCol, + [](auto const& rho0, auto const& sel) { return collisiontagger::twoTrackResonancePasses(rho0, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackTwoTrackResonance, "Tag collisions for femto-pair-track-two-track-resonance wagon(s)", false); + + // femto-producer-two-photon-resonances also has no mutual-exclusivity gate between Pi0/Eta. + void processTrackTwoPhotonResonance(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoPi0s const& pi0sThisCol, FemtoEtas const& etasThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + if (!mTrackPi0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackPi0Variants, tracksThisCol, trackPassesFn, pi0sThisCol, + [](auto const& pi0, auto const& sel) { return collisiontagger::twoPhotonResonancePasses(pi0, sel); }); + } + if (!mTrackEtaVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mTrackEtaVariants, tracksThisCol, trackPassesFn, etasThisCol, + [](auto const& eta, auto const& sel) { return collisiontagger::twoPhotonResonancePasses(eta, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTrackTwoPhotonResonance, "Tag collisions for femto-pair-track-two-photon-resonance wagon(s)", false); + + // No track table involved here at all -- same as processV0V0 above, checked purely between the + // v0 tables (lambda/k0short) and the resonance tables (phi/kstar0/rho0) themselves. + void processV0TwoTrackResonance(FemtoCollision const& /*col*/, FemtoLambdas const& lambdasThisCol, FemtoK0shorts const& k0shortsThisCol, FemtoPhis const& phisThisCol, FemtoKstar0s const& kstar0sThisCol, FemtoRho0s const& rho0sThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto lambdaPassesFn = [](auto const& lambda, auto const& sel) { return collisiontagger::lambdaPasses(lambda, sel); }; + auto k0shortPassesFn = [](auto const& k0short, auto const& sel) { return collisiontagger::k0shortPasses(k0short, sel); }; + if (!mLambdaPhiVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mLambdaPhiVariants, lambdasThisCol, lambdaPassesFn, phisThisCol, + [](auto const& phi, auto const& sel) { return collisiontagger::twoTrackResonancePasses(phi, sel); }); + } + if (!mLambdaKstar0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mLambdaKstar0Variants, lambdasThisCol, lambdaPassesFn, kstar0sThisCol, + [](auto const& kstar0, auto const& sel) { return collisiontagger::kstar0Passes(kstar0, sel); }); + } + if (!mLambdaRho0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mLambdaRho0Variants, lambdasThisCol, lambdaPassesFn, rho0sThisCol, + [](auto const& rho0, auto const& sel) { return collisiontagger::twoTrackResonancePasses(rho0, sel); }); + } + if (!mK0shortPhiVariants.empty()) { + collisiontagger::tagCrossVariants(tag, mK0shortPhiVariants, k0shortsThisCol, k0shortPassesFn, phisThisCol, + [](auto const& phi, auto const& sel) { return collisiontagger::twoTrackResonancePasses(phi, sel); }); + } + if (!mK0shortKstar0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mK0shortKstar0Variants, k0shortsThisCol, k0shortPassesFn, kstar0sThisCol, + [](auto const& kstar0, auto const& sel) { return collisiontagger::kstar0Passes(kstar0, sel); }); + } + if (!mK0shortRho0Variants.empty()) { + collisiontagger::tagCrossVariants(tag, mK0shortRho0Variants, k0shortsThisCol, k0shortPassesFn, rho0sThisCol, + [](auto const& rho0, auto const& sel) { return collisiontagger::twoTrackResonancePasses(rho0, sel); }); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processV0TwoTrackResonance, "Tag collisions for femto-pair-v0-two-track-resonance wagon(s)", false); + + void processTripletTrackTrackTrack(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagTripletVariants(tag, mTrackTrackTrackVariants, tracksThisCol, + [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTripletTrackTrackTrack, "Tag collisions for femto-triplet-track-track-track wagon(s)", false); + + void processTripletTrackTrackV0(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoLambdas const& lambdasThisCol) + { + collisiontagger::CollisionTagBitset tag; + collisiontagger::tagCrossTripletVariants(tag, mTrackTrackLambdaVariants, tracksThisCol, [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }, lambdasThisCol, [](auto const& lambda, auto const& sel) { return collisiontagger::lambdaPasses(lambda, sel); }); + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTripletTrackTrackV0, "Tag collisions for femto-triplet-track-track-v0 wagon(s)", false); + + void processTripletTrackTrackCascade(FemtoCollision const& /*col*/, FemtoTracks const& tracksThisCol, FemtoXis const& xisThisCol, FemtoOmegas const& omegasThisCol) + { + collisiontagger::CollisionTagBitset tag; + auto trackPassesFn = [](auto const& track, auto const& sel) { return collisiontagger::trackPasses(track, sel); }; + auto cascadePassesFn = [](auto const& cascade, auto const& sel) { return collisiontagger::lambdaPasses(cascade, sel); }; + if (!mTrackTrackXiVariants.empty()) { + collisiontagger::tagCrossTripletVariants(tag, mTrackTrackXiVariants, tracksThisCol, trackPassesFn, xisThisCol, cascadePassesFn); + } + if (!mTrackTrackOmegaVariants.empty()) { + collisiontagger::tagCrossTripletVariants(tag, mTrackTrackOmegaVariants, tracksThisCol, trackPassesFn, omegasThisCol, cascadePassesFn); + } + producedTags(static_cast(tag.to_ullong())); + } + PROCESS_SWITCH(FemtoProducerCollisionTagger, processTripletTrackTrackCascade, "Tag collisions for femto-triplet-track-track-cascade wagon(s)", false); +}; + +o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& context) +{ + o2::framework::WorkflowSpec workflow{ + o2::framework::adaptAnalysisTask(context), + }; + return workflow; +} diff --git a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx index 6b433496642..28dada11562 100644 --- a/PWGCF/Femto/Tasks/femtoPairD0D0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairD0D0.cxx @@ -18,6 +18,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -32,11 +33,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -68,6 +71,10 @@ struct FemtoPairD0D0 { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by processMixedEventWithTagging. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -75,6 +82,9 @@ struct FemtoPairD0D0 { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processMixedEventWithTagging below. + collisiontagger::ConfTagging confTaggingD0D0; + // setup for daughters // separate binning per d0 slot, so that both hypotheses can be binned independently trackhistmanager::ConfD01PosDauBinning confD01PosDauBinning; @@ -141,9 +151,9 @@ struct FemtoPairD0D0 { closepairrejection::ConfCprD0DaugherD0DaughterPos confCprPos; closepairrejection::ConfCprD0DaugherD0DaughterNeg confCprNeg; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessSameEvent || doprocessMixedEvent; + bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessMixedEventWithTagging; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; if (processData && processMc) { @@ -153,6 +163,19 @@ struct FemtoPairD0D0 { LOG(fatal) << "Neither data nor mc processing is enabled. Breaking..."; } + if (doprocessMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingD0D0.Role); + if (bit < 0) { + LOG(fatal) << "processMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processD0D0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingD0D0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (D0D0): " << bit; + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -208,6 +231,16 @@ struct FemtoPairD0D0 { } PROCESS_SWITCH(FemtoPairD0D0, processSameEventMc, "Enable processing same event processing for d0-d0 with mc information", false); + // Opt-in counterpart of processMixedEvent() below -- same "Partition declared and bound inside + // process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingD0D0.internalTagMask); + taggedCollisions.bindTable(cols); + pairD0D0Builder.processMixedEvent(taggedCollisions, tracks, d0Partition1, d0Partition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairD0D0, processMixedEventWithTagging, "Enable processing mixed event processing for d0-d0, pre-filtered using the collision tagger", false); + void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) { pairD0D0Builder.processMixedEvent(cols, tracks, d0Partition1, d0Partition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx index 0f190a841cf..dc27468a21b 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCascade.cxx @@ -18,6 +18,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -33,11 +34,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -56,6 +59,10 @@ struct FemtoPairTrackCascade { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in FColTags , only needed by processXiMixedEventWithTagging/processOmegaMixedEventWithTagging + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + using FemtoTracks = o2::soa::Join; using FemtoXis = o2::soa::Join; using FemtoOmegas = o2::soa::Join; @@ -78,6 +85,11 @@ struct FemtoPairTrackCascade { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processXiMixedEventWithTagging/processOmegaMixedEventWithTagging -- + // two separate instances since Xi and Omega are tagged independently (see init()). + collisiontagger::ConfTagging confTaggingXi; + collisiontagger::ConfTagging confTaggingOmega; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -185,9 +197,9 @@ struct FemtoPairTrackCascade { closepairrejection::ConfCprTrackCascadeBachelor confCprBachelor; closepairrejection::ConfCprTrackV0Daughter confCprV0Daughter; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessOmegaSameEvent || doprocessOmegaMixedEvent; + bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessOmegaSameEvent || doprocessOmegaMixedEvent || doprocessXiMixedEventWithTagging || doprocessOmegaMixedEventWithTagging; bool processFitVars = doprocessXiSameEventWithFitVars || doprocessXiMixedEventWithFitVars || doprocessOmegaSameEventWithFitVars || doprocessOmegaMixedEventWithFitVars; bool processMc = doprocessXiSameEventMc || doprocessXiMixedEventMc || doprocessOmegaSameEventMc || doprocessOmegaMixedEventMc; @@ -195,8 +207,33 @@ struct FemtoPairTrackCascade { LOG(fatal) << "More than one of data, FitVars or mc processing is activated. Breaking..."; } - bool processXi = doprocessXiSameEvent || doprocessXiSameEventWithFitVars || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventWithFitVars || doprocessXiMixedEventMc; - bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventWithFitVars || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventWithFitVars || doprocessOmegaMixedEventMc; + bool processXi = doprocessXiSameEvent || doprocessXiSameEventWithFitVars || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventWithFitVars || doprocessXiMixedEventMc || doprocessXiMixedEventWithTagging; + bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventWithFitVars || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventWithFitVars || doprocessOmegaMixedEventMc || doprocessOmegaMixedEventWithTagging; + + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessXiMixedEventWithTagging || doprocessOmegaMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessXiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingXi.Role); + if (bit < 0) { + LOG(fatal) << "processXiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackCascade enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingXi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Xi): " << bit; + } + if (doprocessOmegaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingOmega.Role); + if (bit < 0) { + LOG(fatal) << "processOmegaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackCascade enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingOmega.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Omega): " << bit; + } + } if (processXi && processOmega) { LOG(fatal) << "Both xi-track and omega-track processing is enabled. Breaking..."; @@ -280,6 +317,17 @@ struct FemtoPairTrackCascade { } PROCESS_SWITCH(FemtoPairTrackCascade, processXiSameEventMc, "Enable processing same event processing for tracks and xis with mc information", false); + // Opt-in counterpart of processXiMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. Requires + // a femto-collision-tagger device (processTrackCascade enabled) in the same workflow -- see init(). + void processXiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoXis const& /*xis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingXi.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackXiBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processXiMixedEventWithTagging, "Enable processing mixed event processing for tracks and xis, pre-filtered using the collision tagger", false); + void processXiMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoXis const& /*xis*/) { pairTrackXiBuilder.processMixedEvent(cols, tracks, trackPartition, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -316,6 +364,15 @@ struct FemtoPairTrackCascade { } PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaSameEventMc, "Enable processing same event processing for tracks and omegas with mc information", false); + // Opt-in counterpart of processOmegaMixedEvent() below -- same idiom as processXiMixedEventWithTagging above. + void processOmegaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoOmegas const& /*omegas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingOmega.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackOmegaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCascade, processOmegaMixedEventWithTagging, "Enable processing mixed event processing for tracks and omegas, pre-filtered using the collision tagger", false); + void processOmegaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoOmegas const& /*omegas*/) { pairTrackOmegaBuilder.processMixedEvent(cols, tracks, trackPartition, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx index 172c3c2ffc8..2e0370d99f9 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackCharmHadron.cxx @@ -18,6 +18,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -33,11 +34,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -60,6 +63,10 @@ struct FemtoPairTrackCharmHadron { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in: carries FColTags, only needed by processD0MixedEventWithTagging/processLcMixedEventWithTagging. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + using FemtoTracksWithLabel = o2::soa::Join; using FemtoD0sWithLabel = o2::soa::Join; using FemtoLcsWithLabel = o2::soa::Join; @@ -72,6 +79,11 @@ struct FemtoPairTrackCharmHadron { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processD0MixedEventWithTagging/processLcMixedEventWithTagging -- two + // separate instances since D0 and Lc are tagged independently (see init()). + collisiontagger::ConfTagging confTaggingD0; + collisiontagger::ConfTagging confTaggingLc; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -174,11 +186,11 @@ struct FemtoPairTrackCharmHadron { closepairrejection::ConfCprTrackLcKaon confCprLcKaon; closepairrejection::ConfCprTrackLcPion confCprLcPion; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processD0 = doprocessD0SameEvent || doprocessD0MixedEvent; + bool processD0 = doprocessD0SameEvent || doprocessD0MixedEvent || doprocessD0MixedEventWithTagging; bool processD0Mc = doprocessD0SameEventMc || doprocessD0MixedEventMc; - bool processLc = doprocessLcSameEvent || doprocessLcMixedEvent; + bool processLc = doprocessLcSameEvent || doprocessLcMixedEvent || doprocessLcMixedEventWithTagging; bool processLcMc = doprocessLcSameEventMc || doprocessLcMixedEventMc; if ((static_cast(processD0) + static_cast(processD0Mc) + static_cast(processLc) + static_cast(processLcMc)) > 1) { @@ -187,6 +199,31 @@ struct FemtoPairTrackCharmHadron { bool processData = processD0 || processLc; + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessD0MixedEventWithTagging || doprocessLcMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessD0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingD0.Role); + if (bit < 0) { + LOG(fatal) << "processD0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackD0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingD0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (D0): " << bit; + } + if (doprocessLcMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLc.Role); + if (bit < 0) { + LOG(fatal) << "processLcMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackLc enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLc.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Lc): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -255,6 +292,16 @@ struct FemtoPairTrackCharmHadron { } PROCESS_SWITCH(FemtoPairTrackCharmHadron, processD0SameEvent, "Enable processing same event processing for tracks and D0s", true); + // Opt-in counterpart of processD0MixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processD0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingD0.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackD0Builder.processMixedEvent(taggedCollisions, tracks, trackPartition, d0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCharmHadron, processD0MixedEventWithTagging, "Enable processing mixed event processing for tracks and D0s, pre-filtered using the collision tagger", false); + void processD0MixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoD0s const& /*d0s*/) { pairTrackD0Builder.processMixedEvent(cols, tracks, trackPartition, d0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -279,6 +326,15 @@ struct FemtoPairTrackCharmHadron { } PROCESS_SWITCH(FemtoPairTrackCharmHadron, processLcSameEvent, "Enable processing same event processing for tracks and Lcs", false); + // Opt-in counterpart of processLcMixedEvent() below -- same idiom as processD0MixedEventWithTagging above. + void processLcMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLcs const& /*lcs*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLc.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackLcBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, lcPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackCharmHadron, processLcMixedEventWithTagging, "Enable processing mixed event processing for tracks and Lcs, pre-filtered using the collision tagger", false); + void processLcMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLcs const& /*lcs*/) { pairTrackLcBuilder.processMixedEvent(cols, tracks, trackPartition, lcPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx index affa4a1fcff..3c1385d945d 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackKink.cxx @@ -17,6 +17,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/kinkBuilder.h" #include "PWGCF/Femto/Core/kinkHistManager.h" #include "PWGCF/Femto/Core/modes.h" @@ -34,11 +35,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -57,6 +60,10 @@ struct FemtoPairTrackKink { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + using FemtoTracks = o2::soa::Join; using FemtoSigmas = o2::soa::Join; using FemtoSigmaPlus = o2::soa::Join; @@ -76,6 +83,12 @@ struct FemtoPairTrackKink { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- three separate + // instances since Sigma/SigmaPlus/SigmaPlusPcm are tagged independently (see init()). + collisiontagger::ConfTagging confTaggingSigma; + collisiontagger::ConfTagging confTaggingSigmaPlus; + collisiontagger::ConfTagging confTaggingSigmaPlusPcm; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -184,19 +197,19 @@ struct FemtoPairTrackKink { // setup cpr closepairrejection::ConfCprTrackKinkDaughter confCpr; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessSigmaSameEvent || doprocessSigmaMixedEvent || doprocessSigmaPlusSameEvent || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusPcmSameEvent || doprocessSigmaPlusPcmMixedEvent; + bool processData = doprocessSigmaSameEvent || doprocessSigmaMixedEvent || doprocessSigmaPlusSameEvent || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusPcmSameEvent || doprocessSigmaPlusPcmMixedEvent || doprocessSigmaMixedEventWithTagging || doprocessSigmaPlusMixedEventWithTagging || doprocessSigmaPlusPcmMixedEventWithTagging; bool processMc = doprocessSigmaSameEventMc || doprocessSigmaMixedEventMc || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEventMc || doprocessSigmaPlusPcmSameEventMc || doprocessSigmaPlusPcmMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is enabled. Breaking..."; } - bool processSigma = doprocessSigmaSameEvent || doprocessSigmaSameEventMc || doprocessSigmaMixedEvent || doprocessSigmaMixedEventMc; - bool processSigmaPlus = doprocessSigmaPlusSameEvent || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusMixedEventMc; + bool processSigma = doprocessSigmaSameEvent || doprocessSigmaSameEventMc || doprocessSigmaMixedEvent || doprocessSigmaMixedEventMc || doprocessSigmaMixedEventWithTagging; + bool processSigmaPlus = doprocessSigmaPlusSameEvent || doprocessSigmaPlusSameEventMc || doprocessSigmaPlusMixedEvent || doprocessSigmaPlusMixedEventMc || doprocessSigmaPlusMixedEventWithTagging; - bool processSigmaPlusPcm = doprocessSigmaPlusPcmSameEvent || doprocessSigmaPlusPcmSameEventMc || doprocessSigmaPlusPcmMixedEvent || doprocessSigmaPlusPcmMixedEventMc; + bool processSigmaPlusPcm = doprocessSigmaPlusPcmSameEvent || doprocessSigmaPlusPcmSameEventMc || doprocessSigmaPlusPcmMixedEvent || doprocessSigmaPlusPcmMixedEventMc || doprocessSigmaPlusPcmMixedEventWithTagging; if (processSigma && processSigmaPlus) { LOG(fatal) << "Both Sigma-track and SigmaPlus-track processing is enabled. Breaking..."; @@ -205,6 +218,40 @@ struct FemtoPairTrackKink { LOG(fatal) << "Both Sigma/SigmaPlus-track and SigmaPlusPcm-track processing is enabled. Breaking..."; } + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessSigmaMixedEventWithTagging || doprocessSigmaPlusMixedEventWithTagging || doprocessSigmaPlusPcmMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessSigmaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingSigma.Role); + if (bit < 0) { + LOG(fatal) << "processSigmaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackKink enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingSigma.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Sigma): " << bit; + } + if (doprocessSigmaPlusMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingSigmaPlus.Role); + if (bit < 0) { + LOG(fatal) << "processSigmaPlusMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackKink enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingSigmaPlus.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (SigmaPlus): " << bit; + } + if (doprocessSigmaPlusPcmMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingSigmaPlusPcm.Role); + if (bit < 0) { + LOG(fatal) << "processSigmaPlusPcmMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackSigmaPlusPcm enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingSigmaPlusPcm.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (SigmaPlusPcm): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -267,6 +314,16 @@ struct FemtoPairTrackKink { } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaSameEventMc, "Enable processing same event processing for tracks and sigmas with MC information", false); + // Opt-in counterpart of processSigmaMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processSigmaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoSigmas const& /*sigmas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingSigma.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackSigmaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, sigmaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaMixedEventWithTagging, "Enable processing mixed event processing for tracks and sigmas, pre-filtered using the collision tagger", false); + void processSigmaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoSigmas const& /*sigmas*/) { pairTrackSigmaBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -291,6 +348,15 @@ struct FemtoPairTrackKink { } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusSameEventMc, "Enable processing same event processing for tracks and sigma plus with MC information", false); + // Opt-in counterpart of processSigmaPlusMixedEvent() below -- same idiom as processSigmaMixedEventWithTagging above. + void processSigmaPlusMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoSigmaPlus const& /*sigmaplus*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingSigmaPlus.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackSigmaPlusBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, sigmaPlusPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusMixedEventWithTagging, "Enable processing mixed event processing for tracks and sigma plus, pre-filtered using the collision tagger", false); + void processSigmaPlusMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoSigmaPlus const& /*sigmaplus*/) { pairTrackSigmaPlusBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPlusPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -315,6 +381,15 @@ struct FemtoPairTrackKink { } PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmSameEventMc, "Enable processing same event processing for tracks and sigma plus (PCM) with MC information", false); + // Opt-in counterpart of processSigmaPlusPcmMixedEvent() below -- same idiom as processSigmaMixedEventWithTagging above. + void processSigmaPlusPcmMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoSigmaPlusPcm const& /*sigmapluspcm*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingSigmaPlusPcm.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackSigmaPlusPcmBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, sigmaPlusPcmPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackKink, processSigmaPlusPcmMixedEventWithTagging, "Enable processing mixed event processing for tracks and sigma plus (PCM), pre-filtered using the collision tagger", false); + void processSigmaPlusPcmMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoSigmaPlusPcm const& /*sigmapluspcm*/) { pairTrackSigmaPlusPcmBuilder.processMixedEvent(cols, tracks, trackPartition, sigmaPlusPcmPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx index 660f368891a..11379064023 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTrack.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -32,11 +33,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -55,11 +58,26 @@ struct FemtoPairTrackTrack { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in: carries FColTags (see TableProducer/femtoProducerCollisionTagger.cxx), only needed by + // processMixedEventWithTagging. collisionFilter below (declared on the base FemtoCollisions + // columns) auto-applies to this joined/filtered type too, exactly as it already does for + // FilteredFemtoCollisionsWithLabel above; the extra tag-bit condition is then applied via a + // Partition declared locally inside processMixedEventWithTagging itself (see there), the same + // "Partition declared and bound inside process()" idiom as Tutorials/src/eventMixing.cxx's + // MixedEventsPartitionedTracks -- so nothing here needs a second top-level Filter. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + // collision table with event shape, same filter as standard collisions using FemtoCollisionsWithEventShape = o2::soa::Join; using FilteredFemtoCollisionsWithEventShape = o2::soa::Filtered; using FilteredFemtoCollisionWithEventShape = FilteredFemtoCollisionsWithEventShape::iterator; + // opt-in: carries both FColShapes and FColTags, for processMixedEventWithTaggingAndEventShape -- + // same collisionFilter auto-apply reasoning as FemtoCollisionsWithTags above. + using FemtoCollisionsWithEventShapeAndTags = o2::soa::Join; + using FilteredFemtoCollisionsWithEventShapeAndTags = o2::soa::Filtered; + using FemtoTracks = o2::soa::Join; // for analysis which require particles at high pt, add tof mass so sidebands can be used @@ -84,6 +102,10 @@ struct FemtoPairTrackTrack { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processMixedEventWithTagging -- see processMixedEventWithTagging for + // how it's used. + collisiontagger::ConfTagging confTagging; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelections1; particlecleaner::ConfTrackCleaner1 confTrackCleaner1; @@ -140,14 +162,32 @@ struct FemtoPairTrackTrack { o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrack", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventWithMass) + static_cast(doprocessSameEventWithEventShape) + static_cast(doprocessSameEventWithFitVars) + static_cast(doprocessSameEventMc)) > 1 || - (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithMass) + static_cast(doprocessMixedEventWithEventShape) + static_cast(doprocessMixedEventWithFitVars) + static_cast(doprocessMixedEventMc)) > 1) { + (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithMass) + static_cast(doprocessMixedEventWithEventShape) + static_cast(doprocessMixedEventWithFitVars) + static_cast(doprocessMixedEventMc) + static_cast(doprocessMixedEventWithTagging) + static_cast(doprocessMixedEventWithTaggingAndMass) + static_cast(doprocessMixedEventWithTaggingAndEventShape) + static_cast(doprocessMixedEventWithTaggingAndFitVars)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } - bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessSameEventWithMass || doprocessMixedEventWithMass || doprocessSameEventWithEventShape || doprocessMixedEventWithEventShape; - bool processFitVars = doprocessSameEventWithFitVars || doprocessMixedEventWithFitVars; + + if (doprocessMixedEventWithTagging || doprocessMixedEventWithTaggingAndMass || doprocessMixedEventWithTaggingAndEventShape || doprocessMixedEventWithTaggingAndFitVars) { + // discoverTaggedRoles() is the same canonical device-list walk femtoProducerCollisionTagger.cxx uses + // to assign bits -- replaying it here (rather than asking the tagger directly, which has no + // way to communicate a runtime-computed value back out through RunningWorkflowInfo) is how + // this task finds out which bit(s) ended up being its own. + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTagging.Role); + if (bit < 0) { + LOG(fatal) << "A processMixedEventWithTagging* function is enabled, but no femto-collision-tagger " + << "device (with processTrackTrack enabled) tagging this device (" << ownDevice.name + << ") was found in this workflow."; + } + confTagging.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << ": " << bit; + } + bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessSameEventWithMass || doprocessMixedEventWithMass || doprocessSameEventWithEventShape || doprocessMixedEventWithEventShape || doprocessMixedEventWithTagging || doprocessMixedEventWithTaggingAndMass || doprocessMixedEventWithTaggingAndEventShape; + bool processFitVars = doprocessSameEventWithFitVars || doprocessMixedEventWithFitVars || doprocessMixedEventWithTaggingAndFitVars; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; if (static_cast(processData) + static_cast(processFitVars) + static_cast(processMc) > 1) { LOG(fatal) << "More than one of data, purity or mc processing is activated. Breaking..."; @@ -220,6 +260,55 @@ struct FemtoPairTrackTrack { } PROCESS_SWITCH(FemtoPairTrackTrack, processSameEventMc, "Enable processing same event processing", false); + // Opt-in counterpart of processMixedEvent() below: `cols` already gets the standard + // collisionFilter applied (Filtered picks it up automatically, exactly + // as FilteredFemtoCollisionsWithLabel above does). On top of that, a Partition is declared and + // bound right here, locally, to pick out only the collisions the tagger found a qualifying pair + // in -- the same "declare + bindTable() inside process()" idiom as + // Tutorials/src/eventMixing.cxx's MixedEventsPartitionedTracks, so this needs no change to + // pairProcessHelpers.h/pairBuilder.h: processMixedEvent is called exactly as below, just with a + // further-filtered collision view. Requires a femto-collision-tagger device (processTrackTrack + // enabled) in the same workflow -- see init(). + void processMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTagging.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithTagging, "Enable processing mixed event processing, pre-filtered using the collision tagger", false); + + // Same tagging filter as processMixedEventWithTagging above, carrying FemtoTracksWithMass + // instead of plain FemtoTracks -- the tag bit means the same thing regardless of which track + // table variant is used downstream, since it's computed from the same TrackSelection1/2 cut + // values either way. + void processMixedEventWithTaggingAndMass(FilteredFemtoCollisionsWithTags const& cols, FemtoTracksWithMass const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTagging.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackWithMassPartition1, trackWithMassPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithTaggingAndMass, "Enable processing mixed event processing, pre-filtered using the collision tagger (with track masses)", false); + + // Same tagging filter as processMixedEventWithTagging above, on a collision table that also + // carries FColShapes, needed for the kVtxCentEventPlaneAngle mixing policy. + void processMixedEventWithTaggingAndEventShape(FilteredFemtoCollisionsWithEventShapeAndTags const& cols, FemtoTracks const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTagging.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithTaggingAndEventShape, "Enable processing mixed event processing, pre-filtered using the collision tagger, with event shape information", false); + + // Same tagging filter as processMixedEventWithTagging above, carrying FemtoTracksWithFitVars + // instead of plain FemtoTracks -- same rationale as processMixedEventWithTaggingAndMass above. + void processMixedEventWithTaggingAndFitVars(FilteredFemtoCollisionsWithTags const& cols, FemtoTracksWithFitVars const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTagging.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackWithFitVarsPartition1, trackWithFitVarsPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); + } + PROCESS_SWITCH(FemtoPairTrackTrack, processMixedEventWithTaggingAndFitVars, "Enable processing mixed event processing, pre-filtered using the collision tagger (with FitVars tables)", false); + void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks) { pairTrackTrackBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent, mixBinsVtxCentEventPlaneAngle); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx index 66a4cea7412..24fa1adee36 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoPhotonResonance.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -33,10 +34,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -58,6 +61,10 @@ struct FemtoPairTrackTwoPhotonResonance { using FemtoPi0s = o2::soa::Join; using FemtoEtas = o2::soa::Join; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -65,6 +72,12 @@ struct FemtoPairTrackTwoPhotonResonance { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- two separate + // instances since Pi0/Eta are mutually exclusive on one instance, but tagged independently + // (see init()). + collisiontagger::ConfTagging confTaggingPi0; + collisiontagger::ConfTagging confTaggingEta; + // setup tracks trackbuilder::ConfTrackSelection1 trackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -141,13 +154,40 @@ struct FemtoPairTrackTwoPhotonResonance { // table, so there is no trajectory to compare against a track's -- see closePairRejection.h) closepairrejection::ConfCprTrackTwoPhotonResonanceDaughter confCpr; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if (static_cast(doprocessPi0SameEvent || doprocessPi0MixedEvent) + static_cast(doprocessEtaSameEvent || doprocessEtaMixedEvent) > 1) { + if (static_cast(doprocessPi0SameEvent || doprocessPi0MixedEvent || doprocessPi0MixedEventWithTagging) + + static_cast(doprocessEtaSameEvent || doprocessEtaMixedEvent || doprocessEtaMixedEventWithTagging) > + 1) { LOG(fatal) << "Can only process SE/ME for pi0-tracks or eta-tracks"; } + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessPi0MixedEventWithTagging || doprocessEtaMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessPi0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingPi0.Role); + if (bit < 0) { + LOG(fatal) << "processPi0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoPhotonResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingPi0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Pi0): " << bit; + } + if (doprocessEtaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingEta.Role); + if (bit < 0) { + LOG(fatal) << "processEtaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoPhotonResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingEta.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Eta): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -162,14 +202,14 @@ struct FemtoPairTrackTwoPhotonResonance { auto pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); // setup for pi0 - if (doprocessPi0SameEvent || doprocessPi0MixedEvent) { + if (doprocessPi0SameEvent || doprocessPi0MixedEvent || doprocessPi0MixedEventWithTagging) { auto pi0HistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceHistSpecMap(confPi0Binning); auto pairTrackPi0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); pairTrackPi0Builder.init(&hRegistry, confCollisionBinning, trackSelection, pi0Selection, confCpr, confMixing, confPairBinning, confPairCuts, confPhotonDauQaBinning, colHistSpec, trackHistSpec, pi0HistSpec, dauHistSpec, pairTrackPi0HistSpec, cprHistSpec, pairCleanerHistSpec); } // setup for eta - if (doprocessEtaSameEvent || doprocessEtaMixedEvent) { + if (doprocessEtaSameEvent || doprocessEtaMixedEvent || doprocessEtaMixedEventWithTagging) { auto etaHistSpec = twophotonresonancehistmanager::makeTwoPhotonResonanceHistSpecMap(confEtaBinning); auto pairTrackEtaHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); pairTrackEtaBuilder.init(&hRegistry, confCollisionBinning, trackSelection, etaSelection, confCpr, confMixing, confPairBinning, confPairCuts, confPhotonDauQaBinning, colHistSpec, trackHistSpec, etaHistSpec, dauHistSpec, pairTrackEtaHistSpec, cprHistSpec, pairCleanerHistSpec); @@ -184,6 +224,16 @@ struct FemtoPairTrackTwoPhotonResonance { } PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processPi0SameEvent, "Enable same event processing for tracks and pi0s", true); + // Opt-in counterpart of processPi0MixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processPi0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingPi0.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackPi0Builder.processMixedEvent(taggedCollisions, trackPartition, pi0Partition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processPi0MixedEventWithTagging, "Enable mixed event processing for tracks and pi0s, pre-filtered using the collision tagger", false); + void processPi0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& /*tracks*/, FemtoPi0s const& /*pi0s*/, FemtoPhotons const& photons) { pairTrackPi0Builder.processMixedEvent(cols, trackPartition, pi0Partition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -196,6 +246,16 @@ struct FemtoPairTrackTwoPhotonResonance { } PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processEtaSameEvent, "Enable same event processing for tracks and etas", false); + // Opt-in counterpart of processEtaMixedEvent() below -- same idiom as + // processPi0MixedEventWithTagging above. + void processEtaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& /*tracks*/, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingEta.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackEtaBuilder.processMixedEvent(taggedCollisions, trackPartition, etaPartition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoPhotonResonance, processEtaMixedEventWithTagging, "Enable mixed event processing for tracks and etas, pre-filtered using the collision tagger", false); + void processEtaMixedEvent(FilteredCollisions const& cols, FemtoTracks const& /*tracks*/, FemtoEtas const& /*etas*/, FemtoPhotons const& photons) { pairTrackEtaBuilder.processMixedEvent(cols, trackPartition, etaPartition, photons, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx index 25602952698..bb46e8cbc1a 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackTwoTrackResonance.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -32,10 +33,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -57,6 +60,10 @@ struct FemtoPairTrackTwoTrackResonance { using FemtoKstar0s = o2::soa::Join; using FemtoRho0s = o2::soa::Join; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -64,6 +71,13 @@ struct FemtoPairTrackTwoTrackResonance { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- three separate + // instances since Phi/Kstar0/Rho0 are mutually exclusive on one instance, but tagged + // independently (see init()). + collisiontagger::ConfTagging confTaggingPhi; + collisiontagger::ConfTagging confTaggingKstar0; + collisiontagger::ConfTagging confTaggingRho0; + // setup tracks trackbuilder::ConfTrackSelection1 trackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -162,13 +176,50 @@ struct FemtoPairTrackTwoTrackResonance { // setup cpr closepairrejection::ConfCprTrackResonanceDaughter confCpr; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if ((static_cast(doprocessPhiSameEvent || doprocessPhiMixedEvent) + static_cast(doprocessKstar0SameEvent || doprocessKstar0MixedEvent)) + static_cast(doprocessRho0SameEvent || doprocessRho0MixedEvent) > 1) { + if ((static_cast(doprocessPhiSameEvent || doprocessPhiMixedEvent || doprocessPhiMixedEventWithTagging) + + static_cast(doprocessKstar0SameEvent || doprocessKstar0MixedEvent || doprocessKstar0MixedEventWithTagging)) + + static_cast(doprocessRho0SameEvent || doprocessRho0MixedEvent || doprocessRho0MixedEventWithTagging) > + 1) { LOG(fatal) << "Can only process SE/ME for phi-tracks, rho-tracks or k0*-tracks"; } + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessPhiMixedEventWithTagging || doprocessKstar0MixedEventWithTagging || doprocessRho0MixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessPhiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingPhi.Role); + if (bit < 0) { + LOG(fatal) << "processPhiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingPhi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Phi): " << bit; + } + if (doprocessKstar0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingKstar0.Role); + if (bit < 0) { + LOG(fatal) << "processKstar0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingKstar0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Kstar0): " << bit; + } + if (doprocessRho0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingRho0.Role); + if (bit < 0) { + LOG(fatal) << "processRho0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTrackTwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingRho0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Rho0): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -184,21 +235,21 @@ struct FemtoPairTrackTwoTrackResonance { auto pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); // setup for phi - if (doprocessPhiSameEvent || doprocessPhiMixedEvent) { + if (doprocessPhiSameEvent || doprocessPhiMixedEvent || doprocessPhiMixedEventWithTagging) { auto phiHistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confPhiBinning); auto pairTrackPhiHistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); pairTrackPhiBuilder.init(&hRegistry, confCollisionBinning, trackSelection, phiSelection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, phiHistSpec, posDauSpec, negDauSpec, pairTrackPhiHistSpec, cprHistSpec, pairCleanerHistSpec); } // setup for kstar0 - if (doprocessKstar0SameEvent || doprocessKstar0MixedEvent) { + if (doprocessKstar0SameEvent || doprocessKstar0MixedEvent || doprocessKstar0MixedEventWithTagging) { auto kstar0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confKstar0Binning); auto pairTrackKstar0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); pairTrackKstar0Builder.init(&hRegistry, confCollisionBinning, trackSelection, kstar0Selection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, kstar0HistSpec, posDauSpec, negDauSpec, pairTrackKstar0HistSpec, cprHistSpec, pairCleanerHistSpec); } // setup for kstar0 - if (doprocessRho0SameEvent || doprocessRho0MixedEvent) { + if (doprocessRho0SameEvent || doprocessRho0MixedEvent || doprocessRho0MixedEventWithTagging) { auto rho0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confRho0Binning); auto pairTrackRho0HistSpec = pairhistmanager::makePairHistSpecMap(confPairBinning, confMixing); pairTrackRho0Builder.init(&hRegistry, confCollisionBinning, trackSelection, rho0Selection, confCpr, confMixing, confPairBinning, confPairCuts, colHistSpec, trackHistSpec, rho0HistSpec, posDauSpec, negDauSpec, pairTrackRho0HistSpec, cprHistSpec, pairCleanerHistSpec); @@ -213,6 +264,16 @@ struct FemtoPairTrackTwoTrackResonance { } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processPhiSameEvent, "Enable processing same event processing for tracks and phis", true); + // Opt-in counterpart of processPhiMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processPhiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoPhis const& /*phis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingPhi.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackPhiBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processPhiMixedEventWithTagging, "Enable processing mixed event processing for tracks and phis, pre-filtered using the collision tagger", false); + void processPhiMixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoPhis const& /*phis*/) { pairTrackPhiBuilder.processMixedEvent(cols, tracks, trackPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -225,6 +286,16 @@ struct FemtoPairTrackTwoTrackResonance { } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processKstar0SameEvent, "Enable processing same event processing for tracks and kstar0s", false); + // Opt-in counterpart of processKstar0MixedEvent() below -- same idiom as + // processPhiMixedEventWithTagging above. + void processKstar0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoKstar0s const& /*kstar0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingKstar0.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackKstar0Builder.processMixedEvent(taggedCollisions, tracks, trackPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processKstar0MixedEventWithTagging, "Enable processing mixed event processing for tracks and kstar0s, pre-filtered using the collision tagger", false); + void processKstar0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoKstar0s const& /*kstar0s*/) { pairTrackKstar0Builder.processMixedEvent(cols, tracks, trackPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -237,6 +308,16 @@ struct FemtoPairTrackTwoTrackResonance { } PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processRho0SameEvent, "Enable processing same event processing for tracks and rho0s", false); + // Opt-in counterpart of processRho0MixedEvent() below -- same idiom as + // processPhiMixedEventWithTagging above. + void processRho0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoRho0s const& /*rho0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingRho0.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackRho0Builder.processMixedEvent(taggedCollisions, tracks, trackPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackTwoTrackResonance, processRho0MixedEventWithTagging, "Enable processing mixed event processing for tracks and rho0s, pre-filtered using the collision tagger", false); + void processRho0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoRho0s const& /*rho0s*/) { pairTrackRho0Builder.processMixedEvent(cols, tracks, trackPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx index 054e8bc40cf..de4eba27609 100644 --- a/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairTrackV0.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -33,11 +34,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -56,6 +59,12 @@ struct FemtoPairTrackV0 { using FilteredFemtoCollisionsWithLabel = o2::soa::Filtered; using FilteredFemtoCollisionWithLabel = FilteredFemtoCollisionsWithLabel::iterator; + // opt-in: carries FColTags (see TableProducer/femtoProducerCollisionTagger.cxx), only needed by + // processLambdaMixedEventWithTagging/processK0shortMixedEventWithTagging -- same + // collisionFilter auto-apply reasoning as in femtoPairTrackTrack.cxx. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + using FemtoTracks = o2::soa::Join; using FemtoLambdas = o2::soa::Join; using FemtoK0shorts = o2::soa::Join; @@ -78,6 +87,12 @@ struct FemtoPairTrackV0 { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processLambdaMixedEventWithTagging/processK0shortMixedEventWithTagging + // -- two separate instances since Lambda and K0short are tagged independently (see init()) and + // each gets its own tag bit. + collisiontagger::ConfTagging confTaggingLambda; + collisiontagger::ConfTagging confTaggingK0short; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelection; trackhistmanager::ConfTrackBinning1 confTrackBinning; @@ -175,9 +190,9 @@ struct FemtoPairTrackV0 { // setup cpr closepairrejection::ConfCprTrackV0Daughter confCpr; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessLambdaSameEvent || doprocessLambdaMixedEvent || doprocessK0shortSameEvent || doprocessK0shortMixedEvent; + bool processData = doprocessLambdaSameEvent || doprocessLambdaMixedEvent || doprocessK0shortSameEvent || doprocessK0shortMixedEvent || doprocessLambdaMixedEventWithTagging || doprocessK0shortMixedEventWithTagging; bool processFitVars = doprocessLambdaSameEventWithFitVars || doprocessLambdaMixedEventWithFitVars || doprocessK0shortSameEventWithFitVars || doprocessK0shortMixedEventWithFitVars; bool processMc = doprocessLambdaSameEventMc || doprocessLambdaMixedEventMc || doprocessK0shortSameEventMc || doprocessK0shortMixedEventMc; @@ -185,13 +200,42 @@ struct FemtoPairTrackV0 { LOG(fatal) << "More than one of data, FitVars or mc processing is activated. Breaking..."; } - bool processLambda = doprocessLambdaSameEvent || doprocessLambdaSameEventWithFitVars || doprocessLambdaSameEventMc || doprocessLambdaMixedEvent || doprocessLambdaMixedEventWithFitVars || doprocessLambdaMixedEventMc; - bool processK0short = doprocessK0shortSameEvent || doprocessK0shortSameEventWithFitVars || doprocessK0shortSameEventMc || doprocessK0shortMixedEvent || doprocessK0shortMixedEventWithFitVars || doprocessK0shortMixedEventMc; + bool processLambda = doprocessLambdaSameEvent || doprocessLambdaSameEventWithFitVars || doprocessLambdaSameEventMc || doprocessLambdaMixedEvent || doprocessLambdaMixedEventWithFitVars || doprocessLambdaMixedEventMc || doprocessLambdaMixedEventWithTagging; + bool processK0short = doprocessK0shortSameEvent || doprocessK0shortSameEventWithFitVars || doprocessK0shortSameEventMc || doprocessK0shortMixedEvent || doprocessK0shortMixedEventWithFitVars || doprocessK0shortMixedEventMc || doprocessK0shortMixedEventWithTagging; if (processLambda && processK0short) { LOG(fatal) << "Both lambda-track and k0short-track processing is enabled. Breaking..."; } + // Each species discovers its own tag bit independently (see Core/collisionTagger.h): + // same-event-only for one species and tagged-mixed-event for the other is a legitimate + // combination, so neither discovery is gated on the other's process switch. + if (doprocessLambdaMixedEventWithTagging || doprocessK0shortMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessLambdaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambda.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaMixedEventWithTagging is enabled, but no femto-collision-tagger " + << "device (with processTrackV0 enabled) tagging this device (" << ownDevice.name + << ") was found in this workflow."; + } + confTaggingLambda.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Lambda): " << bit; + } + if (doprocessK0shortMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0short.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortMixedEventWithTagging is enabled, but no femto-collision-tagger " + << "device (with processTrackV0 enabled) tagging this device (" << ownDevice.name + << ") was found in this workflow."; + } + confTaggingK0short.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0short): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -263,6 +307,17 @@ struct FemtoPairTrackV0 { } PROCESS_SWITCH(FemtoPairTrackV0, processLambdaSameEventMc, "Enable processing same event processing for tracks and lambdas with MC information", false); + // Opt-in counterpart of processLambdaMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. Requires a + // femto-collision-tagger device (processTrackV0 enabled) in the same workflow -- see init(). + void processLambdaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambda.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackLambdaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackV0, processLambdaMixedEventWithTagging, "Enable processing mixed event processing for tracks and lambdas, pre-filtered using the collision tagger", false); + void processLambdaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambas*/) { pairTrackLambdaBuilder.processMixedEvent(cols, tracks, trackPartition, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -299,6 +354,17 @@ struct FemtoPairTrackV0 { } PROCESS_SWITCH(FemtoPairTrackV0, processK0shortSameEventMc, "Enable processing same event processing for tracks and k0shorts with MC information", false); + // Opt-in counterpart of processK0shortMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. Requires a + // femto-collision-tagger device (processTrackV0 enabled) in the same workflow -- see init(). + void processK0shortMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0short.internalTagMask); + taggedCollisions.bindTable(cols); + pairTrackK0shortBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition, k0shortPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairTrackV0, processK0shortMixedEventWithTagging, "Enable processing mixed event processing for tracks and k0shorts, pre-filtered using the collision tagger", false); + void processK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) { pairTrackK0shortBuilder.processMixedEvent(cols, tracks, trackPartition, k0shortPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx index bfb115b7d87..6bf172bbb95 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0TwoTrackResonance.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -34,10 +35,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -63,6 +66,10 @@ struct FemtoPairV0TwoTrackResonance { using FemtoKstar0s = o2::soa::Join; using FemtoRho0s = o2::soa::Join; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -70,6 +77,16 @@ struct FemtoPairV0TwoTrackResonance { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- six separate + // instances since all six v0-resonance combinations are mutually exclusive on one instance, but + // tagged independently (see init()). + collisiontagger::ConfTagging confTaggingLambdaPhi; + collisiontagger::ConfTagging confTaggingLambdaKstar0; + collisiontagger::ConfTagging confTaggingLambdaRho0; + collisiontagger::ConfTagging confTaggingK0shortPhi; + collisiontagger::ConfTagging confTaggingK0shortKstar0; + collisiontagger::ConfTagging confTaggingK0shortRho0; + // setup for daughters for v0s trackhistmanager::ConfV01PosDauBinning confV0PosDauBinning; trackhistmanager::ConfV01NegDauBinning confV0NegDauBinning; @@ -277,20 +294,82 @@ struct FemtoPairV0TwoTrackResonance { o2::framework::HistogramRegistry hRegistry{"FemtoV0TwoTrackResonance", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { // all pair combinations share the same histogram prefixes, so only one combination can be processed per workflow instance - if (static_cast(doprocessLambdaPhiSameEvent || doprocessLambdaPhiMixedEvent) + - static_cast(doprocessLambdaKstar0SameEvent || doprocessLambdaKstar0MixedEvent) + - static_cast(doprocessLambdaRho0SameEvent || doprocessLambdaRho0MixedEvent) + - static_cast(doprocessK0shortPhiSameEvent || doprocessK0shortPhiMixedEvent) + - static_cast(doprocessK0shortKstar0SameEvent || doprocessK0shortKstar0MixedEvent) + - static_cast(doprocessK0shortRho0SameEvent || doprocessK0shortRho0MixedEvent) > + if (static_cast(doprocessLambdaPhiSameEvent || doprocessLambdaPhiMixedEvent || doprocessLambdaPhiMixedEventWithTagging) + + static_cast(doprocessLambdaKstar0SameEvent || doprocessLambdaKstar0MixedEvent || doprocessLambdaKstar0MixedEventWithTagging) + + static_cast(doprocessLambdaRho0SameEvent || doprocessLambdaRho0MixedEvent || doprocessLambdaRho0MixedEventWithTagging) + + static_cast(doprocessK0shortPhiSameEvent || doprocessK0shortPhiMixedEvent || doprocessK0shortPhiMixedEventWithTagging) + + static_cast(doprocessK0shortKstar0SameEvent || doprocessK0shortKstar0MixedEvent || doprocessK0shortKstar0MixedEventWithTagging) + + static_cast(doprocessK0shortRho0SameEvent || doprocessK0shortRho0MixedEvent || doprocessK0shortRho0MixedEventWithTagging) > 1) { LOG(fatal) << "Can only process one v0-resonance combination (lambda/k0short x phi/kstar0/rho0)"; } + // Each combination discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessLambdaPhiMixedEventWithTagging || doprocessLambdaKstar0MixedEventWithTagging || doprocessLambdaRho0MixedEventWithTagging || + doprocessK0shortPhiMixedEventWithTagging || doprocessK0shortKstar0MixedEventWithTagging || doprocessK0shortRho0MixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessLambdaPhiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaPhi.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaPhiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaPhi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaPhi): " << bit; + } + if (doprocessLambdaKstar0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaKstar0.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaKstar0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaKstar0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaKstar0): " << bit; + } + if (doprocessLambdaRho0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaRho0.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaRho0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaRho0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaRho0): " << bit; + } + if (doprocessK0shortPhiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0shortPhi.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortPhiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingK0shortPhi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0shortPhi): " << bit; + } + if (doprocessK0shortKstar0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0shortKstar0.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortKstar0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingK0shortKstar0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0shortKstar0): " << bit; + } + if (doprocessK0shortRho0MixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0shortRho0.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortRho0MixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0TwoTrackResonance enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingK0shortRho0.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0shortRho0): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -314,42 +393,42 @@ struct FemtoPairV0TwoTrackResonance { auto pairCleanerHistSpec = paircleaner::makePairCleanerHistSpecMap(confPairCleaner); // setup for lambda-phi - if (doprocessLambdaPhiSameEvent || doprocessLambdaPhiMixedEvent) { + if (doprocessLambdaPhiSameEvent || doprocessLambdaPhiMixedEvent || doprocessLambdaPhiMixedEventWithTagging) { auto lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); auto phiHistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confPhiBinning); pairLambdaPhiBuilder.init(&hRegistry, confCollisionBinning, lambdaSelection, phiSelection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, v0PosDauSpec, v0NegDauSpec, phiHistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for lambda-kstar0 - if (doprocessLambdaKstar0SameEvent || doprocessLambdaKstar0MixedEvent) { + if (doprocessLambdaKstar0SameEvent || doprocessLambdaKstar0MixedEvent || doprocessLambdaKstar0MixedEventWithTagging) { auto lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); auto kstar0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confKstar0Binning); pairLambdaKstar0Builder.init(&hRegistry, confCollisionBinning, lambdaSelection, kstar0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, v0PosDauSpec, v0NegDauSpec, kstar0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for lambda-rho0 - if (doprocessLambdaRho0SameEvent || doprocessLambdaRho0MixedEvent) { + if (doprocessLambdaRho0SameEvent || doprocessLambdaRho0MixedEvent || doprocessLambdaRho0MixedEventWithTagging) { auto lambdaHistSpec = v0histmanager::makeV0HistSpecMap(confLambdaBinning); auto rho0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confRho0Binning); pairLambdaRho0Builder.init(&hRegistry, confCollisionBinning, lambdaSelection, rho0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, lambdaHistSpec, v0PosDauSpec, v0NegDauSpec, rho0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for k0short-phi - if (doprocessK0shortPhiSameEvent || doprocessK0shortPhiMixedEvent) { + if (doprocessK0shortPhiSameEvent || doprocessK0shortPhiMixedEvent || doprocessK0shortPhiMixedEventWithTagging) { auto k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); auto phiHistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confPhiBinning); pairK0shortPhiBuilder.init(&hRegistry, confCollisionBinning, k0shortSelection, phiSelection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, v0PosDauSpec, v0NegDauSpec, phiHistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for k0short-kstar0 - if (doprocessK0shortKstar0SameEvent || doprocessK0shortKstar0MixedEvent) { + if (doprocessK0shortKstar0SameEvent || doprocessK0shortKstar0MixedEvent || doprocessK0shortKstar0MixedEventWithTagging) { auto k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); auto kstar0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confKstar0Binning); pairK0shortKstar0Builder.init(&hRegistry, confCollisionBinning, k0shortSelection, kstar0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, v0PosDauSpec, v0NegDauSpec, kstar0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); } // setup for k0short-rho0 - if (doprocessK0shortRho0SameEvent || doprocessK0shortRho0MixedEvent) { + if (doprocessK0shortRho0SameEvent || doprocessK0shortRho0MixedEvent || doprocessK0shortRho0MixedEventWithTagging) { auto k0shortHistSpec = v0histmanager::makeV0HistSpecMap(confK0shortBinning); auto rho0HistSpec = twotrackresonancehistmanager::makeTwoTrackResonanceHistSpecMap(confRho0Binning); pairK0shortRho0Builder.init(&hRegistry, confCollisionBinning, k0shortSelection, rho0Selection, confCprPos, confCprNeg, confMixing, confPairBinning, confPairCuts, colHistSpec, k0shortHistSpec, v0PosDauSpec, v0NegDauSpec, rho0HistSpec, resoPosDauSpec, resoNegDauSpec, pairV0TwoTrackResonanceHistSpec, cprHistSpecPos, cprHistSpecNeg, pairCleanerHistSpec); @@ -365,6 +444,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaPhiSameEvent, "Enable processing same event processing for lambdas and phis", true); + // Opt-in counterpart of processLambdaPhiMixedEvent() below -- same "Partition declared and + // bound inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processLambdaPhiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoPhis const& /*phis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaPhi.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaPhiBuilder.processMixedEvent(taggedCollisions, tracks, lambdaPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaPhiMixedEventWithTagging, "Enable processing mixed event processing for lambdas and phis, pre-filtered using the collision tagger", false); + void processLambdaPhiMixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoPhis const& /*phis*/) { pairLambdaPhiBuilder.processMixedEvent(cols, tracks, lambdaPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -378,6 +467,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaKstar0SameEvent, "Enable processing same event processing for lambdas and kstar0s", false); + // Opt-in counterpart of processLambdaKstar0MixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processLambdaKstar0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoKstar0s const& /*kstar0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaKstar0.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaKstar0Builder.processMixedEvent(taggedCollisions, tracks, lambdaPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaKstar0MixedEventWithTagging, "Enable processing mixed event processing for lambdas and kstar0s, pre-filtered using the collision tagger", false); + void processLambdaKstar0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoKstar0s const& /*kstar0s*/) { pairLambdaKstar0Builder.processMixedEvent(cols, tracks, lambdaPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -391,6 +490,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaRho0SameEvent, "Enable processing same event processing for lambdas and rho0s", false); + // Opt-in counterpart of processLambdaRho0MixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processLambdaRho0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoRho0s const& /*rho0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaRho0.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaRho0Builder.processMixedEvent(taggedCollisions, tracks, lambdaPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processLambdaRho0MixedEventWithTagging, "Enable processing mixed event processing for lambdas and rho0s, pre-filtered using the collision tagger", false); + void processLambdaRho0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoRho0s const& /*rho0s*/) { pairLambdaRho0Builder.processMixedEvent(cols, tracks, lambdaPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -404,6 +513,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortPhiSameEvent, "Enable processing same event processing for k0shorts and phis", false); + // Opt-in counterpart of processK0shortPhiMixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processK0shortPhiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoPhis const& /*phis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0shortPhi.internalTagMask); + taggedCollisions.bindTable(cols); + pairK0shortPhiBuilder.processMixedEvent(taggedCollisions, tracks, k0shortPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortPhiMixedEventWithTagging, "Enable processing mixed event processing for k0shorts and phis, pre-filtered using the collision tagger", false); + void processK0shortPhiMixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoPhis const& /*phis*/) { pairK0shortPhiBuilder.processMixedEvent(cols, tracks, k0shortPartition, phiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -417,6 +536,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortKstar0SameEvent, "Enable processing same event processing for k0shorts and kstar0s", false); + // Opt-in counterpart of processK0shortKstar0MixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processK0shortKstar0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoKstar0s const& /*kstar0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0shortKstar0.internalTagMask); + taggedCollisions.bindTable(cols); + pairK0shortKstar0Builder.processMixedEvent(taggedCollisions, tracks, k0shortPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortKstar0MixedEventWithTagging, "Enable processing mixed event processing for k0shorts and kstar0s, pre-filtered using the collision tagger", false); + void processK0shortKstar0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoKstar0s const& /*kstar0s*/) { pairK0shortKstar0Builder.processMixedEvent(cols, tracks, k0shortPartition, kstar0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -430,6 +559,16 @@ struct FemtoPairV0TwoTrackResonance { } PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortRho0SameEvent, "Enable processing same event processing for k0shorts and rho0s", false); + // Opt-in counterpart of processK0shortRho0MixedEvent() below -- same idiom as + // processLambdaPhiMixedEventWithTagging above. + void processK0shortRho0MixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoRho0s const& /*rho0s*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0shortRho0.internalTagMask); + taggedCollisions.bindTable(cols); + pairK0shortRho0Builder.processMixedEvent(taggedCollisions, tracks, k0shortPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0TwoTrackResonance, processK0shortRho0MixedEventWithTagging, "Enable processing mixed event processing for k0shorts and rho0s, pre-filtered using the collision tagger", false); + void processK0shortRho0MixedEvent(FilteredCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/, FemtoRho0s const& /*rho0s*/) { pairK0shortRho0Builder.processMixedEvent(cols, tracks, k0shortPartition, rho0Partition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx index 371102a368a..9d9f7b8698b 100644 --- a/PWGCF/Femto/Tasks/femtoPairV0V0.cxx +++ b/PWGCF/Femto/Tasks/femtoPairV0V0.cxx @@ -16,6 +16,7 @@ #include "PWGCF/Femto/Core/closePairRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairBuilder.h" #include "PWGCF/Femto/Core/pairCleaner.h" @@ -32,11 +33,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -72,6 +75,10 @@ struct FemtoPairV0V0 { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -79,6 +86,13 @@ struct FemtoPairV0V0 { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- three separate + // instances since lambda-lambda/k0short-k0short/lambda-k0short are mutually exclusive on one + // instance, but tagged independently (see init()). + collisiontagger::ConfTagging confTaggingLambdaLambda; + collisiontagger::ConfTagging confTaggingK0shortK0short; + collisiontagger::ConfTagging confTaggingLambdaK0short; + // setup for daughters // separate binning per v0 slot, so that e.g. lambda (proton/pion daughters) and // k0short (pion/pion daughters) can be binned independently @@ -208,11 +222,11 @@ struct FemtoPairV0V0 { closepairrejection::ConfCprV0DaugherV0DaughterPos confCprPos; closepairrejection::ConfCprV0DaugherV0DaughterNeg confCprNeg; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - bool processData = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || - doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || - doprocessLambdaK0shortSameEvent || doprocessLambdaK0shortMixedEvent; + bool processData = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || doprocessLambdaLambdaMixedEventWithTagging || + doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || doprocessK0shortK0shortMixedEventWithTagging || + doprocessLambdaK0shortSameEvent || doprocessLambdaK0shortMixedEvent || doprocessLambdaK0shortMixedEventWithTagging; bool processMc = doprocessLambdaLambdaSameEventMc || doprocessLambdaLambdaMixedEventMc || doprocessK0shortK0shortSameEventMc || doprocessK0shortK0shortMixedEventMc || doprocessLambdaK0shortSameEventMc || doprocessLambdaK0shortMixedEventMc; @@ -224,17 +238,51 @@ struct FemtoPairV0V0 { LOG(fatal) << "Neither data nor mc processing is enabled. Breaking..."; } - bool processLambdaLambda = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || + bool processLambdaLambda = doprocessLambdaLambdaSameEvent || doprocessLambdaLambdaMixedEvent || doprocessLambdaLambdaMixedEventWithTagging || doprocessLambdaLambdaSameEventMc || doprocessLambdaLambdaMixedEventMc; - bool processK0shortK0short = doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || + bool processK0shortK0short = doprocessK0shortK0shortSameEvent || doprocessK0shortK0shortMixedEvent || doprocessK0shortK0shortMixedEventWithTagging || doprocessK0shortK0shortSameEventMc || doprocessK0shortK0shortMixedEventMc; - bool processLambdaK0short = doprocessLambdaK0shortSameEvent || doprocessLambdaK0shortMixedEvent || + bool processLambdaK0short = doprocessLambdaK0shortSameEvent || doprocessLambdaK0shortMixedEvent || doprocessLambdaK0shortMixedEventWithTagging || doprocessLambdaK0shortSameEventMc || doprocessLambdaK0shortMixedEventMc; if (static_cast(processLambdaLambda) + static_cast(processK0shortK0short) + static_cast(processLambdaK0short) > 1) { LOG(fatal) << "Only one of lambda-lambda, k0short-k0short or lambda-k0short processing can be enabled. Breaking..."; } + // Each pair type discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessLambdaLambdaMixedEventWithTagging || doprocessK0shortK0shortMixedEventWithTagging || doprocessLambdaK0shortMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessLambdaLambdaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaLambda.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaLambdaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0V0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaLambda.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaLambda): " << bit; + } + if (doprocessK0shortK0shortMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingK0shortK0short.Role); + if (bit < 0) { + LOG(fatal) << "processK0shortK0shortMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0V0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingK0shortK0short.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (K0shortK0short): " << bit; + } + if (doprocessLambdaK0shortMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingLambdaK0short.Role); + if (bit < 0) { + LOG(fatal) << "processLambdaK0shortMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processV0V0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingLambdaK0short.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (LambdaK0short): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -322,6 +370,16 @@ struct FemtoPairV0V0 { } PROCESS_SWITCH(FemtoPairV0V0, processLambdaLambdaSameEventMc, "Enable processing same event processing for lambda-lambda with mc information", false); + // Opt-in counterpart of processLambdaLambdaMixedEvent() below -- same "Partition declared and + // bound inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processLambdaLambdaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaLambda.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaLambdaBuilder.processMixedEvent(taggedCollisions, tracks, lambdaPartition1, lambdaPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0V0, processLambdaLambdaMixedEventWithTagging, "Enable processing mixed event processing for lambda-lambda, pre-filtered using the collision tagger", false); + void processLambdaLambdaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) { pairLambdaLambdaBuilder.processMixedEvent(cols, tracks, lambdaPartition1, lambdaPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -346,6 +404,16 @@ struct FemtoPairV0V0 { } PROCESS_SWITCH(FemtoPairV0V0, processK0shortK0shortSameEventMc, "Enable processing same event processing for k0short-k0short with mc information", false); + // Opt-in counterpart of processK0shortK0shortMixedEvent() below -- same idiom as + // processLambdaLambdaMixedEventWithTagging above. + void processK0shortK0shortMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingK0shortK0short.internalTagMask); + taggedCollisions.bindTable(cols); + pairK0shortK0shortBuilder.processMixedEvent(taggedCollisions, tracks, k0shortPartition1, k0shortPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0V0, processK0shortK0shortMixedEventWithTagging, "Enable processing mixed event processing for k0short-k0short, pre-filtered using the collision tagger", false); + void processK0shortK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoK0shorts const& /*k0shorts*/) { pairK0shortK0shortBuilder.processMixedEvent(cols, tracks, k0shortPartition1, k0shortPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -370,6 +438,16 @@ struct FemtoPairV0V0 { } PROCESS_SWITCH(FemtoPairV0V0, processLambdaK0shortSameEventMc, "Enable processing same event processing for lambda-k0short with mc information", false); + // Opt-in counterpart of processLambdaK0shortMixedEvent() below -- same idiom as + // processLambdaLambdaMixedEventWithTagging above. + void processLambdaK0shortMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoK0shorts const& /*k0shorts*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingLambdaK0short.internalTagMask); + taggedCollisions.bindTable(cols); + pairLambdaK0shortBuilder.processMixedEvent(taggedCollisions, tracks, lambdaPartition1, k0shortPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoPairV0V0, processLambdaK0shortMixedEventWithTagging, "Enable processing mixed event processing for lambda-k0short, pre-filtered using the collision tagger", false); + void processLambdaK0shortMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/, FemtoK0shorts const& /*k0shorts*/) { pairLambdaK0shortBuilder.processMixedEvent(cols, tracks, lambdaPartition1, k0shortPartition2, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx index 319fc73ea14..b1ddcf9426c 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackCascade.cxx @@ -19,6 +19,7 @@ #include "PWGCF/Femto/Core/closeTripletRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/particleCleaner.h" @@ -34,11 +35,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -66,12 +69,22 @@ struct FemtoTripletTrackTrackCascade { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by the processXxxMixedEventWithTagging functions below. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions collisionbuilder::ConfCollisionSelection collisionSelection; o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by the processXxxMixedEventWithTagging functions below -- two separate + // instances since Xi and Omega are mutually exclusive on one instance, but tagged independently + // (see init()). + collisiontagger::ConfTagging confTaggingXi; + collisiontagger::ConfTagging confTaggingOmega; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelections1; trackhistmanager::ConfTrackBinning1 confTrackBinning1; @@ -193,23 +206,49 @@ struct FemtoTripletTrackTrackCascade { closepairrejection::ConfCprTrackCascadeBachelor confCprBachelor; closepairrejection::ConfCprTrackV0Daughter confCprV0Daughter; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if ((static_cast(doprocessXiSameEvent) + static_cast(doprocessXiSameEventMc)) > 1 || (static_cast(doprocessXiMixedEvent) + static_cast(doprocessXiMixedEventMc)) > 1 || (static_cast(doprocessOmegaSameEvent) + static_cast(doprocessOmegaSameEventMc)) > 1 || (static_cast(doprocessOmegaMixedEvent) + static_cast(doprocessOmegaMixedEventMc)) > 1) { + if ((static_cast(doprocessXiSameEvent) + static_cast(doprocessXiSameEventMc)) > 1 || (static_cast(doprocessXiMixedEvent) + static_cast(doprocessXiMixedEventWithTagging) + static_cast(doprocessXiMixedEventMc)) > 1 || (static_cast(doprocessOmegaSameEvent) + static_cast(doprocessOmegaSameEventMc)) > 1 || (static_cast(doprocessOmegaMixedEvent) + static_cast(doprocessOmegaMixedEventWithTagging) + static_cast(doprocessOmegaMixedEventMc)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } - bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessOmegaSameEvent || doprocessOmegaMixedEvent; + bool processData = doprocessXiSameEvent || doprocessXiMixedEvent || doprocessXiMixedEventWithTagging || doprocessOmegaSameEvent || doprocessOmegaMixedEvent || doprocessOmegaMixedEventWithTagging; bool processMc = doprocessXiSameEventMc || doprocessXiMixedEventMc || doprocessOmegaSameEventMc || doprocessOmegaMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is activated. Breaking..."; } - bool processXi = doprocessXiSameEvent || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventMc; - bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventMc; + bool processXi = doprocessXiSameEvent || doprocessXiSameEventMc || doprocessXiMixedEvent || doprocessXiMixedEventWithTagging || doprocessXiMixedEventMc; + bool processOmega = doprocessOmegaSameEvent || doprocessOmegaSameEventMc || doprocessOmegaMixedEvent || doprocessOmegaMixedEventWithTagging || doprocessOmegaMixedEventMc; if (processXi && processOmega) { LOG(fatal) << "Both xi-track and omega-track processing is enabled. Breaking..."; } + + // Each species discovers its own tag bit independently (see Core/collisionTagger.h). + if (doprocessXiMixedEventWithTagging || doprocessOmegaMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + if (doprocessXiMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingXi.Role); + if (bit < 0) { + LOG(fatal) << "processXiMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTripletTrackTrackCascade enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingXi.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Xi): " << bit; + } + if (doprocessOmegaMixedEventWithTagging) { + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingOmega.Role); + if (bit < 0) { + LOG(fatal) << "processOmegaMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTripletTrackTrackCascade enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingOmega.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << " (Omega): " << bit; + } + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -280,6 +319,16 @@ struct FemtoTripletTrackTrackCascade { } PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processXiSameEventMc, "Enable processing same event processing for tracks and xis with mc information", false); + // Opt-in counterpart of processXiMixedEvent() below -- same "Partition declared and bound + // inside process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processXiMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoXis const& /*xis*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingXi.internalTagMask); + taggedCollisions.bindTable(cols); + tripletTrackTrackXiBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processXiMixedEventWithTagging, "Enable processing mixed event processing for tracks and xis, pre-filtered using the collision tagger", false); + void processXiMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoXis const& /*xis*/) { tripletTrackTrackXiBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, xiPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); @@ -304,6 +353,16 @@ struct FemtoTripletTrackTrackCascade { } PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processOmegaSameEventMc, "Enable processing same event processing for tracks and omegas with mc information", false); + // Opt-in counterpart of processOmegaMixedEvent() below -- same idiom as + // processXiMixedEventWithTagging above. + void processOmegaMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoOmegas const& /*omegas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingOmega.internalTagMask); + taggedCollisions.bindTable(cols); + tripletTrackTrackOmegaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackCascade, processOmegaMixedEventWithTagging, "Enable processing mixed event processing for tracks and omegas, pre-filtered using the collision tagger", false); + void processOmegaMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoOmegas const& /*omegas*/) { tripletTrackTrackOmegaBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, omegaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx index 6c80679fc17..04660e2e342 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackTrack.cxx @@ -17,6 +17,7 @@ #include "PWGCF/Femto/Core/closeTripletRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/particleCleaner.h" @@ -32,11 +33,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -61,6 +64,10 @@ struct FemtoTripletTrackTrackTrack { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by processMixedEventWithTagging. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -68,6 +75,9 @@ struct FemtoTripletTrackTrackTrack { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processMixedEventWithTagging below. + collisiontagger::ConfTagging confTaggingTriplet; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelections1; trackhistmanager::ConfTrackBinning1 confTrackBinning1; @@ -126,17 +136,30 @@ struct FemtoTripletTrackTrackTrack { o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackTrack", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventMc)) > 1 || (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventMc)) > 1) { + if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventMc)) > 1 || (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithTagging) + static_cast(doprocessMixedEventMc)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } - bool processData = doprocessSameEvent || doprocessMixedEvent; + bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessMixedEventWithTagging; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is activated. Breaking..."; } + if (doprocessMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingTriplet.Role); + if (bit < 0) { + LOG(fatal) << "processMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTripletTrackTrackTrack enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingTriplet.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << ": " << bit; + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -183,6 +206,16 @@ struct FemtoTripletTrackTrackTrack { } PROCESS_SWITCH(FemtoTripletTrackTrackTrack, processSameEventMc, "Enable processing same event processing", false); + // Opt-in counterpart of processMixedEvent() below -- same "Partition declared and bound inside + // process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingTriplet.internalTagMask); + taggedCollisions.bindTable(cols); + tripletTrackTrackTrackBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, trackPartition3, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackTrack, processMixedEventWithTagging, "Enable processing mixed event processing, pre-filtered using the collision tagger", false); + void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks) { tripletTrackTrackTrackBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, trackPartition3, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); diff --git a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx index 22dd856be44..f2b821e2f26 100644 --- a/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx +++ b/PWGCF/Femto/Tasks/femtoTripletTrackTrackV0.cxx @@ -17,6 +17,7 @@ #include "PWGCF/Femto/Core/closeTripletRejection.h" #include "PWGCF/Femto/Core/collisionBuilder.h" #include "PWGCF/Femto/Core/collisionHistManager.h" +#include "PWGCF/Femto/Core/collisionTagger.h" #include "PWGCF/Femto/Core/modes.h" #include "PWGCF/Femto/Core/pairCleaner.h" #include "PWGCF/Femto/Core/particleCleaner.h" @@ -34,11 +35,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include @@ -68,6 +71,10 @@ struct FemtoTripletTrackTrackV0 { using FemtoMcParticlesWithLabel = o2::soa::Join; + // opt-in: carries FColTags, only needed by processMixedEventWithTagging. + using FemtoCollisionsWithTags = o2::soa::Join; + using FilteredFemtoCollisionsWithTags = o2::soa::Filtered; + o2::framework::SliceCache cache; // setup collisions @@ -75,6 +82,9 @@ struct FemtoTripletTrackTrackV0 { o2::framework::expressions::Filter collisionFilter = MAKE_COLLISION_FILTER(collisionSelection); colhistmanager::ConfCollisionBinning confCollisionBinning; + // opt-in, only needed by processMixedEventWithTagging below. + collisiontagger::ConfTagging confTaggingTriplet; + // setup tracks trackbuilder::ConfTrackSelection1 confTrackSelections1; trackhistmanager::ConfTrackBinning1 confTrackBinning1; @@ -148,17 +158,30 @@ struct FemtoTripletTrackTrackV0 { o2::framework::HistogramRegistry hRegistry{"FemtoTrackTrackV0", {}, o2::framework::OutputObjHandlingPolicy::AnalysisObject}; - void init(o2::framework::InitContext&) + void init(o2::framework::InitContext& context) { - if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventMc)) > 1 || (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventMc)) > 1) { + if ((static_cast(doprocessSameEvent) + static_cast(doprocessSameEventMc)) > 1 || (static_cast(doprocessMixedEvent) + static_cast(doprocessMixedEventWithTagging) + static_cast(doprocessMixedEventMc)) > 1) { LOG(fatal) << "More than 1 same or mixed event process function is activated. Breaking..."; } - bool processData = doprocessSameEvent || doprocessMixedEvent; + bool processData = doprocessSameEvent || doprocessMixedEvent || doprocessMixedEventWithTagging; bool processMc = doprocessSameEventMc || doprocessMixedEventMc; if (processData && processMc) { LOG(fatal) << "Both data and mc processing is activated. Breaking..."; } + if (doprocessMixedEventWithTagging) { + auto const& workflows = context.services().get(); + auto const& ownDevice = context.services().get(); + auto roles = collisiontagger::discoverTaggedRoles(workflows); + int bit = collisiontagger::findOwnBit(roles, ownDevice.name, confTaggingTriplet.Role); + if (bit < 0) { + LOG(fatal) << "processMixedEventWithTagging is enabled, but no femto-collision-tagger device " + << "(with processTripletTrackTrackV0 enabled) tagging this device (" << ownDevice.name << ") was found in this workflow."; + } + confTaggingTriplet.internalTagMask.value = static_cast(1) << bit; + LOG(info) << "Collision tagger bit for " << ownDevice.name << ": " << bit; + } + // setup columnpolicy for binning // default values are used during instantiation, so we need to explicity update them here mixBinsVtxMult = {{confMixing.vtxBins.value, confMixing.multBins.value}}; @@ -211,6 +234,16 @@ struct FemtoTripletTrackTrackV0 { } PROCESS_SWITCH(FemtoTripletTrackTrackV0, processSameEventMc, "Enable processing same event processing", false); + // Opt-in counterpart of processMixedEvent() below -- same "Partition declared and bound inside + // process()" idiom as femtoPairTrackTrack.cxx's processMixedEventWithTagging. + void processMixedEventWithTagging(FilteredFemtoCollisionsWithTags const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) + { + o2::framework::Partition taggedCollisions = ncheckbit(o2::aod::femtocollisions::collisionTag, confTaggingTriplet.internalTagMask); + taggedCollisions.bindTable(cols); + tripletTrackTrackLambdaBuilder.processMixedEvent(taggedCollisions, tracks, trackPartition1, trackPartition2, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent); + } + PROCESS_SWITCH(FemtoTripletTrackTrackV0, processMixedEventWithTagging, "Enable processing mixed event processing, pre-filtered using the collision tagger", false); + void processMixedEvent(FilteredFemtoCollisions const& cols, FemtoTracks const& tracks, FemtoLambdas const& /*lambdas*/) { tripletTrackTrackLambdaBuilder.processMixedEvent(cols, tracks, trackPartition1, trackPartition2, lambdaPartition, cache, mixBinsVtxMult, mixBinsVtxCent, mixBinsVtxMultCent);