diff --git a/PWGCF/Femto/Core/baseSelection.h b/PWGCF/Femto/Core/baseSelection.h index 8ada2f09ed1..6e347e850d2 100644 --- a/PWGCF/Femto/Core/baseSelection.h +++ b/PWGCF/Femto/Core/baseSelection.h @@ -18,16 +18,16 @@ #include "PWGCF/Femto/Core/selectionContainer.h" +#include "Framework/HistogramRegistry.h" + #include "fairlogger/Logger.h" #include #include -#include #include #include #include #include -#include #include namespace o2::analysis::femto @@ -58,24 +58,41 @@ class BaseSelection /// \param limitType Type of limit (from limits::LimitType). /// \param skipMostPermissiveBit Whether to skip the loosest threshold in the bitmask. /// \param isMinimalCut Whether this cut is mandatory or optional. - void addSelection(std::vector const& selectionValues, int observableIndex, limits::LimitType limitType, bool skipMostPermissiveBit, bool isMinimalCut) + void addSelection(int observableIndex, + std::string const& selectionName, + std::vector const& selectionValues, + limits::LimitType limitType, + bool skipMostPermissiveBit, + bool isMinimalCut, + bool isOptionCut) { + // check index if (static_cast(observableIndex) >= NumObservables) { LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables; } - if (!selectionValues.empty() && skipMostPermissiveBit) { - mNSelections += selectionValues.size() - 1; - } else { - mNSelections += selectionValues.size(); + // init selection container for selection at given index + mSelectionContainers.at(observableIndex) = SelectionContainer(selectionName, selectionValues, limitType, skipMostPermissiveBit, isMinimalCut, isOptionCut); + + // check if any selections are configured + if (mSelectionContainers.at(observableIndex).isEmpty()) { + return; } - if (mNSelections >= sizeof(BitmaskType) * CHAR_BIT) { - LOG(fatal) << "Too many selections. At most " << sizeof(BitmaskType) * CHAR_BIT << " are supported"; + + // keep track of selections and bits + mNSelectionBits += mSelectionContainers.at(observableIndex).getShift(); + mNSelection += mSelectionContainers.at(observableIndex).getNSelections(); + + if (mNSelectionBits > sizeof(BitmaskType) * CHAR_BIT) { + LOG(fatal) << "Too many selections. At most " << sizeof(BitmaskType) * CHAR_BIT << " number of bits are supported"; + } + // check if any selection is minimal + if (mSelectionContainers.at(observableIndex).isMinimalCut()) { + mHasMinimalSelection = true; } - // check if any cut is optional - if (!isMinimalCut) { + // check if selection is optional + if (mSelectionContainers.at(observableIndex).isOptionalCut()) { mHasOptionalSelection = true; } - mSelectionContainers.at(observableIndex) = SelectionContainer(selectionValues, limitType, skipMostPermissiveBit, isMinimalCut); } /// \brief Add a function-based selection for a specific observable. @@ -87,50 +104,70 @@ class BaseSelection /// \param limitType Type of limit. /// \param skipMostPermissiveBit Whether to skip the loosest threshold in the bitmask. /// \param isMinimalCut Whether this cut is mandatory or optional. - void addSelection(std::string const& baseName, + void addSelection(int observableIndex, + std::string const& selectionName, T lowerLimit, T upperLimit, - std::vector const& selectionValues, - int observableIndex, + std::vector const& functions, limits::LimitType limitType, bool skipMostPermissiveBit, - bool isMinimalCut) + bool isMinimalCut, + bool isOptionalCut) { if (static_cast(observableIndex) >= NumObservables) { LOG(fatal) << "Observable is not valid. Observable (index) has to be smaller than " << NumObservables; } - if (skipMostPermissiveBit) { - mNSelections += selectionValues.size() - 1; - } else { - mNSelections += selectionValues.size(); + mSelectionContainers.at(observableIndex) = SelectionContainer(selectionName, lowerLimit, upperLimit, functions, limitType, skipMostPermissiveBit, isMinimalCut, isOptionalCut); + + // check if any selections are configured + if (mSelectionContainers.at(observableIndex).isEmpty()) { + return; } - if (mNSelections >= sizeof(BitmaskType) * CHAR_BIT) { + + // advance mNSelections so we can use it as offset for next selection + mNSelectionBits += mSelectionContainers.at(observableIndex).getShift(); + mNSelection += mSelectionContainers.at(observableIndex).getNSelections(); + + if (mNSelectionBits > sizeof(BitmaskType) * CHAR_BIT) { LOG(fatal) << "Too many selections. At most " << sizeof(BitmaskType) * CHAR_BIT << " are supported"; } - mSelectionContainers.at(observableIndex) = SelectionContainer(baseName, lowerLimit, upperLimit, selectionValues, limitType, skipMostPermissiveBit, isMinimalCut); + // keep track of selection selections + // check if any cut is minimal + if (mSelectionContainers.at(observableIndex).isMinimalCut()) { + mHasMinimalSelection = true; + } + // check if any selection is optional + if (mSelectionContainers.at(observableIndex).isOptionalCut()) { + mHasOptionalSelection = true; + } } /// \brief Add a boolean based selection for a specific observable. /// \param mode Whether the selection is not applied, minimal or optional cut /// \param observableIndex Index of the observable. - void addSelection(int mode, int observableIndex) + void addSelection(int observableIndex, + std::string const& selectionName, + int mode) { switch (mode) { case -1: // cut is optional and we store bit for the cut - mNSelections += 1; + mSelectionContainers.at(observableIndex) = SelectionContainer(selectionName, std::vector{1}, limits::LimitType::kEqual, false, false, true); mHasOptionalSelection = true; - mSelectionContainers.at(observableIndex) = SelectionContainer(std::vector{1}, limits::LimitType::kEqual, false, false); + mNSelectionBits += 1; + mNSelection += 1; break; case 0: // cut is not applied, initalize with empty vector, so we bail out later - mSelectionContainers.at(observableIndex) = SelectionContainer(std::vector{}, limits::LimitType::kEqual, false, false); + mSelectionContainers.at(observableIndex) = SelectionContainer(selectionName, std::vector{}, limits::LimitType::kEqual, false, false, false); break; - case 1: // cut is added as mininal selection (since it is only one value, not extra bit is stored) - mSelectionContainers.at(observableIndex) = SelectionContainer(std::vector{1}, limits::LimitType::kEqual, true, true); + case 1: // cut is added as mininal selection (since it is only one value, no extra bit is stored) + mSelectionContainers.at(observableIndex) = SelectionContainer(selectionName, std::vector{1}, limits::LimitType::kEqual, true, true, false); + mHasMinimalSelection = true; + mNSelection += 1; break; default: LOG(fatal) << "Invalid switch for boolean selection"; } - if (mNSelections >= sizeof(BitmaskType) * CHAR_BIT) { + if (mNSelectionBits > sizeof(BitmaskType) * CHAR_BIT) { LOG(fatal) << "Too many selections. At most " << sizeof(BitmaskType) * CHAR_BIT << " are supported"; } } @@ -147,12 +184,19 @@ class BaseSelection void reset() { mFinalBitmask.reset(); - mPassesMinimalSelections = true; - // will be true if no optional cut has been defined and - // will be set to false if we have optional cuts (but will be set to true in the case at least one optional cut succeeds) - mPassesOptionalSelections = !mHasOptionalSelection; + for (auto& container : mSelectionContainers) { // o2-linter: disable=const-ref-in-for-loop (object modified in loop) + container.reset(); + } + if (mHasMinimalSelection) { + mPassesMinimalSelections = true; + } + if (mHasOptionalSelection) { + mPassesOptionalSelections = false; + } } + void reset(int observableIndex) { mSelectionContainers.at(observableIndex).reset(); } + /// \brief Evaluate a single observable against its configured selections. /// \param observableIndex Index of the observable. /// \param value Value of the observable. @@ -164,20 +208,24 @@ class BaseSelection } // if any previous observable did not pass minimal selections, there is no point in setting bitmask for other observables // minimal selection for each observable is computed after adding it - if (mPassesMinimalSelections == false) { + if (!mPassesMinimalSelections) { return; } // set bitmask for given observable mSelectionContainers.at(observableIndex).evaluate(value); // check if minimal selction for this observable holds // if one minimal selection is not fullfilled, the condition failes - if (mSelectionContainers.at(observableIndex).passesAsMinimalCut() == false) { - mPassesMinimalSelections = false; + if (mHasMinimalSelection) { + if (!mSelectionContainers.at(observableIndex).passesAsMinimalCut()) { + mPassesMinimalSelections = false; + } } // check if any optional selection holds // if one optional selection is fullfilled, the condition succeeds - if (mSelectionContainers.at(observableIndex).passesAsOptionalCut() == true) { - mPassesOptionalSelections = true; + if (mHasOptionalSelection) { + if (mSelectionContainers.at(observableIndex).passesAsOptionalCut()) { + mPassesOptionalSelections = true; + } } } @@ -192,18 +240,22 @@ class BaseSelection } // if any previous observable did not pass minimal selections, there is no point in setting bitmask for other observables // minimal selection for each observable is computed after adding it - if (mPassesMinimalSelections == false) { + if (!mPassesMinimalSelections) { return; } // set bitmask for given observable mSelectionContainers.at(observableIndex).evaluate(values); // check if minimal selction for this observable holds - if (mSelectionContainers.at(observableIndex).passesAsMinimalCut() == false) { - mPassesMinimalSelections = false; + if (mHasMinimalSelection) { + if (mSelectionContainers.at(observableIndex).passesAsMinimalCut() == false) { + mPassesMinimalSelections = false; + } } // check if any optional selection holds - if (mSelectionContainers.at(observableIndex).passesAsOptionalCut() == true) { - mPassesOptionalSelections = true; + if (mHasOptionalSelection) { + if (mSelectionContainers.at(observableIndex).passesAsOptionalCut() == true) { + mPassesOptionalSelections = true; + } } } @@ -214,7 +266,16 @@ class BaseSelection /// \return True if all required and at least one optional cut (if present) is passed. bool passesAllRequiredSelections() const { - return mPassesMinimalSelections && mPassesOptionalSelections; + if (mHasMinimalSelection && !mHasOptionalSelection) { + return mPassesMinimalSelections; + } + if (!mHasMinimalSelection && mHasOptionalSelection) { + return mPassesOptionalSelections; + } + if (mHasMinimalSelection && mHasOptionalSelection) { + return mPassesMinimalSelections && mPassesOptionalSelections; + } + return true; } /// \brief Check if the optional selection for a specific observable is passed. @@ -226,14 +287,18 @@ class BaseSelection } /// \brief Assemble the global selection bitmask from individual observable selections. + template void assembleBitmask() { - // if minimal selections are not passed, just set bitmask to 0 - if (mPassesMinimalSelections == false) { + mHistRegistry->fill(HIST(HistName), mNSelection); + // if the required selections are not passed, we can break early + if (!this->passesAllRequiredSelections()) { mFinalBitmask.reset(); return; } + mHistRegistry->fill(HIST(HistName), mNSelection + 1); + int binCenter = 0; // to assemble bitmask, convert all bitmask into integers // shift the current one and add the new bits for (auto const& selectionContainer : mSelectionContainers) { @@ -241,8 +306,21 @@ class BaseSelection if (selectionContainer.isEmpty()) { continue; } - // Shift the result to make space and add the new value - mFinalBitmask = (mFinalBitmask << selectionContainer.getShift()) | selectionContainer.getBitmask(); + // Shift the result to its offset and add the new values + mFinalBitmask |= (selectionContainer.getBitmask() << selectionContainer.getOffset()); + + for (int j = 0; j < selectionContainer.getNSelections(); ++j) { + if (j == 0 && selectionContainer.isMinimalCut()) { + // minimal cuts are always filled + mHistRegistry->fill(HIST(HistName), binCenter); + } else { + // use container's internal offset for checking the bit + if (mFinalBitmask.test(selectionContainer.getBitPosition(j))) { + mHistRegistry->fill(HIST(HistName), binCenter); + } + } + binCenter++; + } } } @@ -262,76 +340,92 @@ class BaseSelection mSelectionContainers.at(observableIndex).setBitmask(bitmask); } - /// \brief Print detailed information about all configured selections. - /// \tparam MapType Type used in the observable name map (usually an enum or int). - /// \param objectName Name of the current object (e.g. particle species). - /// \param observableNames Map from observable index to human-readable names. - template - void printSelections(const std::string& objectName, const std::unordered_map& observableNames) const + T getLoosestSelection(int observableIndex) const { return mSelectionContainers.at(observableIndex).getLoosestSelection(); } + + void printSelections(const std::string& objectName) const { LOG(info) << "Printing Configuration of " << objectName; - - size_t globalBitIndex = 0; // Tracks bit position across all containers - - for (size_t idx = mSelectionContainers.size(); idx-- > 0;) { + for (size_t idx = 0; idx < mSelectionContainers.size(); ++idx) { const auto& container = mSelectionContainers[idx]; if (container.isEmpty()) { continue; } - R key = static_cast(idx); - const std::string& name = observableNames.count(key) ? observableNames.at(key) : "[Unknown]"; - - LOG(info) << "Observable: " << name << " (index " << idx << ")"; - LOG(info) << " Limit type : " << container.getLimitTypeAsString(); - LOG(info) << " Minimal cut : " << (container.isMinimalCut() ? "yes" : "no"); - LOG(info) << " Skip most permissive : " << (container.skipMostPermissiveBit() ? "yes" : "no"); - LOG(info) << " Bitmask shift : " << container.getShift(); - LOG(info) << " Selections : "; + LOG(info) << " Observable: " << container.getSelectionName() << " (index " << idx << ")"; + LOG(info) << " Limit type : " << container.getLimitTypeAsString(); + LOG(info) << " Skip most permissive Bit : " << (container.skipMostPermissiveBit() ? "yes" : "no"); + LOG(info) << " Minimal cut : " << (container.isMinimalCut() ? "yes" : "no"); + LOG(info) << " Optional cut : " << (container.isOptionalCut() ? "yes" : "no"); + LOG(info) << " Bitmask offset : " << container.getOffset(); + LOG(info) << " Bitmask shift : " << container.getShift(); + LOG(info) << " Selections:"; + const bool useFunctions = container.isUsingFunctions(); const auto& values = container.getSelectionValues(); const auto& functions = container.getSelectionFunction(); - bool useFunctions = !functions.empty(); - size_t numSelections = useFunctions ? functions.size() : values.size(); - bool skipMostPermissive = container.skipMostPermissiveBit(); const auto& comments = container.getComments(); - bool hasComments = !comments.empty(); - int valWidth = 20; - int bitWidth = 30; + for (int j = 0; j < container.getNSelections(); ++j) { - for (size_t j = 0; j < numSelections; ++j) { std::stringstream line; + std::string sel = useFunctions ? std::string(functions[j].GetExpFormula().Data()) : std::to_string(values[j]); - // Selection string (either value or function) - const std::string& sel = useFunctions ? std::string(functions[j].GetFormula()->GetExpFormula().Data()) : std::to_string(values[j]); - line << " " << std::left << std::setw(valWidth) << sel; - if (hasComments) { - line << "(" << comments.at(j) << ")"; - } - // Bitmask - if (skipMostPermissive && j == 0) { - line << std::setw(bitWidth) << "-> loosest minimal selection, no bit saved"; + line << " " << std::left << std::setw(25) << sel; + + if (j == 0 && container.isMinimalCut()) { + line << "-> minimal cut, no bit saved"; } else { - const uint64_t bitmask = uint64_t{1} << globalBitIndex++; - std::stringstream hexStream; - hexStream << "-> bitmask: 0x" << std::uppercase << std::hex << bitmask; - line << std::setw(bitWidth) << hexStream.str(); + int bit = container.getOffset() + (j - (container.isMinimalCut() ? 1 : 0)); + line << "-> Bit: 0x" << std::hex << std::uppercase << (1ULL << bit) << std::dec; + } + + if (!comments.empty()) { + line << " (" << comments.at(j) << ")"; } LOG(info) << line.str(); } - LOG(info) << ""; // blank line between observables + LOG(info) << ""; } LOG(info) << "Printing done"; } + template + void setupContainers(o2::framework::HistogramRegistry* registry) + { + mHistRegistry = registry; + // Create histogram with correct number of bins + int nBins = mNSelection + 2; + mHistRegistry->add(HistName, "; Selection Bits; Entries", o2::framework::kTH1F, {{nBins, -0.5, nBins - 0.5}}); + + size_t binIndex = 0; + int offset = 0; + for (size_t idx = 0; idx < mSelectionContainers.size(); ++idx) { + auto& container = mSelectionContainers[idx]; + if (container.isEmpty()) { + continue; + } + container.setOffset(offset); + offset += container.getShift(); + for (int j = 0; j < container.getNSelections(); j++) { + std::string label = container.getBinLabel(j); + mHistRegistry->get(HIST(HistName))->GetXaxis()->SetBinLabel(binIndex + 1, label.c_str()); + binIndex++; + } + } + mHistRegistry->get(HIST(HistName))->GetXaxis()->SetBinLabel(mNSelection + 1, "All analyzed"); + mHistRegistry->get(HIST(HistName))->GetXaxis()->SetBinLabel(mNSelection + 2, "All passed"); + } + protected: + o2::framework::HistogramRegistry* mHistRegistry = nullptr; std::array, NumObservables> mSelectionContainers = {}; ///< Array containing all selections std::bitset mFinalBitmask = {}; ///< final bitmaks - size_t mNSelections = 0; ///< Number of selections + std::size_t mNSelectionBits = 0; ///< Number of selections (all - minimal selections) + int mNSelection = 0; ///< Number of selections all selections + bool mHasMinimalSelection = false; ///< Set to true if all minimal (mandatory) selections are passed bool mPassesMinimalSelections = true; ///< Set to true if all minimal (mandatory) selections are passed bool mHasOptionalSelection = false; ///< Set to true if at least one selections is optional - bool mPassesOptionalSelections = true; ///< Set to true if at least one optional (non-mandatory) selections is passed + bool mPassesOptionalSelections = false; ///< Set to true if at least one optional (non-mandatory) selections is passed }; } // namespace o2::analysis::femto diff --git a/PWGCF/Femto/Core/cascadeBuilder.h b/PWGCF/Femto/Core/cascadeBuilder.h index 5c73652b055..ee43cdd319c 100644 --- a/PWGCF/Femto/Core/cascadeBuilder.h +++ b/PWGCF/Femto/Core/cascadeBuilder.h @@ -142,8 +142,10 @@ enum CascadeSels { kCascadeSelsMax }; -const char cascadeSelsName[] = "Cascade Selection Object"; -const std::unordered_map cascadeSelsToString = { +constexpr char XiSelHistName[] = "hXiSelection"; +constexpr char OmegaSelHistName[] = "hOmegaSelection"; +constexpr char CascadeSelsName[] = "Cascade Selection Object"; +const std::unordered_map cascadeSelectionNames = { {kCascadeCpaMin, "Cascade CPA Min"}, {kCascadeDcaDaughMax, "Cascade DCA Daughters Max"}, {kCascadeTransRadMin, "Cascade Transverse Radius Min"}, @@ -167,7 +169,7 @@ const std::unordered_map cascadeSelsToString = { /// \class FemtoDreamTrackCuts /// \brief Cut class to contain and execute all cuts applied to tracks -template +template class CascadeSelection : public BaseSelection { public: @@ -175,21 +177,21 @@ class CascadeSelection : public BaseSelection - void configure(T1 const& config, T2 const& filter) + void configure(o2::framework::HistogramRegistry* registry, T1 const& config, T2 const& filter) { if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { mXiMassLowerLimit = filter.massXiMin.value; mXiMassUpperLimit = filter.massXiMax.value; mOmegaMassLowerLimit = filter.rejectMassOmegaMin.value; mOmegaMassUpperLimit = filter.rejectMassOmegaMax.value; - this->addSelection(config.bachelorTpcPion.value, kBachelorTpcPion, limits::kAbsUpperLimit, true, true); + this->addSelection(kBachelorTpcPion, cascadeSelectionNames.at(kBachelorTpcPion), config.bachelorTpcPion.value, limits::kAbsUpperLimit, true, true, false); } if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { mOmegaMassLowerLimit = filter.massOmegaMin.value; mOmegaMassUpperLimit = filter.massOmegaMax.value; mXiMassLowerLimit = filter.rejectMassXiMin.value; mXiMassUpperLimit = filter.rejectMassXiMax.value; - this->addSelection(config.bachelorTpcKaon.value, kBachelorTpcKaon, limits::kAbsUpperLimit, true, true); + this->addSelection(kBachelorTpcKaon, cascadeSelectionNames.at(kBachelorTpcKaon), config.bachelorTpcKaon.value, limits::kAbsUpperLimit, true, true, false); } mPtMin = filter.ptMin.value; @@ -201,19 +203,21 @@ class CascadeSelection : public BaseSelectionaddSelection(config.posDauTpc.value, kPosDauTpc, limits::kAbsUpperLimit, true, true); - this->addSelection(config.negDauTpc.value, kNegDauTpc, limits::kAbsUpperLimit, true, true); - - this->addSelection(config.cascadeCpaMin.value, kCascadeCpaMin, limits::kLowerLimit, true, true); - this->addSelection(config.cascadeTransRadMin.value, kCascadeTransRadMin, limits::kLowerLimit, true, true); - this->addSelection(config.cascadeDcaDauMax.value, kCascadeDcaDaughMax, limits::kAbsUpperLimit, true, true); - this->addSelection(config.lambdaCpaMin.value, kLambdaCpaMin, limits::kLowerLimit, true, true); - this->addSelection(config.lambdaTransRadMin.value, kLambdaTransRadMin, limits::kLowerLimit, true, true); - this->addSelection(config.lambdaDcaDauMax.value, kLambdaDcaDauMax, limits::kAbsUpperLimit, true, true); - this->addSelection(config.lambdaDcaToPvMin.value, kLambdaDcaToPvMin, limits::kLowerLimit, true, true); - this->addSelection(config.dauAbsEtaMax.value, kDauAbsEtaMax, limits::kAbsUpperLimit, true, true); - this->addSelection(config.dauDcaMin.value, kDauDcaMin, limits::kAbsLowerLimit, true, true); - this->addSelection(config.dauTpcClustersMin.value, kDauTpcClsMin, limits::kLowerLimit, true, true); + this->addSelection(kPosDauTpc, cascadeSelectionNames.at(kPosDauTpc), config.posDauTpc.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kNegDauTpc, cascadeSelectionNames.at(kNegDauTpc), config.negDauTpc.value, limits::kAbsUpperLimit, true, true, false); + + this->addSelection(kCascadeCpaMin, cascadeSelectionNames.at(kCascadeCpaMin), config.cascadeCpaMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kCascadeTransRadMin, cascadeSelectionNames.at(kCascadeTransRadMin), config.cascadeTransRadMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kCascadeDcaDaughMax, cascadeSelectionNames.at(kCascadeDcaDaughMax), config.cascadeDcaDauMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kLambdaCpaMin, cascadeSelectionNames.at(kLambdaCpaMin), config.lambdaCpaMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kLambdaTransRadMin, cascadeSelectionNames.at(kLambdaTransRadMin), config.lambdaTransRadMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kLambdaDcaDauMax, cascadeSelectionNames.at(kLambdaDcaDauMax), config.lambdaDcaDauMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kLambdaDcaToPvMin, cascadeSelectionNames.at(kLambdaDcaToPvMin), config.lambdaDcaToPvMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kDauAbsEtaMax, cascadeSelectionNames.at(kDauAbsEtaMax), config.dauAbsEtaMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kDauDcaMin, cascadeSelectionNames.at(kDauDcaMin), config.dauDcaMin.value, limits::kAbsLowerLimit, true, true, false); + this->addSelection(kDauTpcClsMin, cascadeSelectionNames.at(kDauTpcClsMin), config.dauTpcClustersMin.value, limits::kLowerLimit, true, true, false); + + this->setupContainers(registry); }; template @@ -263,31 +267,44 @@ class CascadeSelection : public BaseSelectionassembleBitmask(); + this->assembleBitmask(); }; template - bool checkFilters(const T& cascade) const + bool checkCandidate(const T& cascade) const { - return ((cascade.pt() > mPtMin && cascade.pt() < mPtMax) && - (cascade.eta() > mEtaMin && cascade.eta() < mEtaMax) && - (cascade.phi() > mPhiMin && cascade.phi() < mPhiMax) && - (cascade.mLambda() > mLambdaMassMin && cascade.mLambda() < mLambdaMassMax)); - } + // check kinematics + const bool kinematicsOK = + (cascade.pt() > mPtMin && cascade.pt() < mPtMax) && + (cascade.eta() > mEtaMin && cascade.eta() < mEtaMax) && + (cascade.phi() > mPhiMin && cascade.phi() < mPhiMax); + + if (!kinematicsOK) { + return false; + } - template - bool checkHypothesis(T const& cascadeCandidate) - { - // no need to check PID of the bachelor/daughters here, they are set as minimal cuts - if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { - return (mXiMassLowerLimit < cascadeCandidate.mXi() && mXiMassUpperLimit > cascadeCandidate.mXi()) && // inside xi mass window - (cascadeCandidate.mOmega() < mOmegaMassLowerLimit || cascadeCandidate.mOmega() > mOmegaMassUpperLimit); // outside omega mass window + // check mass of daughter lambda + const bool lambdaOK = + (cascade.mLambda() > mLambdaMassMin && cascade.mLambda() < mLambdaMassMax); + + if (!lambdaOK) { + return false; } + + // check mass hypothesis if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { - return (mOmegaMassLowerLimit < cascadeCandidate.mOmega() && mOmegaMassUpperLimit > cascadeCandidate.mOmega()) && // inside omega mass window - (cascadeCandidate.mXi() < mXiMassLowerLimit || cascadeCandidate.mXi() > mXiMassUpperLimit); // outside xi mass window + // Xi candidate must be inside Xi window and outside Omega + return (cascade.mXi() > mXiMassLowerLimit && cascade.mXi() < mXiMassUpperLimit) && + (cascade.mOmega() < mOmegaMassLowerLimit || cascade.mOmega() > mOmegaMassUpperLimit); + } + + if constexpr (modes::isEqual(cascadeType, modes::Cascade::kOmega)) { + // Omega candidate must be inside Omega window and outside Xi + return (cascade.mOmega() > mOmegaMassLowerLimit && cascade.mOmega() < mOmegaMassUpperLimit) && + (cascade.mXi() < mXiMassLowerLimit || cascade.mXi() > mXiMassUpperLimit); } - return false; + + return false; // should never happen } protected: @@ -327,7 +344,7 @@ struct ConfCascadeTables : o2::framework::ConfigurableGroup { o2::framework::Configurable produceOmegaExtras{"produceOmegaExtras", -1, "Produce OmegaExtras (-1: auto; 0 off; 1 on)"}; }; -template +template class CascadeBuilder { public: @@ -335,9 +352,8 @@ class CascadeBuilder ~CascadeBuilder() = default; template - void init(T1& config, T2& filter, T3& table, T4& initContext) + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext) { - mCascadeSelection.configure(config, filter); if constexpr (modes::isEqual(cascadeType, modes::Cascade::kXi)) { LOG(info) << "Initialize femto Xi builder..."; mProduceXis = utils::enableTable("FXis_001", table.produceXis.value, initContext); @@ -353,10 +369,13 @@ class CascadeBuilder if (mProduceXis || mProduceXiExtras || mProduceXiMasks || mProduceOmegas || mProduceOmegaMasks || mProduceOmegaExtras) { mFillAnyTable = true; - mCascadeSelection.printSelections(cascadeSelsName, cascadeSelsToString); } else { - LOG(info) << "No tables configured"; + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; } + mCascadeSelection.configure(registry, config, filter); + mCascadeSelection.printSelections(CascadeSelsName); LOG(info) << "Initialization done..."; } @@ -371,11 +390,11 @@ class CascadeBuilder int32_t posDaughterIndex = 0; int32_t negDaughterIndex = 0; for (const auto& cascade : fullCascades) { - if (!mCascadeSelection.checkFilters(cascade)) { + if (!mCascadeSelection.checkCandidate(cascade)) { continue; } mCascadeSelection.applySelections(cascade, fullTracks, col); - if (mCascadeSelection.passesAllRequiredSelections() && mCascadeSelection.checkHypothesis(cascade)) { + if (mCascadeSelection.passesAllRequiredSelections()) { auto bachelor = cascade.template bachelor_as(); auto posDaughter = cascade.template posTrack_as(); @@ -450,7 +469,7 @@ class CascadeBuilder bool fillAnyTable() { return mFillAnyTable; } private: - CascadeSelection mCascadeSelection; + CascadeSelection mCascadeSelection; bool mFillAnyTable = false; bool mProduceXis = false; bool mProduceXiMasks = false; diff --git a/PWGCF/Femto/Core/collisionBuilder.h b/PWGCF/Femto/Core/collisionBuilder.h index bf594b1076c..468a5ac7b0c 100644 --- a/PWGCF/Femto/Core/collisionBuilder.h +++ b/PWGCF/Femto/Core/collisionBuilder.h @@ -138,8 +138,9 @@ enum CollisionSels { kCollisionSelsMax }; +constexpr char ColSelHistName[] = "hCollisionSelection"; const char colSelsName[] = "Collision Selection Object"; -const std::unordered_map colSelsToString = { +const std::unordered_map collisionSelectionNames = { {kSel8, "Sel8"}, {kNoSameBunchPileUp, "No same bunch pileup"}, {kIsVertexItsTpc, "Is vertex ITS TPC"}, @@ -162,6 +163,7 @@ const std::unordered_map colSelsToString = { }; +template class CollisionSelection : public BaseSelection { public: @@ -169,7 +171,7 @@ class CollisionSelection : public BaseSelection - void configure(T1 const& filter, T2 const& config) + void configure(o2::framework::HistogramRegistry* registry, T1 const& filter, T2 const& config) { // cuts mVtxZMin = filter.vtxZMin.value; @@ -184,26 +186,28 @@ class CollisionSelection : public BaseSelectionaddSelection(config.sel8.value, kSel8); - this->addSelection(config.noSameBunchPileup.value, kNoSameBunchPileUp); - this->addSelection(config.isGoodZvtxFt0VsPv.value, kIsGoodZvtxFt0VsPv); - this->addSelection(config.noCollInTimeRangeNarrow.value, kNoCollInTimeRangeNarrow); - this->addSelection(config.noCollInTimeRangeStrict.value, kNoCollInTimeRangeStrict); - this->addSelection(config.noCollInTimeRangeStandard.value, kNoCollInTimeRangeStandard); - this->addSelection(config.noCollInRofStrict.value, kNoCollInRofStrict); - this->addSelection(config.noCollInRofStandard.value, kNoCollInRofStandard); - this->addSelection(config.noHighMultCollInPrevRof.value, kNoHighMultCollInPrevRof); - this->addSelection(config.isGoodItsLayer3.value, kIsGoodItsLayer3); - this->addSelection(config.isGoodItsLayer0123.value, kIsGoodItsLayer0123); - this->addSelection(config.isGoodItsLayersAll.value, kIsGoodItsLayersAll); - this->addSelection(config.occupancyMin.value, kOccupancyMin, limits::kLowerLimit, true, true); - this->addSelection(config.occupancyMax.value, kOccupancyMax, limits::kUpperLimit, true, true); - this->addSelection(config.sphericityMin.value, kSphericityMin, limits::kLowerLimit, true, true); - this->addSelection(config.sphericityMax.value, kSphericityMax, limits::kUpperLimit, true, true); + this->addSelection(kSel8, collisionSelectionNames.at(kSel8), config.sel8.value); + this->addSelection(kNoSameBunchPileUp, collisionSelectionNames.at(kNoSameBunchPileUp), config.noSameBunchPileup.value); + this->addSelection(kIsGoodZvtxFt0VsPv, collisionSelectionNames.at(kIsGoodZvtxFt0VsPv), config.isGoodZvtxFt0VsPv.value); + this->addSelection(kNoCollInTimeRangeNarrow, collisionSelectionNames.at(kNoCollInTimeRangeNarrow), config.noCollInTimeRangeNarrow.value); + this->addSelection(kNoCollInTimeRangeStrict, collisionSelectionNames.at(kNoCollInTimeRangeStrict), config.noCollInTimeRangeStrict.value); + this->addSelection(kNoCollInTimeRangeStandard, collisionSelectionNames.at(kNoCollInTimeRangeStandard), config.noCollInTimeRangeStandard.value); + this->addSelection(kNoCollInRofStrict, collisionSelectionNames.at(kNoCollInRofStrict), config.noCollInRofStrict.value); + this->addSelection(kNoCollInRofStandard, collisionSelectionNames.at(kNoCollInRofStandard), config.noCollInRofStandard.value); + this->addSelection(kNoHighMultCollInPrevRof, collisionSelectionNames.at(kNoHighMultCollInPrevRof), config.noHighMultCollInPrevRof.value); + this->addSelection(kIsGoodItsLayer3, collisionSelectionNames.at(kIsGoodItsLayer3), config.isGoodItsLayer3.value); + this->addSelection(kIsGoodItsLayer0123, collisionSelectionNames.at(kIsGoodItsLayer0123), config.isGoodItsLayer0123.value); + this->addSelection(kIsGoodItsLayersAll, collisionSelectionNames.at(kIsGoodItsLayersAll), config.isGoodItsLayersAll.value); + this->addSelection(kOccupancyMin, collisionSelectionNames.at(kOccupancyMin), config.occupancyMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kOccupancyMax, collisionSelectionNames.at(kOccupancyMax), config.occupancyMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kSphericityMin, collisionSelectionNames.at(kSphericityMin), config.sphericityMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kSphericityMax, collisionSelectionNames.at(kSphericityMax), config.sphericityMax.value, limits::kUpperLimit, true, true, false); std::vector triggerValues(config.triggers.value.size(), 1.f); - this->addSelection(triggerValues, kTriggers, limits::kEqualArray, false, true); + this->addSelection(kTriggers, collisionSelectionNames.at(kTriggers), triggerValues, limits::kEqualArray, false, false, true); this->addComments(kTriggers, config.triggers.value); + + this->setupContainers(registry); }; void setMagneticField(int MagField) @@ -302,7 +306,7 @@ class CollisionSelection : public BaseSelectionevaluateObservable(kTriggers, trigger); } - this->assembleBitmask(); + this->assembleBitmask(); }; protected: @@ -345,6 +349,7 @@ struct ConfCollisionTables : o2::framework::ConfigurableGroup { o2::framework::Configurable produceQns{"produceQns", -1, "Produce Qn (-1: auto; 0 off; 1 on)"}; }; +template class CollisionBuilder { public: @@ -352,9 +357,24 @@ class CollisionBuilder ~CollisionBuilder() = default; template - void init(T1& confFilter, T2& confBits, T3& confRct, T4& confCcdb, T5& confTable, T6& initContext) + void init(o2::framework::HistogramRegistry* registry, T1& confFilter, T2& confBits, T3& confRct, T4& confCcdb, T5& confTable, T6& initContext) { - mCollisionSelection.configure(confFilter, confBits); + LOG(info) << "Initialize femto collision builder..."; + mProducedCollisions = utils::enableTable("FCols_001", confTable.produceCollisions.value, initContext); + mProducedCollisionMasks = utils::enableTable("FColMasks_001", confTable.produceCollisionMasks.value, initContext); + mProducedPositions = utils::enableTable("FColPos_001", confTable.producePositions.value, initContext); + mProducedSphericities = utils::enableTable("FColSphericities_001", confTable.produceSphericities.value, initContext); + mProducedMultiplicities = utils::enableTable("FColMults_001", confTable.produceMults.value, initContext); + mProducedCentralities = utils::enableTable("FColCents_001", confTable.produceCents.value, initContext); + mProduceQns = utils::enableTable("FColQnBins_001", confTable.produceQns.value, initContext); + if (mProducedCollisions || mProducedCollisionMasks || mProducedPositions || mProducedSphericities || mProducedMultiplicities || mProducedCentralities) { + mFillAnyTable = true; + } else { + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; + } + if (!confBits.triggers.value.empty()) { mUseTrigger = true; for (size_t i = 0; i < confBits.triggers.value.size(); ++i) { @@ -371,20 +391,8 @@ class CollisionBuilder } mGrpPath = confCcdb.grpPath.value; - LOG(info) << "Initialize femto collision builder..."; - mProducedCollisions = utils::enableTable("FCols_001", confTable.produceCollisions.value, initContext); - mProducedCollisionMasks = utils::enableTable("FColMasks_001", confTable.produceCollisionMasks.value, initContext); - mProducedPositions = utils::enableTable("FColPos_001", confTable.producePositions.value, initContext); - mProducedSphericities = utils::enableTable("FColSphericities_001", confTable.produceSphericities.value, initContext); - mProducedMultiplicities = utils::enableTable("FColMults_001", confTable.produceMults.value, initContext); - mProducedCentralities = utils::enableTable("FColCents_001", confTable.produceCents.value, initContext); - mProduceQns = utils::enableTable("FColQnBins_001", confTable.produceQns.value, initContext); - if (mProducedCollisions || mProducedCollisionMasks || mProducedPositions || mProducedSphericities || mProducedMultiplicities || mProducedCentralities) { - mFillAnyTable = true; - mCollisionSelection.printSelections(colSelsName, colSelsToString); - } else { - LOG(info) << "No tables configured"; - } + mCollisionSelection.configure(registry, confFilter, confBits); + mCollisionSelection.printSelections(colSelsName); LOG(info) << "Initialization done..."; } @@ -408,8 +416,8 @@ class CollisionBuilder mCollisionSelection.setMagneticField(mMagField); mCollisionSelection.setSphericity(tracks); - mCollisionSelection.setMultiplicity(col); - mCollisionSelection.setCentrality(col); + mCollisionSelection.template setMultiplicity(col); + mCollisionSelection.template setCentrality(col); std::vector triggerDecisions = {}; if (mUseTrigger) { @@ -477,7 +485,7 @@ class CollisionBuilder } private: - CollisionSelection mCollisionSelection; + CollisionSelection mCollisionSelection; Zorro mZorro; bool mUseTrigger = false; int mRunNumber = -1; diff --git a/PWGCF/Femto/Core/kinkBuilder.h b/PWGCF/Femto/Core/kinkBuilder.h index 1f10d9a5f38..9f95efa3e3a 100644 --- a/PWGCF/Femto/Core/kinkBuilder.h +++ b/PWGCF/Femto/Core/kinkBuilder.h @@ -157,8 +157,10 @@ enum KinkSeles { kKinkSelsMax }; +constexpr char SigmaSelHistName[] = "hSigmaSelection"; +constexpr char SigmaPlusSelHistName[] = "hSigmaPlusSelection"; const char kinkSelsName[] = "Kink selection object"; -const std::unordered_map kinkSelsToStrings = { +const std::unordered_map kinkSelectionNames = { {kKinkTopoDcaMax, "kinkTopoDcaMax"}, {kTransRadMin, "transRadMin"}, {kTransRadMax, "transRadMax"}, @@ -176,15 +178,15 @@ const std::unordered_map kinkSelsToStrings = { /// \class KinkCuts /// \brief Cut class to contain and execute all cuts applied to kinks -template +template class KinkSelection : public BaseSelection { public: - KinkSelection() {} - virtual ~KinkSelection() = default; + KinkSelection() = default; + ~KinkSelection() = default; template - void configure(T1& config, T2& filter) + void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter) { mPtMin = filter.ptMin.value; mPtMax = filter.ptMax.value; @@ -197,28 +199,30 @@ class KinkSelection : public BaseSelectionaddSelection(config.chaDauTpcPion.value, kChaDaughTpcPion, limits::kAbsUpperLimit, true, true); + this->addSelection(kChaDaughTpcPion, kinkSelectionNames.at(kChaDaughTpcPion), config.chaDauTpcPion.value, limits::kAbsUpperLimit, true, true, false); } if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { mMassSigmaPlusLowerLimit = filter.massMinSigmaPlus.value; mMassSigmaPlusUpperLimit = filter.massMaxSigmaPlus.value; mPidThreshold = config.pidThres.value; - this->addSelection(config.chaDauTpcProton.value, kChaDaughTpcProton, limits::kAbsUpperLimit, true, true); - this->addSelection(config.chaDauTofProton.value, kChaDaughTofProton, limits::kUpperLimit, true, true); + this->addSelection(kChaDaughTpcProton, kinkSelectionNames.at(kChaDaughTpcProton), config.chaDauTpcProton.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kChaDaughTofProton, kinkSelectionNames.at(kChaDaughTofProton), config.chaDauTofProton.value, limits::kUpperLimit, false, false, true); } - this->addSelection(config.kinkTopoDcaMax.value, kKinkTopoDcaMax, limits::kUpperLimit, true, true); - this->addSelection(config.transRadMin.value, kTransRadMin, limits::kLowerLimit, true, true); - this->addSelection(config.transRadMax.value, kTransRadMax, limits::kUpperLimit, true, true); - this->addSelection(config.dauAbsEtaMax.value, kDauAbsEtaMax, limits::kAbsUpperLimit, true, true); - this->addSelection(config.dauDcaPvMin.value, kDauDcaPvMin, limits::kLowerLimit, true, true); - this->addSelection(config.mothDcaPvMax.value, kMothDcaPvMax, limits::kUpperLimit, true, true); - this->addSelection(config.alphaAPMin.value, kAlphaAPMin, limits::kLowerLimit, true, true); - this->addSelection(config.alphaAPMax.value, kAlphaAPMax, limits::kUpperLimit, true, true); - this->addSelection(config.qtAPMin.value, kQtAPMin, limits::kLowerLimit, true, true); - this->addSelection(config.qtAPMax.value, kQtAPMax, limits::kUpperLimit, true, true); - this->addSelection(config.cosPointingAngleMin.value, kCosPointingAngleMin, limits::kLowerLimit, true, true); + this->addSelection(kKinkTopoDcaMax, kinkSelectionNames.at(kKinkTopoDcaMax), config.kinkTopoDcaMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kTransRadMin, kinkSelectionNames.at(kTransRadMin), config.transRadMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kTransRadMax, kinkSelectionNames.at(kTransRadMax), config.transRadMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kDauAbsEtaMax, kinkSelectionNames.at(kDauAbsEtaMax), config.dauAbsEtaMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kDauDcaPvMin, kinkSelectionNames.at(kDauDcaPvMin), config.dauDcaPvMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kMothDcaPvMax, kinkSelectionNames.at(kMothDcaPvMax), config.mothDcaPvMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kAlphaAPMin, kinkSelectionNames.at(kAlphaAPMin), config.alphaAPMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kAlphaAPMax, kinkSelectionNames.at(kAlphaAPMax), config.alphaAPMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kQtAPMin, kinkSelectionNames.at(kQtAPMin), config.qtAPMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kQtAPMax, kinkSelectionNames.at(kQtAPMax), config.qtAPMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kCosPointingAngleMin, kinkSelectionNames.at(kCosPointingAngleMin), config.cosPointingAngleMin.value, limits::kLowerLimit, true, true, false); + + this->setupContainers(registry); }; template @@ -286,7 +290,7 @@ class KinkSelection : public BaseSelectionassembleBitmask(); + this->assembleBitmask(); }; template @@ -307,7 +311,7 @@ class KinkSelection : public BaseSelection - bool checkHypothesis(T const& kinkCand) const + bool checkMass(T const& kinkCand) const { if constexpr (modes::isEqual(kinkType, modes::Kink::kSigma)) { float sigmaMass = kinkCand.mSigmaMinus(); @@ -356,17 +360,16 @@ struct ConfKinkTables : o2::framework::ConfigurableGroup { o2::framework::Configurable produceSigmaPlusExtras{"produceSigmaPlusExtras", -1, "Produce SigmaPlusExtras (-1: auto; 0 off; 1 on)"}; }; -template +template class KinkBuilder { public: - KinkBuilder() {} - virtual ~KinkBuilder() = default; + KinkBuilder() = default; + ~KinkBuilder() = default; template - void init(T1& config, T2& filter, T3& table, T4& initContext) + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext) { - mKinkSelection.configure(config, filter); if constexpr (modes::isEqual(kinkType, modes::Kink::kSigma)) { LOG(info) << "Initialize femto Sigma builder..."; mProduceSigmas = utils::enableTable("FSigmas_001", table.produceSigmas.value, initContext); @@ -383,16 +386,13 @@ class KinkBuilder if (mProduceSigmas || mProduceSigmaMasks || mProduceSigmaExtras || mProduceSigmaPlus || mProduceSigmaPlusMasks || mProduceSigmaPlusExtras) { mFillAnyTable = true; - mKinkSelection.printSelections(kinkSelsName, kinkSelsToStrings); - if constexpr (modes::isEqual(kinkType, modes::Kink::kSigma)) { - LOG(info) << "Sigma tables enabled: Sigmas=" << mProduceSigmas << " Masks=" << mProduceSigmaMasks << " Extras=" << mProduceSigmaExtras; - } - if constexpr (modes::isEqual(kinkType, modes::Kink::kSigmaPlus)) { - LOG(info) << "SigmaPlus tables enabled: SigmaPlus=" << mProduceSigmaPlus << " Masks=" << mProduceSigmaPlusMasks << " Extras=" << mProduceSigmaPlusExtras; - } } else { - LOG(info) << "No tables configured"; + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; } + mKinkSelection.configure(registry, config, filter); + mKinkSelection.printSelections(kinkSelsName); LOG(info) << "Initialization done..."; } @@ -400,7 +400,6 @@ class KinkBuilder void fillKinks(T1& collisionProducts, T2& trackProducts, T3& kinkProducts, T4 const& kinks, T5 const& tracks, T6& trackBuilder, T7& indexMap) { if (!mFillAnyTable) { - LOG(info) << "KinkBuilder: No tables configured to be filled"; return; } int64_t daughterIndex = 0; @@ -410,7 +409,7 @@ class KinkBuilder continue; } - if (!mKinkSelection.checkHypothesis(kink)) { + if (!mKinkSelection.checkMass(kink)) { continue; } @@ -549,7 +548,7 @@ class KinkBuilder bool fillAnyTable() { return mFillAnyTable; } private: - KinkSelection mKinkSelection; + KinkSelection mKinkSelection; bool mFillAnyTable = false; bool mProduceSigmas = false; bool mProduceSigmaMasks = false; diff --git a/PWGCF/Femto/Core/selectionContainer.h b/PWGCF/Femto/Core/selectionContainer.h index f97dfc4f6fd..ca08b657d96 100644 --- a/PWGCF/Femto/Core/selectionContainer.h +++ b/PWGCF/Femto/Core/selectionContainer.h @@ -83,11 +83,18 @@ class SelectionContainer /// \param limitType Type of limit (from limits::LimitType). /// \param SkipMostPermissiveBit Whether to skip the most permissive bit in the bitmask. /// \param IsMinimalCut Whether this selection should be treated as a minimal required cut. - SelectionContainer(std::vector const& SelectionValues, limits::LimitType limitType, bool SkipMostPermissiveBit, bool IsMinimalCut) - : mSelectionValues(SelectionValues), + SelectionContainer(std::string const& SelectionName, + std::vector const& SelectionValues, + limits::LimitType limitType, + bool SkipMostPermissiveBit, + bool IsMinimalCut, + bool isOptionalCut) + : mSelectionName(SelectionName), + mSelectionValues(SelectionValues), mLimitType(limitType), mSkipMostPermissiveBit(SkipMostPermissiveBit), - mIsMinimalCut(IsMinimalCut) + mIsMinimalCut(IsMinimalCut), + mIsOptionalCut(isOptionalCut) { if (mSelectionValues.size() > sizeof(BitmaskType) * CHAR_BIT) { LOG(fatal) << "Too many selections for single a observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; @@ -104,22 +111,25 @@ class SelectionContainer /// \param limitType Type of limit. /// \param skipMostPermissiveBit Whether to skip the most permissive bit in the bitmask. /// \param IsMinimalCut Whether this selection should be treated as a minimal required cut. - SelectionContainer(std::string const& baseName, + SelectionContainer(std::string const& SelectionName, T lowerLimit, T upperLimit, std::vector const& functions, limits::LimitType limitType, bool skipMostPermissiveBit, - bool IsMinimalCut) - : mLimitType(limitType), + bool IsMinimalCut, + bool isOptionalCut) + : mSelectionName(SelectionName), + mLimitType(limitType), mSkipMostPermissiveBit(skipMostPermissiveBit), - mIsMinimalCut(IsMinimalCut) + mIsMinimalCut(IsMinimalCut), + mIsOptionalCut(isOptionalCut) { if (functions.size() > sizeof(BitmaskType) * CHAR_BIT) { LOG(fatal) << "Too many selections for single a observable. Limit is " << sizeof(BitmaskType) * CHAR_BIT; } for (std::size_t i = 0; i < functions.size(); i++) { - mSelectionFunctions.emplace_back((baseName + std::to_string(i)).c_str(), functions.at(i).c_str(), lowerLimit, upperLimit); + mSelectionFunctions.emplace_back((mSelectionName + std::to_string(i)).c_str(), functions.at(i).c_str(), lowerLimit, upperLimit); } // functions for selection are not necessarily ordered correctly // use value at midpoint to order them @@ -156,11 +166,11 @@ class SelectionContainer switch (mLimitType) { case (limits::kUpperFunctionLimit): case (limits::kAbsUpperFunctionLimit): - std::sort(mSelectionFunctions.begin(), mSelectionFunctions.end(), [value](TF1 a, TF1 b) { return a.Eval(value) > b.Eval(value); }); + std::sort(mSelectionFunctions.begin(), mSelectionFunctions.end(), [value](TF1 const& a, TF1 const& b) { return a.Eval(value) > b.Eval(value); }); break; case (limits::kLowerFunctionLimit): case (limits::kAbsLowerFunctionLimit): - std::sort(mSelectionFunctions.begin(), mSelectionFunctions.end(), [value](TF1 a, TF1 b) { return a.Eval(value) < b.Eval(value); }); + std::sort(mSelectionFunctions.begin(), mSelectionFunctions.end(), [value](TF1 const& a, TF1 const& b) { return a.Eval(value) < b.Eval(value); }); break; default: break; @@ -177,6 +187,7 @@ class SelectionContainer } std::vector const& getComments() const { return mComments; } + std::string const& getSelectionName() const { return mSelectionName; } /// \brief Update selection limits using internal functions evaluated at a given value. /// \param value Input value to evaluate functions at. @@ -295,33 +306,36 @@ class SelectionContainer // check if any bit is set // in case were bits are evaluted in order, if the loosests fails, all fail, so testing any is safe here return mBitmask.any(); - } else { - // if selection is not marked as a minimal cut, we return true by default - return true; } + // if a selection is not marked as minimal cut we return true by default + return true; } /// \brief Check whether any optional cuts are fulfilled. /// \return True if at least one optional cut is passed. bool passesAsOptionalCut() const { - // if selection is marekd as minimal cut, we return false by default - if (mIsMinimalCut) { - return false; - } else { + if (mIsOptionalCut) { // check if any bit is set + // in case were bits are evaluted in order, if the loosests fails, all fail, so testing any is safe here return mBitmask.any(); } + // if a selection is not marked as optional cut we return false by default + return false; } /// \brief Get the loosest (most permissive) selection value. /// \return First (loosest) selection value. T getLoosestSelection() const { return mSelectionValues.at(0); } - /// \brief Check if there are any selection values configured. + /// \brief Check if there are any selection values configured. We also init values in case of function so this is safe /// \return True if no selections are configured. bool isEmpty() const { return mSelectionValues.empty(); } + /// \brief Check if there are any selection values configured. + /// \return True if no selections are configured. + bool isUsingFunctions() const { return !mSelectionFunctions.empty(); } + /// \brief Get the number of bits to shift for the final bitmask. /// \return Number of bits to shift. int getShift() const @@ -336,6 +350,41 @@ class SelectionContainer } } + void setOffset(int offset) { mOffset = offset; } + int getOffset() const { return mOffset; } + + int getNSelections() const { return mSelectionValues.size(); } + + std::string getBinLabel(int selectionIndex) const + { + std::ostringstream oss; + std::string sectionDelimiter = ":::"; + std::string valueDelimiter = "___"; + oss << "SelectionName" << valueDelimiter << mSelectionName << sectionDelimiter + << "LimitType" << valueDelimiter << getLimitTypeAsString() << sectionDelimiter + << "MinimalCut" << valueDelimiter << (mIsMinimalCut ? "1" : "0") << sectionDelimiter + << "SkipMostPermissiveBit" << valueDelimiter << (mSkipMostPermissiveBit ? "1" : "0") << sectionDelimiter + << "OptionalCut" << valueDelimiter << (mIsOptionalCut ? "1" : "0") << sectionDelimiter + << "Shift" << valueDelimiter << getShift() << sectionDelimiter + << "Offset" << valueDelimiter << mOffset << sectionDelimiter + << "Value" << valueDelimiter << (mSelectionFunctions.empty() ? std::to_string(mSelectionValues.at(selectionIndex)) : mSelectionFunctions.at(selectionIndex).GetExpFormula().Data()) << sectionDelimiter + << "BitPosition" << valueDelimiter << (mSkipMostPermissiveBit ? (selectionIndex == 0 ? "X" : std::to_string(mOffset + selectionIndex - 1)) : std::to_string(mOffset + selectionIndex)); + return oss.str(); + } + + int getBitPosition(int selectionIndex) const + { + if (selectionIndex == 0 && mSkipMostPermissiveBit) { + LOG(fatal) << "Trying to accessed the bit position of a skipped selection. Breaking..."; + return -1; + } + if (mSkipMostPermissiveBit) { + return mOffset + selectionIndex - 1; + } else { + return mOffset + selectionIndex; + } + } + /// \brief Get string representation of the limit type. /// \return String name of the limit type. std::string getLimitTypeAsString() const { return limits::limitTypeAsStrings[mLimitType]; } @@ -348,22 +397,31 @@ class SelectionContainer /// \return Vector of selection values. std::vector const& getSelectionFunction() const { return mSelectionFunctions; } - /// \brief Check if this container is marked as minimal cut. + /// \brief Check if this container is marked as minimal cut /// \return True if minimal cut, false otherwise. bool isMinimalCut() const { return mIsMinimalCut; } + /// \brief Check if this container is marked as optional cut + /// \return True if minimal cut, false otherwise. + bool isOptionalCut() const { return mIsOptionalCut; } + /// \brief Check whether the most permissive bit is skipped. /// \return True if skipped, false otherwise. bool skipMostPermissiveBit() const { return mSkipMostPermissiveBit; } + void reset() { mBitmask.reset(); } + private: + std::string mSelectionName = std::string(""); std::vector mSelectionValues = {}; ///< Values used for the selection - std::vector mComments = {}; ///< Comments for the values std::vector mSelectionFunctions = {}; ///< Function used for the selection limits::LimitType mLimitType = limits::kLimitTypeLast; ///< Limit type of selection - std::bitset mBitmask = {}; ///< bitmask for the observable bool mSkipMostPermissiveBit = false; ///< whether to skip the last bit or not bool mIsMinimalCut = false; ///< whether to use this observable for minimal selection or not + bool mIsOptionalCut = false; ///< whether to use this observable for minimal selection or not + std::vector mComments = {}; ///< Comments for the values + std::bitset mBitmask = {}; ///< bitmask for the observable + int mOffset = 0; }; } // namespace o2::analysis::femto diff --git a/PWGCF/Femto/Core/trackBuilder.h b/PWGCF/Femto/Core/trackBuilder.h index 7bcb977473e..f744a1a3873 100644 --- a/PWGCF/Femto/Core/trackBuilder.h +++ b/PWGCF/Femto/Core/trackBuilder.h @@ -64,52 +64,68 @@ struct ConfTrackBits : o2::framework::ConfigurableGroup { o2::framework::Configurable> dcaxyMax{"dcaxyMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_xy| as a function of pT. Has to be a valid TForumal, where x=pt"}; o2::framework::Configurable> dcazMax{"dcazMax", {"0.004 + 0.013*pow(x, -1)"}, "Maximum |dca_z| as a function of pT. Has to be a valid TForumal, where x=pt"}; - o2::framework::Configurable minMomentumForTof{"minMomentumForTof", 2.0f, "Minimum momentum to required TOF PID (all species)"}; - - // track its pid cuts - o2::framework::Configurable> itsElectron{"itsElectron", {}, "Maximum |nsigma| for electron PID"}; - o2::framework::Configurable> itsPion{"itsPion", {}, "Maximum |nsigma| for pion PID"}; - o2::framework::Configurable> itsKaon{"itsKaon", {}, "Maximum |nsigma| for kaon PID"}; - o2::framework::Configurable> itsProton{"itsProton", {}, "Maximum |nsigma| for proton PID"}; - o2::framework::Configurable> itsDeuteron{"itsDeuteron", {}, "Maximum |nsigma| for deuteron PID"}; - o2::framework::Configurable> itsTriton{"itsTriton", {}, "Maximum |nsigma| for trition PID"}; - o2::framework::Configurable> itsHelium{"itsHelium", {}, "Maximum |nsigma| for helium PID"}; - - // track tpc pid cuts - o2::framework::Configurable> tpcElectron{"tpcElectron", {}, "Maximum |nsigma| for electron PID"}; - o2::framework::Configurable> tpcPion{"tpcPion", {}, "Maximum |nsigma| for pion PID"}; - o2::framework::Configurable> tpcKaon{"tpcKaon", {}, "Maximum |nsigma| for kaon PID"}; - o2::framework::Configurable> tpcProton{"tpcProton", {}, "Maximum |nsigma| for proton PID"}; - o2::framework::Configurable> tpcDeuteron{"tpcDeuteron", {}, "Maximum |nsigma| for deuteron PID"}; - o2::framework::Configurable> tpcTriton{"tpcTriton", {}, "Maximum |nsigma| for trition PID"}; - o2::framework::Configurable> tpcHelium{"tpcHelium", {}, "Maximum |nsigma| for helium PID"}; - - // track tof pid cuts - o2::framework::Configurable> tofElectron{"tofElectron", {}, "Maximum |nsigma| for electron PID"}; - o2::framework::Configurable> tofPion{"tofPion", {}, "Maximum |nsigma| for pion PID"}; - o2::framework::Configurable> tofKaon{"tofKaon", {}, "Maximum |nsigma| for kaon PID"}; - o2::framework::Configurable> tofProton{"tofProton", {}, "Maximum |nsigma| for proton PID"}; - o2::framework::Configurable> tofDeuteron{"tofDeuteron", {}, "Maximum |nsigma| for deuteron PID"}; - o2::framework::Configurable> tofTriton{"tofTriton", {}, "Maximum |nsigma| for trition PID"}; - o2::framework::Configurable> tofHelium{"tofHelium", {}, "Maximum |nsigma| for helium PID"}; - - // track tpcits pid cuts - o2::framework::Configurable> tpcitsElectron{"tpcitsElectron", {}, "Maximum |nsigma| for electron PID"}; - o2::framework::Configurable> tpcitsPion{"tpcitsPion", {}, "Maximum |nsigma| for pion PID"}; - o2::framework::Configurable> tpcitsKaon{"tpcitsKaon", {}, "Maximum |nsigma| for kaon PID"}; - o2::framework::Configurable> tpcitsProton{"tpcitsProton", {3.f}, "Maximum |nsigma| for proton PID"}; - o2::framework::Configurable> tpcitsDeuteron{"tpcitsDeuteron", {}, "Maximum |nsigma| for deuteron PID"}; - o2::framework::Configurable> tpcitsTriton{"tpcitsTriton", {}, "Maximum |nsigma| for trition PID"}; - o2::framework::Configurable> tpcitsHelium{"tpcitsHelium", {}, "Maximum |nsigma| for helium PID"}; - - // track tpctof pid cuts - o2::framework::Configurable> tpctofElectron{"tpctofElectron", {}, "Maximum |nsigma| for electron PID"}; - o2::framework::Configurable> tpctofPion{"tpctofPion", {}, "Maximum |nsigma| for pion PID"}; - o2::framework::Configurable> tpctofKaon{"tpctofKaon", {}, "Maximum |nsigma| for kaon PID"}; - o2::framework::Configurable> tpctofProton{"tpctofProton", {3.f}, "Maximum |nsigma| for proton PID"}; - o2::framework::Configurable> tpctofDeuteron{"tpctofDeuteron", {}, "Maximum |nsigma| for deuteron PID"}; - o2::framework::Configurable> tpctofTriton{"tpctofTriton", {}, "Maximum |nsigma| for trition PID"}; - o2::framework::Configurable> tpctofHelium{"tpctofHelium", {}, "Maximum |nsigma| for helium PID"}; + // Electron PID cuts + o2::framework::Configurable pidIsOptionalElectron{"pidIsOptionalElectron", false, "Make election PID optional"}; + o2::framework::Configurable minMomTofElectron{"minMomTofElectron", 0.3, "Minimum momentum to required TOF PID for Electron"}; + o2::framework::Configurable> itsElectron{"itsElectron", {}, "Maximum |nsigma| for Electron PID"}; + o2::framework::Configurable> tpcElectron{"tpcElectron", {}, "Maximum |nsigma| for Electron PID"}; + o2::framework::Configurable> tofElectron{"tofElectron", {}, "Maximum |nsigma| for Electron PID"}; + o2::framework::Configurable> tpcitsElectron{"tpcitsElectron", {}, "Maximum |nsigma| for Electron PID"}; + o2::framework::Configurable> tpctofElectron{"tpctofElectron", {}, "Maximum |nsigma| for Electron PID"}; + + // Pion PID cuts + o2::framework::Configurable pidIsOptionalPion{"pidIsOptionalPion", false, "Make election PID optional"}; + o2::framework::Configurable minMomTofPion{"minMomTofPion", 0.5, "Minimum momentum to required TOF PID for Pion"}; + o2::framework::Configurable> itsPion{"itsPion", {}, "Maximum |nsigma| for Pion PID"}; + o2::framework::Configurable> tpcPion{"tpcPion", {}, "Maximum |nsigma| for Pion PID"}; + o2::framework::Configurable> tofPion{"tofPion", {}, "Maximum |nsigma| for Pion PID"}; + o2::framework::Configurable> tpcitsPion{"tpcitsPion", {}, "Maximum |nsigma| for Pion PID"}; + o2::framework::Configurable> tpctofPion{"tpctofPion", {}, "Maximum |nsigma| for Pion PID"}; + + // Kaon PID cuts + o2::framework::Configurable pidIsOptionalKaon{"pidIsOptionalKaon", false, "Make election PID optional"}; + o2::framework::Configurable minMomTofKaon{"minMomTofKaon", 0.4, "Minimum momentum to required TOF PID for Kaon"}; + o2::framework::Configurable> itsKaon{"itsKaon", {}, "Maximum |nsigma| for Kaon PID"}; + o2::framework::Configurable> tpcKaon{"tpcKaon", {}, "Maximum |nsigma| for Kaon PID"}; + o2::framework::Configurable> tofKaon{"tofKaon", {}, "Maximum |nsigma| for Kaon PID"}; + o2::framework::Configurable> tpcitsKaon{"tpcitsKaon", {}, "Maximum |nsigma| for Kaon PID"}; + o2::framework::Configurable> tpctofKaon{"tpctofKaon", {}, "Maximum |nsigma| for Kaon PID"}; + + // Proton PID cuts + o2::framework::Configurable pidIsOptionalProton{"pidIsOptionalProton", true, "Make election PID optional"}; + o2::framework::Configurable minMomTofProton{"minMomTofProton", 0.75, "Minimum momentum to required TOF PID for Proton"}; + o2::framework::Configurable> itsProton{"itsProton", {}, "Maximum |nsigma| for Proton PID"}; + o2::framework::Configurable> tpcProton{"tpcProton", {}, "Maximum |nsigma| for Proton PID"}; + o2::framework::Configurable> tofProton{"tofProton", {}, "Maximum |nsigma| for Proton PID"}; + o2::framework::Configurable> tpcitsProton{"tpcitsProton", {3.f}, "Maximum |nsigma| for Proton PID"}; + o2::framework::Configurable> tpctofProton{"tpctofProton", {3.f}, "Maximum |nsigma| for Proton PID"}; + + // Deuteron PID cuts + o2::framework::Configurable pidIsOptionalDeuteron{"pidIsOptionalDeuteron", false, "Make election PID optional"}; + o2::framework::Configurable minMomTofDeuteron{"minMomTofDeuteron", 1.2, "Minimum momentum to required TOF PID for Deuteron"}; + o2::framework::Configurable> itsDeuteron{"itsDeuteron", {}, "Maximum |nsigma| for Deuteron PID"}; + o2::framework::Configurable> tpcDeuteron{"tpcDeuteron", {}, "Maximum |nsigma| for Deuteron PID"}; + o2::framework::Configurable> tofDeuteron{"tofDeuteron", {}, "Maximum |nsigma| for Deuteron PID"}; + o2::framework::Configurable> tpcitsDeuteron{"tpcitsDeuteron", {}, "Maximum |nsigma| for Deuteron PID"}; + o2::framework::Configurable> tpctofDeuteron{"tpctofDeuteron", {}, "Maximum |nsigma| for Deuteron PID"}; + + // Triton PID cuts + o2::framework::Configurable pidIsOptionalTriton{"pidIsOptionalTriton", false, "Make election PID optional"}; + o2::framework::Configurable minMomTofTriton{"minMomTofTriton", 1.4, "Minimum momentum to required TOF PID for Triton"}; + o2::framework::Configurable> itsTriton{"itsTriton", {}, "Maximum |nsigma| for Triton PID"}; + o2::framework::Configurable> tpcTriton{"tpcTriton", {}, "Maximum |nsigma| for Triton PID"}; + o2::framework::Configurable> tofTriton{"tofTriton", {}, "Maximum |nsigma| for Triton PID"}; + o2::framework::Configurable> tpcitsTriton{"tpcitsTriton", {}, "Maximum |nsigma| for Triton PID"}; + o2::framework::Configurable> tpctofTriton{"tpctofTriton", {}, "Maximum |nsigma| for Triton PID"}; + + // Helium PID cuts + o2::framework::Configurable pidIsOptionalHelium{"pidIsOptionalHelium", false, "Make election PID optional"}; + o2::framework::Configurable minMomTofHelium{"minMomTofHelium", 1.6, "Minimum momentum to required TOF PID for Helium"}; + o2::framework::Configurable> itsHelium{"itsHelium", {}, "Maximum |nsigma| for Helium PID"}; + o2::framework::Configurable> tpcHelium{"tpcHelium", {}, "Maximum |nsigma| for Helium PID"}; + o2::framework::Configurable> tofHelium{"tofHelium", {}, "Maximum |nsigma| for Helium PID"}; + o2::framework::Configurable> tpcitsHelium{"tpcitsHelium", {}, "Maximum |nsigma| for Helium PID"}; + o2::framework::Configurable> tpctofHelium{"tpctofHelium", {}, "Maximum |nsigma| for Helium PID"}; }; // define the template structure for TrackSelection @@ -201,8 +217,9 @@ enum TrackSels { kTrackSelsMax }; -const char trackSelsName[] = "Track Selection Object"; -const std::unordered_map trackSelsToString = { +constexpr char TrackSelHistName[] = "hTrackSelection"; +constexpr char TrackSelsName[] = "Track Selection Object"; +const std::unordered_map trackSelectionNames = { {kTPCnClsMin, "Min. number of TPC clusters"}, {kTPCcRowsMin, "Min. number of crossed TPC rows"}, {kTPCnClsOvercRowsMin, "Min. fraction of TPC clusters over TPC crossed rows"}, @@ -256,6 +273,7 @@ const std::unordered_map trackSelsToString = { /// \class FemtoDreamTrackCuts /// \brief Cut class to contain and execute all cuts applied to tracks +template class TrackSelection : public BaseSelection { public: @@ -263,7 +281,7 @@ class TrackSelection : public BaseSelection - void configure(T1& config, T2& filter) + void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter) { mPtMin = filter.ptMin; mPtMax = filter.ptMax; @@ -271,66 +289,75 @@ class TrackSelection : public BaseSelectionaddSelection(config.tpcClustersMin.value, kTPCnClsMin, limits::kLowerLimit, true, true); - this->addSelection(config.tpcCrossedRowsMin.value, kTPCcRowsMin, limits::kLowerLimit, true, true); - this->addSelection(config.tpcClustersOverCrossedRows.value, kTPCnClsOvercRowsMin, limits::kLowerLimit, true, true); - this->addSelection(config.tpcSharedClustersMax.value, kTPCsClsMax, limits::kUpperLimit, true, true); - this->addSelection(config.tpcSharedClusterFractionMax.value, kTPCsClsFracMax, limits::kUpperLimit, true, true); - this->addSelection(config.itsClustersMin.value, kITSnClsMin, limits::kLowerLimit, true, true); - this->addSelection(config.itsIbClustersMin.value, kITSnClsIbMin, limits::kLowerLimit, true, true); - this->addSelection(config.dcaxyMax.name, filter.ptMin.value, filter.ptMax.value, config.dcaxyMax.value, kDCAxyMax, limits::kAbsUpperFunctionLimit, true, true); - this->addSelection(config.dcazMax.name, filter.ptMin.value, filter.ptMax.value, config.dcazMax.value, kDCAzMax, limits::kAbsUpperFunctionLimit, true, true); - - // add selections for its pid - this->addSelection(config.itsElectron.value, kItsElectron, limits::kAbsUpperLimit, false, false); - this->addSelection(config.itsPion.value, kItsPion, limits::kAbsUpperLimit, false, false); - this->addSelection(config.itsKaon.value, kItsKaon, limits::kAbsUpperLimit, false, false); - this->addSelection(config.itsProton.value, kItsProton, limits::kAbsUpperLimit, false, false); - this->addSelection(config.itsDeuteron.value, kItsDeuteron, limits::kAbsUpperLimit, false, false); - this->addSelection(config.itsTriton.value, kItsTriton, limits::kAbsUpperLimit, false, false); - this->addSelection(config.itsHelium.value, kItsHelium, limits::kAbsUpperLimit, false, false); - // add selections for tpc pid - this->addSelection(config.tpcElectron.value, kTpcElectron, limits::kAbsUpperLimit, false, false); - this->addSelection(config.tpcPion.value, kTpcPion, limits::kAbsUpperLimit, false, false); - this->addSelection(config.tpcKaon.value, kTpcKaon, limits::kAbsUpperLimit, false, false); - this->addSelection(config.tpcProton.value, kTpcProton, limits::kAbsUpperLimit, false, false); - this->addSelection(config.tpcDeuteron.value, kTpcDeuteron, limits::kAbsUpperLimit, false, false); - this->addSelection(config.tpcTriton.value, kTpcTriton, limits::kAbsUpperLimit, false, false); - this->addSelection(config.tpcHelium.value, kTpcHelium, limits::kAbsUpperLimit, false, false); - // add selections for tof pid - this->addSelection(config.tofElectron.value, kTofElectron, limits::kAbsUpperLimit, false, false); - this->addSelection(config.tofPion.value, kTofPion, limits::kAbsUpperLimit, false, false); - this->addSelection(config.tofKaon.value, kTofKaon, limits::kAbsUpperLimit, false, false); - this->addSelection(config.tofProton.value, kTofProton, limits::kAbsUpperLimit, false, false); - this->addSelection(config.tofDeuteron.value, kTofDeuteron, limits::kAbsUpperLimit, false, false); - this->addSelection(config.tofTriton.value, kTofTriton, limits::kAbsUpperLimit, false, false); - this->addSelection(config.tofHelium.value, kTofHelium, limits::kAbsUpperLimit, false, false); - // add selections for tpcits pid - this->addSelection(config.tpcitsElectron.value, kTpcitsElectron, limits::kUpperLimit, false, false); - this->addSelection(config.tpcitsPion.value, kTpcitsPion, limits::kUpperLimit, false, false); - this->addSelection(config.tpcitsKaon.value, kTpcitsKaon, limits::kUpperLimit, false, false); - this->addSelection(config.tpcitsProton.value, kTpcitsProton, limits::kUpperLimit, false, false); - this->addSelection(config.tpcitsDeuteron.value, kTpcitsDeuteron, limits::kUpperLimit, false, false); - this->addSelection(config.tpcitsTriton.value, kTpcitsTriton, limits::kUpperLimit, false, false); - this->addSelection(config.tpcitsHelium.value, kTpcitsHelium, limits::kUpperLimit, false, false); - // add selections for tpctof pid - this->addSelection(config.tpctofElectron.value, kTpctofElectron, limits::kUpperLimit, false, false); - this->addSelection(config.tpctofPion.value, kTpctofPion, limits::kUpperLimit, false, false); - this->addSelection(config.tpctofKaon.value, kTpctofKaon, limits::kUpperLimit, false, false); - this->addSelection(config.tpctofProton.value, kTpctofProton, limits::kUpperLimit, false, false); - this->addSelection(config.tpctofDeuteron.value, kTpctofDeuteron, limits::kUpperLimit, false, false); - this->addSelection(config.tpctofTriton.value, kTpctofTriton, limits::kUpperLimit, false, false); - this->addSelection(config.tpctofHelium.value, kTpctofHelium, limits::kUpperLimit, false, false); - } - - template - bool hasTofAboveThreshold(T const& track) const - { - // If track momentum exceeds threshold, we require valid TOF info - return !(track.p() > mMinimalMomentumForTof && !track.hasTOF()); + this->addSelection(kTPCnClsMin, trackSelectionNames.at(kTPCnClsMin), config.tpcClustersMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kTPCcRowsMin, trackSelectionNames.at(kTPCcRowsMin), config.tpcCrossedRowsMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kTPCnClsOvercRowsMin, trackSelectionNames.at(kTPCnClsOvercRowsMin), config.tpcClustersOverCrossedRows.value, limits::kLowerLimit, true, true, false); + this->addSelection(kTPCsClsMax, trackSelectionNames.at(kTPCsClsMax), config.tpcSharedClustersMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kTPCsClsFracMax, trackSelectionNames.at(kTPCsClsFracMax), config.tpcSharedClusterFractionMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kITSnClsMin, trackSelectionNames.at(kITSnClsMin), config.itsClustersMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kITSnClsIbMin, trackSelectionNames.at(kITSnClsIbMin), config.itsIbClustersMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kDCAxyMax, trackSelectionNames.at(kDCAxyMax), filter.ptMin, filter.ptMax.value, config.dcaxyMax.value, limits::kAbsUpperFunctionLimit, true, true, false); + this->addSelection(kDCAzMax, trackSelectionNames.at(kDCAzMax), filter.ptMin.value, filter.ptMax.value, config.dcazMax.value, limits::kAbsUpperFunctionLimit, true, true, false); + + // add selections for Electron pid + this->addSelection(kItsElectron, trackSelectionNames.at(kItsElectron), config.itsElectron.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalElectron); + this->addSelection(kTpcElectron, trackSelectionNames.at(kTpcElectron), config.tpcElectron.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalElectron); + this->addSelection(kTofElectron, trackSelectionNames.at(kTofElectron), config.tofElectron.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalElectron); + this->addSelection(kTpcitsElectron, trackSelectionNames.at(kTpcitsElectron), config.tpcitsElectron.value, limits::kUpperLimit, false, false, config.pidIsOptionalElectron); + this->addSelection(kTpctofElectron, trackSelectionNames.at(kTpctofElectron), config.tpctofElectron.value, limits::kUpperLimit, false, false, config.pidIsOptionalElectron); + mElectronTofThres = config.minMomTofElectron.value; + + // add selections for Pion pid + this->addSelection(kItsPion, trackSelectionNames.at(kItsPion), config.itsPion.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalPion); + this->addSelection(kTpcPion, trackSelectionNames.at(kTpcPion), config.tpcPion.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalPion); + this->addSelection(kTofPion, trackSelectionNames.at(kTofPion), config.tofPion.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalPion); + this->addSelection(kTpcitsPion, trackSelectionNames.at(kTpcitsPion), config.tpcitsPion.value, limits::kUpperLimit, false, false, config.pidIsOptionalPion); + this->addSelection(kTpctofPion, trackSelectionNames.at(kTpctofPion), config.tpctofPion.value, limits::kUpperLimit, false, false, config.pidIsOptionalPion); + mPionTofThres = config.minMomTofPion.value; + + // add selections for Kaon pid + this->addSelection(kItsKaon, trackSelectionNames.at(kItsKaon), config.itsKaon.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalKaon); + this->addSelection(kTpcKaon, trackSelectionNames.at(kTpcKaon), config.tpcKaon.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalKaon); + this->addSelection(kTofKaon, trackSelectionNames.at(kTofKaon), config.tofKaon.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalKaon); + this->addSelection(kTpcitsKaon, trackSelectionNames.at(kTpcitsKaon), config.tpcitsKaon.value, limits::kUpperLimit, false, false, config.pidIsOptionalKaon); + this->addSelection(kTpctofKaon, trackSelectionNames.at(kTpctofKaon), config.tpctofKaon.value, limits::kUpperLimit, false, false, config.pidIsOptionalKaon); + mKaonTofThres = config.minMomTofKaon.value; + + // add selections for Proton pid + this->addSelection(kItsProton, trackSelectionNames.at(kItsProton), config.itsProton.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalProton); + this->addSelection(kTpcProton, trackSelectionNames.at(kTpcProton), config.tpcProton.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalProton); + this->addSelection(kTofProton, trackSelectionNames.at(kTofProton), config.tofProton.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalProton); + this->addSelection(kTpcitsProton, trackSelectionNames.at(kTpcitsProton), config.tpcitsProton.value, limits::kUpperLimit, false, false, config.pidIsOptionalProton); + this->addSelection(kTpctofProton, trackSelectionNames.at(kTpctofProton), config.tpctofProton.value, limits::kUpperLimit, false, false, config.pidIsOptionalProton); + mProtonTofThres = config.minMomTofProton.value; + + // add selections for Deuteron pid + this->addSelection(kItsDeuteron, trackSelectionNames.at(kItsDeuteron), config.itsDeuteron.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalDeuteron); + this->addSelection(kTpcDeuteron, trackSelectionNames.at(kTpcDeuteron), config.tpcDeuteron.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalDeuteron); + this->addSelection(kTofDeuteron, trackSelectionNames.at(kTofDeuteron), config.tofDeuteron.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalDeuteron); + this->addSelection(kTpcitsDeuteron, trackSelectionNames.at(kTpcitsDeuteron), config.tpcitsDeuteron.value, limits::kUpperLimit, false, false, config.pidIsOptionalDeuteron); + this->addSelection(kTpctofDeuteron, trackSelectionNames.at(kTpctofDeuteron), config.tpctofDeuteron.value, limits::kUpperLimit, false, false, config.pidIsOptionalDeuteron); + mDeuteronTofThres = config.minMomTofDeuteron.value; + + // add selections for Triton pid + this->addSelection(kItsTriton, trackSelectionNames.at(kItsTriton), config.itsTriton.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalTriton); + this->addSelection(kTpcTriton, trackSelectionNames.at(kTpcTriton), config.tpcTriton.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalTriton); + this->addSelection(kTofTriton, trackSelectionNames.at(kTofTriton), config.tofTriton.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalTriton); + this->addSelection(kTpcitsTriton, trackSelectionNames.at(kTpcitsTriton), config.tpcitsTriton.value, limits::kUpperLimit, false, false, config.pidIsOptionalTriton); + this->addSelection(kTpctofTriton, trackSelectionNames.at(kTpctofTriton), config.tpctofTriton.value, limits::kUpperLimit, false, false, config.pidIsOptionalTriton); + mTritonTofThres = config.minMomTofTriton.value; + + // add selections for Helium pid + this->addSelection(kItsHelium, trackSelectionNames.at(kItsHelium), config.itsHelium.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalHelium); + this->addSelection(kTpcHelium, trackSelectionNames.at(kTpcHelium), config.tpcHelium.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalHelium); + this->addSelection(kTofHelium, trackSelectionNames.at(kTofHelium), config.tofHelium.value, limits::kAbsUpperLimit, false, false, config.pidIsOptionalHelium); + this->addSelection(kTpcitsHelium, trackSelectionNames.at(kTpcitsHelium), config.tpcitsHelium.value, limits::kUpperLimit, false, false, config.pidIsOptionalHelium); + this->addSelection(kTpctofHelium, trackSelectionNames.at(kTpctofHelium), config.tpctofHelium.value, limits::kUpperLimit, false, false, config.pidIsOptionalHelium); + mHeliumTofThres = config.minMomTofHelium.value; + + this->setupContainers(registry); } template @@ -341,6 +368,43 @@ class TrackSelection : public BaseSelection mPhiMin && track.phi() < mPhiMax)); } + template + void evaluatePid(T1 const& Track, + float tofThreshold, + float nsigmaIts, + float nsigmaTpc, + float nsigmaTof, + TrackSels its, + TrackSels tpc, + TrackSels tof, + TrackSels tpcits, + TrackSels tpctof) + { + // if track is below threshold, just check every PID + if (Track.p() < tofThreshold) { + this->evaluateObservable(its, nsigmaIts); + this->evaluateObservable(tpc, nsigmaTpc); + this->evaluateObservable(tpcits, std::hypot(nsigmaTpc, nsigmaIts)); + this->evaluateObservable(tof, nsigmaTof); + this->evaluateObservable(tpctof, std::hypot(nsigmaTpc, nsigmaTof)); + return; + } + // if track is above threshold, check if TOF PID is available + // if not, we dont check any selection and they stay at reseted values, i.e. the cut fails + if (Track.hasTOF()) { + // if tof inforamtion is available, check them first + this->evaluateObservable(tof, nsigmaTof); + this->evaluateObservable(tpctof, std::hypot(nsigmaTpc, nsigmaTof)); + // if both failed, the bitmask will be 0 and there is no need to check tpc and its information since we do not want to have this track + // so if we just bail out here, the PID for this particle type will failed for its, tpc and tof + if (this->passesOptionalSelection(tof) || this->passesOptionalSelection(tpctof)) { + this->evaluateObservable(its, nsigmaIts); + this->evaluateObservable(tpc, nsigmaTpc); + this->evaluateObservable(tpcits, std::hypot(nsigmaTpc, nsigmaIts)); + } + } + } + template void applySelections(T const& Track) { @@ -360,58 +424,97 @@ class TrackSelection : public BaseSelectionupdateLimits(kDCAzMax, Track.pt()); this->evaluateObservable(kDCAzMax, Track.dcaZ()); - // its pid - this->evaluateObservable(kItsElectron, Track.itsNSigmaEl()); - this->evaluateObservable(kItsPion, Track.itsNSigmaPi()); - this->evaluateObservable(kItsKaon, Track.itsNSigmaKa()); - this->evaluateObservable(kItsProton, Track.itsNSigmaPr()); - this->evaluateObservable(kItsDeuteron, Track.itsNSigmaDe()); - this->evaluateObservable(kItsTriton, Track.itsNSigmaTr()); - this->evaluateObservable(kItsHelium, Track.itsNSigmaHe()); - - // tpc pid - this->evaluateObservable(kTpcElectron, Track.tpcNSigmaEl()); - this->evaluateObservable(kTpcPion, Track.tpcNSigmaPi()); - this->evaluateObservable(kTpcKaon, Track.tpcNSigmaKa()); - this->evaluateObservable(kTpcProton, Track.tpcNSigmaPr()); - this->evaluateObservable(kTpcDeuteron, Track.tpcNSigmaDe()); - this->evaluateObservable(kTpcTriton, Track.tpcNSigmaTr()); - this->evaluateObservable(kTpcHelium, Track.tpcNSigmaHe()); - - // tof pid - this->evaluateObservable(kTofElectron, Track.tofNSigmaEl()); - this->evaluateObservable(kTofPion, Track.tofNSigmaPi()); - this->evaluateObservable(kTofKaon, Track.tofNSigmaKa()); - this->evaluateObservable(kTofProton, Track.tofNSigmaPr()); - this->evaluateObservable(kTofDeuteron, Track.tofNSigmaDe()); - this->evaluateObservable(kTofTriton, Track.tofNSigmaTr()); - this->evaluateObservable(kTofHelium, Track.tofNSigmaHe()); - - // combined tpc + its pid - this->evaluateObservable(kTpcitsElectron, std::hypot(Track.tpcNSigmaEl(), Track.itsNSigmaEl())); - this->evaluateObservable(kTpcitsPion, std::hypot(Track.tpcNSigmaPi(), Track.itsNSigmaPi())); - this->evaluateObservable(kTpcitsKaon, std::hypot(Track.tpcNSigmaKa(), Track.itsNSigmaKa())); - this->evaluateObservable(kTpcitsProton, std::hypot(Track.tpcNSigmaPr(), Track.itsNSigmaPr())); - this->evaluateObservable(kTpcitsDeuteron, std::hypot(Track.tpcNSigmaDe(), Track.itsNSigmaDe())); - this->evaluateObservable(kTpcitsTriton, std::hypot(Track.tpcNSigmaTr(), Track.itsNSigmaTr())); - this->evaluateObservable(kTpcitsHelium, std::hypot(Track.tpcNSigmaHe(), Track.itsNSigmaHe())); - - // combined tpc + tof pid - this->evaluateObservable(kTpctofElectron, std::hypot(Track.tpcNSigmaEl(), Track.tofNSigmaEl())); - this->evaluateObservable(kTpctofPion, std::hypot(Track.tpcNSigmaPi(), Track.tofNSigmaPi())); - this->evaluateObservable(kTpctofKaon, std::hypot(Track.tpcNSigmaKa(), Track.tofNSigmaKa())); - this->evaluateObservable(kTpctofProton, std::hypot(Track.tpcNSigmaPr(), Track.tofNSigmaPr())); - this->evaluateObservable(kTpctofDeuteron, std::hypot(Track.tpcNSigmaDe(), Track.tofNSigmaDe())); - this->evaluateObservable(kTpctofTriton, std::hypot(Track.tpcNSigmaTr(), Track.tofNSigmaTr())); - this->evaluateObservable(kTpctofHelium, std::hypot(Track.tpcNSigmaHe(), Track.tofNSigmaHe())); - - this->assembleBitmask(); - }; + this->evaluatePid(Track, + mElectronTofThres, + Track.itsNSigmaEl(), + Track.tpcNSigmaEl(), + Track.tofNSigmaEl(), + kItsElectron, + kTpcElectron, + kTofElectron, + kTpcitsElectron, + kTpctofElectron); + + this->evaluatePid(Track, + mPionTofThres, + Track.itsNSigmaPi(), + Track.tpcNSigmaPi(), + Track.tofNSigmaPi(), + kItsPion, + kTpcPion, + kTofPion, + kTpcitsPion, + kTpctofPion); + + this->evaluatePid(Track, + mKaonTofThres, + Track.itsNSigmaKa(), + Track.tpcNSigmaKa(), + Track.tofNSigmaKa(), + kItsKaon, + kTpcKaon, + kTofKaon, + kTpcitsKaon, + kTpctofKaon); + + this->evaluatePid(Track, + mProtonTofThres, + Track.itsNSigmaPr(), + Track.tpcNSigmaPr(), + Track.tofNSigmaPr(), + kItsProton, + kTpcProton, + kTofProton, + kTpcitsProton, + kTpctofProton); + + this->evaluatePid(Track, + mDeuteronTofThres, + Track.itsNSigmaDe(), + Track.tpcNSigmaDe(), + Track.tofNSigmaDe(), + kItsDeuteron, + kTpcDeuteron, + kTofDeuteron, + kTpcitsDeuteron, + kTpctofDeuteron); + + this->evaluatePid(Track, + mTritonTofThres, + Track.itsNSigmaTr(), + Track.tpcNSigmaTr(), + Track.tofNSigmaTr(), + kItsTriton, + kTpcTriton, + kTofTriton, + kTpcitsTriton, + kTpctofTriton); + + this->evaluatePid(Track, + mHeliumTofThres, + Track.itsNSigmaHe(), + Track.tpcNSigmaHe(), + Track.tofNSigmaHe(), + kItsHelium, + kTpcHelium, + kTofHelium, + kTpcitsHelium, + kTpctofHelium); + + this->assembleBitmask(); + } protected: - float mMinimalMomentumForTof = 2.f; + float mElectronTofThres = 99.f; + float mPionTofThres = 99.f; + float mKaonTofThres = 99.f; + float mProtonTofThres = 99.f; + float mDeuteronTofThres = 99.f; + float mTritonTofThres = 99.f; + float mHeliumTofThres = 99.f; + float mPtMin = 0.f; - float mPtMax = 0.f; + float mPtMax = 99.f; float mEtaMin = -0.9; float mEtaMax = 0.9; float mPhiMin = 0; @@ -447,6 +550,7 @@ struct ConfTrackTables : o2::framework::ConfigurableGroup { o2::framework::Configurable produceHeliumPids{"produceHeliumPids", -1, "Produce HeliumPids (-1: auto; 0 off; 1 on)"}; }; +template class TrackBuilder { public: @@ -454,9 +558,8 @@ class TrackBuilder ~TrackBuilder() = default; template - void init(T1& config, T2& filter, T3& table, T4& initContext) + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext) { - mTrackSelection.configure(config, filter); LOG(info) << "Initialize femto track builder..."; mProduceTracks = utils::enableTable("FTracks_001", table.produceTracks.value, initContext); @@ -473,10 +576,13 @@ class TrackBuilder if (mProduceTracks || mProduceTrackMasks || mProduceTrackDcas || mProduceTrackExtras || mProduceElectronPids || mProducePionPids || mProduceKaonPids || mProduceProtonPids || mProduceDeuteronPids || mProduceTritonPids || mProduceHeliumPids) { mFillAnyTable = true; - mTrackSelection.printSelections(trackSelsName, trackSelsToString); } else { - LOG(info) << "No tables configured"; + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; } + mTrackSelection.configure(registry, config, filter); + mTrackSelection.printSelections(TrackSelsName); LOG(info) << "Initialization done..."; } @@ -487,7 +593,7 @@ class TrackBuilder return; } for (const auto& track : tracks) { - if (!mTrackSelection.checkFilters(track) || !mTrackSelection.hasTofAboveThreshold(track)) { + if (!mTrackSelection.checkFilters(track)) { continue; } mTrackSelection.applySelections(track); @@ -598,7 +704,7 @@ class TrackBuilder } private: - TrackSelection mTrackSelection; + TrackSelection mTrackSelection; bool mFillAnyTable = false; bool mProduceTracks = false; bool mProduceTrackMasks = false; diff --git a/PWGCF/Femto/Core/twoTrackResonanceBuilder.h b/PWGCF/Femto/Core/twoTrackResonanceBuilder.h index 8086df4516e..defdc4f963a 100644 --- a/PWGCF/Femto/Core/twoTrackResonanceBuilder.h +++ b/PWGCF/Femto/Core/twoTrackResonanceBuilder.h @@ -27,6 +27,7 @@ #include "CommonConstants/MathConstants.h" #include "CommonConstants/PhysicsConstants.h" +#include "Framework/ASoAHelpers.h" #include "Framework/AnalysisHelpers.h" #include "Framework/Configurable.h" @@ -198,8 +199,12 @@ enum TwoTrackResonanceSels { kResonanceSelsMax }; -const char twoTrackResonanceSelsName[] = "TwoTrackResonance Selection Object"; -const std::unordered_map twoTrackResonanceSelsToString = { +constexpr char PhiSelHistName[] = "hPhiSelection"; +constexpr char RhoSelHistName[] = "hRhoSelection"; +constexpr char Kstar0SelHistName[] = "hKstar0Selection"; +constexpr char Kstar0barSelHistName[] = "hKstar0BarSelection"; +constexpr char TwoTrackResonanceSelsName[] = "TwoTrackResonance Selection Object"; +const std::unordered_map twoTrackResonanceSelectionNames = { {kDauEtaAbsMax, "Max. |eta| of daughters"}, {kDauTpcClusterMin, "Min. number of TPC clusters of daughters"}, {kDauDcaxyAbsMax, "Max. |DCA_xy| of daughters"}, @@ -225,7 +230,7 @@ const std::unordered_map twoTrackResonanceSe /// \class FemtoDreamTrackCuts /// \brief Cut class to contain and execute all cuts applied to tracks -template +template class TwoTrackResonanceSelection : public BaseSelection { public: @@ -233,47 +238,47 @@ class TwoTrackResonanceSelection : public BaseSelection - void configure(T1& config, T2& filter, T3& daughterFilter) + void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& daughterFilter) { if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kPhi)) { mPosDaughterMass = o2::constants::physics::MassKPlus; mNegDaughterMass = o2::constants::physics::MassKMinus; - this->addSelection(config.posDauTpcKaon.value, kPosDauTpcKaon, limits::kAbsUpperLimit, false, false); - this->addSelection(config.posDauTofKaon.value, kPosDauTofKaon, limits::kAbsUpperLimit, false, false); - this->addSelection(config.posDauTpctofKaon.value, kPosDauTpctofKaon, limits::kUpperLimit, false, false); - this->addSelection(config.negDauTpcKaon.value, kNegDauTpcKaon, limits::kAbsUpperLimit, false, false); - this->addSelection(config.negDauTofKaon.value, kNegDauTofKaon, limits::kAbsUpperLimit, false, false); - this->addSelection(config.negDauTpctofKaon.value, kNegDauTpctofKaon, limits::kUpperLimit, false, false); + this->addSelection(kPosDauTpcKaon, twoTrackResonanceSelectionNames.at(kPosDauTpcKaon), config.posDauTpcKaon.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kPosDauTofKaon, twoTrackResonanceSelectionNames.at(kPosDauTofKaon), config.posDauTofKaon.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kPosDauTpctofKaon, twoTrackResonanceSelectionNames.at(kPosDauTpctofKaon), config.posDauTpctofKaon.value, limits::kUpperLimit, false, false, true); + this->addSelection(kNegDauTpcKaon, twoTrackResonanceSelectionNames.at(kNegDauTpcKaon), config.negDauTpcKaon.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kNegDauTofKaon, twoTrackResonanceSelectionNames.at(kNegDauTofKaon), config.negDauTofKaon.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kNegDauTpctofKaon, twoTrackResonanceSelectionNames.at(kNegDauTpctofKaon), config.negDauTpctofKaon.value, limits::kUpperLimit, false, false, true); } if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kRho0)) { mPosDaughterMass = o2::constants::physics::MassPiPlus; mNegDaughterMass = o2::constants::physics::MassPiMinus; - this->addSelection(config.posDauTpcPion.value, kPosDauTpcPion, limits::kAbsUpperLimit, false, false); - this->addSelection(config.posDauTofPion.value, kPosDauTofPion, limits::kAbsUpperLimit, false, false); - this->addSelection(config.posDauTpctofPion.value, kPosDauTpctofPion, limits::kUpperLimit, false, false); - this->addSelection(config.negDauTpcPion.value, kNegDauTpcPion, limits::kAbsUpperLimit, false, false); - this->addSelection(config.negDauTofPion.value, kNegDauTofPion, limits::kAbsUpperLimit, false, false); - this->addSelection(config.negDauTpctofPion.value, kNegDauTpctofPion, limits::kUpperLimit, false, false); + this->addSelection(kPosDauTpcPion, twoTrackResonanceSelectionNames.at(kPosDauTpcPion), config.posDauTpcPion.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kPosDauTofPion, twoTrackResonanceSelectionNames.at(kPosDauTofPion), config.posDauTofPion.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kPosDauTpctofPion, twoTrackResonanceSelectionNames.at(kPosDauTpctofPion), config.posDauTpctofPion.value, limits::kUpperLimit, false, false, true); + this->addSelection(kNegDauTpcPion, twoTrackResonanceSelectionNames.at(kNegDauTpcPion), config.negDauTpcPion.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kNegDauTofPion, twoTrackResonanceSelectionNames.at(kNegDauTofPion), config.negDauTofPion.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kNegDauTpctofPion, twoTrackResonanceSelectionNames.at(kNegDauTpctofPion), config.negDauTpctofPion.value, limits::kUpperLimit, false, false, true); } if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0)) { mPosDaughterMass = o2::constants::physics::MassKPlus; mNegDaughterMass = o2::constants::physics::MassPiMinus; - this->addSelection(config.posDauTpcKaon.value, kPosDauTpcKaon, limits::kAbsUpperLimit, false, false); - this->addSelection(config.posDauTofKaon.value, kPosDauTofKaon, limits::kAbsUpperLimit, false, false); - this->addSelection(config.posDauTpctofKaon.value, kPosDauTpctofKaon, limits::kUpperLimit, false, false); - this->addSelection(config.negDauTpcPion.value, kNegDauTpcPion, limits::kAbsUpperLimit, false, false); - this->addSelection(config.negDauTofPion.value, kNegDauTofPion, limits::kAbsUpperLimit, false, false); - this->addSelection(config.negDauTpctofPion.value, kNegDauTpctofPion, limits::kUpperLimit, false, false); + this->addSelection(kPosDauTpcKaon, twoTrackResonanceSelectionNames.at(kPosDauTpcKaon), config.posDauTpcKaon.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kPosDauTofKaon, twoTrackResonanceSelectionNames.at(kPosDauTofKaon), config.posDauTofKaon.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kPosDauTpctofKaon, twoTrackResonanceSelectionNames.at(kPosDauTpctofKaon), config.posDauTpctofKaon.value, limits::kUpperLimit, false, false, true); + this->addSelection(kNegDauTpcPion, twoTrackResonanceSelectionNames.at(kNegDauTpcPion), config.negDauTpcPion.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kNegDauTofPion, twoTrackResonanceSelectionNames.at(kNegDauTofPion), config.negDauTofPion.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kNegDauTpctofPion, twoTrackResonanceSelectionNames.at(kNegDauTpctofPion), config.negDauTpctofPion.value, limits::kUpperLimit, false, false, true); } if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0Bar)) { mPosDaughterMass = o2::constants::physics::MassPiPlus; mNegDaughterMass = o2::constants::physics::MassKMinus; - this->addSelection(config.posDauTpcPion.value, kPosDauTpcPion, limits::kAbsUpperLimit, false, false); - this->addSelection(config.posDauTofPion.value, kPosDauTofPion, limits::kAbsUpperLimit, false, false); - this->addSelection(config.posDauTpctofPion.value, kPosDauTpctofPion, limits::kUpperLimit, false, false); - this->addSelection(config.negDauTpcKaon.value, kNegDauTpcKaon, limits::kAbsUpperLimit, false, false); - this->addSelection(config.negDauTofKaon.value, kNegDauTofKaon, limits::kAbsUpperLimit, false, false); - this->addSelection(config.negDauTpctofKaon.value, kNegDauTpctofKaon, limits::kUpperLimit, false, false); + this->addSelection(kPosDauTpcPion, twoTrackResonanceSelectionNames.at(kPosDauTpcPion), config.posDauTpcPion.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kPosDauTofPion, twoTrackResonanceSelectionNames.at(kPosDauTofPion), config.posDauTofPion.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kPosDauTpctofPion, twoTrackResonanceSelectionNames.at(kPosDauTpctofPion), config.posDauTpctofPion.value, limits::kUpperLimit, false, false, true); + this->addSelection(kNegDauTpcKaon, twoTrackResonanceSelectionNames.at(kNegDauTpcKaon), config.negDauTpcKaon.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kNegDauTofKaon, twoTrackResonanceSelectionNames.at(kNegDauTofKaon), config.negDauTofKaon.value, limits::kAbsUpperLimit, false, false, true); + this->addSelection(kNegDauTpctofKaon, twoTrackResonanceSelectionNames.at(kNegDauTpctofKaon), config.negDauTpctofKaon.value, limits::kUpperLimit, false, false, true); } mMassMin = filter.massMin.value; @@ -285,18 +290,19 @@ class TwoTrackResonanceSelection : public BaseSelectionaddSelection(config.dauEtaMax.value, kDauEtaAbsMax, limits::kAbsUpperLimit, true, true); - this->addSelection(config.dauTpcClustersMin.value, kDauTpcClusterMin, limits::kLowerLimit, true, true); - this->addSelection(config.dauDcaxyMax.name, daughterFilter.ptMin.value, daughterFilter.ptMax.value, config.dauDcaxyMax.value, kDauDcaxyAbsMax, limits::kAbsUpperFunctionLimit, true, true); - this->addSelection(config.dauDcazMax.name, daughterFilter.ptMin.value, daughterFilter.ptMax.value, config.dauDcazMax.value, kDauDcazAbsMax, limits::kAbsUpperFunctionLimit, true, true); + this->addSelection(kDauEtaAbsMax, twoTrackResonanceSelectionNames.at(kDauEtaAbsMax), config.dauEtaMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kDauTpcClusterMin, twoTrackResonanceSelectionNames.at(kDauTpcClusterMin), config.dauTpcClustersMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kDauDcaxyAbsMax, twoTrackResonanceSelectionNames.at(kDauDcaxyAbsMax), daughterFilter.ptMin.value, daughterFilter.ptMax.value, config.dauDcaxyMax.value, limits::kAbsUpperFunctionLimit, true, true, false); + this->addSelection(kDauDcazAbsMax, twoTrackResonanceSelectionNames.at(kDauDcazAbsMax), daughterFilter.ptMin.value, daughterFilter.ptMax.value, config.dauDcazMax.value, limits::kAbsUpperFunctionLimit, true, true, false); + this->addSelection(kPosDauMinMomForTof, twoTrackResonanceSelectionNames.at(kPosDauMinMomForTof), config.posDauMinMomForTof.value, limits::kUpperLimit, false, false, false); // momentum threshold for TOF is no minimal/optional cut + this->addSelection(kPosDauPtMin, twoTrackResonanceSelectionNames.at(kPosDauPtMin), config.posDauPtMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kPosDauPtMax, twoTrackResonanceSelectionNames.at(kPosDauPtMax), config.posDauPtMax.value, limits::kUpperLimit, true, true, false); - this->addSelection(config.posDauMinMomForTof.value, kPosDauMinMomForTof, limits::kLowerLimit, false, false); // momentum threshold for TOF is no minimal cut - this->addSelection(config.posDauPtMin.value, kPosDauPtMin, limits::kLowerLimit, true, true); - this->addSelection(config.posDauPtMax.value, kPosDauPtMax, limits::kUpperLimit, true, true); + this->addSelection(kNegDauMinMomForTof, twoTrackResonanceSelectionNames.at(kNegDauMinMomForTof), config.negDauMinMomForTof.value, limits::kUpperLimit, false, false, false); // momentum threshold for TOF is no minimal/optional cut + this->addSelection(kNegDauPtMin, twoTrackResonanceSelectionNames.at(kNegDauPtMin), config.negDauPtMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kNegDauPtMax, twoTrackResonanceSelectionNames.at(kNegDauPtMax), config.negDauPtMax.value, limits::kUpperLimit, true, true, false); - this->addSelection(config.negDauMinMomForTof.value, kNegDauMinMomForTof, limits::kLowerLimit, false, false); // momentum threshold for TOF is no minimal cut - this->addSelection(config.negDauPtMin.value, kNegDauPtMin, limits::kLowerLimit, true, true); - this->addSelection(config.negDauPtMax.value, kNegDauPtMax, limits::kUpperLimit, true, true); + this->setupContainers(registry); }; template @@ -314,7 +320,7 @@ class TwoTrackResonanceSelection : public BaseSelection mMassMin && mMass < mMassMax) && (mPt > mPtMin && mPt < mPtMax) && @@ -322,14 +328,6 @@ class TwoTrackResonanceSelection : public BaseSelection mPhiMin && mPhi < mPhiMax)); } - template - bool hasTofAboveThreshold(T const& positiveDaughter, T const& negativeDaughter) - { - bool posDauHasTofAboveThreshold = !this->passesOptionalSelection(kPosDauMinMomForTof) || positiveDaughter.hasTOF(); // is always true if momentum is below threshold - bool negDauHasTofAboveThreshold = !this->passesOptionalSelection(kNegDauMinMomForTof) || negativeDaughter.hasTOF(); // is always true if momentum is below threshold - return posDauHasTofAboveThreshold && negDauHasTofAboveThreshold; - } - float getPt() const { return mPt; } float getEta() const { return mEta; } float getPhi() const { return mPhi; } @@ -339,7 +337,7 @@ class TwoTrackResonanceSelection : public BaseSelectionreset(); - // for resoanace topological selectsion are in general not possible, so only selections on the daughters are performed + // for resonances, topological selection are in general not possible, so only selections on the daughters are performed // common daugher selections std::array etaDaughters = {std::fabs(posDaughter.eta()), std::fabs(negDaughter.eta())}; @@ -369,56 +367,65 @@ class TwoTrackResonanceSelection : public BaseSelectionsetBitmask(kDauDcazAbsMax, bitmaskDca); + float tofThreshold = 99.; + // positive daughter selections this->evaluateObservable(kPosDauMinMomForTof, posDaughter.p()); this->evaluateObservable(kPosDauPtMin, posDaughter.pt()); this->evaluateObservable(kPosDauPtMax, posDaughter.pt()); - this->evaluateObservable(kPosDauTpcPion, posDaughter.tpcNSigmaPi()); - this->evaluateObservable(kPosDauTofPion, posDaughter.tofNSigmaPi()); - this->evaluateObservable(kPosDauTpctofPion, std::hypot(posDaughter.tpcNSigmaPi(), posDaughter.tofNSigmaPi())); - - this->evaluateObservable(kPosDauTpcKaon, posDaughter.tpcNSigmaKa()); - this->evaluateObservable(kPosDauTofKaon, posDaughter.tofNSigmaKa()); - this->evaluateObservable(kPosDauTpctofKaon, std::hypot(posDaughter.tpcNSigmaKa(), posDaughter.tofNSigmaKa())); + tofThreshold = this->getLoosestSelection(kPosDauMinMomForTof); + if (posDaughter.p() <= tofThreshold) { + this->evaluateObservable(kPosDauTpcPion, posDaughter.tpcNSigmaPi()); + this->evaluateObservable(kPosDauTofPion, posDaughter.tofNSigmaPi()); + this->evaluateObservable(kPosDauTpctofPion, std::hypot(posDaughter.tpcNSigmaPi(), posDaughter.tofNSigmaPi())); + this->evaluateObservable(kPosDauTpcKaon, posDaughter.tpcNSigmaKa()); + this->evaluateObservable(kPosDauTofKaon, posDaughter.tofNSigmaKa()); + this->evaluateObservable(kPosDauTpctofKaon, std::hypot(posDaughter.tpcNSigmaKa(), posDaughter.tofNSigmaKa())); + } else if (posDaughter.p() > tofThreshold && posDaughter.hasTOF()) { + this->evaluateObservable(kPosDauTofPion, posDaughter.tofNSigmaPi()); + this->evaluateObservable(kPosDauTpctofPion, std::hypot(posDaughter.tpcNSigmaPi(), posDaughter.tofNSigmaPi())); + this->evaluateObservable(kPosDauTofKaon, posDaughter.tofNSigmaKa()); + this->evaluateObservable(kPosDauTpctofKaon, std::hypot(posDaughter.tpcNSigmaKa(), posDaughter.tofNSigmaKa())); + if (this->passesOptionalSelection(kPosDauTofPion) || + this->passesOptionalSelection(kPosDauTpctofPion) || + this->passesOptionalSelection(kPosDauTofKaon) || + this->passesOptionalSelection(kPosDauTpctofKaon)) { + this->evaluateObservable(kPosDauTpcPion, posDaughter.tpcNSigmaPi()); + this->evaluateObservable(kPosDauTpcKaon, posDaughter.tpcNSigmaKa()); + } + } // negative daughter selections this->evaluateObservable(kNegDauMinMomForTof, negDaughter.p()); this->evaluateObservable(kNegDauPtMin, negDaughter.pt()); this->evaluateObservable(kNegDauPtMax, negDaughter.pt()); - this->evaluateObservable(kNegDauTpcPion, negDaughter.tpcNSigmaPi()); - this->evaluateObservable(kNegDauTofPion, negDaughter.tofNSigmaPi()); - this->evaluateObservable(kNegDauTpctofPion, std::hypot(negDaughter.tpcNSigmaPi(), negDaughter.tofNSigmaPi())); - - this->evaluateObservable(kNegDauTpcKaon, negDaughter.tpcNSigmaKa()); - this->evaluateObservable(kNegDauTofKaon, negDaughter.tofNSigmaKa()); - this->evaluateObservable(kNegDauTpctofKaon, std::hypot(negDaughter.tpcNSigmaKa(), negDaughter.tofNSigmaKa())); + tofThreshold = this->getLoosestSelection(kNegDauMinMomForTof); + if (negDaughter.p() < tofThreshold) { + this->evaluateObservable(kNegDauTpcPion, negDaughter.tpcNSigmaPi()); + this->evaluateObservable(kNegDauTofPion, negDaughter.tofNSigmaPi()); + this->evaluateObservable(kNegDauTpctofPion, std::hypot(negDaughter.tpcNSigmaPi(), negDaughter.tofNSigmaPi())); + this->evaluateObservable(kNegDauTpcKaon, negDaughter.tpcNSigmaKa()); + this->evaluateObservable(kNegDauTofKaon, negDaughter.tofNSigmaKa()); + this->evaluateObservable(kNegDauTpctofKaon, std::hypot(negDaughter.tpcNSigmaKa(), negDaughter.tofNSigmaKa())); + } else if (negDaughter.p() > tofThreshold && negDaughter.hasTOF()) { + this->evaluateObservable(kNegDauTofPion, negDaughter.tofNSigmaPi()); + this->evaluateObservable(kNegDauTpctofPion, std::hypot(negDaughter.tpcNSigmaPi(), negDaughter.tofNSigmaPi())); + this->evaluateObservable(kNegDauTofKaon, negDaughter.tofNSigmaKa()); + this->evaluateObservable(kNegDauTpctofKaon, std::hypot(negDaughter.tpcNSigmaKa(), negDaughter.tofNSigmaKa())); + if (this->passesOptionalSelection(kNegDauTofPion) || + this->passesOptionalSelection(kNegDauTpctofPion) || + this->passesOptionalSelection(kNegDauTofKaon) || + this->passesOptionalSelection(kNegDauTpctofKaon)) { + this->evaluateObservable(kNegDauTpcPion, negDaughter.tpcNSigmaPi()); + this->evaluateObservable(kNegDauTpcKaon, negDaughter.tpcNSigmaKa()); + } + } - this->assembleBitmask(); + this->assembleBitmask(); }; - bool checkHypothesis() - { - if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kRho0)) { - return (this->passesOptionalSelection(kPosDauTpcPion) || this->passesOptionalSelection(kPosDauTofPion) || this->passesOptionalSelection(kPosDauTpctofPion)) && - (this->passesOptionalSelection(kNegDauTpcPion) || this->passesOptionalSelection(kNegDauTofPion) || this->passesOptionalSelection(kNegDauTpctofPion)); - } - if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kPhi)) { - return (this->passesOptionalSelection(kPosDauTpcKaon) || this->passesOptionalSelection(kPosDauTofKaon) || this->passesOptionalSelection(kPosDauTpctofKaon)) && - (this->passesOptionalSelection(kNegDauTpcKaon) || this->passesOptionalSelection(kNegDauTofKaon) || this->passesOptionalSelection(kNegDauTpctofKaon)); - } - if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0)) { - return (this->passesOptionalSelection(kPosDauTpcKaon) || this->passesOptionalSelection(kPosDauTofKaon) || this->passesOptionalSelection(kPosDauTpctofKaon)) && - (this->passesOptionalSelection(kNegDauTpcPion) || this->passesOptionalSelection(kNegDauTofPion) || this->passesOptionalSelection(kNegDauTpctofPion)); - } - if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kKstar0Bar)) { - return (this->passesOptionalSelection(kPosDauTpcPion) || this->passesOptionalSelection(kPosDauTofPion) || this->passesOptionalSelection(kPosDauTpctofPion)) && - (this->passesOptionalSelection(kNegDauTpcKaon) || this->passesOptionalSelection(kNegDauTofKaon) || this->passesOptionalSelection(kNegDauTpctofKaon)); - } - return false; - } - protected: // (cached) kinematic variables of the resonance float mPt = 0.f; @@ -460,7 +467,7 @@ struct ConfTwoTrackResonanceTables : o2::framework::ConfigurableGroup { o2::framework::Configurable produceRho0Masks{"produceRho0Masks", -1, "Produce Rho0Masks (-1: auto; 0 off; 1 on)"}; }; -template +template class TwoTrackResonanceBuilder { public: @@ -468,9 +475,8 @@ class TwoTrackResonanceBuilder ~TwoTrackResonanceBuilder() = default; template - void init(T1& config, T2& filter, T3& daughterFilter, T4& table, T5 initContext) + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& daughterFilter, T4& table, T5 initContext) { - mTwoTrackResonanceSelection.configure(config, filter, daughterFilter); if constexpr (modes::isEqual(resoType, modes::TwoTrackResonance::kPhi)) { LOG(info) << "Initialize femto Phi builder..."; mProducePhis = utils::enableTable("FPhis_001", table.producePhis.value, initContext); @@ -494,10 +500,13 @@ class TwoTrackResonanceBuilder if (mProducePhis || mProducePhiMasks || mProduceKstar0s || mProduceKstar0Masks || mProduceRho0s || mProduceRho0Masks) { mFillAnyTable = true; - mTwoTrackResonanceSelection.printSelections(twoTrackResonanceSelsName, twoTrackResonanceSelsToString); } else { - LOG(info) << "No tables configured"; + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; } + mTwoTrackResonanceSelection.configure(registry, config, filter, daughterFilter); + mTwoTrackResonanceSelection.printSelections(TwoTrackResonanceSelsName); LOG(info) << "Initialization done..."; } @@ -507,11 +516,8 @@ class TwoTrackResonanceBuilder if (!mFillAnyTable) { return; } - // combinations object ? - for (auto const& positiveTrack : groupPositiveTracks) { - for (auto const& negativeTrack : groupNegativeTracks) { - this->fillResonance(collisionProducts, trackProducts, resonanceProducts, positiveTrack, negativeTrack, trackBuilder, indexMap); - } + for (auto const& [positiveTrack, negativeTrack] : o2::soa::combinations(o2::soa::CombinationsFullIndexPolicy(groupPositiveTracks, groupNegativeTracks))) { + this->fillResonance(collisionProducts, trackProducts, resonanceProducts, positiveTrack, negativeTrack, trackBuilder, indexMap); } } @@ -519,14 +525,11 @@ class TwoTrackResonanceBuilder void fillResonance(T1& collisionProducts, T2& trackProducts, T3& resonanceProducts, T4 const& posDaughter, T4 const& negDaughter, T5& trackBuilder, T6& indexMap) { - mTwoTrackResonanceSelection.applySelections(posDaughter, negDaughter); // for resonances selection are only applied to daughter tracks - if (!mTwoTrackResonanceSelection.hasTofAboveThreshold(posDaughter, negDaughter) || !mTwoTrackResonanceSelection.passesAllRequiredSelections()) { - return; - } mTwoTrackResonanceSelection.reconstructResonance(posDaughter, negDaughter); - if (!mTwoTrackResonanceSelection.checkFilters() || !mTwoTrackResonanceSelection.checkHypothesis()) { + if (!mTwoTrackResonanceSelection.checkCandidate()) { return; } + mTwoTrackResonanceSelection.applySelections(posDaughter, negDaughter); // for resonances selection are only applied to daughter tracks int64_t posDaughterIndex = 0; int64_t negDaughterIndex = 0; posDaughterIndex = trackBuilder.template getDaughterIndex(posDaughter, trackProducts, collisionProducts, indexMap); @@ -595,7 +598,7 @@ class TwoTrackResonanceBuilder } private: - TwoTrackResonanceSelection mTwoTrackResonanceSelection; + TwoTrackResonanceSelection mTwoTrackResonanceSelection; bool mFillAnyTable = false; bool mProducePhis = false; bool mProducePhiMasks = false; diff --git a/PWGCF/Femto/Core/v0Builder.h b/PWGCF/Femto/Core/v0Builder.h index b0a567643b9..d015686dee5 100644 --- a/PWGCF/Femto/Core/v0Builder.h +++ b/PWGCF/Femto/Core/v0Builder.h @@ -132,7 +132,7 @@ using ConfK0shortSelection1 = ConfK0shortSelection; using ConfK0shortSelection2 = ConfK0shortSelection; /// The different selections for v0s -enum V0Seles { +enum V0Sels { // selections for lambdas kCpaMin, ///< Min. CPA (cosine pointing angle) kDcaDaughMax, ///< Max. DCA of the daughters at decay vertex @@ -154,8 +154,11 @@ enum V0Seles { kV0SelsMax }; -const char v0SelsName[] = "K0short selection object"; -const std::unordered_map v0SelsToStrings = { +constexpr char LambdaSelHistName[] = "hLambdaSelection"; +constexpr char AntilambdaSelHistName[] = "hAntiLambdaSelection"; +constexpr char K0shortSelHistName[] = "hK0shortSelection"; +constexpr char V0SelsName[] = "V0 selection object"; +const std::unordered_map v0SelectionNames = { {kCpaMin, "Min. CPA (cosine pointing angle)"}, {kDcaDaughMax, "Max. DCA of the daughters at decay vertex"}, {kDecayVtxMax, "Max. distance of decay vertex in x,y,z"}, @@ -173,7 +176,7 @@ const std::unordered_map v0SelsToStrings = { /// \class FemtoDreamTrackCuts /// \brief Cut class to contain and execute all cuts applied to tracks -template +template class V0Selection : public BaseSelection { public: @@ -181,7 +184,7 @@ class V0Selection : public BaseSelection - void configure(T1& config, T2& filter) + void configure(o2::framework::HistogramRegistry* registry, T1& config, T2& filter) { mPtMin = filter.ptMin.value; mPtMax = filter.ptMax.value; @@ -197,13 +200,13 @@ class V0Selection : public BaseSelectionaddSelection(config.posDauTpcProton.value, kPosDaughTpcProton, limits::kAbsUpperLimit, true, true); - this->addSelection(config.negDauTpcPion.value, kNegDaughTpcPion, limits::kAbsUpperLimit, true, true); + this->addSelection(kPosDaughTpcProton, v0SelectionNames.at(kPosDaughTpcProton), config.posDauTpcProton.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kNegDaughTpcPion, v0SelectionNames.at(kNegDaughTpcPion), config.negDauTpcPion.value, limits::kAbsUpperLimit, true, true, false); } if constexpr (modes::isEqual(v0Type, modes::V0::kAntiLambda)) { - this->addSelection(config.posDauTpcPion.value, kPosDaughTpcPion, limits::kAbsUpperLimit, true, true); - this->addSelection(config.negDauTpcProton.value, kNegDaughTpcProton, limits::kAbsUpperLimit, true, true); + this->addSelection(kPosDaughTpcPion, v0SelectionNames.at(kPosDaughTpcPion), config.posDauTpcPion.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kNegDaughTpcProton, v0SelectionNames.at(kNegDaughTpcProton), config.negDauTpcProton.value, limits::kAbsUpperLimit, true, true, false); } } if constexpr (modes::isEqual(v0Type, modes::V0::kK0short)) { @@ -211,17 +214,19 @@ class V0Selection : public BaseSelectionaddSelection(config.posDauTpcPion.value, kPosDaughTpcPion, limits::kAbsUpperLimit, true, true); - this->addSelection(config.negDauTpcPion.value, kNegDaughTpcPion, limits::kAbsUpperLimit, true, true); + this->addSelection(kPosDaughTpcPion, v0SelectionNames.at(kPosDaughTpcPion), config.posDauTpcPion.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kNegDaughTpcPion, v0SelectionNames.at(kNegDaughTpcPion), config.negDauTpcPion.value, limits::kAbsUpperLimit, true, true, false); } - this->addSelection(config.dcaDauMax.value, kDcaDaughMax, limits::kAbsUpperLimit, true, true); - this->addSelection(config.cpaMin.value, kCpaMin, limits::kLowerLimit, true, true); - this->addSelection(config.transRadMin.value, kTransRadMin, limits::kLowerLimit, true, true); - this->addSelection(config.transRadMax.value, kTransRadMax, limits::kUpperLimit, true, true); - this->addSelection(config.dauAbsEtaMax.value, kDauAbsEtaMax, limits::kAbsUpperLimit, true, true); - this->addSelection(config.dauDcaMin.value, kDauDcaMin, limits::kAbsLowerFunctionLimit, true, true); - this->addSelection(config.dauTpcClustersMin.value, kDauTpcClsMin, limits::kLowerLimit, true, true); + this->addSelection(kDcaDaughMax, v0SelectionNames.at(kDcaDaughMax), config.dcaDauMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kCpaMin, v0SelectionNames.at(kCpaMin), config.cpaMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kTransRadMin, v0SelectionNames.at(kTransRadMin), config.transRadMin.value, limits::kLowerLimit, true, true, false); + this->addSelection(kTransRadMax, v0SelectionNames.at(kTransRadMax), config.transRadMax.value, limits::kUpperLimit, true, true, false); + this->addSelection(kDauAbsEtaMax, v0SelectionNames.at(kDauAbsEtaMax), config.dauAbsEtaMax.value, limits::kAbsUpperLimit, true, true, false); + this->addSelection(kDauDcaMin, v0SelectionNames.at(kDauDcaMin), config.dauDcaMin.value, limits::kAbsLowerFunctionLimit, true, true, false); + this->addSelection(kDauTpcClsMin, v0SelectionNames.at(kDauTpcClsMin), config.dauTpcClustersMin.value, limits::kLowerLimit, true, true, false); + + this->setupContainers(registry); } template @@ -259,34 +264,35 @@ class V0Selection : public BaseSelectionevaluateObservable(kNegDaughTpcPion, negDaughter.tpcNSigmaPi()); this->evaluateObservable(kNegDaughTpcProton, negDaughter.tpcNSigmaPr()); - this->assembleBitmask(); - } - - template - bool checkFilters(const T& v0) const - { - return ((v0.pt() > mPtMin && v0.pt() < mPtMax) && - (v0.eta() > mEtaMin && v0.eta() < mEtaMax) && - (v0.phi() > mPhiMin && v0.phi() < mPhiMax)); + this->assembleBitmask(); } template - bool checkHypothesis(T const& v0candidate) const + bool checkCandidate(const T& v0) const { - // no need to check PID of the daughters here, they are set as minimal cuts + // check kinematics first + const bool kinematicsOK = + (v0.pt() > mPtMin && v0.pt() < mPtMax) && + (v0.eta() > mEtaMin && v0.eta() < mEtaMax) && + (v0.phi() > mPhiMin && v0.phi() < mPhiMax); + if (!kinematicsOK) { + return false; + } + // now check mass hypothesis if constexpr (modes::isEqual(v0Type, modes::V0::kLambda)) { - return (v0candidate.mLambda() > mMassLambdaLowerLimit && v0candidate.mLambda() < mMassLambdaUpperLimit) && // inside Lambda window - (v0candidate.mK0Short() < mMassK0shortLowerLimit || v0candidate.mK0Short() > mMassK0shortUpperLimit); // outside K0short window + return (v0.mLambda() > mMassLambdaLowerLimit && v0.mLambda() < mMassLambdaUpperLimit) && // inside Λ + (v0.mK0Short() < mMassK0shortLowerLimit || v0.mK0Short() > mMassK0shortUpperLimit); // outside K0s } + if constexpr (modes::isEqual(v0Type, modes::V0::kAntiLambda)) { - return // check PID for daughters - (v0candidate.mAntiLambda() > mMassLambdaLowerLimit && v0candidate.mAntiLambda() < mMassLambdaUpperLimit) && // inside AntiLambda window - (v0candidate.mK0Short() < mMassK0shortLowerLimit || v0candidate.mK0Short() > mMassK0shortUpperLimit); // outside K0short window + return (v0.mAntiLambda() > mMassLambdaLowerLimit && v0.mAntiLambda() < mMassLambdaUpperLimit) && // inside Λbar + (v0.mK0Short() < mMassK0shortLowerLimit || v0.mK0Short() > mMassK0shortUpperLimit); // outside K0s } + if constexpr (modes::isEqual(v0Type, modes::V0::kK0short)) { - return (v0candidate.mK0Short() > mMassK0shortLowerLimit && v0candidate.mK0Short() < mMassK0shortUpperLimit) && // inside K0short window - (v0candidate.mLambda() < mMassLambdaLowerLimit || v0candidate.mLambda() > mMassLambdaUpperLimit) && // outside Lambda window - (v0candidate.mAntiLambda() < mMassLambdaLowerLimit || v0candidate.mAntiLambda() > mMassLambdaUpperLimit); // outside AntiLambda window + return (v0.mK0Short() > mMassK0shortLowerLimit && v0.mK0Short() < mMassK0shortUpperLimit) && // inside K0s + (v0.mLambda() < mMassLambdaLowerLimit || v0.mLambda() > mMassLambdaUpperLimit) && // outside Λ + (v0.mAntiLambda() < mMassLambdaLowerLimit || v0.mAntiLambda() > mMassLambdaUpperLimit); // outside Λbar } return false; } @@ -326,7 +332,7 @@ struct ConfV0Tables : o2::framework::ConfigurableGroup { o2::framework::Configurable produceK0shortExtras{"produceK0shortExtras", -1, "Produce K0shortExtras (-1: auto; 0 off; 1 on)"}; }; -template +template class V0Builder { public: @@ -334,9 +340,8 @@ class V0Builder ~V0Builder() = default; template - void init(T1& config, T2& filter, T3& table, T4& initContext) + void init(o2::framework::HistogramRegistry* registry, T1& config, T2& filter, T3& table, T4& initContext) { - mV0Selection.configure(config, filter); if constexpr (modes::isEqual(v0Type, modes::V0::kLambda) || modes::isEqual(v0Type, modes::V0::kAntiLambda)) { if constexpr (modes::isEqual(v0Type, modes::V0::kLambda)) { LOG(info) << "Initialize femto Lambda builder..."; @@ -356,10 +361,13 @@ class V0Builder } if (mProduceLambdas || mProduceLambdaMasks || mProduceLambdaExtras || mProduceK0shorts || mProduceK0shortMasks || mProduceK0shortExtras) { mFillAnyTable = true; - mV0Selection.printSelections(v0SelsName, v0SelsToStrings); } else { - LOG(info) << "No tables configured"; + LOG(info) << "No tables configured, Selection object will not be configured..."; + LOG(info) << "Initialization done..."; + return; } + mV0Selection.configure(registry, config, filter); + mV0Selection.printSelections(V0SelsName); LOG(info) << "Initialization done..."; } @@ -372,11 +380,11 @@ class V0Builder int64_t posDaughterIndex = 0; int64_t negDaughterIndex = 0; for (const auto& v0 : v0s) { - if (!mV0Selection.checkFilters(v0)) { + if (!mV0Selection.checkCandidate(v0)) { continue; } mV0Selection.applySelections(v0, tracks); - if (mV0Selection.passesAllRequiredSelections() && mV0Selection.checkHypothesis(v0)) { + if (mV0Selection.passesAllRequiredSelections()) { auto posDaughter = v0.template posTrack_as(); auto negDaughter = v0.template negTrack_as(); posDaughterIndex = trackBuilder.template getDaughterIndex(posDaughter, trackProducts, collisionProducts, indexMap); @@ -461,7 +469,7 @@ class V0Builder bool fillAnyTable() { return mFillAnyTable; } private: - V0Selection mV0Selection; + V0Selection mV0Selection; bool mFillAnyTable = false; bool mProduceLambdas = false; bool mProduceLambdaMasks = false; diff --git a/PWGCF/Femto/DataModel/FemtoTables.h b/PWGCF/Femto/DataModel/FemtoTables.h index 968a60b4b19..f5ffde686e9 100644 --- a/PWGCF/Femto/DataModel/FemtoTables.h +++ b/PWGCF/Femto/DataModel/FemtoTables.h @@ -163,7 +163,7 @@ DECLARE_SOA_COLUMN(ItsClusterSizes, itsClusterSizes, uint32_t); //! Its clu // tpc related information DECLARE_SOA_COLUMN(TpcSignal, tpcSignal, float); //! Tpc signal -DECLARE_SOA_COLUMN(TpcInnerParam, tpcInnerParam, bool); //! Momentum at inner wall of Tpc +DECLARE_SOA_COLUMN(TpcInnerParam, tpcInnerParam, float); //! Momentum at inner wall of Tpc DECLARE_SOA_COLUMN(TpcNClsFound, tpcNClsFound, uint8_t); //! Number of Tpc clusters DECLARE_SOA_COLUMN(TpcNClsCrossedRows, tpcNClsCrossedRows, uint8_t); //! Number of Tpc crossed rows DECLARE_SOA_DYNAMIC_COLUMN(TpcCrossedRowsOverFound, tpcCrossedRowsOverFound, //! Number of crossed rows over found Tpc clusters diff --git a/PWGCF/Femto/Macros/cutculator.py b/PWGCF/Femto/Macros/cutculator.py new file mode 100755 index 00000000000..4b522e4e220 --- /dev/null +++ b/PWGCF/Femto/Macros/cutculator.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 + +#!/usr/bin/env python3 + +# 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. + +"""! +@brief CutCulator (Compute bitmask for selecting particles in the Femto Framework) +@author Anton Riedel , Technical University of Munich +""" + +import ROOT +import argparse + +VALUE_DELIM = "___" +SECTION_DELIM = ":::" + + +def parse_bin_label(label): + """Parse a bin label into a dictionary.""" + result = {} + sections = label.split(SECTION_DELIM) + for sec in sections: + if VALUE_DELIM not in sec: + continue + key, value = sec.split(VALUE_DELIM, 1) + result[key] = value + return result + + +def ask_user_selection(group): + """Prompt user to select bin(s) for this selection group.""" + # Separate minimal and optional bins + minimal_bins = [b for b in group if b.get("MinimalCut", "0") == "1" and b.get("OptionalCut", "0") == "0"] + optional_bins = [b for b in group if b.get("OptionalCut", "0") == "1"] + + selected_bins = [] + + # Minimal selection (cannot skip, 0 = loosest minimal) + if minimal_bins: + print(f"\nSelection: {group[0].get('SelectionName', 'unknown')}") + for idx, b in enumerate(minimal_bins): + print(f" [{idx}] {b.get('Value', '')}") + while True: + sel_input = input("Enter index for minimal cut (0 = loosest minimal): ") + if sel_input.strip() == "": + sel_input = "0" + try: + sel_idx = int(sel_input) + if 0 <= sel_idx < len(minimal_bins): + selected_bins.append(minimal_bins[sel_idx]) + break + except ValueError: + pass + print("Invalid input. Please enter a valid index.") + + # Optional selection (can skip with 0) + if optional_bins: + print(f"Selection: {group[0].get('SelectionName', 'unknown')} (optional selection, 0 to skip)") + for idx, b in enumerate(optional_bins, start=1): + print(f" [{idx}] {b.get('Value', '')}") + while True: + sel_input = input("Enter indices separated by space (0 to skip): ") + if not sel_input.strip() or sel_input.strip() == "0": + break + try: + indices = [int(x) for x in sel_input.split()] + if all(0 <= i <= len(optional_bins) for i in indices): + for i in indices: + if i != 0: + selected_bins.append(optional_bins[i - 1]) + break + except ValueError: + pass + print("Invalid input. Please enter valid indices separated by space.") + + return selected_bins + + +def main(rootfile_path, tdir_path="femto-producer"): + print(f"Opening ROOT file: {rootfile_path}") + f = ROOT.TFile.Open(rootfile_path) + if not f: + print("Cannot open ROOT file") + return + + print(f"Accessing directory: {tdir_path}") + d = f.Get(tdir_path) + if not d: + print(f"Cannot access directory {tdir_path}") + return + + histograms = [k.GetName() for k in d.GetListOfKeys() if k.ReadObj().InheritsFrom("TH1")] + if not histograms: + print("No histograms found") + return + + print("\nHistograms found in directory:") + for i, hname in enumerate(histograms): + print(f" [{i}] {hname}") + hidx = int(input("\nSelect histogram index: ")) + hname = histograms[hidx] + hist = d.Get(hname) + nbins = hist.GetNbinsX() + print(f"\nUsing histogram: {hname}") + print(f"Histogram contains {nbins} bins.\n") + + # parse all bins, ignoring the last 2 special bins + bins = [] + for i in range(1, nbins - 2 + 1): + label = hist.GetXaxis().GetBinLabel(i) + if not label: + continue + bdict = parse_bin_label(label) + bdict["_bin_index"] = i + bins.append(bdict) + + # group by SelectionName + groups = {} + for b in bins: + sel_name = b.get("SelectionName", f"unknown_{b['_bin_index']}") + groups.setdefault(sel_name, []).append(b) + + selected_bins = [] + + for group in groups.values(): + res = ask_user_selection(group) + if res: + selected_bins.extend(res) + + # compute bitmask from selected bins + bitmask = 0 + for b in selected_bins: + pos = b.get("BitPosition", "") + if pos.upper() == "X": + continue + bitmask |= 1 << int(pos) + + print("\nFinal selected bitmask:") + print(f"Decimal: {bitmask}") + print(f"Binary: {bin(bitmask)}") + print(f"Hex: {hex(bitmask)}") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("rootfile", help="Path to ROOT file") + parser.add_argument("--dir", default="femto-producer", help="TDirectory path in ROOT file") + args = parser.parse_args() + main(args.rootfile, args.dir) diff --git a/PWGCF/Femto/TableProducer/femtoProducer.cxx b/PWGCF/Femto/TableProducer/femtoProducer.cxx index b6e0b021b4e..fa4779369d0 100644 --- a/PWGCF/Femto/TableProducer/femtoProducer.cxx +++ b/PWGCF/Femto/TableProducer/femtoProducer.cxx @@ -87,12 +87,12 @@ struct FemtoProducer { collisionbuilder::ConfCollisionFilters confCollisionFilters; collisionbuilder::ConfCollisionBits confCollisionBits; collisionbuilder::ConfCollisionRctFlags confCollisionRctFlags; - collisionbuilder::CollisionBuilder collisionBuilder; + collisionbuilder::CollisionBuilder collisionBuilder; // track builder trackbuilder::TrackBuilderProducts trackBuilderProducts; trackbuilder::ConfTrackTables confTrackTables; - trackbuilder::TrackBuilder trackBuilder; + trackbuilder::TrackBuilder trackBuilder; trackbuilder::ConfTrackBits confTrackBits; trackbuilder::ConfTrackFilters confTrackFilters; @@ -101,28 +101,28 @@ struct FemtoProducer { v0builder::ConfV0Tables confV0Tables; v0builder::ConfV0Filters confV0Filters; v0builder::ConfK0shortBits confK0shortBits; - v0builder::V0Builder k0shortBuilder; + v0builder::V0Builder k0shortBuilder; v0builder::ConfLambdaBits confLambdaBits; - v0builder::V0Builder lambdaBuilder; - v0builder::V0Builder antilambdaBuilder; + v0builder::V0Builder lambdaBuilder; + v0builder::V0Builder antilambdaBuilder; // cascade builder cascadebuilder::CascadeBuilderProducts cascadeBuilderProducts; cascadebuilder::ConfCascadeTables confCascadeTables; cascadebuilder::ConfCascadeFilters confCascadeFilters; cascadebuilder::ConfXiBits confXiBits; - cascadebuilder::CascadeBuilder xiBuilder; + cascadebuilder::CascadeBuilder xiBuilder; cascadebuilder::ConfOmegaBits confOmegaBits; - cascadebuilder::CascadeBuilder omegaBuilder; + cascadebuilder::CascadeBuilder omegaBuilder; // kink builder kinkbuilder::KinkBuilderProducts kinkBuilderProducts; kinkbuilder::ConfKinkTables confKinkTables; kinkbuilder::ConfKinkFilters confKinkFilters; kinkbuilder::ConfSigmaBits confSigmaBits; - kinkbuilder::KinkBuilder sigmaBuilder; + kinkbuilder::KinkBuilder sigmaBuilder; kinkbuilder::ConfSigmaPlusBits confSigmaPlusBits; - kinkbuilder::KinkBuilder sigmaPlusBuilder; + kinkbuilder::KinkBuilder sigmaPlusBuilder; // resonance daughter filters and partitions twotrackresonancebuilder::ConfTwoTrackResonanceDaughterFilters confResonanceDaughterFilters; @@ -145,14 +145,14 @@ struct FemtoProducer { twotrackresonancebuilder::ConfTwoTrackResonanceTables confTwoTrackResonanceTables; twotrackresonancebuilder::ConfRhoFilters confRhoFilters; twotrackresonancebuilder::ConfRho0Bits confRho0Bits; - twotrackresonancebuilder::TwoTrackResonanceBuilder rho0Builder; + twotrackresonancebuilder::TwoTrackResonanceBuilder rho0Builder; twotrackresonancebuilder::ConfPhiFilters confPhiFilters; twotrackresonancebuilder::ConfPhiBits confPhiBits; - twotrackresonancebuilder::TwoTrackResonanceBuilder phiBuilder; + twotrackresonancebuilder::TwoTrackResonanceBuilder phiBuilder; twotrackresonancebuilder::ConfKstarFilters confKstarFilters; twotrackresonancebuilder::ConfKstar0Bits confKstar0Bits; - twotrackresonancebuilder::TwoTrackResonanceBuilder kstar0Builder; - twotrackresonancebuilder::TwoTrackResonanceBuilder kstar0barBuilder; + twotrackresonancebuilder::TwoTrackResonanceBuilder kstar0Builder; + twotrackresonancebuilder::TwoTrackResonanceBuilder kstar0barBuilder; // histogramming // add histograms in next iteration @@ -172,29 +172,29 @@ struct FemtoProducer { ccdb->setCreatedNotAfter(now); // collision selection - collisionBuilder.init(confCollisionFilters, confCollisionBits, confCollisionRctFlags, confCcdb, confCollisionTables, context); + collisionBuilder.init(&hRegistry, confCollisionFilters, confCollisionBits, confCollisionRctFlags, confCcdb, confCollisionTables, context); // configure track builder - trackBuilder.init(confTrackBits, confTrackFilters, confTrackTables, context); + trackBuilder.init(&hRegistry, confTrackBits, confTrackFilters, confTrackTables, context); // configure v0 builder - k0shortBuilder.init(confK0shortBits, confV0Filters, confV0Tables, context); - lambdaBuilder.init(confLambdaBits, confV0Filters, confV0Tables, context); - antilambdaBuilder.init(confLambdaBits, confV0Filters, confV0Tables, context); + k0shortBuilder.init(&hRegistry, confK0shortBits, confV0Filters, confV0Tables, context); + lambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context); + antilambdaBuilder.init(&hRegistry, confLambdaBits, confV0Filters, confV0Tables, context); // configure kink builder - sigmaBuilder.init(confSigmaBits, confKinkFilters, confKinkTables, context); - sigmaPlusBuilder.init(confSigmaPlusBits, confKinkFilters, confKinkTables, context); + sigmaBuilder.init(&hRegistry, confSigmaBits, confKinkFilters, confKinkTables, context); + sigmaPlusBuilder.init(&hRegistry, confSigmaPlusBits, confKinkFilters, confKinkTables, context); // cascade selections - xiBuilder.init(confXiBits, confCascadeFilters, confCascadeTables, context); - omegaBuilder.init(confOmegaBits, confCascadeFilters, confCascadeTables, context); + xiBuilder.init(&hRegistry, confXiBits, confCascadeFilters, confCascadeTables, context); + omegaBuilder.init(&hRegistry, confOmegaBits, confCascadeFilters, confCascadeTables, context); // configure resonance selections - rho0Builder.init(confRho0Bits, confRhoFilters, confResonanceDaughterFilters, confTwoTrackResonanceTables, context); - phiBuilder.init(confPhiBits, confPhiFilters, confResonanceDaughterFilters, confTwoTrackResonanceTables, context); - kstar0Builder.init(confKstar0Bits, confKstarFilters, confResonanceDaughterFilters, confTwoTrackResonanceTables, context); - kstar0barBuilder.init(confKstar0Bits, confKstarFilters, confResonanceDaughterFilters, confTwoTrackResonanceTables, context); + rho0Builder.init(&hRegistry, confRho0Bits, confRhoFilters, confResonanceDaughterFilters, confTwoTrackResonanceTables, context); + phiBuilder.init(&hRegistry, confPhiBits, confPhiFilters, confResonanceDaughterFilters, confTwoTrackResonanceTables, context); + kstar0Builder.init(&hRegistry, confKstar0Bits, confKstarFilters, confResonanceDaughterFilters, confTwoTrackResonanceTables, context); + kstar0barBuilder.init(&hRegistry, confKstar0Bits, confKstarFilters, confResonanceDaughterFilters, confTwoTrackResonanceTables, context); if ((xiBuilder.fillAnyTable() || omegaBuilder.fillAnyTable()) && (!doprocessTracksV0sCascadesRun3pp && !doprocessTracksV0sCascadesKinksRun3pp)) { LOG(fatal) << "At least one cascade table is enabled, but wrong process function is enabled. Breaking...";